feat: complete UI redesign — client creation tabs, footer, home page, CIF & occupation fields

- Client creation: replaced vertical stepper with 3-tab layout (General, KYC & Nominee, Preview)
- All form fields redesigned with appearance="outline" and section grouping
- Preview tab with profile card, info tables, KYC details, family members
- Footer redesigned: security bar, LincShield certifications, regulatory & legal, quick navigation, support & contact, LincPay branding
- Home page redesigned: welcome banner, quick action cards, info row
- Client view header redesigned: ID chips row (Account No, CIF Number, External Id), 4-column details grid
- Added CIF Number column to clients list table
- Added CIF Number display to client view sidebar and personal data tab
- Added occupationCategoryId and occupationId dropdowns to client creation form
- Added occupation fields to client view sidebar, personal data tab, and preview
- Added 51+ missing translation keys to en-US.json
- Toolbar: added Last login chip
- Registered faUserPlus icon

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Aditya
2026-03-20 19:19:47 +05:30
co-authored by Claude Opus 4.6
parent b3a4cce0cf
commit 7b1f2e8ad7
81 changed files with 8384 additions and 3923 deletions
@@ -51,150 +51,116 @@
</div>
<div class="mat-typography account-card-title">
<mat-card-title class="make-flex content">
<h3 class="flex-95">
<i
class="fa fa-stop"
[ngClass]="clientViewData.status.code | statusLookup"
matTooltip="{{ 'labels.status.' + clientViewData.status.value | translate }}"
></i>
<b>{{ 'labels.inputs.Client Name' | translate }} :</b>
<!-- Top Row: Name + Status + Actions -->
<mat-card-title class="cv-title-row">
<div class="cv-name-block">
<i class="fa fa-stop" [ngClass]="clientViewData.status.code | statusLookup"
matTooltip="{{ 'labels.status.' + clientViewData.status.value | translate }}"></i>
<mifosx-entity-name
[entityName]="
complianceHideClientData ? maskName(clientViewData.displayName) : clientViewData.displayName
"
[display]="'right'"
[hideCopy]="complianceHideClientData"
></mifosx-entity-name>
</h3>
<div class="flex-5 align-end-center">
<button mat-icon-button [matMenuTriggerFor]="clientMenu" aria-label="Client actions" yPosition="below">
<mat-icon matListIcon class="actions-menu">
<fa-icon icon="bars" size="sm"></fa-icon>
</mat-icon>
</button>
[entityName]="complianceHideClientData ? maskName(clientViewData.displayName) : clientViewData.displayName"
[display]="'right'" [hideCopy]="complianceHideClientData"></mifosx-entity-name>
<span class="cv-status-chip" [ngClass]="clientViewData.status.code | statusLookup">
{{ 'labels.status.' + clientViewData.status.value | translate }}
</span>
</div>
<button mat-icon-button [matMenuTriggerFor]="clientMenu" aria-label="Client actions">
<mat-icon matListIcon class="actions-menu"><fa-icon icon="bars" size="sm"></fa-icon></mat-icon>
</button>
</mat-card-title>
<!-- ID Chips Row -->
<div class="cv-id-row">
<div class="cv-id-chip">
<span class="cv-id-label">{{ 'labels.inputs.Account No' | translate }}</span>
<mifosx-account-number accountNo="{{ clientViewData.accountNo }}"></mifosx-account-number>
</div>
@if (clientViewData.cifNumber) {
<div class="cv-id-chip">
<span class="cv-id-label">{{ 'labels.inputs.CIF Number' | translate }}</span>
<mifosx-account-number accountNo="{{ clientViewData.cifNumber }}"></mifosx-account-number>
</div>
}
@if (clientViewData.externalId) {
<div class="cv-id-chip">
<span class="cv-id-label">{{ 'labels.inputs.External Id' | translate }}</span>
<mifosx-external-identifier
[externalId]="complianceHideClientData ? maskValue(clientViewData.externalId) : clientViewData.externalId"
[completed]="true" [hideCopy]="complianceHideClientData"></mifosx-external-identifier>
</div>
}
</div>
<!-- Details Grid -->
<mat-card-subtitle>
<div class="layout-row responsive-column">
<div class="flex-50">
<table class="account-overview">
<tbody>
<tr>
<td>
<b>{{ 'labels.inputs.Office' | translate }}</b>
</td>
<td><mifosx-entity-name [entityName]="clientViewData.officeName"></mifosx-entity-name></td>
</tr>
<tr>
<td>
<b>{{ 'labels.inputs.Client' | translate }}</b>
</td>
<td><mifosx-account-number accountNo="{{ clientViewData.accountNo }}"></mifosx-account-number></td>
</tr>
<tr>
<td>
<b>{{ 'labels.inputs.External Id' | translate }}</b>
</td>
<td>
<mifosx-external-identifier
[externalId]="
complianceHideClientData ? maskValue(clientViewData.externalId) : clientViewData.externalId
"
[completed]="true"
[hideCopy]="complianceHideClientData"
></mifosx-external-identifier>
</td>
</tr>
@if (clientViewData.activationDate) {
<tr>
<td>
<b>{{ 'labels.inputs.Activation Date' | translate }}</b>
</td>
<td>{{ clientViewData.activationDate | dateFormat }}</td>
</tr>
}
<tr>
<td>
<b>{{ 'labels.inputs.Staff' | translate }}</b>
</td>
@if (clientViewData.staffName) {
<td>{{ clientViewData.staffName }}</td>
}
@if (!clientViewData.staffName) {
<td>{{ 'labels.inputs.Unassigned' | translate }}</td>
}
</tr>
</tbody>
</table>
<div class="cv-details-grid">
<div class="cv-detail">
<span class="cv-dl">{{ 'labels.inputs.Office' | translate }}</span>
<span class="cv-dv">{{ clientViewData.officeName }}</span>
</div>
<div class="flex-50">
<table class="account-overview">
<tbody>
@if (clientViewData.groups.length > 0) {
<tr>
<td>
<b>{{ 'labels.inputs.Member Of' | translate }}</b>
</td>
<td>
@for (group of clientViewData.groups; track group) {
<span>
<span class="m-r-3">{{ group.name }}</span>
</span>
}
</td>
</tr>
}
@if (clientViewData.clientType) {
<tr>
<td>
<b>{{ 'labels.inputs.Client Type' | translate }}</b>
</td>
<td>{{ clientViewData.clientType.name }}</td>
</tr>
@if (clientViewData.clientClassification) {
<tr>
<td>
<b>{{ 'labels.inputs.Client Classification' | translate }}</b>
</td>
<td>{{ clientViewData.clientClassification.name }}</td>
</tr>
}
}
<tr>
<td>
<b>{{ 'labels.inputs.Mobile Number' | translate }}</b>
</td>
<td>
<mifosx-external-identifier
[externalId]="
complianceHideClientData ? maskValue(clientViewData.mobileNo) : clientViewData.mobileNo
"
[completed]="true"
[hideCopy]="complianceHideClientData"
></mifosx-external-identifier>
</td>
</tr>
<tr>
<td>
<b>{{ 'labels.inputs.Email' | translate }}</b>
</td>
<td>
<mifosx-external-identifier
[externalId]="
complianceHideClientData
? maskEmail(clientViewData.emailAddress)
: clientViewData.emailAddress
"
[completed]="true"
[hideCopy]="complianceHideClientData"
></mifosx-external-identifier>
</td>
</tr>
</tbody>
</table>
<div class="cv-detail">
<span class="cv-dl">{{ 'labels.inputs.Staff' | translate }}</span>
<span class="cv-dv">{{ clientViewData.staffName || ('labels.inputs.Unassigned' | translate) }}</span>
</div>
@if (clientViewData.activationDate) {
<div class="cv-detail">
<span class="cv-dl">{{ 'labels.inputs.Activation Date' | translate }}</span>
<span class="cv-dv">{{ clientViewData.activationDate | dateFormat }}</span>
</div>
}
@if (clientViewData.clientType) {
<div class="cv-detail">
<span class="cv-dl">{{ 'labels.inputs.Client Type' | translate }}</span>
<span class="cv-dv">{{ clientViewData.clientType.name }}</span>
</div>
}
@if (clientViewData.clientClassification) {
<div class="cv-detail">
<span class="cv-dl">{{ 'labels.inputs.Client Classification' | translate }}</span>
<span class="cv-dv">{{ clientViewData.clientClassification.name }}</span>
</div>
}
@if (clientViewData.mobileNo) {
<div class="cv-detail">
<span class="cv-dl">{{ 'labels.inputs.Mobile Number' | translate }}</span>
<span class="cv-dv">
<mifosx-external-identifier
[externalId]="complianceHideClientData ? maskValue(clientViewData.mobileNo) : clientViewData.mobileNo"
[completed]="true" [hideCopy]="complianceHideClientData"></mifosx-external-identifier>
</span>
</div>
}
@if (clientViewData.emailAddress) {
<div class="cv-detail">
<span class="cv-dl">{{ 'labels.inputs.Email' | translate }}</span>
<span class="cv-dv">
<mifosx-external-identifier
[externalId]="complianceHideClientData ? maskEmail(clientViewData.emailAddress) : clientViewData.emailAddress"
[completed]="true" [hideCopy]="complianceHideClientData"></mifosx-external-identifier>
</span>
</div>
}
@if (clientViewData.occupationCategory) {
<div class="cv-detail">
<span class="cv-dl">{{ 'labels.inputs.Occupation Category' | translate }}</span>
<span class="cv-dv">{{ clientViewData.occupationCategory.name }}</span>
</div>
}
@if (clientViewData.occupation) {
<div class="cv-detail">
<span class="cv-dl">{{ 'labels.inputs.Occupation' | translate }}</span>
<span class="cv-dv">{{ clientViewData.occupation.name }}</span>
</div>
}
@if (clientViewData.groups?.length > 0) {
<div class="cv-detail">
<span class="cv-dl">{{ 'labels.inputs.Member Of' | translate }}</span>
<span class="cv-dv">
@for (group of clientViewData.groups; track group) {
<span>{{ group.name }} </span>
}
</span>
</div>
}
</div>
</mat-card-subtitle>
</div>