Files
cbsnexora/src/app/clients/clients.component.html
T
niteshandClaude Sonnet 5 56f13ade67 fix(clients): latest-identifier ordering for KYC docs, show client ID in list
Repeat KYC document uploads create a new client_identifier row rather than
updating in place, so loadExistingKycIdentifier() must prefer the newest
matching row instead of the first one returned by the API. Also swap the
Clients list "Account No." column to show the client's numeric ID instead.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-29 11:32:41 +05:30

187 lines
7.1 KiB
HTML

<!--
Copyright since 2025 Mifos Initiative
This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at http://mozilla.org/MPL/2.0/.
-->
<mat-card class="container">
<!-- Header Bar -->
<div class="table-header">
<div class="table-header-row">
<div class="table-title-group">
<h2 class="table-title">
<div class="t-logomark"><span></span><span></span><span></span><span></span></div>
{{ 'labels.menus.Clients' | translate }}
</h2>
<span class="table-count" *ngIf="totalRows">{{ totalRows }} records</span>
</div>
<div class="table-buttons">
<button
mat-stroked-button
[routerLink]="['../', 'organization', 'bulk-import', 'Clients']"
*mifosxHasPermission="'CREATE_CLIENT'"
class="btn-import"
>
<fa-icon icon="download"></fa-icon>
{{ 'labels.buttons.Import Client' | translate }}
</button>
<button
mat-flat-button
color="primary"
[routerLink]="['create']"
*mifosxHasPermission="'CREATE_CLIENT'"
class="btn-create"
>
<fa-icon icon="plus"></fa-icon>
{{ 'labels.buttons.Create Client' | translate }}
</button>
</div>
</div>
<div class="table-filters">
<mat-form-field class="table-search" appearance="outline">
<input
matInput
placeholder="{{ 'labels.text.SearchByClient' | translate }}"
(keydown.enter)="search($event.target.value)"
/>
</mat-form-field>
<mat-form-field class="table-filter" appearance="outline">
<mat-select [(value)]="statusFilter" (selectionChange)="applyClientFilters()" placeholder="Status">
<mat-option value="">All Status</mat-option>
<mat-option value="active">Active</mat-option>
<mat-option value="pending">Pending</mat-option>
<mat-option value="closed">Closed</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field class="table-filter" appearance="outline">
<mat-select [(value)]="legalFormFilter" (selectionChange)="applyClientFilters()" placeholder="Legal Form">
<mat-option value="">All Forms</mat-option>
<mat-option value="1">Individual</mat-option>
<mat-option value="2">Non-Individual</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field class="table-filter" appearance="outline">
<mat-select [(value)]="ckycFilter" (selectionChange)="applyClientFilters()" placeholder="CKYC">
<mat-option value="">All CKYC</mat-option>
<mat-option value="done">CKYC Done</mat-option>
<mat-option value="pending">CKYC Pending</mat-option>
</mat-select>
</mat-form-field>
</div>
</div>
<!-- Table -->
<div [hidden]="!existsClientsToFilter" class="table-container">
@if (isLoading) {
<mat-progress-bar mode="indeterminate" class="table-loader"></mat-progress-bar>
}
<table mat-table [dataSource]="dataSource" matSort (matSortChange)="sortChanged($event)" class="modern-table">
<!-- Name Column -->
<ng-container matColumnDef="displayName">
<th mat-header-cell *matHeaderCellDef mat-sort-header>{{ 'labels.inputs.name' | translate }}</th>
<td mat-cell *matCellDef="let row" class="cell-link">
<mat-icon class="row-legal-icon" [ngClass]="row.legalForm?.id === 2 ? 'entity' : 'person'">{{
row.legalForm?.id === 2 ? 'business' : 'person'
}}</mat-icon>
{{ hideClientData ? maskName(row.displayName) : row.displayName }}
@if (row._ckycDone) {
<mat-icon class="row-ckyc-icon" matTooltip="CKYC Done">verified</mat-icon>
}
</td>
</ng-container>
<!-- Client ID Column -->
<ng-container matColumnDef="accountNumber">
<th mat-header-cell *matHeaderCellDef mat-sort-header>Client ID</th>
<td mat-cell *matCellDef="let row" class="cell-mono">{{ row.id }}</td>
</ng-container>
<!-- CIF Number Column -->
<ng-container matColumnDef="cifNumber">
<th mat-header-cell *matHeaderCellDef mat-sort-header>{{ 'labels.inputs.CIF Number' | translate }}</th>
<td mat-cell *matCellDef="let row" class="cell-mono">{{ row.cifNumber }}</td>
</ng-container>
<!-- Legal Form Column -->
<ng-container matColumnDef="legalForm">
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Legal Form' | translate }}</th>
<td mat-cell *matCellDef="let row">
<span class="legal-form-badge" [ngClass]="row.legalForm?.id === 2 ? 'entity' : 'person'">
{{ row.legalForm?.id === 2 ? 'Non-Individual' : 'Individual' }}
</span>
</td>
</ng-container>
<!-- Status Column -->
<ng-container matColumnDef="status">
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Status' | translate }}</th>
<td mat-cell *matCellDef="let row">
<span class="status-badge" [ngClass]="row.status.code | statusLookup">
{{ row.status.value | translate }}
</span>
</td>
</ng-container>
<!-- Profile Completion Column -->
<ng-container matColumnDef="profileCompletion">
<th mat-header-cell *matHeaderCellDef>Profile</th>
<td mat-cell *matCellDef="let row">
<div class="profile-steps">
@for (step of getProfileSteps(row); track step.label) {
<span
class="step-dot"
[class.done]="step.done"
[matTooltip]="step.label + (step.done ? ' ✓' : ' — Pending')"
>
{{ step.label.charAt(0) }}
</span>
}
</div>
</td>
</ng-container>
<!-- Actions Column -->
<ng-container matColumnDef="actions">
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Actions' | translate }}</th>
<td mat-cell *matCellDef="let row" (click)="$event.stopPropagation()">
<div class="action-icons">
<button mat-button class="action-chip action-chip--view" [routerLink]="[row.id, 'general']">
<mat-icon>visibility</mat-icon>
View
</button>
<button mat-button class="action-chip action-chip--edit" [routerLink]="[row.id, 'edit']">
<mat-icon>edit_note</mat-icon>
Edit
</button>
</div>
</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns" class="table-row"></tr>
</table>
<mat-paginator
[pageSize]="pageSize"
[pageSizeOptions]="[5, 50, 100]"
[length]="totalRows"
(page)="pageChanged($event)"
showFirstLastButtons
></mat-paginator>
</div>
<!-- Empty State -->
<div class="empty-state" *ngIf="notExistsClientsToFilter">
<fa-icon icon="search" class="empty-icon"></fa-icon>
<p class="empty-text">{{ 'labels.text.No client was found' | translate }}</p>
</div>
</mat-card>