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:
co-authored by
Claude Opus 4.6
parent
b3a4cce0cf
commit
7b1f2e8ad7
@@ -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>
|
||||
|
||||
@@ -30,16 +30,121 @@
|
||||
}
|
||||
}
|
||||
|
||||
.account-overview {
|
||||
color: white;
|
||||
min-width: 85%;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.make-flex {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.mat-card-title-group {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* ── Title Row ── */
|
||||
.cv-title-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.cv-name-block {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: #fff;
|
||||
|
||||
i.fa-stop {
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
.cv-status-chip {
|
||||
font-size: 10px;
|
||||
font-weight: 700;
|
||||
padding: 2px 10px;
|
||||
border-radius: 12px;
|
||||
letter-spacing: 0.4px;
|
||||
text-transform: uppercase;
|
||||
background: rgba(255, 255, 255, 0.15);
|
||||
color: rgba(255, 255, 255, 0.9);
|
||||
}
|
||||
|
||||
/* ── ID Chips Row ── */
|
||||
.cv-id-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
margin-bottom: 14px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.cv-id-chip {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
padding: 4px 12px;
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
border: 1px solid rgba(255, 255, 255, 0.15);
|
||||
border-radius: 5px;
|
||||
color: #fff;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.cv-id-label {
|
||||
font-size: 10px;
|
||||
font-weight: 600;
|
||||
color: rgba(255, 255, 255, 0.55);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.4px;
|
||||
}
|
||||
|
||||
/* ── Details Grid ── */
|
||||
.cv-details-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
gap: 6px 16px;
|
||||
}
|
||||
|
||||
.cv-detail {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1px;
|
||||
padding: 4px 0;
|
||||
}
|
||||
|
||||
.cv-dl {
|
||||
font-size: 10px;
|
||||
color: rgba(255, 255, 255, 0.5);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.cv-dv {
|
||||
font-size: 13px;
|
||||
color: rgba(255, 255, 255, 0.95);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.actions-menu {
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
/* ── Responsive ── */
|
||||
@media (max-width: 1024px) {
|
||||
.cv-details-grid {
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.cv-details-grid {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
}
|
||||
|
||||
.cv-id-row {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.cv-details-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,6 +87,10 @@
|
||||
<span class="label">{{ 'labels.inputs.Account No' | translate }}</span>
|
||||
<span class="value">{{ clientViewData.accountNo || '-' }}</span>
|
||||
</div>
|
||||
<div class="data-item">
|
||||
<span class="label">{{ 'labels.inputs.CIF Number' | translate }}</span>
|
||||
<span class="value">{{ clientViewData.cifNumber || '-' }}</span>
|
||||
</div>
|
||||
<div class="data-item">
|
||||
<span class="label">{{ 'labels.inputs.External Id' | translate }}</span>
|
||||
<span class="value">{{ clientViewData.externalId || '-' }}</span>
|
||||
@@ -178,6 +182,18 @@
|
||||
<span class="value">{{ clientViewData.clientClassification.name }}</span>
|
||||
</div>
|
||||
}
|
||||
@if (clientViewData.occupationCategory) {
|
||||
<div class="data-item">
|
||||
<span class="label">{{ 'labels.inputs.Occupation Category' | translate }}</span>
|
||||
<span class="value">{{ clientViewData.occupationCategory.name }}</span>
|
||||
</div>
|
||||
}
|
||||
@if (clientViewData.occupation) {
|
||||
<div class="data-item">
|
||||
<span class="label">{{ 'labels.inputs.Occupation' | translate }}</span>
|
||||
<span class="value">{{ clientViewData.occupation.name }}</span>
|
||||
</div>
|
||||
}
|
||||
@if (!clientViewData.clientType && !clientViewData.clientClassification) {
|
||||
<div class="data-item">
|
||||
<span class="value no-data">{{ 'labels.commons.No classification data available' | translate }}</span>
|
||||
|
||||
Reference in New Issue
Block a user