feat: non-individual entity UI improvements — hide individual-only sections, fix legal form display, send extended fields to API

- Hide Family Members tab/section for non-individual entities in client view, create flow, and preview
- Hide Financial & Classification section for non-individual entities in personal data and preview
- Fix legal form column in clients list — fetch legalForm from client API for correct display
- Add person/business icon before client name in list rows
- Remove unsupportedNpd filter — send authorizedSignatory, additionalInfo, dateOfRegistration to API
- Add registration number format hint (mat-hint) based on constitution type
- Add spacing between verification row and constitution type dropdowns
- Reduce table cell padding and prevent action buttons from being clipped
- Add missing MPL-2.0 license headers

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Aditya
2026-04-04 15:10:51 +05:30
co-authored by Claude Opus 4.6
parent ab01111424
commit b890d24bb7
522 changed files with 51991 additions and 21885 deletions
@@ -6,40 +6,50 @@
file, You can obtain one at http://mozilla.org/MPL/2.0/.
-->
<div class="container m-b-20 layout-row align-end gap-20px">
<div #buttonAddEdit class="in-block">
<button mat-raised-button color="primary" [routerLink]="'manage'" *mifosxHasPermission="'UPDATE_CURRENCY'">
<fa-icon icon="plus" class="m-r-10"></fa-icon>
{{ 'labels.buttons.Add/Edit' | translate }}
</button>
</div>
</div>
<div class="container">
<div #filter class="layout-row gap-20px">
<mat-form-field class="flex-fill">
<mat-label>{{ 'labels.inputs.Filter' | translate }}</mat-label>
<input matInput (keyup)="applyFilter($event.target.value)" />
</mat-form-field>
<div class="table-header">
<div class="table-title-group">
<h2 class="table-title">
<i class="fas fa-cogs title-icon"></i>
{{ 'labels.heading.Currency Configuration' | translate }}
</h2>
</div>
<div class="table-actions">
<mat-form-field class="table-search" appearance="outline">
<i matPrefix class="fas fa-search"></i>
<mat-label>{{ 'labels.inputs.Filter' | translate }}</mat-label>
<input matInput (keyup)="applyFilter($event.target.value)" />
</mat-form-field>
<div class="table-buttons">
<div #buttonAddEdit class="in-block">
<button mat-flat-button color="primary" class="btn-create" [routerLink]="'manage'" *mifosxHasPermission="'UPDATE_CURRENCY'">
<fa-icon icon="plus" class="m-r-10"></fa-icon>
{{ 'labels.buttons.Add/Edit' | translate }}
</button>
</div>
</div>
</div>
</div>
<div #tableCurrencies class="mat-elevation-z8">
<table mat-table [dataSource]="dataSource" matSort>
<ng-container matColumnDef="name">
<th mat-header-cell *matHeaderCellDef mat-sort-header>{{ 'labels.inputs.Currency Name' | translate }}</th>
<td mat-cell *matCellDef="let currency">{{ currency.name }}</td>
</ng-container>
<div #filter class="table-container">
<div #tableCurrencies>
<table mat-table [dataSource]="dataSource" matSort class="modern-table">
<ng-container matColumnDef="name">
<th mat-header-cell *matHeaderCellDef mat-sort-header>{{ 'labels.inputs.Currency Name' | translate }}</th>
<td mat-cell *matCellDef="let currency">{{ currency.name }}</td>
</ng-container>
<ng-container matColumnDef="code">
<th mat-header-cell *matHeaderCellDef mat-sort-header>{{ 'labels.inputs.Currency Code' | translate }}</th>
<td mat-cell *matCellDef="let currency">{{ currency.code }}</td>
</ng-container>
<ng-container matColumnDef="code">
<th mat-header-cell *matHeaderCellDef mat-sort-header>{{ 'labels.inputs.Currency Code' | translate }}</th>
<td mat-cell *matCellDef="let currency">{{ currency.code }}</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns"></tr>
</table>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns" class="table-row"></tr>
</table>
<mat-paginator [pageSizeOptions]="[10, 25, 50, 100]" showFirstLastButtons></mat-paginator>
<mat-paginator [pageSizeOptions]="[10, 25, 50, 100]" showFirstLastButtons></mat-paginator>
</div>
</div>
</div>
@@ -6,6 +6,8 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
@use '../../shared/styles/list-layout' as *;
table {
width: 100%;
}
@@ -6,72 +6,98 @@
file, You can obtain one at http://mozilla.org/MPL/2.0/.
-->
<div class="container">
<mat-card>
<div #currencyFormRef class="currency-form-container">
<form #formRef="ngForm" [formGroup]="currencyForm" class="layout-row gap-20px" (ngSubmit)="addCurrency()">
<mat-form-field class="currency-search-field">
<mat-label>{{ 'labels.inputs.Currency' | translate }}</mat-label>
<mat-select required formControlName="currency">
<mat-option>
<ngx-mat-select-search
placeholderLabel="{{ placeHolderLabel }}"
noEntriesFoundLabel="{{ noEntriesFoundLabel }}"
[formControl]="filterFormCtrl"
></ngx-mat-select-search>
</mat-option>
@for (currency of currencyData | async; track currency) {
<mat-option [value]="currency"> ({{ currency.code }}) {{ currency.name }} </mat-option>
}
</mat-select>
@if (currencyForm.controls.currency.hasError('required')) {
<mat-error>
{{ 'labels.inputs.Currency' | translate }} {{ 'labels.commons.is' | translate }}
<strong>{{ 'labels.commons.required' | translate }}</strong>
</mat-error>
}
</mat-form-field>
<div class="mc-container">
<!-- Header -->
<div class="mc-header">
<button mat-stroked-button [routerLink]="['../']" class="mc-back">
<i class="fas fa-arrow-left"></i> {{ 'labels.heading.Currency Configuration' | translate }}
</button>
<h2 class="mc-header__title">{{ 'labels.buttons.Add/Edit' | translate }}</h2>
</div>
<button
type="submit"
mat-raised-button
class="add-currency-button"
color="primary"
[disabled]="!currencyForm.valid"
[attr.aria-label]="'labels.buttons.Add Currency' | translate"
[title]="'labels.buttons.Add Currency' | translate"
*mifosxHasPermission="'UPDATE_CURRENCY'"
>
<fa-icon icon="plus"></fa-icon>
</button>
</form>
<!-- Card -->
<div class="mc-card">
<!-- Add Currency Section -->
<div class="mc-section">
<div class="mc-section__header">
<i class="fas fa-plus-circle"></i>
<h3>{{ 'labels.buttons.Add Currency' | translate }}</h3>
</div>
<div class="mc-add" #currencyFormRef>
<form #formRef="ngForm" [formGroup]="currencyForm" (ngSubmit)="addCurrency()" class="mc-add__form">
<mat-form-field appearance="outline" class="mc-add__field">
<mat-label>{{ 'labels.inputs.Currency' | translate }}</mat-label>
<mat-select required formControlName="currency">
<mat-option>
<ngx-mat-select-search
placeholderLabel="{{ placeHolderLabel }}"
noEntriesFoundLabel="{{ noEntriesFoundLabel }}"
[formControl]="filterFormCtrl"
></ngx-mat-select-search>
</mat-option>
@for (currency of currencyData | async; track currency) {
<mat-option [value]="currency"> ({{ currency.code }}) {{ currency.name }} </mat-option>
}
</mat-select>
@if (currencyForm.controls.currency.hasError('required')) {
<mat-error>
{{ 'labels.inputs.Currency' | translate }} {{ 'labels.commons.is' | translate }}
<strong>{{ 'labels.commons.required' | translate }}</strong>
</mat-error>
}
</mat-form-field>
<button
type="submit"
mat-flat-button
color="primary"
class="mc-add__btn"
[disabled]="!currencyForm.valid"
[attr.aria-label]="'labels.buttons.Add Currency' | translate"
[title]="'labels.buttons.Add Currency' | translate"
*mifosxHasPermission="'UPDATE_CURRENCY'"
>
<i class="fas fa-plus"></i> Add
</button>
</form>
</div>
</div>
<mat-grid-list cols="2" rowHeight="50px">
@for (currency of selectedCurrencies; track currency; let i = $index) {
<mat-grid-tile class="grid-tile-content">
<div class="grid-container">
<div>
<button
type="button"
mat-button
color="warn"
(click)="deleteCurrency(currency.code, i)"
[attr.aria-label]="'labels.buttons.Delete' | translate"
[title]="'labels.buttons.Delete' | translate"
*mifosxHasPermission="'UPDATE_CURRENCY'"
>
<fa-icon icon="trash"></fa-icon>
</button>
<!-- Selected Currencies Section -->
<div class="mc-section">
<div class="mc-section__header">
<i class="fas fa-coins"></i>
<h3>Selected Currencies</h3>
</div>
<div class="mc-list">
@for (currency of selectedCurrencies; track currency; let i = $index) {
<div class="mc-currency">
<div class="mc-currency__info">
<span class="mc-currency__code">{{ currency.code }}</span>
<span class="mc-currency__name">{{ currency.name }}</span>
</div>
<span class="label m-l-10 p-t-5"
><b>{{ currency.name }}</b></span
<button
type="button"
mat-icon-button
class="mc-currency__delete"
(click)="deleteCurrency(currency.code, i)"
[attr.aria-label]="'labels.buttons.Delete' | translate"
[title]="'labels.buttons.Delete' | translate"
*mifosxHasPermission="'UPDATE_CURRENCY'"
>
<i class="fas fa-trash"></i>
</button>
</div>
</mat-grid-tile>
}
</mat-grid-list>
</mat-card>
}
@if (!selectedCurrencies || selectedCurrencies.length === 0) {
<div class="mc-empty">
<i class="fas fa-coins"></i>
<p>No currencies selected</p>
</div>
}
</div>
</div>
</div>
</div>
<ng-template #templateCurrencyFormRef let-popover="popover">
@@ -6,46 +6,121 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
.container {
width: 74rem;
$primary: #1353a0;
$accent: #0ea5e9;
$border: #e2e8f0;
$gray-50: #f8f9fa;
$gray-200: #e8eaed;
$gray-500: #9aa0a6;
$gray-700: #5f6368;
$gray-900: #202124;
.mc-container { max-width: 700px; margin: 0 auto; padding: 20px 16px; }
.mc-header {
display: flex; align-items: center; gap: 14px; margin-bottom: 20px;
&__title { font-size: 18px; font-weight: 600; color: $gray-900; margin: 0; }
}
.currency-form-container {
padding: 16px 24px;
margin-bottom: 16px;
.mc-back {
border-radius: 8px !important; font-size: 12px !important; font-weight: 600 !important;
height: 32px !important; border-color: $border !important; color: $gray-500 !important;
i { margin-right: 6px; }
&:hover { color: $primary !important; border-color: $primary !important; }
}
.currency-search-field {
flex: 1 1 auto;
min-width: 0;
margin-right: 20px;
.mc-card { background: #fff; border: 1px solid $border; border-radius: 10px; overflow: hidden; }
.mc-section {
&__header {
display: flex; align-items: center; gap: 10px; padding: 12px 24px;
background: $gray-50; border-bottom: 1px solid $gray-200;
i { font-size: 14px; color: $accent; }
h3 { margin: 0; font-size: 14px; font-weight: 600; color: $gray-900; }
}
& + & { border-top: 1px solid $gray-200; }
}
.add-currency-button {
// Add currency form
.mc-add { padding: 16px 24px; }
.mc-add__form {
display: flex;
align-items: flex-start;
gap: 12px;
}
.mc-add__field { flex: 1; width: 100%; }
.mc-add__btn {
flex-shrink: 0;
min-width: 56px;
width: 56px;
padding: 0;
height: 36px !important;
border-radius: 8px !important;
font-size: 13px !important;
font-weight: 600 !important;
margin-top: 4px;
box-shadow: 0 1px 3px rgba($primary, 0.3) !important;
i { margin-right: 6px; font-size: 12px; }
}
// Currency list
.mc-list { padding: 8px 24px 16px; }
.mc-currency {
display: flex;
align-items: center;
justify-content: center;
align-self: center;
margin-bottom: 35px;
justify-content: space-between;
padding: 10px 14px;
background: $gray-50;
border: 1px solid rgba(0, 0, 0, 0.04);
border-radius: 8px;
margin-bottom: 6px;
transition: background 0.12s;
&:hover { background: #f1f5f9; }
&__info {
display: flex;
align-items: center;
gap: 10px;
}
&__code {
font-size: 11px;
font-weight: 700;
color: $primary;
background: rgba($primary, 0.08);
padding: 2px 8px;
border-radius: 4px;
letter-spacing: 0.5px;
font-family: 'Roboto Mono', monospace;
}
&__name {
font-size: 14px;
font-weight: 600;
color: $gray-900;
}
&__delete {
color: #ef4444 !important;
width: 32px !important;
height: 32px !important;
line-height: 32px !important;
font-size: 13px;
&:hover { background: rgba(239, 68, 68, 0.08) !important; }
}
}
.currency-grid {
max-width: 40%;
justify-content: left !important;
}
mat-grid-tile {
justify-content: left !important;
}
.grid-container {
.mc-empty {
display: flex;
flex-direction: row;
justify-content: left;
width: 100%;
height: 100%;
flex-direction: column;
align-items: center;
padding: 32px 16px;
color: #cbd5e1;
i { font-size: 28px; margin-bottom: 8px; }
p { font-size: 13px; color: $gray-500; margin: 0; }
}