Merge branch 'feature/merge-updates' from cbsnexora into feature/ui-reports-product
Conflict resolutions: - src/main.scss: kept both new dialog backdrops (CIC/PSL/ALM/CIMS/CKYC/Acct) and incoming dialog panel styles (.abd-dialog-panel, .arc-dialog-panel) - client-general-step.component.html/.ts: kept HEAD's agriculture/non-agri income fields, education/degree/religion/social-category dropdowns, and inject() refactor matching the incoming pattern - kyc-management.component.html/.scss/.ts: kept HEAD's Home Visit terminology, takeAction() method, hv-* and vkyc-* dialog styles; accepted incoming .km-warn-d style addition Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -120,284 +120,277 @@
|
||||
|
||||
<!-- SECTION 1: ZIP Details -->
|
||||
@if (activeSection === 'zip') {
|
||||
<div class="cv-section">
|
||||
<div class="cv-section__head">
|
||||
<div class="cv-section__title">
|
||||
<fa-icon icon="file-archive"></fa-icon>
|
||||
<div>
|
||||
<h3>ZIP Batch Details</h3>
|
||||
<p>CERSAI ZIP archives grouped by creation date</p>
|
||||
</div>
|
||||
</div>
|
||||
<span class="cv-badge cv-badge--blue">Phase 2</span>
|
||||
</div>
|
||||
|
||||
@if (loading) {
|
||||
<div class="cv-state cv-state--loading">
|
||||
<fa-icon icon="spinner" animation="spin"></fa-icon>
|
||||
<span>Loading batches…</span>
|
||||
</div>
|
||||
}
|
||||
|
||||
@if (error && !loading) {
|
||||
<div class="cv-state cv-state--error">
|
||||
<fa-icon icon="exclamation-triangle"></fa-icon>
|
||||
<span>{{ error }}</span>
|
||||
</div>
|
||||
}
|
||||
|
||||
@if (!loading && !error && groups.length === 0) {
|
||||
<div class="cv-state cv-state--empty">
|
||||
<fa-icon icon="file-archive" size="2x"></fa-icon>
|
||||
<h4>No batches found</h4>
|
||||
<p>Create a batch from the Record Master to see it here.</p>
|
||||
</div>
|
||||
}
|
||||
|
||||
@if (!loading && groups.length > 0) {
|
||||
<div class="cv-groups">
|
||||
@for (group of groups; track group.dateLabel) {
|
||||
<div class="cv-group" [class.cv-group--open]="group.expanded">
|
||||
<button class="cv-group__head" (click)="toggleGroup(group)">
|
||||
<div class="cv-group__left">
|
||||
<fa-icon [icon]="group.expanded ? 'chevron-down' : 'chevron-right'" class="cv-group__chev"></fa-icon>
|
||||
<div class="cv-group__date-pill">
|
||||
<fa-icon icon="calendar-check"></fa-icon>
|
||||
<span>{{ group.dateLabel }}</span>
|
||||
</div>
|
||||
<div class="cv-group__summary">
|
||||
<span><strong>{{ group.batchCount }}</strong> batches</span>
|
||||
<span class="cv-group__sep">·</span>
|
||||
<span><strong>{{ group.totalRecordCount }}</strong> records</span>
|
||||
<span class="cv-group__sep">·</span>
|
||||
<span>{{ formatBytes(group.totalSizeBytes) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="cv-group__right">
|
||||
@if (group.pendingCount > 0) {
|
||||
<span class="cv-pill cv-pill--amber">
|
||||
<fa-icon icon="hourglass-half"></fa-icon> {{ group.pendingCount }} pending
|
||||
</span>
|
||||
}
|
||||
@if (group.uploadedCount > 0) {
|
||||
<span class="cv-pill cv-pill--green">
|
||||
<fa-icon icon="check-double"></fa-icon> {{ group.uploadedCount }} uploaded
|
||||
</span>
|
||||
}
|
||||
</div>
|
||||
</button>
|
||||
|
||||
@if (group.expanded) {
|
||||
<div class="cv-group__body">
|
||||
<table class="cv-batches">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Batch ID</th>
|
||||
<th>Filename</th>
|
||||
<th class="cv-batches__num">Records</th>
|
||||
<th class="cv-batches__num">Size</th>
|
||||
<th>Status</th>
|
||||
<th class="cv-batches__actions">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@for (batch of group.batches; track trackBatch($index, batch)) {
|
||||
<tr>
|
||||
<td class="cv-batches__bid">
|
||||
<fa-icon icon="tag"></fa-icon>
|
||||
<span>{{ batch.batchId }}</span>
|
||||
</td>
|
||||
<td class="cv-batches__file">
|
||||
<fa-icon icon="file-archive"></fa-icon>
|
||||
<span>{{ batch.filename }}</span>
|
||||
</td>
|
||||
<td class="cv-batches__num">{{ batch.recordCount }}</td>
|
||||
<td class="cv-batches__num">{{ formatBytes(batch.sizeBytes) }}</td>
|
||||
<td>
|
||||
@if (batch.uploadedToCersai) {
|
||||
<span class="cv-pill cv-pill--green">
|
||||
<fa-icon icon="check-circle"></fa-icon> Uploaded
|
||||
</span>
|
||||
} @else {
|
||||
<span class="cv-pill cv-pill--amber">
|
||||
<fa-icon icon="hourglass-half"></fa-icon> Pending
|
||||
</span>
|
||||
}
|
||||
</td>
|
||||
<td class="cv-batches__actions">
|
||||
<button
|
||||
class="cv-action-btn"
|
||||
(click)="openManifest(batch, $event)"
|
||||
title="View manifest"
|
||||
>
|
||||
<fa-icon icon="eye"></fa-icon>
|
||||
</button>
|
||||
@if (batch.documentId) {
|
||||
<button
|
||||
class="cv-action-btn cv-action-btn--primary"
|
||||
(click)="downloadZip(batch, $event)"
|
||||
title="Download ZIP"
|
||||
>
|
||||
<fa-icon icon="download"></fa-icon>
|
||||
</button>
|
||||
}
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
}
|
||||
<div class="cv-section">
|
||||
<div class="cv-section__head">
|
||||
<div class="cv-section__title">
|
||||
<fa-icon icon="file-archive"></fa-icon>
|
||||
<div>
|
||||
<h3>ZIP Batch Details</h3>
|
||||
<p>CERSAI ZIP archives grouped by creation date</p>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
<span class="cv-badge cv-badge--blue">Phase 2</span>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
@if (loading) {
|
||||
<div class="cv-state cv-state--loading">
|
||||
<fa-icon icon="spinner" animation="spin"></fa-icon>
|
||||
<span>Loading batches…</span>
|
||||
</div>
|
||||
}
|
||||
|
||||
@if (error && !loading) {
|
||||
<div class="cv-state cv-state--error">
|
||||
<fa-icon icon="exclamation-triangle"></fa-icon>
|
||||
<span>{{ error }}</span>
|
||||
</div>
|
||||
}
|
||||
|
||||
@if (!loading && !error && groups.length === 0) {
|
||||
<div class="cv-state cv-state--empty">
|
||||
<fa-icon icon="file-archive" size="2x"></fa-icon>
|
||||
<h4>No batches found</h4>
|
||||
<p>Create a batch from the Record Master to see it here.</p>
|
||||
</div>
|
||||
}
|
||||
|
||||
@if (!loading && groups.length > 0) {
|
||||
<div class="cv-groups">
|
||||
@for (group of groups; track group.dateLabel) {
|
||||
<div class="cv-group" [class.cv-group--open]="group.expanded">
|
||||
<button class="cv-group__head" (click)="toggleGroup(group)">
|
||||
<div class="cv-group__left">
|
||||
<fa-icon [icon]="group.expanded ? 'chevron-down' : 'chevron-right'" class="cv-group__chev"></fa-icon>
|
||||
<div class="cv-group__date-pill">
|
||||
<fa-icon icon="calendar-check"></fa-icon>
|
||||
<span>{{ group.dateLabel }}</span>
|
||||
</div>
|
||||
<div class="cv-group__summary">
|
||||
<span
|
||||
><strong>{{ group.batchCount }}</strong> batches</span
|
||||
>
|
||||
<span class="cv-group__sep">·</span>
|
||||
<span
|
||||
><strong>{{ group.totalRecordCount }}</strong> records</span
|
||||
>
|
||||
<span class="cv-group__sep">·</span>
|
||||
<span>{{ formatBytes(group.totalSizeBytes) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="cv-group__right">
|
||||
@if (group.pendingCount > 0) {
|
||||
<span class="cv-pill cv-pill--amber">
|
||||
<fa-icon icon="hourglass-half"></fa-icon> {{ group.pendingCount }} pending
|
||||
</span>
|
||||
}
|
||||
@if (group.uploadedCount > 0) {
|
||||
<span class="cv-pill cv-pill--green">
|
||||
<fa-icon icon="check-double"></fa-icon> {{ group.uploadedCount }} uploaded
|
||||
</span>
|
||||
}
|
||||
</div>
|
||||
</button>
|
||||
|
||||
@if (group.expanded) {
|
||||
<div class="cv-group__body">
|
||||
<table class="cv-batches">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Batch ID</th>
|
||||
<th>Filename</th>
|
||||
<th class="cv-batches__num">Records</th>
|
||||
<th class="cv-batches__num">Size</th>
|
||||
<th>Status</th>
|
||||
<th class="cv-batches__actions">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@for (batch of group.batches; track trackBatch($index, batch)) {
|
||||
<tr>
|
||||
<td class="cv-batches__bid">
|
||||
<fa-icon icon="tag"></fa-icon>
|
||||
<span>{{ batch.batchId }}</span>
|
||||
</td>
|
||||
<td class="cv-batches__file">
|
||||
<fa-icon icon="file-archive"></fa-icon>
|
||||
<span>{{ batch.filename }}</span>
|
||||
</td>
|
||||
<td class="cv-batches__num">{{ batch.recordCount }}</td>
|
||||
<td class="cv-batches__num">{{ formatBytes(batch.sizeBytes) }}</td>
|
||||
<td>
|
||||
@if (batch.uploadedToCersai) {
|
||||
<span class="cv-pill cv-pill--green">
|
||||
<fa-icon icon="check-circle"></fa-icon> Uploaded
|
||||
</span>
|
||||
} @else {
|
||||
<span class="cv-pill cv-pill--amber">
|
||||
<fa-icon icon="hourglass-half"></fa-icon> Pending
|
||||
</span>
|
||||
}
|
||||
</td>
|
||||
<td class="cv-batches__actions">
|
||||
<button class="cv-action-btn" (click)="openManifest(batch, $event)" title="View manifest">
|
||||
<fa-icon icon="eye"></fa-icon>
|
||||
</button>
|
||||
@if (batch.documentId) {
|
||||
<button
|
||||
class="cv-action-btn cv-action-btn--primary"
|
||||
(click)="downloadZip(batch, $event)"
|
||||
title="Download ZIP"
|
||||
>
|
||||
<fa-icon icon="download"></fa-icon>
|
||||
</button>
|
||||
}
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
|
||||
<!-- SECTION 2: SFTP Upload + API Match -->
|
||||
@if (activeSection === 'sftp') {
|
||||
<div class="cv-section">
|
||||
<div class="cv-section__head">
|
||||
<div class="cv-section__title">
|
||||
<fa-icon icon="cloud-upload-alt"></fa-icon>
|
||||
<div>
|
||||
<h3>SFTP Upload & ACK · API Match</h3>
|
||||
<p>CERSAI SFTP transfer history with ACK receipts and per-batch logs</p>
|
||||
<div class="cv-section">
|
||||
<div class="cv-section__head">
|
||||
<div class="cv-section__title">
|
||||
<fa-icon icon="cloud-upload-alt"></fa-icon>
|
||||
<div>
|
||||
<h3>SFTP Upload & ACK · API Match</h3>
|
||||
<p>CERSAI SFTP transfer history with ACK receipts and per-batch logs</p>
|
||||
</div>
|
||||
</div>
|
||||
<span class="cv-badge cv-badge--amber">Phase 3-4</span>
|
||||
</div>
|
||||
|
||||
<div class="cv-sftp-kpis">
|
||||
<div class="cv-sftp-kpi cv-sftp-kpi--green">
|
||||
<div class="cv-sftp-kpi__icon"><fa-icon icon="check-circle"></fa-icon></div>
|
||||
<div>
|
||||
<div class="cv-sftp-kpi__value">{{ sftpSuccessCount }}</div>
|
||||
<div class="cv-sftp-kpi__label">Uploaded successfully</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="cv-sftp-kpi cv-sftp-kpi--red">
|
||||
<div class="cv-sftp-kpi__icon"><fa-icon icon="times-circle"></fa-icon></div>
|
||||
<div>
|
||||
<div class="cv-sftp-kpi__value">{{ sftpFailedCount }}</div>
|
||||
<div class="cv-sftp-kpi__label">Failed</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="cv-sftp-kpi cv-sftp-kpi--amber">
|
||||
<div class="cv-sftp-kpi__icon"><fa-icon icon="hourglass-half"></fa-icon></div>
|
||||
<div>
|
||||
<div class="cv-sftp-kpi__value">{{ sftpPendingCount }}</div>
|
||||
<div class="cv-sftp-kpi__label">Pending upload</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<span class="cv-badge cv-badge--amber">Phase 3-4</span>
|
||||
</div>
|
||||
|
||||
<div class="cv-sftp-kpis">
|
||||
<div class="cv-sftp-kpi cv-sftp-kpi--green">
|
||||
<div class="cv-sftp-kpi__icon"><fa-icon icon="check-circle"></fa-icon></div>
|
||||
<div>
|
||||
<div class="cv-sftp-kpi__value">{{ sftpSuccessCount }}</div>
|
||||
<div class="cv-sftp-kpi__label">Uploaded successfully</div>
|
||||
@if (loading) {
|
||||
<div class="cv-state cv-state--loading">
|
||||
<fa-icon icon="spinner" animation="spin"></fa-icon>
|
||||
<span>Loading logs…</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="cv-sftp-kpi cv-sftp-kpi--red">
|
||||
<div class="cv-sftp-kpi__icon"><fa-icon icon="times-circle"></fa-icon></div>
|
||||
<div>
|
||||
<div class="cv-sftp-kpi__value">{{ sftpFailedCount }}</div>
|
||||
<div class="cv-sftp-kpi__label">Failed</div>
|
||||
}
|
||||
|
||||
@if (!loading && sftpLogs.length === 0) {
|
||||
<div class="cv-state cv-state--empty">
|
||||
<fa-icon icon="cloud-upload-alt" size="2x"></fa-icon>
|
||||
<h4>No SFTP upload history yet</h4>
|
||||
<p>Upload a batch from the Record Master to see CERSAI transfer logs here.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="cv-sftp-kpi cv-sftp-kpi--amber">
|
||||
<div class="cv-sftp-kpi__icon"><fa-icon icon="hourglass-half"></fa-icon></div>
|
||||
<div>
|
||||
<div class="cv-sftp-kpi__value">{{ sftpPendingCount }}</div>
|
||||
<div class="cv-sftp-kpi__label">Pending upload</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
@if (loading) {
|
||||
<div class="cv-state cv-state--loading">
|
||||
<fa-icon icon="spinner" animation="spin"></fa-icon>
|
||||
<span>Loading logs…</span>
|
||||
</div>
|
||||
}
|
||||
|
||||
@if (!loading && sftpLogs.length === 0) {
|
||||
<div class="cv-state cv-state--empty">
|
||||
<fa-icon icon="cloud-upload-alt" size="2x"></fa-icon>
|
||||
<h4>No SFTP upload history yet</h4>
|
||||
<p>Upload a batch from the Record Master to see CERSAI transfer logs here.</p>
|
||||
</div>
|
||||
}
|
||||
|
||||
@if (!loading && sftpLogs.length > 0) {
|
||||
<div class="cv-sftp-logs">
|
||||
@for (log of sftpLogs; track log.id) {
|
||||
<div
|
||||
class="cv-sftp-log"
|
||||
[class.cv-sftp-log--success]="isSftpSuccess(log)"
|
||||
[class.cv-sftp-log--failed]="isSftpFailed(log)"
|
||||
>
|
||||
<div class="cv-sftp-log__icon">
|
||||
@if (isSftpSuccess(log)) {
|
||||
<fa-icon icon="check-circle"></fa-icon>
|
||||
} @else if (isSftpFailed(log)) {
|
||||
<fa-icon icon="times-circle"></fa-icon>
|
||||
} @else {
|
||||
<fa-icon icon="hourglass-half"></fa-icon>
|
||||
}
|
||||
</div>
|
||||
<div class="cv-sftp-log__body">
|
||||
<div class="cv-sftp-log__head">
|
||||
<span class="cv-sftp-log__bid">
|
||||
<fa-icon icon="tag"></fa-icon> {{ log.batchId }}
|
||||
</span>
|
||||
@if (!loading && sftpLogs.length > 0) {
|
||||
<div class="cv-sftp-logs">
|
||||
@for (log of sftpLogs; track log.id) {
|
||||
<div
|
||||
class="cv-sftp-log"
|
||||
[class.cv-sftp-log--success]="isSftpSuccess(log)"
|
||||
[class.cv-sftp-log--failed]="isSftpFailed(log)"
|
||||
>
|
||||
<div class="cv-sftp-log__icon">
|
||||
@if (isSftpSuccess(log)) {
|
||||
<span class="cv-pill cv-pill--green">
|
||||
<fa-icon icon="check-circle"></fa-icon> Upload successful
|
||||
</span>
|
||||
<fa-icon icon="check-circle"></fa-icon>
|
||||
} @else if (isSftpFailed(log)) {
|
||||
<span class="cv-pill cv-pill--red">
|
||||
<fa-icon icon="times-circle"></fa-icon> Upload failed
|
||||
</span>
|
||||
<fa-icon icon="times-circle"></fa-icon>
|
||||
} @else {
|
||||
<span class="cv-pill cv-pill--amber">
|
||||
<fa-icon icon="hourglass-half"></fa-icon> Pending
|
||||
</span>
|
||||
<fa-icon icon="hourglass-half"></fa-icon>
|
||||
}
|
||||
<span class="cv-sftp-log__time">
|
||||
<fa-icon icon="clock"></fa-icon>
|
||||
{{ log.uploadedOnLabel || log.createdOnLabel || '—' }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="cv-sftp-log__body">
|
||||
<div class="cv-sftp-log__head">
|
||||
<span class="cv-sftp-log__bid"> <fa-icon icon="tag"></fa-icon> {{ log.batchId }} </span>
|
||||
@if (isSftpSuccess(log)) {
|
||||
<span class="cv-pill cv-pill--green">
|
||||
<fa-icon icon="check-circle"></fa-icon> Upload successful
|
||||
</span>
|
||||
} @else if (isSftpFailed(log)) {
|
||||
<span class="cv-pill cv-pill--red"> <fa-icon icon="times-circle"></fa-icon> Upload failed </span>
|
||||
} @else {
|
||||
<span class="cv-pill cv-pill--amber"> <fa-icon icon="hourglass-half"></fa-icon> Pending </span>
|
||||
}
|
||||
<span class="cv-sftp-log__time">
|
||||
<fa-icon icon="clock"></fa-icon>
|
||||
{{ log.uploadedOnLabel || log.createdOnLabel || '—' }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="cv-sftp-log__meta">
|
||||
<div>
|
||||
<span class="cv-sftp-log__label">Filename</span>
|
||||
<span class="cv-sftp-log__value">{{ log.filename }}</span>
|
||||
</div>
|
||||
<div>
|
||||
<span class="cv-sftp-log__label">Records</span>
|
||||
<span class="cv-sftp-log__value">{{ log.recordCount }}</span>
|
||||
</div>
|
||||
<div>
|
||||
<span class="cv-sftp-log__label">Size</span>
|
||||
<span class="cv-sftp-log__value">{{ formatBytes(log.sizeBytes) }}</span>
|
||||
</div>
|
||||
@if (log.cersaiReference) {
|
||||
<div class="cv-sftp-log__meta">
|
||||
<div>
|
||||
<span class="cv-sftp-log__label">CERSAI Ref</span>
|
||||
<span class="cv-sftp-log__value cv-sftp-log__value--mono">{{ log.cersaiReference }}</span>
|
||||
<span class="cv-sftp-log__label">Filename</span>
|
||||
<span class="cv-sftp-log__value">{{ log.filename }}</span>
|
||||
</div>
|
||||
<div>
|
||||
<span class="cv-sftp-log__label">Records</span>
|
||||
<span class="cv-sftp-log__value">{{ log.recordCount }}</span>
|
||||
</div>
|
||||
<div>
|
||||
<span class="cv-sftp-log__label">Size</span>
|
||||
<span class="cv-sftp-log__value">{{ formatBytes(log.sizeBytes) }}</span>
|
||||
</div>
|
||||
@if (log.cersaiReference) {
|
||||
<div>
|
||||
<span class="cv-sftp-log__label">CERSAI Ref</span>
|
||||
<span class="cv-sftp-log__value cv-sftp-log__value--mono">{{ log.cersaiReference }}</span>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
@if (log.uploadRemarks) {
|
||||
<div class="cv-sftp-log__remarks" [class.cv-sftp-log__remarks--err]="isSftpFailed(log)">
|
||||
<fa-icon [icon]="isSftpFailed(log) ? 'exclamation-triangle' : 'info-circle'"></fa-icon>
|
||||
<span>{{ log.uploadRemarks }}</span>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
@if (log.uploadRemarks) {
|
||||
<div class="cv-sftp-log__remarks" [class.cv-sftp-log__remarks--err]="isSftpFailed(log)">
|
||||
<fa-icon [icon]="isSftpFailed(log) ? 'exclamation-triangle' : 'info-circle'"></fa-icon>
|
||||
<span>{{ log.uploadRemarks }}</span>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
<div class="cv-sftp-log__actions">
|
||||
<button class="cv-action-btn" (click)="openManifest(log, $event)" title="View manifest">
|
||||
<fa-icon icon="eye"></fa-icon>
|
||||
</button>
|
||||
@if (log.documentId) {
|
||||
<button
|
||||
class="cv-action-btn cv-action-btn--primary"
|
||||
(click)="downloadZip(log, $event)"
|
||||
title="Download ZIP"
|
||||
>
|
||||
<fa-icon icon="download"></fa-icon>
|
||||
<div class="cv-sftp-log__actions">
|
||||
<button class="cv-action-btn" (click)="openManifest(log, $event)" title="View manifest">
|
||||
<fa-icon icon="eye"></fa-icon>
|
||||
</button>
|
||||
}
|
||||
@if (log.documentId) {
|
||||
<button
|
||||
class="cv-action-btn cv-action-btn--primary"
|
||||
(click)="downloadZip(log, $event)"
|
||||
title="Download ZIP"
|
||||
>
|
||||
<fa-icon icon="download"></fa-icon>
|
||||
</button>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
|
||||
<!-- Manifest modal -->
|
||||
@@ -451,13 +444,9 @@
|
||||
<div>
|
||||
<span class="cv-kpi__label">Status</span>
|
||||
@if (manifestBatch.uploadedToCersai) {
|
||||
<span class="cv-pill cv-pill--green">
|
||||
<fa-icon icon="check-circle"></fa-icon> Uploaded
|
||||
</span>
|
||||
<span class="cv-pill cv-pill--green"> <fa-icon icon="check-circle"></fa-icon> Uploaded </span>
|
||||
} @else {
|
||||
<span class="cv-pill cv-pill--amber">
|
||||
<fa-icon icon="hourglass-half"></fa-icon> Pending
|
||||
</span>
|
||||
<span class="cv-pill cv-pill--amber"> <fa-icon icon="hourglass-half"></fa-icon> Pending </span>
|
||||
}
|
||||
</div>
|
||||
<div class="cv-manifest-head__grid-span">
|
||||
@@ -526,7 +515,10 @@
|
||||
class="cv-dot"
|
||||
[class.cv-dot--ok]="entry.idProof?.packaged"
|
||||
[class.cv-dot--miss]="!entry.idProof?.packaged"
|
||||
[title]="(entry.idProof?.type || 'ID') + (entry.idProof?.zipEntry ? ' — ' + entry.idProof.zipEntry : '')"
|
||||
[title]="
|
||||
(entry.idProof?.type || 'ID') +
|
||||
(entry.idProof?.zipEntry ? ' — ' + entry.idProof.zipEntry : '')
|
||||
"
|
||||
>
|
||||
<fa-icon [icon]="entry.idProof?.packaged ? 'check' : 'times'"></fa-icon>
|
||||
</span>
|
||||
@@ -536,13 +528,23 @@
|
||||
class="cv-dot"
|
||||
[class.cv-dot--ok]="entry.addressProof?.packaged"
|
||||
[class.cv-dot--miss]="!entry.addressProof?.packaged"
|
||||
[title]="(entry.addressProof?.type || 'Address') + (entry.addressProof?.zipEntry ? ' — ' + entry.addressProof.zipEntry : '')"
|
||||
[title]="
|
||||
(entry.addressProof?.type || 'Address') +
|
||||
(entry.addressProof?.zipEntry ? ' — ' + entry.addressProof.zipEntry : '')
|
||||
"
|
||||
>
|
||||
<fa-icon [icon]="entry.addressProof?.packaged ? 'check' : 'times'"></fa-icon>
|
||||
</span>
|
||||
</td>
|
||||
<td class="cv-td-r cv-manifest-size">
|
||||
{{ formatBytes((entry.photo?.bytes || 0) + (entry.signature?.bytes || 0) + (entry.idProof?.bytes || 0) + (entry.addressProof?.bytes || 0)) }}
|
||||
{{
|
||||
formatBytes(
|
||||
(entry.photo?.bytes || 0) +
|
||||
(entry.signature?.bytes || 0) +
|
||||
(entry.idProof?.bytes || 0) +
|
||||
(entry.addressProof?.bytes || 0)
|
||||
)
|
||||
}}
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
@@ -554,9 +556,7 @@
|
||||
</div>
|
||||
|
||||
<div class="cv-modal__footer">
|
||||
<button class="cv-btn cv-btn--ghost" (click)="closeManifest()">
|
||||
<fa-icon icon="times"></fa-icon> Close
|
||||
</button>
|
||||
<button class="cv-btn cv-btn--ghost" (click)="closeManifest()"><fa-icon icon="times"></fa-icon> Close</button>
|
||||
@if (manifestBatch?.documentId) {
|
||||
<button class="cv-btn cv-btn--primary" (click)="downloadZip(manifestBatch, $event)">
|
||||
<fa-icon icon="download"></fa-icon>
|
||||
|
||||
@@ -91,14 +91,18 @@ $purple-light: #efeafb;
|
||||
cursor: pointer;
|
||||
transition: all 0.15s;
|
||||
|
||||
fa-icon { font-size: 12px; }
|
||||
fa-icon {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
&:hover:not(:disabled) {
|
||||
background: $gray-50;
|
||||
border-color: $gray-500;
|
||||
}
|
||||
|
||||
&--ghost { /* default */ }
|
||||
&--ghost {
|
||||
/* default */
|
||||
}
|
||||
|
||||
&--primary {
|
||||
background: $primary;
|
||||
@@ -139,13 +143,27 @@ $purple-light: #efeafb;
|
||||
justify-content: center;
|
||||
font-size: 15px;
|
||||
flex-shrink: 0;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
|
||||
box-shadow: 0 2px 8px rgb(0 0 0 / 8%);
|
||||
|
||||
&--blue { background: linear-gradient(135deg, $primary, #4285f4); }
|
||||
&--teal { background: linear-gradient(135deg, $teal-700, $green); }
|
||||
&--amber { background: linear-gradient(135deg, $amber, #d4892f); }
|
||||
&--green { background: linear-gradient(135deg, $green, #2fb88c); }
|
||||
&--purple { background: linear-gradient(135deg, $purple, #8a6fd4); }
|
||||
&--blue {
|
||||
background: linear-gradient(135deg, $primary, #4285f4);
|
||||
}
|
||||
|
||||
&--teal {
|
||||
background: linear-gradient(135deg, $teal-700, $green);
|
||||
}
|
||||
|
||||
&--amber {
|
||||
background: linear-gradient(135deg, $amber, #d4892f);
|
||||
}
|
||||
|
||||
&--green {
|
||||
background: linear-gradient(135deg, $green, #2fb88c);
|
||||
}
|
||||
|
||||
&--purple {
|
||||
background: linear-gradient(135deg, $purple, #8a6fd4);
|
||||
}
|
||||
}
|
||||
|
||||
&__label {
|
||||
@@ -209,7 +227,10 @@ $purple-light: #efeafb;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
|
||||
fa-icon { color: $primary; font-size: 11px; }
|
||||
fa-icon {
|
||||
color: $primary;
|
||||
font-size: 11px;
|
||||
}
|
||||
}
|
||||
|
||||
.cv-input {
|
||||
@@ -251,7 +272,7 @@ select.cv-input {
|
||||
border: 1px solid $gray-200 !important;
|
||||
border-radius: 14px !important;
|
||||
padding: 8px !important;
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.03) !important;
|
||||
box-shadow: 0 1px 3px rgb(0 0 0 / 3%) !important;
|
||||
}
|
||||
|
||||
button.cv-tab,
|
||||
@@ -283,7 +304,7 @@ button.cv-tab,
|
||||
color: $gray-500 !important;
|
||||
}
|
||||
|
||||
> span:not(.cv-tab__badge):not(.cv-tab__count) {
|
||||
> span:not(.cv-tab__badge, .cv-tab__count) {
|
||||
display: inline-flex !important;
|
||||
align-items: center !important;
|
||||
}
|
||||
@@ -292,7 +313,9 @@ button.cv-tab,
|
||||
background: $gray-50 !important;
|
||||
color: $gray-900 !important;
|
||||
|
||||
> fa-icon { color: $primary !important; }
|
||||
> fa-icon {
|
||||
color: $primary !important;
|
||||
}
|
||||
}
|
||||
|
||||
&--active {
|
||||
@@ -301,7 +324,9 @@ button.cv-tab,
|
||||
color: $primary-dark !important;
|
||||
box-shadow: 0 2px 8px rgba($primary, 0.12) !important;
|
||||
|
||||
> fa-icon { color: $primary !important; }
|
||||
> fa-icon {
|
||||
color: $primary !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -342,13 +367,20 @@ button.cv-tab,
|
||||
}
|
||||
|
||||
@media (width <= 680px) {
|
||||
.cv-tabs { flex-direction: column !important; }
|
||||
.cv-tabs {
|
||||
flex-direction: column !important;
|
||||
}
|
||||
|
||||
button.cv-tab,
|
||||
.cv-tabs .cv-tab { justify-content: space-between !important; }
|
||||
.cv-tabs .cv-tab {
|
||||
justify-content: space-between !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Hide the per-section header badge since the tab strip already carries it */
|
||||
.cv-section .cv-badge { display: none; }
|
||||
.cv-section .cv-badge {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* ---------- Section card ---------- */
|
||||
.cv-section {
|
||||
@@ -412,8 +444,15 @@ button.cv-tab,
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.3px;
|
||||
|
||||
&--blue { background: $primary-light; color: $primary-dark; }
|
||||
&--amber { background: $amber-light; color: $amber; }
|
||||
&--blue {
|
||||
background: $primary-light;
|
||||
color: $primary-dark;
|
||||
}
|
||||
|
||||
&--amber {
|
||||
background: $amber-light;
|
||||
color: $amber;
|
||||
}
|
||||
}
|
||||
|
||||
/* ---------- State banners ---------- */
|
||||
@@ -426,7 +465,9 @@ button.cv-tab,
|
||||
border-radius: 10px;
|
||||
font-size: 13px;
|
||||
|
||||
fa-icon { font-size: 18px; }
|
||||
fa-icon {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
&--loading {
|
||||
background: $primary-light;
|
||||
@@ -445,9 +486,22 @@ button.cv-tab,
|
||||
color: $gray-500;
|
||||
border: 1px dashed $gray-200;
|
||||
|
||||
fa-icon { color: $primary; margin-bottom: 4px; }
|
||||
h4 { margin: 0; font-size: 15px; font-weight: 600; color: $gray-900; }
|
||||
p { margin: 0; font-size: 12px; }
|
||||
fa-icon {
|
||||
color: $primary;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
h4 {
|
||||
margin: 0;
|
||||
font-size: 15px;
|
||||
font-weight: 600;
|
||||
color: $gray-900;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 0;
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -463,9 +517,13 @@ button.cv-tab,
|
||||
border: 1px solid $gray-200;
|
||||
border-radius: 10px;
|
||||
overflow: hidden;
|
||||
transition: border-color 0.15s, box-shadow 0.15s;
|
||||
transition:
|
||||
border-color 0.15s,
|
||||
box-shadow 0.15s;
|
||||
|
||||
&:hover { border-color: rgba($primary, 0.3); }
|
||||
&:hover {
|
||||
border-color: rgba($primary, 0.3);
|
||||
}
|
||||
|
||||
&--open {
|
||||
border-color: $primary-light;
|
||||
@@ -485,7 +543,9 @@ button.cv-tab,
|
||||
font-family: inherit;
|
||||
text-align: left;
|
||||
|
||||
&:hover { background: $gray-100; }
|
||||
&:hover {
|
||||
background: $gray-100;
|
||||
}
|
||||
}
|
||||
|
||||
&__left {
|
||||
@@ -513,7 +573,10 @@ button.cv-tab,
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
|
||||
fa-icon { color: $primary; font-size: 11px; }
|
||||
fa-icon {
|
||||
color: $primary;
|
||||
font-size: 11px;
|
||||
}
|
||||
}
|
||||
|
||||
&__summary {
|
||||
@@ -523,10 +586,15 @@ button.cv-tab,
|
||||
gap: 8px;
|
||||
flex-wrap: wrap;
|
||||
|
||||
strong { color: $gray-900; font-weight: 700; }
|
||||
strong {
|
||||
color: $gray-900;
|
||||
font-weight: 700;
|
||||
}
|
||||
}
|
||||
|
||||
&__sep { color: $gray-200; }
|
||||
&__sep {
|
||||
color: $gray-200;
|
||||
}
|
||||
|
||||
&__right {
|
||||
display: flex;
|
||||
@@ -550,7 +618,9 @@ button.cv-tab,
|
||||
font-size: 11px;
|
||||
font-weight: 700;
|
||||
|
||||
fa-icon { font-size: 10px; }
|
||||
fa-icon {
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
&--green {
|
||||
background: $green-light;
|
||||
@@ -617,19 +687,31 @@ button.cv-tab,
|
||||
&--green {
|
||||
border-color: rgba($green, 0.3);
|
||||
background: linear-gradient(180deg, $green-light, $white);
|
||||
.cv-sftp-kpi__icon { background: $green; color: $white; }
|
||||
|
||||
.cv-sftp-kpi__icon {
|
||||
background: $green;
|
||||
color: $white;
|
||||
}
|
||||
}
|
||||
|
||||
&--red {
|
||||
border-color: rgba($red, 0.3);
|
||||
background: linear-gradient(180deg, $red-light, $white);
|
||||
.cv-sftp-kpi__icon { background: $red; color: $white; }
|
||||
|
||||
.cv-sftp-kpi__icon {
|
||||
background: $red;
|
||||
color: $white;
|
||||
}
|
||||
}
|
||||
|
||||
&--amber {
|
||||
border-color: rgba($amber, 0.3);
|
||||
background: linear-gradient(180deg, $amber-light, $white);
|
||||
.cv-sftp-kpi__icon { background: $amber; color: $white; }
|
||||
|
||||
.cv-sftp-kpi__icon {
|
||||
background: $amber;
|
||||
color: $white;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -649,12 +731,22 @@ button.cv-tab,
|
||||
border-left-width: 4px;
|
||||
border-radius: 10px;
|
||||
background: $white;
|
||||
transition: background 0.15s, box-shadow 0.15s;
|
||||
transition:
|
||||
background 0.15s,
|
||||
box-shadow 0.15s;
|
||||
|
||||
&:hover { background: #fafbfc; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04); }
|
||||
&:hover {
|
||||
background: #fafbfc;
|
||||
box-shadow: 0 2px 8px rgb(0 0 0 / 4%);
|
||||
}
|
||||
|
||||
&--success { border-left-color: $green; }
|
||||
&--failed { border-left-color: $red; }
|
||||
&--success {
|
||||
border-left-color: $green;
|
||||
}
|
||||
|
||||
&--failed {
|
||||
border-left-color: $red;
|
||||
}
|
||||
|
||||
&__icon {
|
||||
width: 36px;
|
||||
@@ -669,10 +761,20 @@ button.cv-tab,
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
&--success .cv-sftp-log__icon { background: $green-light; color: $green; }
|
||||
&--failed .cv-sftp-log__icon { background: $red-light; color: $red; }
|
||||
&--success .cv-sftp-log__icon {
|
||||
background: $green-light;
|
||||
color: $green;
|
||||
}
|
||||
|
||||
&__body { flex: 1; min-width: 0; }
|
||||
&--failed .cv-sftp-log__icon {
|
||||
background: $red-light;
|
||||
color: $red;
|
||||
}
|
||||
|
||||
&__body {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
&__head {
|
||||
display: flex;
|
||||
@@ -688,7 +790,10 @@ button.cv-tab,
|
||||
font-weight: 700;
|
||||
color: $primary;
|
||||
|
||||
fa-icon { font-size: 11px; margin-right: 4px; }
|
||||
fa-icon {
|
||||
font-size: 11px;
|
||||
margin-right: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
&__time {
|
||||
@@ -696,7 +801,10 @@ button.cv-tab,
|
||||
color: $gray-500;
|
||||
margin-left: auto;
|
||||
|
||||
fa-icon { font-size: 10px; margin-right: 4px; }
|
||||
fa-icon {
|
||||
font-size: 10px;
|
||||
margin-right: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
&__meta {
|
||||
@@ -705,7 +813,12 @@ button.cv-tab,
|
||||
gap: 8px 16px;
|
||||
font-size: 12px;
|
||||
|
||||
> div { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
|
||||
> div {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2px;
|
||||
min-width: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&__label {
|
||||
@@ -739,14 +852,20 @@ button.cv-tab,
|
||||
align-items: flex-start;
|
||||
gap: 8px;
|
||||
|
||||
fa-icon { font-size: 12px; color: $gray-500; margin-top: 2px; }
|
||||
fa-icon {
|
||||
font-size: 12px;
|
||||
color: $gray-500;
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
&--err {
|
||||
background: $red-light;
|
||||
border-color: rgba($red, 0.3);
|
||||
color: $red;
|
||||
|
||||
fa-icon { color: $red; }
|
||||
fa-icon {
|
||||
color: $red;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -763,7 +882,9 @@ button.cv-tab,
|
||||
border-collapse: collapse;
|
||||
font-size: 12px;
|
||||
|
||||
thead tr { background: $gray-50; }
|
||||
thead tr {
|
||||
background: $gray-50;
|
||||
}
|
||||
|
||||
th {
|
||||
text-align: left;
|
||||
@@ -783,10 +904,20 @@ button.cv-tab,
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
tbody tr:hover { background: #f5f8fc; }
|
||||
tbody tr:hover {
|
||||
background: #f5f8fc;
|
||||
}
|
||||
|
||||
&__num { text-align: right; white-space: nowrap; font-variant-numeric: tabular-nums; }
|
||||
&__actions { text-align: right; width: 100px; }
|
||||
&__num {
|
||||
text-align: right;
|
||||
white-space: nowrap;
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
|
||||
&__actions {
|
||||
text-align: right;
|
||||
width: 100px;
|
||||
}
|
||||
|
||||
&__bid {
|
||||
font-family: 'SF Mono', Consolas, monospace;
|
||||
@@ -795,13 +926,21 @@ button.cv-tab,
|
||||
color: $primary;
|
||||
white-space: nowrap;
|
||||
|
||||
fa-icon { color: $primary; margin-right: 6px; font-size: 11px; }
|
||||
fa-icon {
|
||||
color: $primary;
|
||||
margin-right: 6px;
|
||||
font-size: 11px;
|
||||
}
|
||||
}
|
||||
|
||||
&__file {
|
||||
color: $gray-700;
|
||||
|
||||
fa-icon { color: $primary; margin-right: 6px; font-size: 11px; }
|
||||
fa-icon {
|
||||
color: $primary;
|
||||
margin-right: 6px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
span {
|
||||
word-break: break-all;
|
||||
@@ -823,7 +962,9 @@ button.cv-tab,
|
||||
margin-left: 4px;
|
||||
transition: all 0.15s;
|
||||
|
||||
fa-icon { font-size: 12px; }
|
||||
fa-icon {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: $primary-light;
|
||||
@@ -900,7 +1041,7 @@ button.cv-tab,
|
||||
.cv-modal {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
background: rgba(15, 25, 45, 0.55);
|
||||
background: rgb(15 25 45 / 55%);
|
||||
backdrop-filter: blur(3px);
|
||||
z-index: 100;
|
||||
display: flex;
|
||||
@@ -917,7 +1058,7 @@ button.cv-tab,
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 24px 60px rgba(0, 0, 0, 0.25);
|
||||
box-shadow: 0 24px 60px rgb(0 0 0 / 25%);
|
||||
}
|
||||
|
||||
&__header {
|
||||
@@ -1051,7 +1192,10 @@ button.cv-tab,
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
|
||||
fa-icon { color: $primary; font-size: 12px; }
|
||||
fa-icon {
|
||||
color: $primary;
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
.cv-manifest-table-wrap {
|
||||
@@ -1091,14 +1235,31 @@ button.cv-tab,
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
tbody tr:last-child td { border-bottom: none; }
|
||||
tbody tr:hover { background: #f5f8fc; }
|
||||
tbody tr:last-child td {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
tbody tr:hover {
|
||||
background: #f5f8fc;
|
||||
}
|
||||
}
|
||||
|
||||
.cv-th-c { text-align: center !important; width: 38px; }
|
||||
.cv-th-r { text-align: right !important; }
|
||||
.cv-td-c { text-align: center; }
|
||||
.cv-td-r { text-align: right; }
|
||||
.cv-th-c {
|
||||
text-align: center !important;
|
||||
width: 38px;
|
||||
}
|
||||
|
||||
.cv-th-r {
|
||||
text-align: right !important;
|
||||
}
|
||||
|
||||
.cv-td-c {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.cv-td-r {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.cv-manifest-client {
|
||||
display: flex;
|
||||
@@ -1117,7 +1278,10 @@ button.cv-tab,
|
||||
color: $gray-500;
|
||||
font-family: 'SF Mono', Consolas, monospace;
|
||||
|
||||
fa-icon { font-size: 8px; margin-right: 2px; }
|
||||
fa-icon {
|
||||
font-size: 8px;
|
||||
margin-right: 2px;
|
||||
}
|
||||
}
|
||||
|
||||
.cv-dot {
|
||||
@@ -1157,11 +1321,21 @@ button.cv-tab,
|
||||
|
||||
/* ---------- Responsive ---------- */
|
||||
@media (width <= 1100px) {
|
||||
.cv-kpis { grid-template-columns: repeat(3, 1fr); }
|
||||
.cv-kpis {
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
}
|
||||
}
|
||||
|
||||
@media (width <= 700px) {
|
||||
.cv-kpis { grid-template-columns: repeat(2, 1fr); }
|
||||
.cv-filters__grid { grid-template-columns: 1fr; }
|
||||
.cv-manifest-head__grid { grid-template-columns: 1fr; }
|
||||
.cv-kpis {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
}
|
||||
|
||||
.cv-filters__grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.cv-manifest-head__grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -210,7 +210,11 @@ export class CkycVerificationComponent implements OnInit {
|
||||
formatJavaDate(value: any): string {
|
||||
if (!value) return '';
|
||||
if (Array.isArray(value) && value.length >= 3) {
|
||||
const [y, m, d] = value;
|
||||
const [
|
||||
y,
|
||||
m,
|
||||
d
|
||||
] = value;
|
||||
return `${String(d).padStart(2, '0')}-${String(m).padStart(2, '0')}-${y}`;
|
||||
}
|
||||
if (typeof value === 'string') return value;
|
||||
@@ -221,7 +225,14 @@ export class CkycVerificationComponent implements OnInit {
|
||||
if (value == null || value === '') return '';
|
||||
const pad = (n: any) => String(n ?? 0).padStart(2, '0');
|
||||
if (Array.isArray(value) && value.length >= 3) {
|
||||
const [y, mo, d, h = 0, mi = 0, s = 0] = value;
|
||||
const [
|
||||
y,
|
||||
mo,
|
||||
d,
|
||||
h = 0,
|
||||
mi = 0,
|
||||
s = 0
|
||||
] = value;
|
||||
const date = `${pad(d)}-${pad(mo)}-${y}`;
|
||||
if (value.length >= 4) {
|
||||
return `${date} ${pad(h)}:${pad(mi)}:${pad(s)}`;
|
||||
@@ -269,7 +280,14 @@ export class CkycVerificationComponent implements OnInit {
|
||||
|
||||
private dateValue(value: any): number {
|
||||
if (Array.isArray(value) && value.length >= 3) {
|
||||
const [y, mo, d, h = 0, mi = 0, s = 0] = value;
|
||||
const [
|
||||
y,
|
||||
mo,
|
||||
d,
|
||||
h = 0,
|
||||
mi = 0,
|
||||
s = 0
|
||||
] = value;
|
||||
return new Date(y, (mo || 1) - 1, d || 1, h, mi, s).getTime();
|
||||
}
|
||||
if (typeof value === 'string') {
|
||||
@@ -293,10 +311,7 @@ export class CkycVerificationComponent implements OnInit {
|
||||
/** Completion check per manifest entry */
|
||||
isEntryComplete(entry: any): boolean {
|
||||
return (
|
||||
entry?.photo?.packaged &&
|
||||
entry?.signature?.packaged &&
|
||||
entry?.idProof?.packaged &&
|
||||
entry?.addressProof?.packaged
|
||||
entry?.photo?.packaged && entry?.signature?.packaged && entry?.idProof?.packaged && entry?.addressProof?.packaged
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,9 +29,7 @@
|
||||
<!-- 5-Phase pipeline banner -->
|
||||
<div class="ckyc-pipeline">
|
||||
<div class="ckyc-pipeline__head">
|
||||
<span class="ckyc-pipeline__heading">
|
||||
<fa-icon icon="stream"></fa-icon> 5-phase compliance pipeline
|
||||
</span>
|
||||
<span class="ckyc-pipeline__heading"> <fa-icon icon="stream"></fa-icon> 5-phase compliance pipeline </span>
|
||||
<span class="ckyc-pipeline__hint">CBS → CKYC automation flow</span>
|
||||
</div>
|
||||
|
||||
@@ -70,9 +68,7 @@
|
||||
</div>
|
||||
|
||||
<!-- Menu cards -->
|
||||
<h2 class="ckyc-dash__section-title">
|
||||
<fa-icon icon="th-large"></fa-icon> Quick actions
|
||||
</h2>
|
||||
<h2 class="ckyc-dash__section-title"><fa-icon icon="th-large"></fa-icon> Quick actions</h2>
|
||||
|
||||
<div class="ckyc-dash__grid">
|
||||
<a class="ckyc-card ckyc-card--blue" [routerLink]="['record-master']">
|
||||
@@ -82,15 +78,15 @@
|
||||
</div>
|
||||
<div class="ckyc-card__body">
|
||||
<div class="ckyc-card__title">Record Master</div>
|
||||
<p class="ckyc-card__desc">Manage master records, identify clients without CKYC and collect missing artefacts.</p>
|
||||
<p class="ckyc-card__desc">
|
||||
Manage master records, identify clients without CKYC and collect missing artefacts.
|
||||
</p>
|
||||
<div class="ckyc-card__tags">
|
||||
<span class="ckyc-tag">Phase 1</span>
|
||||
<span class="ckyc-tag">Clients</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ckyc-card__cta">
|
||||
Open <fa-icon icon="arrow-right"></fa-icon>
|
||||
</div>
|
||||
<div class="ckyc-card__cta">Open <fa-icon icon="arrow-right"></fa-icon></div>
|
||||
</a>
|
||||
|
||||
<a class="ckyc-card ckyc-card--teal" [routerLink]="['ckyc-verification']">
|
||||
@@ -106,9 +102,7 @@
|
||||
<span class="ckyc-tag">Batches</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ckyc-card__cta">
|
||||
Open <fa-icon icon="arrow-right"></fa-icon>
|
||||
</div>
|
||||
<div class="ckyc-card__cta">Open <fa-icon icon="arrow-right"></fa-icon></div>
|
||||
</a>
|
||||
|
||||
<a class="ckyc-card ckyc-card--amber" [routerLink]="['ckyc-reports']">
|
||||
@@ -124,9 +118,7 @@
|
||||
<span class="ckyc-tag">MIS</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ckyc-card__cta">
|
||||
Open <fa-icon icon="arrow-right"></fa-icon>
|
||||
</div>
|
||||
<div class="ckyc-card__cta">Open <fa-icon icon="arrow-right"></fa-icon></div>
|
||||
</a>
|
||||
|
||||
<a class="ckyc-card ckyc-card--purple" [routerLink]="['ekyc']">
|
||||
@@ -142,9 +134,7 @@
|
||||
<span class="ckyc-tag">Aadhaar</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ckyc-card__cta">
|
||||
Open <fa-icon icon="arrow-right"></fa-icon>
|
||||
</div>
|
||||
<div class="ckyc-card__cta">Open <fa-icon icon="arrow-right"></fa-icon></div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -45,7 +45,7 @@ $purple-bg: #f8f5ff;
|
||||
margin-bottom: 22px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 6px 24px rgba(26, 115, 232, 0.2);
|
||||
box-shadow: 0 6px 24px rgb(26 115 232 / 20%);
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
@@ -55,7 +55,7 @@ $purple-bg: #f8f5ff;
|
||||
width: 320px;
|
||||
height: 320px;
|
||||
border-radius: 50%;
|
||||
background: radial-gradient(circle, rgba(255, 255, 255, 0.15), transparent 70%);
|
||||
background: radial-gradient(circle, rgb(255 255 255 / 15%), transparent 70%);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
@@ -71,8 +71,8 @@ $purple-bg: #f8f5ff;
|
||||
width: 58px;
|
||||
height: 58px;
|
||||
border-radius: 14px;
|
||||
background: rgba(255, 255, 255, 0.18);
|
||||
border: 1px solid rgba(255, 255, 255, 0.3);
|
||||
background: rgb(255 255 255 / 18%);
|
||||
border: 1px solid rgb(255 255 255 / 30%);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
@@ -86,8 +86,8 @@ $purple-bg: #f8f5ff;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 8px 16px;
|
||||
background: rgba(255, 255, 255, 0.15);
|
||||
border: 1px solid rgba(255, 255, 255, 0.3);
|
||||
background: rgb(255 255 255 / 15%);
|
||||
border: 1px solid rgb(255 255 255 / 30%);
|
||||
border-radius: 30px;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
@@ -95,7 +95,10 @@ $purple-bg: #f8f5ff;
|
||||
flex-shrink: 0;
|
||||
position: relative;
|
||||
|
||||
fa-icon { color: #9fe1cb; font-size: 14px; }
|
||||
fa-icon {
|
||||
color: #9fe1cb;
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -139,7 +142,9 @@ $purple-bg: #f8f5ff;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
|
||||
fa-icon { color: $primary; }
|
||||
fa-icon {
|
||||
color: $primary;
|
||||
}
|
||||
}
|
||||
|
||||
&__hint {
|
||||
@@ -155,8 +160,14 @@ $purple-bg: #f8f5ff;
|
||||
overflow-x: auto;
|
||||
padding: 4px 2px;
|
||||
|
||||
&::-webkit-scrollbar { height: 4px; }
|
||||
&::-webkit-scrollbar-thumb { background: $gray-200; border-radius: 4px; }
|
||||
&::-webkit-scrollbar {
|
||||
height: 4px;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background: $gray-200;
|
||||
border-radius: 4px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -171,7 +182,9 @@ $purple-bg: #f8f5ff;
|
||||
position: relative;
|
||||
transition: transform 0.15s;
|
||||
|
||||
&:hover { transform: translateY(-2px); }
|
||||
&:hover {
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
&__num {
|
||||
position: absolute;
|
||||
@@ -249,7 +262,9 @@ $purple-bg: #f8f5ff;
|
||||
background: $amber;
|
||||
}
|
||||
|
||||
.ckyc-phase__title { color: #633806; }
|
||||
.ckyc-phase__title {
|
||||
color: #633806;
|
||||
}
|
||||
}
|
||||
|
||||
&--wide {
|
||||
@@ -270,7 +285,10 @@ $purple-bg: #f8f5ff;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.4px;
|
||||
|
||||
fa-icon { color: $primary; font-size: 14px; }
|
||||
fa-icon {
|
||||
color: $primary;
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
/* ---------- Menu grid ---------- */
|
||||
@@ -289,7 +307,10 @@ $purple-bg: #f8f5ff;
|
||||
border-radius: 16px;
|
||||
text-decoration: none;
|
||||
color: $gray-900;
|
||||
transition: transform 0.2s, box-shadow 0.2s, border-color 0.2s;
|
||||
transition:
|
||||
transform 0.2s,
|
||||
box-shadow 0.2s,
|
||||
border-color 0.2s;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
@@ -304,12 +325,14 @@ $purple-bg: #f8f5ff;
|
||||
border-radius: 50%;
|
||||
opacity: 0.12;
|
||||
pointer-events: none;
|
||||
transition: transform 0.4s, opacity 0.3s;
|
||||
transition:
|
||||
transform 0.4s,
|
||||
opacity 0.3s;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
transform: translateY(-3px);
|
||||
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.08);
|
||||
box-shadow: 0 10px 30px rgb(0 0 0 / 8%);
|
||||
|
||||
.ckyc-card__glow {
|
||||
transform: scale(1.25);
|
||||
@@ -320,7 +343,9 @@ $purple-bg: #f8f5ff;
|
||||
background: $primary;
|
||||
color: $white;
|
||||
|
||||
fa-icon { transform: translateX(3px); }
|
||||
fa-icon {
|
||||
transform: translateX(3px);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -336,7 +361,7 @@ $purple-bg: #f8f5ff;
|
||||
margin-bottom: 16px;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
box-shadow: 0 6px 18px rgba(0, 0, 0, 0.1);
|
||||
box-shadow: 0 6px 18px rgb(0 0 0 / 10%);
|
||||
}
|
||||
|
||||
&__body {
|
||||
@@ -394,9 +419,11 @@ $purple-bg: #f8f5ff;
|
||||
.ckyc-card__icon {
|
||||
background: linear-gradient(135deg, $primary, #4285f4);
|
||||
}
|
||||
|
||||
.ckyc-card__glow {
|
||||
background: radial-gradient(circle, $primary, transparent 60%);
|
||||
}
|
||||
|
||||
&:hover .ckyc-card__cta {
|
||||
background: $primary;
|
||||
border-color: $primary;
|
||||
@@ -407,9 +434,11 @@ $purple-bg: #f8f5ff;
|
||||
.ckyc-card__icon {
|
||||
background: linear-gradient(135deg, $teal, #1d9e75);
|
||||
}
|
||||
|
||||
.ckyc-card__glow {
|
||||
background: radial-gradient(circle, $teal, transparent 60%);
|
||||
}
|
||||
|
||||
&:hover .ckyc-card__cta {
|
||||
background: $teal;
|
||||
border-color: $teal;
|
||||
@@ -420,9 +449,11 @@ $purple-bg: #f8f5ff;
|
||||
.ckyc-card__icon {
|
||||
background: linear-gradient(135deg, $amber, #d4892f);
|
||||
}
|
||||
|
||||
.ckyc-card__glow {
|
||||
background: radial-gradient(circle, $amber, transparent 60%);
|
||||
}
|
||||
|
||||
&:hover .ckyc-card__cta {
|
||||
background: $amber;
|
||||
border-color: $amber;
|
||||
@@ -433,15 +464,16 @@ $purple-bg: #f8f5ff;
|
||||
.ckyc-card__icon {
|
||||
background: linear-gradient(135deg, $purple, #8a6fd4);
|
||||
}
|
||||
|
||||
.ckyc-card__glow {
|
||||
background: radial-gradient(circle, $purple, transparent 60%);
|
||||
}
|
||||
|
||||
&:hover .ckyc-card__cta {
|
||||
background: $purple;
|
||||
border-color: $purple;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.ckyc-tag {
|
||||
@@ -458,12 +490,26 @@ $purple-bg: #f8f5ff;
|
||||
|
||||
/* ---------- Responsive ---------- */
|
||||
@media (width <= 900px) {
|
||||
.ckyc-dash { padding: 16px 18px 30px; }
|
||||
.ckyc-dash__hero { flex-direction: column; align-items: flex-start; }
|
||||
.ckyc-pipeline__track { flex-direction: row; flex-wrap: nowrap; }
|
||||
.ckyc-dash {
|
||||
padding: 16px 18px 30px;
|
||||
}
|
||||
|
||||
.ckyc-dash__hero {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.ckyc-pipeline__track {
|
||||
flex-flow: row nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
@media (width <= 600px) {
|
||||
.ckyc-dash__grid { grid-template-columns: 1fr; }
|
||||
.ckyc-phase { min-width: 120px; }
|
||||
.ckyc-dash__grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.ckyc-phase {
|
||||
min-width: 120px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,12 +28,7 @@
|
||||
|
||||
<!-- 3-tab filter -->
|
||||
<div class="rm-tabs">
|
||||
<button
|
||||
type="button"
|
||||
class="rm-tab"
|
||||
[class.rm-tab--active]="activeTab === 'all'"
|
||||
(click)="setTab('all')"
|
||||
>
|
||||
<button type="button" class="rm-tab" [class.rm-tab--active]="activeTab === 'all'" (click)="setTab('all')">
|
||||
<fa-icon icon="list"></fa-icon>
|
||||
<span>All clients</span>
|
||||
<span class="rm-tab__count">{{ tabCounts.all }}</span>
|
||||
@@ -75,12 +70,7 @@
|
||||
|
||||
<div class="rm-field">
|
||||
<label class="rm-label"><fa-icon icon="cube"></fa-icon> Loan product ID</label>
|
||||
<input
|
||||
type="number"
|
||||
class="rm-input"
|
||||
formControlName="productId"
|
||||
placeholder="Optional"
|
||||
/>
|
||||
<input type="number" class="rm-input" formControlName="productId" placeholder="Optional" />
|
||||
</div>
|
||||
|
||||
<div class="rm-field">
|
||||
@@ -186,9 +176,7 @@
|
||||
<div class="rm-row__avatar">{{ row.fullName?.charAt(0) || '?' }}</div>
|
||||
<div class="rm-row__ident">
|
||||
<div class="rm-row__name">{{ row.fullName }}</div>
|
||||
<div class="rm-row__account">
|
||||
<fa-icon icon="hashtag"></fa-icon> {{ row.accountNo }}
|
||||
</div>
|
||||
<div class="rm-row__account"><fa-icon icon="hashtag"></fa-icon> {{ row.accountNo }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
@@ -208,16 +196,36 @@
|
||||
</td>
|
||||
<td class="rm-table__artefacts">
|
||||
<div class="rm-row__chips">
|
||||
<span class="rm-chip" [class.rm-chip--ok]="row.hasPhoto" [class.rm-chip--miss]="!row.hasPhoto" title="Photo">
|
||||
<span
|
||||
class="rm-chip"
|
||||
[class.rm-chip--ok]="row.hasPhoto"
|
||||
[class.rm-chip--miss]="!row.hasPhoto"
|
||||
title="Photo"
|
||||
>
|
||||
<fa-icon [icon]="row.hasPhoto ? 'check' : 'times'"></fa-icon> P
|
||||
</span>
|
||||
<span class="rm-chip" [class.rm-chip--ok]="row.hasSignature" [class.rm-chip--miss]="!row.hasSignature" title="Signature">
|
||||
<span
|
||||
class="rm-chip"
|
||||
[class.rm-chip--ok]="row.hasSignature"
|
||||
[class.rm-chip--miss]="!row.hasSignature"
|
||||
title="Signature"
|
||||
>
|
||||
<fa-icon [icon]="row.hasSignature ? 'check' : 'times'"></fa-icon> S
|
||||
</span>
|
||||
<span class="rm-chip" [class.rm-chip--ok]="row.hasIdProof" [class.rm-chip--miss]="!row.hasIdProof" title="ID Proof">
|
||||
<span
|
||||
class="rm-chip"
|
||||
[class.rm-chip--ok]="row.hasIdProof"
|
||||
[class.rm-chip--miss]="!row.hasIdProof"
|
||||
title="ID Proof"
|
||||
>
|
||||
<fa-icon [icon]="row.hasIdProof ? 'check' : 'times'"></fa-icon> ID
|
||||
</span>
|
||||
<span class="rm-chip" [class.rm-chip--ok]="row.hasAddressProof" [class.rm-chip--miss]="!row.hasAddressProof" title="Address Proof">
|
||||
<span
|
||||
class="rm-chip"
|
||||
[class.rm-chip--ok]="row.hasAddressProof"
|
||||
[class.rm-chip--miss]="!row.hasAddressProof"
|
||||
title="Address Proof"
|
||||
>
|
||||
<fa-icon [icon]="row.hasAddressProof ? 'check' : 'times'"></fa-icon> Ad
|
||||
</span>
|
||||
</div>
|
||||
@@ -233,11 +241,7 @@
|
||||
</div>
|
||||
</td>
|
||||
<td class="rm-table__actions">
|
||||
<button
|
||||
class="rm-action-btn"
|
||||
(click)="openView(row, $event)"
|
||||
title="View details"
|
||||
>
|
||||
<button class="rm-action-btn" (click)="openView(row, $event)" title="View details">
|
||||
<fa-icon icon="eye"></fa-icon>
|
||||
</button>
|
||||
<button
|
||||
@@ -366,9 +370,7 @@
|
||||
}
|
||||
|
||||
<!-- KYC snapshot -->
|
||||
<div class="rm-view__section-title">
|
||||
<fa-icon icon="shield-alt"></fa-icon> KYC Snapshot
|
||||
</div>
|
||||
<div class="rm-view__section-title"><fa-icon icon="shield-alt"></fa-icon> KYC Snapshot</div>
|
||||
<div class="rm-view__kpis">
|
||||
<div class="rm-kpi">
|
||||
<div class="rm-kpi__label"><fa-icon icon="check-double"></fa-icon> KYC Status</div>
|
||||
@@ -376,8 +378,14 @@
|
||||
<div
|
||||
class="rm-kpi__value rm-kpi__pill"
|
||||
[class.rm-kpi__pill--ok]="viewDetails.kycStatusCode === 'clientKycStatus.verified'"
|
||||
[class.rm-kpi__pill--warn]="viewDetails.kycStatusCode === 'clientKycStatus.pending' || viewDetails.kycStatusCode === 'clientKycStatus.initiated'"
|
||||
[class.rm-kpi__pill--err]="viewDetails.kycStatusCode === 'clientKycStatus.rejected' || viewDetails.kycStatusCode === 'clientKycStatus.expired'"
|
||||
[class.rm-kpi__pill--warn]="
|
||||
viewDetails.kycStatusCode === 'clientKycStatus.pending' ||
|
||||
viewDetails.kycStatusCode === 'clientKycStatus.initiated'
|
||||
"
|
||||
[class.rm-kpi__pill--err]="
|
||||
viewDetails.kycStatusCode === 'clientKycStatus.rejected' ||
|
||||
viewDetails.kycStatusCode === 'clientKycStatus.expired'
|
||||
"
|
||||
>
|
||||
{{ viewDetails.kycStatus }}
|
||||
</div>
|
||||
@@ -409,14 +417,32 @@
|
||||
</div>
|
||||
|
||||
<!-- 4 documents -->
|
||||
<div class="rm-view__section-title">
|
||||
<fa-icon icon="folder-open"></fa-icon> CKYC Documents
|
||||
</div>
|
||||
<div class="rm-view__section-title"><fa-icon icon="folder-open"></fa-icon> CKYC Documents</div>
|
||||
<div class="rm-view__docs">
|
||||
<ng-container *ngTemplateOutlet="docTile; context: { slot: viewDetails.docs.photo, label: 'Photo / Image of Client', icon: 'user-circle' }"></ng-container>
|
||||
<ng-container *ngTemplateOutlet="docTile; context: { slot: viewDetails.docs.signature, label: 'Signature of Client', icon: 'file-signature' }"></ng-container>
|
||||
<ng-container *ngTemplateOutlet="docTile; context: { slot: viewDetails.docs.idProof, label: 'Identity Proof', icon: 'id-card' }"></ng-container>
|
||||
<ng-container *ngTemplateOutlet="docTile; context: { slot: viewDetails.docs.addressProof, label: 'Address Proof', icon: 'home' }"></ng-container>
|
||||
<ng-container
|
||||
*ngTemplateOutlet="
|
||||
docTile;
|
||||
context: { slot: viewDetails.docs.photo, label: 'Photo / Image of Client', icon: 'user-circle' }
|
||||
"
|
||||
></ng-container>
|
||||
<ng-container
|
||||
*ngTemplateOutlet="
|
||||
docTile;
|
||||
context: { slot: viewDetails.docs.signature, label: 'Signature of Client', icon: 'file-signature' }
|
||||
"
|
||||
></ng-container>
|
||||
<ng-container
|
||||
*ngTemplateOutlet="
|
||||
docTile;
|
||||
context: { slot: viewDetails.docs.idProof, label: 'Identity Proof', icon: 'id-card' }
|
||||
"
|
||||
></ng-container>
|
||||
<ng-container
|
||||
*ngTemplateOutlet="
|
||||
docTile;
|
||||
context: { slot: viewDetails.docs.addressProof, label: 'Address Proof', icon: 'home' }
|
||||
"
|
||||
></ng-container>
|
||||
</div>
|
||||
|
||||
<ng-template #docTile let-slot="slot" let-label="label" let-icon="icon">
|
||||
@@ -432,10 +458,7 @@
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<fa-icon
|
||||
class="rm-doc__state"
|
||||
[icon]="slot.present ? 'check-circle' : 'times-circle'"
|
||||
></fa-icon>
|
||||
<fa-icon class="rm-doc__state" [icon]="slot.present ? 'check-circle' : 'times-circle'"></fa-icon>
|
||||
</div>
|
||||
|
||||
@if (slot.files?.length) {
|
||||
@@ -475,9 +498,7 @@
|
||||
</div>
|
||||
|
||||
<div class="rm-modal__footer">
|
||||
<button class="rm-btn rm-btn--ghost" (click)="closeView()">
|
||||
<fa-icon icon="times"></fa-icon> Close
|
||||
</button>
|
||||
<button class="rm-btn rm-btn--ghost" (click)="closeView()"><fa-icon icon="times"></fa-icon> Close</button>
|
||||
<button class="rm-btn rm-btn--primary" (click)="collectCkycFromView()">
|
||||
<fa-icon icon="cloud-upload-alt"></fa-icon>
|
||||
<span>Collect CKYC</span>
|
||||
@@ -554,9 +575,7 @@
|
||||
}
|
||||
</div>
|
||||
<div class="rm-step__content">
|
||||
<div class="rm-step__label">
|
||||
<fa-icon [icon]="phase.icon"></fa-icon> {{ phase.label }}
|
||||
</div>
|
||||
<div class="rm-step__label"><fa-icon [icon]="phase.icon"></fa-icon> {{ phase.label }}</div>
|
||||
@if (phase.message) {
|
||||
<div class="rm-step__msg">{{ phase.message }}</div>
|
||||
}
|
||||
@@ -677,7 +696,10 @@
|
||||
class="rm-manifest__chip"
|
||||
[class.rm-manifest__chip--ok]="entry.idProof?.packaged"
|
||||
[class.rm-manifest__chip--miss]="!entry.idProof?.packaged"
|
||||
[title]="(entry.idProof?.type || 'Identity Proof') + (entry.idProof?.zipEntry ? ' — ' + entry.idProof.zipEntry : '')"
|
||||
[title]="
|
||||
(entry.idProof?.type || 'Identity Proof') +
|
||||
(entry.idProof?.zipEntry ? ' — ' + entry.idProof.zipEntry : '')
|
||||
"
|
||||
>
|
||||
<fa-icon [icon]="entry.idProof?.packaged ? 'check' : 'times'"></fa-icon>
|
||||
</span>
|
||||
@@ -687,13 +709,23 @@
|
||||
class="rm-manifest__chip"
|
||||
[class.rm-manifest__chip--ok]="entry.addressProof?.packaged"
|
||||
[class.rm-manifest__chip--miss]="!entry.addressProof?.packaged"
|
||||
[title]="(entry.addressProof?.type || 'Address Proof') + (entry.addressProof?.zipEntry ? ' — ' + entry.addressProof.zipEntry : '')"
|
||||
[title]="
|
||||
(entry.addressProof?.type || 'Address Proof') +
|
||||
(entry.addressProof?.zipEntry ? ' — ' + entry.addressProof.zipEntry : '')
|
||||
"
|
||||
>
|
||||
<fa-icon [icon]="entry.addressProof?.packaged ? 'check' : 'times'"></fa-icon>
|
||||
</span>
|
||||
</td>
|
||||
<td class="rm-manifest__td-right rm-manifest__size">
|
||||
{{ formatBytes((entry.photo?.bytes || 0) + (entry.signature?.bytes || 0) + (entry.idProof?.bytes || 0) + (entry.addressProof?.bytes || 0)) }}
|
||||
{{
|
||||
formatBytes(
|
||||
(entry.photo?.bytes || 0) +
|
||||
(entry.signature?.bytes || 0) +
|
||||
(entry.idProof?.bytes || 0) +
|
||||
(entry.addressProof?.bytes || 0)
|
||||
)
|
||||
}}
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
@@ -718,14 +750,14 @@
|
||||
|
||||
<div class="rm-modal__footer">
|
||||
<button class="rm-btn rm-btn--ghost" [disabled]="zipRunning" (click)="closeZipDialog()">
|
||||
@if (zipResult && !zipResult.error) { Close } @else { Cancel }
|
||||
@if (zipResult && !zipResult.error) {
|
||||
Close
|
||||
} @else {
|
||||
Cancel
|
||||
}
|
||||
</button>
|
||||
@if (!zipResult || zipResult.error) {
|
||||
<button
|
||||
class="rm-btn rm-btn--amber"
|
||||
[disabled]="zipRunning"
|
||||
(click)="startZipCreation()"
|
||||
>
|
||||
<button class="rm-btn rm-btn--amber" [disabled]="zipRunning" (click)="startZipCreation()">
|
||||
@if (zipRunning) {
|
||||
<fa-icon icon="spinner" animation="spin"></fa-icon>
|
||||
<span>Creating…</span>
|
||||
@@ -762,9 +794,8 @@
|
||||
|
||||
<div class="rm-modal__body">
|
||||
<p class="rm-modal__intro">
|
||||
Transferring batch to the <strong>CERSAI SFTP</strong> drop server and waiting for
|
||||
acknowledgement. On success, the returned <strong>CKYC numbers</strong> will be saved
|
||||
against each client's master record.
|
||||
Transferring batch to the <strong>CERSAI SFTP</strong> drop server and waiting for acknowledgement. On
|
||||
success, the returned <strong>CKYC numbers</strong> will be saved against each client's master record.
|
||||
</p>
|
||||
|
||||
@if (uploadResult?.status === 'ALREADY_UPLOADED') {
|
||||
@@ -798,9 +829,7 @@
|
||||
}
|
||||
</div>
|
||||
<div class="rm-step__content">
|
||||
<div class="rm-step__label">
|
||||
<fa-icon [icon]="phase.icon"></fa-icon> {{ phase.label }}
|
||||
</div>
|
||||
<div class="rm-step__label"><fa-icon [icon]="phase.icon"></fa-icon> {{ phase.label }}</div>
|
||||
@if (phase.message) {
|
||||
<div class="rm-step__msg">{{ phase.message }}</div>
|
||||
}
|
||||
@@ -824,7 +853,9 @@
|
||||
<div class="rm-modal__result-grid">
|
||||
<div>
|
||||
<span class="rm-modal__result-label">CERSAI Reference</span>
|
||||
<span class="rm-modal__result-value rm-modal__result-value--mono">{{ uploadResult.cersaiReference }}</span>
|
||||
<span class="rm-modal__result-value rm-modal__result-value--mono">{{
|
||||
uploadResult.cersaiReference
|
||||
}}</span>
|
||||
</div>
|
||||
<div>
|
||||
<span class="rm-modal__result-label">Batch ID</span>
|
||||
@@ -860,11 +891,15 @@
|
||||
</div>
|
||||
<div class="rm-modal__result-grid-span">
|
||||
<span class="rm-modal__result-label">Checksum sent</span>
|
||||
<span class="rm-modal__result-value rm-modal__result-value--mono">{{ uploadResult.checksumSent }}</span>
|
||||
<span class="rm-modal__result-value rm-modal__result-value--mono">{{
|
||||
uploadResult.checksumSent
|
||||
}}</span>
|
||||
</div>
|
||||
<div class="rm-modal__result-grid-span">
|
||||
<span class="rm-modal__result-label">Checksum verified</span>
|
||||
<span class="rm-modal__result-value rm-modal__result-value--mono">{{ uploadResult.checksumVerified }}</span>
|
||||
<span class="rm-modal__result-value rm-modal__result-value--mono">{{
|
||||
uploadResult.checksumVerified
|
||||
}}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -918,14 +953,14 @@
|
||||
|
||||
<div class="rm-modal__footer">
|
||||
<button class="rm-btn rm-btn--ghost" [disabled]="uploadRunning" (click)="closeUploadDialog()">
|
||||
@if (uploadResult && !uploadResult.error) { Close } @else { Cancel }
|
||||
@if (uploadResult && !uploadResult.error) {
|
||||
Close
|
||||
} @else {
|
||||
Cancel
|
||||
}
|
||||
</button>
|
||||
@if (!uploadResult || uploadResult.error) {
|
||||
<button
|
||||
class="rm-btn rm-btn--amber"
|
||||
[disabled]="uploadRunning"
|
||||
(click)="startSftpUpload()"
|
||||
>
|
||||
<button class="rm-btn rm-btn--amber" [disabled]="uploadRunning" (click)="startSftpUpload()">
|
||||
@if (uploadRunning) {
|
||||
<fa-icon icon="spinner" animation="spin"></fa-icon>
|
||||
<span>Uploading…</span>
|
||||
|
||||
@@ -24,7 +24,9 @@ $amber: #ba7517;
|
||||
$amber-light: #faeeda;
|
||||
$teal-900: #085041;
|
||||
|
||||
.rm-container { padding: 20px 24px 40px; }
|
||||
.rm-container {
|
||||
padding: 20px 24px 40px;
|
||||
}
|
||||
|
||||
.rm-header {
|
||||
display: flex;
|
||||
@@ -51,7 +53,7 @@ $teal-900: #085041;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 18px;
|
||||
box-shadow: 0 3px 10px rgba(26, 115, 232, 0.25);
|
||||
box-shadow: 0 3px 10px rgb(26 115 232 / 25%);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
@@ -84,9 +86,14 @@ $teal-900: #085041;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
|
||||
fa-icon { font-size: 13px; }
|
||||
fa-icon {
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
&--ghost { color: $gray-700 !important; border-color: $gray-200 !important; }
|
||||
&--ghost {
|
||||
color: $gray-700 !important;
|
||||
border-color: $gray-200 !important;
|
||||
}
|
||||
|
||||
&--primary {
|
||||
box-shadow: 0 1px 3px rgba($primary, 0.3) !important;
|
||||
@@ -102,7 +109,7 @@ $teal-900: #085041;
|
||||
border: 1px solid $gray-200 !important;
|
||||
border-radius: 14px !important;
|
||||
padding: 8px !important;
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.03) !important;
|
||||
box-shadow: 0 1px 3px rgb(0 0 0 / 3%) !important;
|
||||
}
|
||||
|
||||
button.rm-tab,
|
||||
@@ -147,7 +154,9 @@ button.rm-tab,
|
||||
background: $gray-50 !important;
|
||||
color: $gray-900 !important;
|
||||
|
||||
fa-icon { color: $primary !important; }
|
||||
fa-icon {
|
||||
color: $primary !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -175,7 +184,9 @@ button.rm-tab.rm-tab--active,
|
||||
color: $primary-dark !important;
|
||||
box-shadow: 0 2px 8px rgba($primary, 0.12) !important;
|
||||
|
||||
fa-icon { color: $primary !important; }
|
||||
fa-icon {
|
||||
color: $primary !important;
|
||||
}
|
||||
|
||||
.rm-tab__count {
|
||||
background: $primary !important;
|
||||
@@ -190,7 +201,9 @@ button.rm-tab--amber.rm-tab--active,
|
||||
color: #633806 !important;
|
||||
box-shadow: 0 2px 8px rgba($amber, 0.18) !important;
|
||||
|
||||
fa-icon { color: $amber !important; }
|
||||
fa-icon {
|
||||
color: $amber !important;
|
||||
}
|
||||
|
||||
.rm-tab__count {
|
||||
background: $amber !important;
|
||||
@@ -205,7 +218,9 @@ button.rm-tab--green.rm-tab--active,
|
||||
color: $teal-900 !important;
|
||||
box-shadow: 0 2px 8px rgba($green, 0.18) !important;
|
||||
|
||||
fa-icon { color: $green !important; }
|
||||
fa-icon {
|
||||
color: $green !important;
|
||||
}
|
||||
|
||||
.rm-tab__count {
|
||||
background: $green !important;
|
||||
@@ -214,8 +229,13 @@ button.rm-tab--green.rm-tab--active,
|
||||
}
|
||||
|
||||
@media (width <= 680px) {
|
||||
.rm-tabs { flex-direction: column; }
|
||||
.rm-tab { justify-content: space-between; }
|
||||
.rm-tabs {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.rm-tab {
|
||||
justify-content: space-between;
|
||||
}
|
||||
}
|
||||
|
||||
/* Filters */
|
||||
@@ -294,7 +314,10 @@ button.rm-tab--green.rm-tab--active,
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
|
||||
fa-icon { color: $primary; font-size: 11px; }
|
||||
fa-icon {
|
||||
color: $primary;
|
||||
font-size: 11px;
|
||||
}
|
||||
}
|
||||
|
||||
.rm-input {
|
||||
@@ -310,18 +333,20 @@ button.rm-tab--green.rm-tab--active,
|
||||
box-sizing: border-box;
|
||||
transition: all 0.15s;
|
||||
|
||||
&::placeholder { color: $gray-500; }
|
||||
&::placeholder {
|
||||
color: $gray-500;
|
||||
}
|
||||
|
||||
&:focus {
|
||||
outline: none;
|
||||
border-color: $primary;
|
||||
background: $white;
|
||||
box-shadow: 0 0 0 3px rgba(26, 115, 232, 0.1);
|
||||
box-shadow: 0 0 0 3px rgb(26 115 232 / 10%);
|
||||
}
|
||||
|
||||
&--sm {
|
||||
width: auto;
|
||||
padding: 6px 10px 6px 10px;
|
||||
padding: 6px 10px;
|
||||
font-size: 12px;
|
||||
height: 30px;
|
||||
}
|
||||
@@ -330,8 +355,6 @@ button.rm-tab--green.rm-tab--active,
|
||||
/* Native <select> — custom chevron, no overlap */
|
||||
select.rm-input {
|
||||
appearance: none;
|
||||
-webkit-appearance: none;
|
||||
-moz-appearance: none;
|
||||
padding-right: 34px;
|
||||
background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'><path fill='%235f6368' d='M0 0l5 6 5-6z'/></svg>");
|
||||
background-repeat: no-repeat;
|
||||
@@ -344,6 +367,7 @@ select.rm-input {
|
||||
}
|
||||
|
||||
/* Native date input — cleaner calendar icon */
|
||||
/* stylelint-disable-next-line selector-no-qualifying-type */
|
||||
input[type='date'].rm-input {
|
||||
padding-right: 10px;
|
||||
|
||||
@@ -354,11 +378,23 @@ input[type='date'].rm-input {
|
||||
transition: opacity 0.15s;
|
||||
}
|
||||
|
||||
&:hover::-webkit-calendar-picker-indicator { opacity: 0.9; }
|
||||
&:hover::-webkit-calendar-picker-indicator {
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
&::-webkit-datetime-edit { color: $gray-900; padding-top: 1px; }
|
||||
&::-webkit-datetime-edit-fields-wrapper { color: $gray-900; }
|
||||
&::-webkit-datetime-edit-text { color: $gray-500; padding: 0 2px; }
|
||||
&::-webkit-datetime-edit {
|
||||
color: $gray-900;
|
||||
padding-top: 1px;
|
||||
}
|
||||
|
||||
&::-webkit-datetime-edit-fields-wrapper {
|
||||
color: $gray-900;
|
||||
}
|
||||
|
||||
&::-webkit-datetime-edit-text {
|
||||
color: $gray-500;
|
||||
padding: 0 2px;
|
||||
}
|
||||
|
||||
&:not(:focus):placeholder-shown::-webkit-datetime-edit {
|
||||
color: $gray-500;
|
||||
@@ -381,7 +417,9 @@ input[type='date'].rm-input {
|
||||
background: $primary-light;
|
||||
border: 1px dashed rgba($primary, 0.3);
|
||||
|
||||
fa-icon { font-size: 20px; }
|
||||
fa-icon {
|
||||
font-size: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
&--error {
|
||||
@@ -389,7 +427,9 @@ input[type='date'].rm-input {
|
||||
background: $red-light;
|
||||
border: 1px solid rgba($red, 0.3);
|
||||
|
||||
fa-icon { font-size: 18px; }
|
||||
fa-icon {
|
||||
font-size: 18px;
|
||||
}
|
||||
}
|
||||
|
||||
&--empty {
|
||||
@@ -397,7 +437,9 @@ input[type='date'].rm-input {
|
||||
background: $gray-50;
|
||||
border: 1px dashed $gray-200;
|
||||
|
||||
fa-icon { color: $green; }
|
||||
fa-icon {
|
||||
color: $green;
|
||||
}
|
||||
|
||||
h3 {
|
||||
margin: 4px 0 0;
|
||||
@@ -406,7 +448,9 @@ input[type='date'].rm-input {
|
||||
color: $gray-900;
|
||||
}
|
||||
|
||||
p { margin: 0; }
|
||||
p {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -433,9 +477,14 @@ input[type='date'].rm-input {
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
|
||||
fa-icon { color: $primary; }
|
||||
fa-icon {
|
||||
color: $primary;
|
||||
}
|
||||
|
||||
strong { color: $gray-900; font-weight: 700; }
|
||||
strong {
|
||||
color: $gray-900;
|
||||
font-weight: 700;
|
||||
}
|
||||
}
|
||||
|
||||
&__selected {
|
||||
@@ -449,7 +498,9 @@ input[type='date'].rm-input {
|
||||
border-radius: 14px;
|
||||
border: 1px solid rgba($green, 0.3);
|
||||
|
||||
strong { font-weight: 700; }
|
||||
strong {
|
||||
font-weight: 700;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -490,12 +541,15 @@ input[type='date'].rm-input {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
tbody tr:last-child td { border-bottom: none; }
|
||||
tbody tr:last-child td {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
&__check {
|
||||
width: 42px;
|
||||
text-align: center;
|
||||
|
||||
/* stylelint-disable-next-line selector-no-qualifying-type */
|
||||
input[type='checkbox'] {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
@@ -504,21 +558,35 @@ input[type='date'].rm-input {
|
||||
}
|
||||
}
|
||||
|
||||
&__artefacts { width: 180px; }
|
||||
&__completion { width: 90px; text-align: center; }
|
||||
&__actions { width: 90px; text-align: right; }
|
||||
&__artefacts {
|
||||
width: 180px;
|
||||
}
|
||||
|
||||
&__completion {
|
||||
width: 90px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
&__actions {
|
||||
width: 90px;
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
|
||||
.rm-row {
|
||||
cursor: pointer;
|
||||
transition: background 0.12s;
|
||||
|
||||
&:hover { background: #f5f8fc; }
|
||||
&:hover {
|
||||
background: #f5f8fc;
|
||||
}
|
||||
|
||||
&--selected {
|
||||
background: $primary-light !important;
|
||||
|
||||
td { border-bottom-color: rgba($primary, 0.15); }
|
||||
td {
|
||||
border-bottom-color: rgba($primary, 0.15);
|
||||
}
|
||||
}
|
||||
|
||||
&__client {
|
||||
@@ -542,7 +610,9 @@ input[type='date'].rm-input {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
&__ident { min-width: 0; }
|
||||
&__ident {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
&__name {
|
||||
font-size: 13px;
|
||||
@@ -560,7 +630,9 @@ input[type='date'].rm-input {
|
||||
font-family: 'SF Mono', Consolas, monospace;
|
||||
margin-top: 1px;
|
||||
|
||||
fa-icon { font-size: 9px; }
|
||||
fa-icon {
|
||||
font-size: 9px;
|
||||
}
|
||||
}
|
||||
|
||||
&__line {
|
||||
@@ -570,9 +642,14 @@ input[type='date'].rm-input {
|
||||
font-size: 12px;
|
||||
color: $gray-700;
|
||||
|
||||
+ .rm-row__line { margin-top: 3px; }
|
||||
+ .rm-row__line {
|
||||
margin-top: 3px;
|
||||
}
|
||||
|
||||
fa-icon { color: $primary; font-size: 10px; }
|
||||
fa-icon {
|
||||
color: $primary;
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
span {
|
||||
white-space: nowrap;
|
||||
@@ -630,10 +707,21 @@ input[type='date'].rm-input {
|
||||
font-weight: 700;
|
||||
border: 1px solid transparent;
|
||||
|
||||
fa-icon { font-size: 8px; }
|
||||
fa-icon {
|
||||
font-size: 8px;
|
||||
}
|
||||
|
||||
&--ok { background: $green-light; color: $teal-900; border-color: rgba($green, 0.2); }
|
||||
&--miss { background: $red-light; color: $red; border-color: rgba($red, 0.2); }
|
||||
&--ok {
|
||||
background: $green-light;
|
||||
color: $teal-900;
|
||||
border-color: rgba($green, 0.2);
|
||||
}
|
||||
|
||||
&--miss {
|
||||
background: $red-light;
|
||||
color: $red;
|
||||
border-color: rgba($red, 0.2);
|
||||
}
|
||||
}
|
||||
|
||||
.rm-action-btn {
|
||||
@@ -647,7 +735,9 @@ input[type='date'].rm-input {
|
||||
margin-left: 4px;
|
||||
transition: all 0.12s;
|
||||
|
||||
fa-icon { font-size: 11px; }
|
||||
fa-icon {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: $primary-light;
|
||||
@@ -681,7 +771,7 @@ input[type='date'].rm-input {
|
||||
background: $gray-900;
|
||||
color: $white;
|
||||
border-radius: 14px;
|
||||
box-shadow: 0 12px 40px rgba(0, 0, 0, 0.25);
|
||||
box-shadow: 0 12px 40px rgb(0 0 0 / 25%);
|
||||
max-width: calc(100vw - 40px);
|
||||
animation: rm-slide-up 0.25s ease-out;
|
||||
|
||||
@@ -697,8 +787,15 @@ input[type='date'].rm-input {
|
||||
gap: 8px;
|
||||
font-size: 13px;
|
||||
|
||||
fa-icon { color: #9fe1cb; }
|
||||
strong { color: $white; font-weight: 700; font-size: 15px; }
|
||||
fa-icon {
|
||||
color: #9fe1cb;
|
||||
}
|
||||
|
||||
strong {
|
||||
color: $white;
|
||||
font-weight: 700;
|
||||
font-size: 15px;
|
||||
}
|
||||
}
|
||||
|
||||
&__warn {
|
||||
@@ -712,7 +809,9 @@ input[type='date'].rm-input {
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
|
||||
fa-icon { font-size: 10px; }
|
||||
fa-icon {
|
||||
font-size: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
&__btns {
|
||||
@@ -723,17 +822,24 @@ input[type='date'].rm-input {
|
||||
}
|
||||
|
||||
@keyframes rm-slide-up {
|
||||
from { transform: translate(-50%, 20px); opacity: 0; }
|
||||
to { transform: translate(-50%, 0); opacity: 1; }
|
||||
from {
|
||||
transform: translate(-50%, 20px);
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
to {
|
||||
transform: translate(-50%, 0);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.rm-btn--ghost-light {
|
||||
background: rgba(255, 255, 255, 0.1) !important;
|
||||
background: rgb(255 255 255 / 10%) !important;
|
||||
color: $white !important;
|
||||
border-color: rgba(255, 255, 255, 0.2) !important;
|
||||
border-color: rgb(255 255 255 / 20%) !important;
|
||||
|
||||
&:hover {
|
||||
background: rgba(255, 255, 255, 0.2) !important;
|
||||
background: rgb(255 255 255 / 20%) !important;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -742,14 +848,16 @@ input[type='date'].rm-input {
|
||||
color: $white !important;
|
||||
border: none !important;
|
||||
|
||||
&:hover { background: #a0651a !important; }
|
||||
&:hover {
|
||||
background: #a0651a !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* ---------- Modal ---------- */
|
||||
.rm-modal {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
background: rgba(15, 25, 45, 0.55);
|
||||
background: rgb(15 25 45 / 55%);
|
||||
backdrop-filter: blur(3px);
|
||||
z-index: 1200;
|
||||
display: flex;
|
||||
@@ -767,7 +875,7 @@ input[type='date'].rm-input {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 24px 60px rgba(0, 0, 0, 0.25);
|
||||
box-shadow: 0 24px 60px rgb(0 0 0 / 25%);
|
||||
animation: rm-zoom 0.25s ease-out;
|
||||
}
|
||||
|
||||
@@ -817,7 +925,10 @@ input[type='date'].rm-input {
|
||||
background: $red-light;
|
||||
}
|
||||
|
||||
&:disabled { opacity: 0.4; cursor: not-allowed; }
|
||||
&:disabled {
|
||||
opacity: 0.4;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
}
|
||||
|
||||
&__body {
|
||||
@@ -832,7 +943,10 @@ input[type='date'].rm-input {
|
||||
line-height: 1.55;
|
||||
margin: 0 0 16px;
|
||||
|
||||
strong { color: $gray-900; font-weight: 700; }
|
||||
strong {
|
||||
color: $gray-900;
|
||||
font-weight: 700;
|
||||
}
|
||||
}
|
||||
|
||||
&__warn {
|
||||
@@ -848,9 +962,18 @@ input[type='date'].rm-input {
|
||||
color: #633806;
|
||||
line-height: 1.5;
|
||||
|
||||
fa-icon { color: $amber; font-size: 14px; flex-shrink: 0; margin-top: 2px; }
|
||||
fa-icon {
|
||||
color: $amber;
|
||||
font-size: 14px;
|
||||
flex-shrink: 0;
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
strong { display: block; font-weight: 700; color: #4d2a05; }
|
||||
strong {
|
||||
display: block;
|
||||
font-weight: 700;
|
||||
color: #4d2a05;
|
||||
}
|
||||
}
|
||||
|
||||
&__result {
|
||||
@@ -870,7 +993,10 @@ input[type='date'].rm-input {
|
||||
color: $teal-900;
|
||||
margin-bottom: 12px;
|
||||
|
||||
fa-icon { color: $green; font-size: 16px; }
|
||||
fa-icon {
|
||||
color: $green;
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
&__result-grid {
|
||||
@@ -920,7 +1046,12 @@ input[type='date'].rm-input {
|
||||
font-size: 12px;
|
||||
color: #5a1a0e;
|
||||
|
||||
fa-icon { color: $red; font-size: 14px; flex-shrink: 0; margin-top: 2px; }
|
||||
fa-icon {
|
||||
color: $red;
|
||||
font-size: 14px;
|
||||
flex-shrink: 0;
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
strong {
|
||||
display: block;
|
||||
@@ -953,7 +1084,9 @@ input[type='date'].rm-input {
|
||||
text-align: left;
|
||||
transition: background 0.15s;
|
||||
|
||||
&:hover { background: $gray-100; }
|
||||
&:hover {
|
||||
background: $gray-100;
|
||||
}
|
||||
|
||||
> fa-icon:first-child {
|
||||
color: $primary;
|
||||
@@ -965,7 +1098,11 @@ input[type='date'].rm-input {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
|
||||
strong { color: $gray-900; font-weight: 700; margin-right: 4px; }
|
||||
strong {
|
||||
color: $gray-900;
|
||||
font-weight: 700;
|
||||
margin-right: 4px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -986,7 +1123,9 @@ input[type='date'].rm-input {
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.3px;
|
||||
|
||||
fa-icon { font-size: 9px; }
|
||||
fa-icon {
|
||||
font-size: 9px;
|
||||
}
|
||||
|
||||
&--full {
|
||||
background: $green-light;
|
||||
@@ -1037,14 +1176,31 @@ input[type='date'].rm-input {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
tbody tr:last-child td { border-bottom: none; }
|
||||
tbody tr:hover { background: #f5f8fc; }
|
||||
tbody tr:last-child td {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
tbody tr:hover {
|
||||
background: #f5f8fc;
|
||||
}
|
||||
}
|
||||
|
||||
&__th-center { text-align: center !important; width: 38px; }
|
||||
&__th-right { text-align: right !important; }
|
||||
&__td-center { text-align: center; }
|
||||
&__td-right { text-align: right; }
|
||||
&__th-center {
|
||||
text-align: center !important;
|
||||
width: 38px;
|
||||
}
|
||||
|
||||
&__th-right {
|
||||
text-align: right !important;
|
||||
}
|
||||
|
||||
&__td-center {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
&__td-right {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
&__client {
|
||||
display: flex;
|
||||
@@ -1063,7 +1219,10 @@ input[type='date'].rm-input {
|
||||
color: $gray-500;
|
||||
font-family: 'SF Mono', Consolas, monospace;
|
||||
|
||||
fa-icon { font-size: 8px; margin-right: 2px; }
|
||||
fa-icon {
|
||||
font-size: 8px;
|
||||
margin-right: 2px;
|
||||
}
|
||||
}
|
||||
|
||||
&__chip {
|
||||
@@ -1165,7 +1324,10 @@ input[type='date'].rm-input {
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
|
||||
fa-icon { font-size: 12px; color: $gray-500; }
|
||||
fa-icon {
|
||||
font-size: 12px;
|
||||
color: $gray-500;
|
||||
}
|
||||
}
|
||||
|
||||
&__msg {
|
||||
@@ -1185,7 +1347,9 @@ input[type='date'].rm-input {
|
||||
.rm-step__label {
|
||||
color: $primary;
|
||||
|
||||
fa-icon { color: $primary; }
|
||||
fa-icon {
|
||||
color: $primary;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1196,12 +1360,16 @@ input[type='date'].rm-input {
|
||||
color: $white;
|
||||
}
|
||||
|
||||
&::after { background: $green; }
|
||||
&::after {
|
||||
background: $green;
|
||||
}
|
||||
|
||||
.rm-step__label {
|
||||
color: $teal-900;
|
||||
|
||||
fa-icon { color: $green; }
|
||||
fa-icon {
|
||||
color: $green;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1212,18 +1380,32 @@ input[type='date'].rm-input {
|
||||
color: $white;
|
||||
}
|
||||
|
||||
.rm-step__label { color: $red; }
|
||||
.rm-step__label {
|
||||
color: $red;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes rm-fade {
|
||||
from { opacity: 0; }
|
||||
to { opacity: 1; }
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes rm-zoom {
|
||||
from { transform: scale(0.95); opacity: 0; }
|
||||
to { transform: scale(1); opacity: 1; }
|
||||
from {
|
||||
transform: scale(0.95);
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
to {
|
||||
transform: scale(1);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* ---------- View details modal ---------- */
|
||||
@@ -1238,7 +1420,7 @@ input[type='date'].rm-input {
|
||||
|
||||
.rm-modal__title--teal fa-icon {
|
||||
background: linear-gradient(135deg, #0f6e56, #1d9e75) !important;
|
||||
box-shadow: 0 3px 10px rgba(29, 158, 117, 0.3) !important;
|
||||
box-shadow: 0 3px 10px rgb(29 158 117 / 30%) !important;
|
||||
}
|
||||
|
||||
.rm-modal__warn--blue {
|
||||
@@ -1247,8 +1429,13 @@ input[type='date'].rm-input {
|
||||
border-left-color: $primary !important;
|
||||
color: #0c447c !important;
|
||||
|
||||
fa-icon { color: $primary !important; }
|
||||
strong { color: #0c447c !important; }
|
||||
fa-icon {
|
||||
color: $primary !important;
|
||||
}
|
||||
|
||||
strong {
|
||||
color: #0c447c !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* CKYC numbers table */
|
||||
@@ -1272,7 +1459,10 @@ input[type='date'].rm-input {
|
||||
gap: 6px;
|
||||
border-bottom: 1px solid rgba($green, 0.2);
|
||||
|
||||
fa-icon { color: $green; font-size: 12px; }
|
||||
fa-icon {
|
||||
color: $green;
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
&__table-wrap {
|
||||
@@ -1308,7 +1498,9 @@ input[type='date'].rm-input {
|
||||
color: $gray-900;
|
||||
}
|
||||
|
||||
tbody tr:last-child td { border-bottom: none; }
|
||||
tbody tr:last-child td {
|
||||
border-bottom: none;
|
||||
}
|
||||
}
|
||||
|
||||
&__mono {
|
||||
@@ -1323,7 +1515,10 @@ input[type='date'].rm-input {
|
||||
background: rgba($green, 0.06);
|
||||
}
|
||||
|
||||
&__status { text-align: right; white-space: nowrap; }
|
||||
&__status {
|
||||
text-align: right;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
.rm-view {
|
||||
@@ -1353,7 +1548,10 @@ input[type='date'].rm-input {
|
||||
box-shadow: 0 3px 10px rgba($primary, 0.25);
|
||||
}
|
||||
|
||||
&__client-info { min-width: 0; flex: 1; }
|
||||
&__client-info {
|
||||
min-width: 0;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
&__client-name {
|
||||
font-size: 16px;
|
||||
@@ -1370,7 +1568,11 @@ input[type='date'].rm-input {
|
||||
flex-wrap: wrap;
|
||||
gap: 6px;
|
||||
|
||||
fa-icon { color: $primary; font-size: 10px; margin-right: 3px; }
|
||||
fa-icon {
|
||||
color: $primary;
|
||||
font-size: 10px;
|
||||
margin-right: 3px;
|
||||
}
|
||||
}
|
||||
|
||||
&__cif {
|
||||
@@ -1381,7 +1583,10 @@ input[type='date'].rm-input {
|
||||
}
|
||||
}
|
||||
|
||||
&__sep { color: $gray-200; margin: 0 2px; }
|
||||
&__sep {
|
||||
color: $gray-200;
|
||||
margin: 0 2px;
|
||||
}
|
||||
|
||||
&__loading {
|
||||
padding: 10px 14px;
|
||||
@@ -1394,7 +1599,9 @@ input[type='date'].rm-input {
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
|
||||
fa-icon { font-size: 14px; }
|
||||
fa-icon {
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
&__section-title {
|
||||
@@ -1408,9 +1615,14 @@ input[type='date'].rm-input {
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
|
||||
fa-icon { color: $primary; font-size: 12px; }
|
||||
fa-icon {
|
||||
color: $primary;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
&:first-of-type { margin-top: 6px; }
|
||||
&:first-of-type {
|
||||
margin-top: 6px;
|
||||
}
|
||||
}
|
||||
|
||||
&__kpis {
|
||||
@@ -1445,7 +1657,10 @@ input[type='date'].rm-input {
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
|
||||
fa-icon { color: $primary; font-size: 10px; }
|
||||
fa-icon {
|
||||
color: $primary;
|
||||
font-size: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
&__value {
|
||||
@@ -1454,7 +1669,11 @@ input[type='date'].rm-input {
|
||||
color: $gray-900;
|
||||
}
|
||||
|
||||
&__missing { color: $gray-500; font-style: italic; font-weight: 500; }
|
||||
&__missing {
|
||||
color: $gray-500;
|
||||
font-style: italic;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
&__pill {
|
||||
display: inline-flex;
|
||||
@@ -1515,7 +1734,10 @@ input[type='date'].rm-input {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
&__body { flex: 1; min-width: 0; }
|
||||
&__body {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
&__label {
|
||||
font-size: 10px;
|
||||
@@ -1570,7 +1792,10 @@ input[type='date'].rm-input {
|
||||
color: $gray-500;
|
||||
font-style: italic;
|
||||
|
||||
fa-icon { color: $primary; font-size: 11px; }
|
||||
fa-icon {
|
||||
color: $primary;
|
||||
font-size: 11px;
|
||||
}
|
||||
}
|
||||
|
||||
&--ok {
|
||||
@@ -1582,8 +1807,13 @@ input[type='date'].rm-input {
|
||||
color: $teal-900;
|
||||
}
|
||||
|
||||
.rm-doc__state { color: $green; }
|
||||
.rm-doc__files { border-top-color: rgba($green, 0.2); }
|
||||
.rm-doc__state {
|
||||
color: $green;
|
||||
}
|
||||
|
||||
.rm-doc__files {
|
||||
border-top-color: rgba($green, 0.2);
|
||||
}
|
||||
}
|
||||
|
||||
&--miss {
|
||||
@@ -1595,7 +1825,9 @@ input[type='date'].rm-input {
|
||||
color: $red;
|
||||
}
|
||||
|
||||
.rm-doc__state { color: $red; }
|
||||
.rm-doc__state {
|
||||
color: $red;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1615,7 +1847,10 @@ input[type='date'].rm-input {
|
||||
border-color: $primary;
|
||||
box-shadow: 0 2px 6px rgba($primary, 0.1);
|
||||
|
||||
.rm-file__open { opacity: 1; color: $primary; }
|
||||
.rm-file__open {
|
||||
opacity: 1;
|
||||
color: $primary;
|
||||
}
|
||||
}
|
||||
|
||||
&__thumb {
|
||||
@@ -1707,7 +1942,7 @@ input[type='date'].rm-input {
|
||||
|
||||
&:hover {
|
||||
border-color: $primary-light;
|
||||
box-shadow: 0 4px 14px rgba(26, 115, 232, 0.1);
|
||||
box-shadow: 0 4px 14px rgb(26 115 232 / 10%);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
@@ -1756,7 +1991,10 @@ input[type='date'].rm-input {
|
||||
font-family: 'SF Mono', Consolas, monospace;
|
||||
margin-top: 2px;
|
||||
|
||||
fa-icon { font-size: 10px; margin-right: 2px; }
|
||||
fa-icon {
|
||||
font-size: 10px;
|
||||
margin-right: 2px;
|
||||
}
|
||||
}
|
||||
|
||||
&__completion {
|
||||
@@ -1835,7 +2073,11 @@ input[type='date'].rm-input {
|
||||
color: $gray-700;
|
||||
min-width: 0;
|
||||
|
||||
fa-icon { color: $primary; font-size: 11px; flex-shrink: 0; }
|
||||
fa-icon {
|
||||
color: $primary;
|
||||
font-size: 11px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
span {
|
||||
white-space: nowrap;
|
||||
@@ -1844,31 +2086,9 @@ input[type='date'].rm-input {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
&--missing { color: $gray-500; font-style: italic; }
|
||||
}
|
||||
|
||||
.rm-chip {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
padding: 4px 9px;
|
||||
border-radius: 14px;
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
border: 1px solid transparent;
|
||||
|
||||
fa-icon { font-size: 10px; }
|
||||
|
||||
&--ok {
|
||||
background: $green-light;
|
||||
color: $teal-900;
|
||||
border-color: rgba($green, 0.2);
|
||||
}
|
||||
|
||||
&--miss {
|
||||
background: $red-light;
|
||||
color: $red;
|
||||
border-color: rgba($red, 0.2);
|
||||
&--missing {
|
||||
color: $gray-500;
|
||||
font-style: italic;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1910,13 +2130,28 @@ input[type='date'].rm-input {
|
||||
border-radius: 8px !important;
|
||||
color: $gray-700 !important;
|
||||
|
||||
&:disabled { color: $gray-200 !important; }
|
||||
&:disabled {
|
||||
color: $gray-200 !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (width <= 640px) {
|
||||
.rm-container { padding: 14px; }
|
||||
.rm-header { flex-direction: column; align-items: flex-start; }
|
||||
.rm-cards { grid-template-columns: 1fr; }
|
||||
.rm-pager { flex-direction: column; gap: 12px; }
|
||||
.rm-container {
|
||||
padding: 14px;
|
||||
}
|
||||
|
||||
.rm-header {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.rm-cards {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.rm-pager {
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -221,12 +221,8 @@ export class RecordMasterComponent implements OnInit {
|
||||
.pipe(takeUntilDestroyed(this.destroyRef))
|
||||
.subscribe({
|
||||
next: (res: any) => {
|
||||
(res?.idProofs || []).forEach((p: any) =>
|
||||
this.proofKindMap.set(String(p.id), 'id')
|
||||
);
|
||||
(res?.addressProofs || []).forEach((p: any) =>
|
||||
this.proofKindMap.set(String(p.id), 'address')
|
||||
);
|
||||
(res?.idProofs || []).forEach((p: any) => this.proofKindMap.set(String(p.id), 'id'));
|
||||
(res?.addressProofs || []).forEach((p: any) => this.proofKindMap.set(String(p.id), 'address'));
|
||||
this.proofKindLoaded = true;
|
||||
// If records are already loaded, re-verify them now
|
||||
if (this.rows.length > 0) this.reverifyArtefacts();
|
||||
@@ -256,9 +252,7 @@ export class RecordMasterComponent implements OnInit {
|
||||
const docTypeId = String(idf?.documentType?.id ?? '');
|
||||
const kind = this.proofKindMap.get(docTypeId);
|
||||
const isActive =
|
||||
idf?.status === 'clientIdentifierStatusType.active' ||
|
||||
idf?.status === 'Active' ||
|
||||
!idf?.status;
|
||||
idf?.status === 'clientIdentifierStatusType.active' || idf?.status === 'Active' || !idf?.status;
|
||||
if (!isActive || !kind) continue;
|
||||
if (kind === 'id') hasId = true;
|
||||
else if (kind === 'address') hasAddr = true;
|
||||
@@ -326,9 +320,7 @@ export class RecordMasterComponent implements OnInit {
|
||||
/** Apply the tab's client-side filter and recompute counts */
|
||||
private applyTabFilter(): void {
|
||||
if (this.activeTab === 'ready') {
|
||||
this.rows = this.allRows.filter(
|
||||
(r) => r.hasPhoto && r.hasSignature && r.hasIdProof && r.hasAddressProof
|
||||
);
|
||||
this.rows = this.allRows.filter((r) => r.hasPhoto && r.hasSignature && r.hasIdProof && r.hasAddressProof);
|
||||
} else {
|
||||
this.rows = this.allRows;
|
||||
}
|
||||
@@ -338,12 +330,9 @@ export class RecordMasterComponent implements OnInit {
|
||||
private computeCounts(all: CkycRecordRow[]) {
|
||||
return {
|
||||
all: all.length,
|
||||
pendingCollection: all.filter(
|
||||
(r) => !r.hasPhoto || !r.hasSignature || !r.hasIdProof || !r.hasAddressProof
|
||||
).length,
|
||||
ready: all.filter(
|
||||
(r) => r.hasPhoto && r.hasSignature && r.hasIdProof && r.hasAddressProof
|
||||
).length
|
||||
pendingCollection: all.filter((r) => !r.hasPhoto || !r.hasSignature || !r.hasIdProof || !r.hasAddressProof)
|
||||
.length,
|
||||
ready: all.filter((r) => r.hasPhoto && r.hasSignature && r.hasIdProof && r.hasAddressProof).length
|
||||
};
|
||||
}
|
||||
|
||||
@@ -390,7 +379,11 @@ export class RecordMasterComponent implements OnInit {
|
||||
}
|
||||
|
||||
openClient(row: CkycRecordRow): void {
|
||||
this.router.navigate(['/clients/', row.clientId, 'general']);
|
||||
this.router.navigate([
|
||||
'/clients/',
|
||||
row.clientId,
|
||||
'general'
|
||||
]);
|
||||
}
|
||||
|
||||
openView(row: CkycRecordRow, event?: Event): void {
|
||||
@@ -402,9 +395,17 @@ export class RecordMasterComponent implements OnInit {
|
||||
row,
|
||||
docs: {
|
||||
photo: { present: row.hasPhoto, name: row.hasPhoto ? 'Profile photo' : 'Not uploaded', files: [] },
|
||||
signature: { present: row.hasSignature, name: row.hasSignature ? 'Client signature' : 'Not uploaded', files: [] },
|
||||
signature: {
|
||||
present: row.hasSignature,
|
||||
name: row.hasSignature ? 'Client signature' : 'Not uploaded',
|
||||
files: []
|
||||
},
|
||||
idProof: { present: row.hasIdProof, name: row.hasIdProof ? 'Identifier' : 'Not uploaded', files: [] },
|
||||
addressProof: { present: row.hasAddressProof, name: row.hasAddressProof ? 'Identifier' : 'Not uploaded', files: [] }
|
||||
addressProof: {
|
||||
present: row.hasAddressProof,
|
||||
name: row.hasAddressProof ? 'Identifier' : 'Not uploaded',
|
||||
files: []
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -486,9 +487,7 @@ export class RecordMasterComponent implements OnInit {
|
||||
const docTypeId = String(idf?.documentType?.id ?? '');
|
||||
const kind = this.proofKindMap.get(docTypeId);
|
||||
const isActive =
|
||||
idf?.status === 'clientIdentifierStatusType.active' ||
|
||||
idf?.status === 'Active' ||
|
||||
!idf?.status;
|
||||
idf?.status === 'clientIdentifierStatusType.active' || idf?.status === 'Active' || !idf?.status;
|
||||
if (!isActive || !kind) continue;
|
||||
const proofName = idf?.documentType?.name || 'Identifier';
|
||||
const rawKey = idf?.documentKey || '';
|
||||
@@ -580,7 +579,11 @@ export class RecordMasterComponent implements OnInit {
|
||||
private formatJavaDate(value: any): string {
|
||||
if (!value) return '';
|
||||
if (Array.isArray(value) && value.length >= 3) {
|
||||
const [y, m, d] = value;
|
||||
const [
|
||||
y,
|
||||
m,
|
||||
d
|
||||
] = value;
|
||||
return `${String(d).padStart(2, '0')}-${String(m).padStart(2, '0')}-${y}`;
|
||||
}
|
||||
if (typeof value === 'string') return value;
|
||||
@@ -608,7 +611,11 @@ export class RecordMasterComponent implements OnInit {
|
||||
|
||||
collectCkyc(row: CkycRecordRow, event: Event): void {
|
||||
event.stopPropagation();
|
||||
this.router.navigate(['/clients/', row.clientId, 'ckyc']);
|
||||
this.router.navigate([
|
||||
'/clients/',
|
||||
row.clientId,
|
||||
'ckyc'
|
||||
]);
|
||||
}
|
||||
|
||||
createClient(): void {
|
||||
@@ -616,7 +623,12 @@ export class RecordMasterComponent implements OnInit {
|
||||
}
|
||||
|
||||
artefactCompletion(row: CkycRecordRow): number {
|
||||
const items = [row.hasPhoto, row.hasSignature, row.hasIdProof, row.hasAddressProof];
|
||||
const items = [
|
||||
row.hasPhoto,
|
||||
row.hasSignature,
|
||||
row.hasIdProof,
|
||||
row.hasAddressProof
|
||||
];
|
||||
return items.filter(Boolean).length;
|
||||
}
|
||||
|
||||
@@ -670,7 +682,11 @@ export class RecordMasterComponent implements OnInit {
|
||||
this.zipRunning = false;
|
||||
this.zipResult = null;
|
||||
this.manifestExpanded = false;
|
||||
this.zipPhases = this.zipPhases.map((p) => ({ ...p, status: 'pending' as const, message: undefined as string | undefined }));
|
||||
this.zipPhases = this.zipPhases.map((p) => ({
|
||||
...p,
|
||||
status: 'pending' as const,
|
||||
message: undefined as string | undefined
|
||||
}));
|
||||
}
|
||||
|
||||
closeZipDialog(): void {
|
||||
@@ -683,7 +699,11 @@ export class RecordMasterComponent implements OnInit {
|
||||
this.zipRunning = true;
|
||||
this.zipResult = null;
|
||||
// Reset phases
|
||||
this.zipPhases = this.zipPhases.map((p) => ({ ...p, status: 'pending' as const, message: undefined as string | undefined }));
|
||||
this.zipPhases = this.zipPhases.map((p) => ({
|
||||
...p,
|
||||
status: 'pending' as const,
|
||||
message: undefined as string | undefined
|
||||
}));
|
||||
|
||||
// Kick off visual phase animation for steps 1-4 while the POST is in flight.
|
||||
// Step 5 (Register batch) is held until the API responds.
|
||||
@@ -745,15 +765,21 @@ export class RecordMasterComponent implements OnInit {
|
||||
// Don't override a phase that's already done/failed by the API response
|
||||
if (this.zipPhases[i].status === 'pending') this.zipPhases[i].status = 'active';
|
||||
}, i * step);
|
||||
setTimeout(() => {
|
||||
if (this.zipPhases[i].status === 'active') this.zipPhases[i].status = 'done';
|
||||
}, (i + 1) * step);
|
||||
setTimeout(
|
||||
() => {
|
||||
if (this.zipPhases[i].status === 'active') this.zipPhases[i].status = 'done';
|
||||
},
|
||||
(i + 1) * step
|
||||
);
|
||||
}
|
||||
// Activate phase 5 after phases 1-4 finish, leave it active until API returns
|
||||
setTimeout(() => {
|
||||
const last = this.zipPhases.length - 1;
|
||||
if (this.zipPhases[last].status === 'pending') this.zipPhases[last].status = 'active';
|
||||
}, (this.zipPhases.length - 1) * step);
|
||||
setTimeout(
|
||||
() => {
|
||||
const last = this.zipPhases.length - 1;
|
||||
if (this.zipPhases[last].status === 'pending') this.zipPhases[last].status = 'active';
|
||||
},
|
||||
(this.zipPhases.length - 1) * step
|
||||
);
|
||||
}
|
||||
|
||||
proceedToUpload(): void {
|
||||
@@ -840,7 +866,14 @@ export class RecordMasterComponent implements OnInit {
|
||||
if (value == null || value === '') return '';
|
||||
const pad = (n: any) => String(n ?? 0).padStart(2, '0');
|
||||
if (Array.isArray(value) && value.length >= 3) {
|
||||
const [y, mo, d, h = 0, mi = 0, s = 0] = value;
|
||||
const [
|
||||
y,
|
||||
mo,
|
||||
d,
|
||||
h = 0,
|
||||
mi = 0,
|
||||
s = 0
|
||||
] = value;
|
||||
const date = `${pad(d)}-${pad(mo)}-${y}`;
|
||||
if (value.length >= 4) {
|
||||
return `${date} ${pad(h)}:${pad(mi)}:${pad(s)}`;
|
||||
@@ -863,14 +896,20 @@ export class RecordMasterComponent implements OnInit {
|
||||
setTimeout(() => {
|
||||
if (this.uploadPhases[i].status === 'pending') this.uploadPhases[i].status = 'active';
|
||||
}, i * step);
|
||||
setTimeout(() => {
|
||||
if (this.uploadPhases[i].status === 'active') this.uploadPhases[i].status = 'done';
|
||||
}, (i + 1) * step);
|
||||
setTimeout(
|
||||
() => {
|
||||
if (this.uploadPhases[i].status === 'active') this.uploadPhases[i].status = 'done';
|
||||
},
|
||||
(i + 1) * step
|
||||
);
|
||||
}
|
||||
setTimeout(() => {
|
||||
const last = this.uploadPhases.length - 1;
|
||||
if (this.uploadPhases[last].status === 'pending') this.uploadPhases[last].status = 'active';
|
||||
}, (this.uploadPhases.length - 1) * step);
|
||||
setTimeout(
|
||||
() => {
|
||||
const last = this.uploadPhases.length - 1;
|
||||
if (this.uploadPhases[last].status === 'pending') this.uploadPhases[last].status = 'active';
|
||||
},
|
||||
(this.uploadPhases.length - 1) * step
|
||||
);
|
||||
}
|
||||
|
||||
goToVerification(): void {
|
||||
|
||||
Reference in New Issue
Block a user