feat: Nexora UI upgrade for Current Account, FD, and RD account views

- Current Account: hero header with navy gradient, pill tabs, Nexora card layout for general tab, txn-tab/chg-tab table styles, bank statement passbook
- Fixed Deposits: fd-* prefixed hero header, KPI tiles, pill tab nav, nx-card general tab layout, txn-tab transactions, chg-tab charges
- Recurring Deposits: rd-* prefixed hero header with three KPI tiles, pill tab nav, nx-card general tab layout, txn-tab transactions, chg-tab charges
- Fix: removed broken FA4-format icon references from action menu buttons

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Jaanu423
2026-05-25 11:02:15 +05:30
co-authored by Claude Sonnet 4.6
parent ad6f629c3f
commit b7d009b22a
30 changed files with 4149 additions and 2090 deletions
@@ -6,25 +6,22 @@
file, You can obtain one at http://mozilla.org/MPL/2.0/.
-->
<div class="tab-container mat-typography">
<div class="layout-row align-start">
<div class="m-b-10">
<h3>{{ 'labels.inputs.Charges' | translate }}</h3>
</div>
@if (this.chargesData) {
<div class="action-button m-b-20">
<button mat-raised-button color="primary" (click)="toggleCharges()">
<div class="chg-tab">
<div class="chg-tab__card">
<div class="chg-tab__toolbar">
<h3 class="chg-tab__title">{{ 'labels.inputs.Charges' | translate }}</h3>
<span class="chg-tab__spacer"></span>
@if (this.chargesData) {
<button mat-flat-button class="chg-tab__toggle" (click)="toggleCharges()">
{{
showInactiveCharges
? viewAllChargeButtons('View Active Charges')
: viewAllChargeButtons('View Inactive Charges')
}}
</button>
</div>
}
</div>
}
</div>
<div class="mat-elevation-z1 m-b-25">
<table mat-table #chargesTable [dataSource]="dataSource">
<ng-container matColumnDef="name">
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Name' | translate }}</th>
@@ -6,30 +6,107 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
.tab-container {
padding: 1%;
margin: 1%;
$navy: #0d1b2a;
$slate: #475569;
$muted: #64748b;
$border: #e2e8f0;
$soft: #f8fafc;
$blue: #1e40af;
h3 {
margin: 1% auto;
.chg-tab {
width: 100%;
}
.chg-tab__card {
background: #fff;
border: 1px solid $border;
border-radius: 14px;
box-shadow: 0 1px 3px rgb(15 23 42 / 4%);
overflow: hidden;
}
.chg-tab__toolbar {
display: flex;
align-items: center;
gap: 14px;
padding: 12px 18px;
background: $soft;
border-bottom: 1px solid $border;
}
.chg-tab__title {
margin: 0;
font-size: 14px;
font-weight: 700;
color: $navy;
letter-spacing: 0.01em;
}
.chg-tab__spacer {
flex: 1;
}
.chg-tab__toggle {
height: 34px !important;
border-radius: 8px !important;
font-weight: 600 !important;
font-size: 13px !important;
padding: 0 14px !important;
background: linear-gradient(135deg, #1e88e5 0%, $blue 100%) !important;
color: #fff !important;
}
table {
width: 100%;
}
::ng-deep .chg-tab__card {
.mat-mdc-header-row {
background: $soft;
}
.action-button {
margin-left: auto;
.mat-mdc-header-cell {
font-size: 11.5px;
font-weight: 800;
color: $slate;
text-transform: uppercase;
letter-spacing: 0.06em;
padding: 12px 14px;
border-bottom: 1px solid $border;
}
table {
width: 100%;
.mat-mdc-cell {
font-size: 13px;
color: $navy;
padding: 11px 14px;
border-bottom: 1px solid #f1f5f9;
}
.mat-mdc-row:hover {
background: #f8fafc;
cursor: pointer;
}
/* Right-align monetary columns. */
.mat-column-due,
.mat-column-paid,
.mat-column-waived,
.mat-column-outstanding {
text-align: right;
font-variant-numeric: tabular-nums;
}
.mat-column-actions {
text-align: center;
width: 140px;
.account-action-button {
min-width: 26px;
padding: 0 6px;
margin: 4px;
line-height: 25px;
}
.select-row:hover {
cursor: pointer;
min-width: 30px !important;
padding: 0 8px !important;
margin: 0 3px !important;
height: 30px !important;
line-height: 30px !important;
border-radius: 7px !important;
}
}
}
@@ -6,239 +6,232 @@
file, You can obtain one at http://mozilla.org/MPL/2.0/.
-->
<mat-card class="account-card">
<mat-card-header class="header layout-column">
<mat-card-title-group class="header-title-group">
<div class="profile-image-container">
<div>
<img
mat-card-md-image
class="profile-image"
matTooltip="{{ 'tooltips.Current Account' | translate }}"
[src]="'assets/images/current_account_placeholder.png'"
/>
</div>
<div class="cur-view">
<!-- ─── Gradient hero header ─────────────────────────────────────── -->
<header class="cur-hero">
<div class="cur-hero__left">
<div class="cur-hero__avatar">
<fa-icon icon="university"></fa-icon>
</div>
<div class="mat-typography account-card-title">
<mat-card-title class="layout-row layout-lt-md-column">
<div class="flex-60">
<h3>
@if (!currentAccountData.subStatus.block) {
<i
class="fa fa-stop"
[ngClass]="currentAccountData.status.code | statusLookup"
[matTooltip]="currentAccountData.status.value"
></i>
}
@if (currentAccountData.subStatus.block) {
<i
class="fa fa-stop"
[ngClass]="currentAccountData.subStatus.value | statusLookup"
[matTooltip]="currentAccountData.subStatus.value"
></i>
}
</h3>
<!-- Product Name Row -->
<span class="product-row">
<span class="m-r-5">{{ 'labels.inputs.Current Account Product' | translate }} :</span>
<span class="m-r-5">
<mifosx-long-text textValue="{{ currentAccountData.currentAccountProductName }}"></mifosx-long-text>
</span>
<div class="cur-hero__info">
<!-- Status pill -->
<div class="cur-hero__status-row">
@if (!currentAccountData.subStatus.block) {
<span
class="cur-status-pill"
[ngClass]="currentAccountData.status.code | statusLookup"
[matTooltip]="currentAccountData.status.value"
>
<span class="cur-status-pill__dot"></span>
{{ currentAccountData.status.value }}
</span>
<!-- Account Number Row -->
<span class="account-number-row">
<span class="m-r-5">{{ 'labels.inputs.Account Number' | translate }} :</span>
<span class="m-r-5">
<mifosx-account-number
accountNo="{{ currentAccountData.accountNo }}"
display="left"
></mifosx-account-number>
</span>
} @else {
<span
class="cur-status-pill cur-status-pill--warn"
[ngClass]="currentAccountData.subStatus.value | statusLookup"
[matTooltip]="currentAccountData.subStatus.value"
>
<span class="cur-status-pill__dot"></span>
{{ currentAccountData.subStatus.value }}
</span>
<!-- External ID Row -->
@if (currentAccountData.externalId) {
<span class="external-id-row">
<span class="m-r-5">{{ 'labels.inputs.External Id' | translate }} :</span>
<span>{{ currentAccountData.externalId }}</span>
</span>
}
<span class="account-overview">
{{ 'labels.text.' + entityType | translate }} {{ 'labels.inputs.name' | translate }}:
}
</div>
<h2 class="cur-hero__title">
<mifosx-long-text textValue="{{ currentAccountData.currentAccountProductName }}"></mifosx-long-text>
</h2>
<div class="cur-hero__meta">
<span class="cur-hero__meta-item">
<span class="cur-hero__meta-label">{{ 'labels.inputs.Account Number' | translate }}</span>
<mifosx-account-number
accountNo="{{ currentAccountData.accountNo }}"
display="left"
></mifosx-account-number>
</span>
<span class="cur-hero__meta-item">
<span class="cur-hero__meta-label">
{{ 'labels.text.' + entityType | translate }} {{ 'labels.inputs.name' | translate }}
</span>
<span class="cur-hero__meta-value">
{{ currentAccountData.clientName || currentAccountData.groupName }}
@if (currentAccountData.clientAccountNo) {
<span class="m-l-10">
<mifosx-account-number accountNo="{{ currentAccountData.clientAccountNo }}"></mifosx-account-number>
</span>
<mifosx-account-number accountNo="{{ currentAccountData.clientAccountNo }}"></mifosx-account-number>
}
</span>
</div>
@if (!currentAccountData.status.rejected && !currentAccountData.status.submittedAndPendingApproval) {
<div class="flex-30">
<h3>{{ 'labels.heading.Account Overview' | translate }}</h3>
<table class="account-overview">
<tbody>
<tr>
<td>{{ 'labels.inputs.Current Balance' | translate }}</td>
<td class="r-amount">
{{
currentAccountData.summary.accountBalance | currency: currency.code : 'symbol-narrow' : '1.2-2'
}}
</td>
</tr>
<tr>
<td>{{ 'labels.inputs.Available Balance' | translate }}</td>
<td class="r-amount">
{{
currentAccountData.summary.availableBalance
| currency: currency.code : 'symbol-narrow' : '1.2-2'
}}
</td>
</tr>
</tbody>
</table>
</div>
}
<div class="flex-5">
<button
mat-icon-button
[matMenuTriggerFor]="accountMenu"
aria-label="Loan account actions"
yPosition="below"
>
<mat-icon matListIcon class="actions-menu">
<fa-icon icon="bars" size="sm"></fa-icon>
</mat-icon>
</button>
</div>
</mat-card-title>
</span>
</div>
</div>
</div>
<mat-menu #accountMenu="matMenu">
@for (item of buttonConfig.singleButtons; track item) {
<button mat-menu-item *mifosxHasPermission="item.taskPermissionName" (click)="doAction(item.name)">
<mat-icon matListIcon>
<fa-icon icon="{{ item.icon }}" size="sm"></fa-icon>
</mat-icon>
<span> {{ 'labels.menus.' + item.name | translate }} </span>
</button>
}
@if (buttonConfig.options && !currentAccountData.subStatus.block) {
<button mat-menu-item [matMenuTriggerFor]="More">{{ 'labels.menus.More' | translate }}</button>
<mat-menu #More="matMenu">
@for (option of buttonConfig.options; track option) {
<span>
<button mat-menu-item *mifosxHasPermission="option.taskPermissionName" (click)="doAction(option.name)">
{{ 'labels.menus.' + option.name | translate }}
</button>
</span>
}
</mat-menu>
}
</mat-menu>
</mat-card-title-group>
</mat-card-header>
<mat-card-content class="content">
@if (isActive) {
<nav mat-tab-nav-bar class="navigation-tabs" [tabPanel]="tabPanel">
<a
mat-tab-link
[routerLink]="['./general']"
routerLinkActive
#general="routerLinkActive"
[active]="general.isActive"
>
{{ 'labels.inputs.General' | translate }}
</a>
<a
mat-tab-link
[routerLink]="['./transactions']"
routerLinkActive
#transactions="routerLinkActive"
[active]="transactions.isActive"
>
{{ 'labels.inputs.Transactions' | translate }}
</a>
<a
mat-tab-link
[routerLink]="['./passbook']"
routerLinkActive
#passbook="routerLinkActive"
[active]="passbook.isActive"
>
{{ 'labels.inputs.Passbook' | translate }}
</a>
<a
mat-tab-link
[routerLink]="['./charges']"
routerLinkActive
#charges="routerLinkActive"
[active]="charges.isActive"
>
{{ 'labels.inputs.Charges' | translate }}
</a>
<a
mat-tab-link
[routerLink]="['./documents']"
routerLinkActive
#currentAccountDocuments="routerLinkActive"
[active]="currentAccountDocuments.isActive"
>
{{ 'labels.inputs.Documents' | translate }}
</a>
<a
mat-tab-link
*mifosxHasPermission="'READ_CURRENTACCOUNTNOTE'"
[routerLink]="['./notes']"
routerLinkActive
#notes="routerLinkActive"
[active]="notes.isActive"
>
{{ 'labels.inputs.Notes' | translate }}
</a>
<ng-container *mifosxHasPermission="'READ_CURRENTACCOUNTCHARGE'">
@if (currentAccountData.clientId) {
<a
mat-tab-link
[routerLink]="['./standing-instructions']"
routerLinkActive
#standingInstructions="routerLinkActive"
[active]="standingInstructions.isActive"
>
{{ 'labels.inputs.Standing Instructions' | translate }}
</a>
}
</ng-container>
<a
mat-tab-link
[routerLink]="['./nominees']"
routerLinkActive
#nominees="routerLinkActive"
[active]="nominees.isActive"
>
Nominees
</a>
@for (currentAccountDatatable of currentAccountDatatables; track currentAccountDatatable) {
<a
mat-tab-link
*mifosxHasPermission="'READ_' + currentAccountDatatable.registeredTableName"
[routerLink]="['./datatables', currentAccountDatatable.registeredTableName]"
routerLinkActive
#datatable="routerLinkActive"
[active]="datatable.isActive"
>
{{ currentAccountDatatable.registeredTableName }}
</a>
}
</nav>
<!-- Balance KPI strip -->
@if (!currentAccountData.status.rejected && !currentAccountData.status.submittedAndPendingApproval) {
<div class="cur-hero__kpis">
<div class="cur-kpi">
<span class="cur-kpi__label">{{ 'labels.inputs.Current Balance' | translate }}</span>
<span class="cur-kpi__value">
{{ currentAccountData.summary.accountBalance | currency: currency.code : 'symbol-narrow' : '1.2-2' }}
</span>
</div>
<div class="cur-kpi cur-kpi--available">
<span class="cur-kpi__label">{{ 'labels.inputs.Available Balance' | translate }}</span>
<span class="cur-kpi__value">
{{ currentAccountData.summary.availableBalance | currency: currency.code : 'symbol-narrow' : '1.2-2' }}
</span>
</div>
</div>
}
<mat-tab-nav-panel #tabPanel>
<!-- Action menu (kebab) -->
<div class="cur-hero__actions">
<button mat-icon-button [matMenuTriggerFor]="accountMenu" aria-label="Account actions" yPosition="below">
<fa-icon icon="ellipsis-v"></fa-icon>
</button>
</div>
<mat-menu #accountMenu="matMenu">
@for (item of buttonConfig.singleButtons; track item) {
<button mat-menu-item *mifosxHasPermission="item.taskPermissionName" (click)="doAction(item.name)">
<mat-icon matListIcon>
<fa-icon icon="{{ item.icon }}" size="sm"></fa-icon>
</mat-icon>
<span> {{ 'labels.menus.' + item.name | translate }} </span>
</button>
}
@if (buttonConfig.options && !currentAccountData.subStatus.block) {
<button mat-menu-item [matMenuTriggerFor]="More">{{ 'labels.menus.More' | translate }}</button>
<mat-menu #More="matMenu">
@for (option of buttonConfig.options; track option) {
<span>
<button mat-menu-item *mifosxHasPermission="option.taskPermissionName" (click)="doAction(option.name)">
{{ 'labels.menus.' + option.name | translate }}
</button>
</span>
}
</mat-menu>
}
</mat-menu>
</header>
<!-- ─── Pill-style tab navigation ─────────────────────────────────── -->
@if (isActive) {
<nav mat-tab-nav-bar class="cur-tabs" [tabPanel]="tabPanel">
<a
mat-tab-link
class="cur-tab"
[routerLink]="['./general']"
routerLinkActive
#general="routerLinkActive"
[active]="general.isActive"
>
<fa-icon icon="info-circle" class="cur-tab__ic"></fa-icon>
<span>{{ 'labels.inputs.General' | translate }}</span>
</a>
<a
mat-tab-link
class="cur-tab"
[routerLink]="['./transactions']"
routerLinkActive
#transactions="routerLinkActive"
[active]="transactions.isActive"
>
<fa-icon icon="exchange-alt" class="cur-tab__ic"></fa-icon>
<span>{{ 'labels.inputs.Transactions' | translate }}</span>
</a>
<a
mat-tab-link
class="cur-tab"
[routerLink]="['./passbook']"
routerLinkActive
#passbook="routerLinkActive"
[active]="passbook.isActive"
>
<fa-icon icon="book" class="cur-tab__ic"></fa-icon>
<span>{{ 'labels.inputs.Passbook' | translate }}</span>
</a>
<a
mat-tab-link
class="cur-tab"
[routerLink]="['./charges']"
routerLinkActive
#charges="routerLinkActive"
[active]="charges.isActive"
>
<fa-icon icon="receipt" class="cur-tab__ic"></fa-icon>
<span>{{ 'labels.inputs.Charges' | translate }}</span>
</a>
<a
mat-tab-link
class="cur-tab"
[routerLink]="['./documents']"
routerLinkActive
#currentAccountDocuments="routerLinkActive"
[active]="currentAccountDocuments.isActive"
>
<fa-icon icon="folder-open" class="cur-tab__ic"></fa-icon>
<span>{{ 'labels.inputs.Documents' | translate }}</span>
</a>
<a
mat-tab-link
class="cur-tab"
*mifosxHasPermission="'READ_CURRENTACCOUNTNOTE'"
[routerLink]="['./notes']"
routerLinkActive
#notes="routerLinkActive"
[active]="notes.isActive"
>
<fa-icon icon="sticky-note" class="cur-tab__ic"></fa-icon>
<span>{{ 'labels.inputs.Notes' | translate }}</span>
</a>
<ng-container *mifosxHasPermission="'READ_CURRENTACCOUNTCHARGE'">
@if (currentAccountData.clientId) {
<a
mat-tab-link
class="cur-tab"
[routerLink]="['./standing-instructions']"
routerLinkActive
#standingInstructions="routerLinkActive"
[active]="standingInstructions.isActive"
>
<fa-icon icon="calendar-check" class="cur-tab__ic"></fa-icon>
<span>{{ 'labels.inputs.Standing Instructions' | translate }}</span>
</a>
}
</ng-container>
<a
mat-tab-link
class="cur-tab"
[routerLink]="['./nominees']"
routerLinkActive
#nominees="routerLinkActive"
[active]="nominees.isActive"
>
<fa-icon icon="user-shield" class="cur-tab__ic"></fa-icon>
<span>Nominees</span>
</a>
@for (currentAccountDatatable of currentAccountDatatables; track currentAccountDatatable) {
<a
mat-tab-link
class="cur-tab"
*mifosxHasPermission="'READ_' + currentAccountDatatable.registeredTableName"
[routerLink]="['./datatables', currentAccountDatatable.registeredTableName]"
routerLinkActive
#datatable="routerLinkActive"
[active]="datatable.isActive"
>
<fa-icon icon="table" class="cur-tab__ic"></fa-icon>
<span>{{ currentAccountDatatable.registeredTableName }}</span>
</a>
}
</nav>
}
<mat-tab-nav-panel #tabPanel>
<div class="cur-body">
<router-outlet></router-outlet>
</mat-tab-nav-panel>
</mat-card-content>
</mat-card>
</div>
</mat-tab-nav-panel>
</div>
@@ -6,12 +6,390 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
mat-card-title {
display: flex;
$navy: #0d1b2a;
$blue: #1e88e5;
$blue-d: #1e40af;
$border: #e2e8f0;
.cur-view {
max-width: 1440px;
margin: 0 auto;
padding: 12px 20px 22px;
}
.product-row,
.account-number-row,
.external-id-row {
display: block;
/* ─── Gradient hero header ──────────────────────────────────────────── */
.cur-hero {
position: relative;
display: grid;
grid-template-columns: minmax(0, 1fr) auto auto;
align-items: stretch;
gap: 14px;
padding: 12px 18px;
border-radius: 12px;
color: #fff;
background: linear-gradient(135deg, #1e3a8a 0%, #1e40af 45%, #4338ca 100%);
box-shadow: 0 6px 18px -10px rgb(15 23 42 / 35%);
overflow: hidden;
&::before {
content: '';
position: absolute;
inset: 0;
background: radial-gradient(circle at top right, rgb(255 255 255 / 12%) 0%, transparent 55%);
pointer-events: none;
}
&__left {
display: flex;
align-items: center;
gap: 12px;
min-width: 0;
position: relative;
z-index: 1;
}
&__avatar {
width: 40px;
height: 40px;
border-radius: 10px;
background: linear-gradient(135deg, rgb(255 255 255 / 22%) 0%, rgb(255 255 255 / 10%) 100%);
border: 1px solid rgb(255 255 255 / 28%);
display: flex;
align-items: center;
justify-content: center;
color: #fff;
font-size: 16px;
flex-shrink: 0;
box-shadow: 0 3px 10px -3px rgb(0 0 0 / 25%);
}
&__info {
display: flex;
flex-direction: column;
gap: 3px;
min-width: 0;
}
&__status-row {
display: flex;
align-items: center;
gap: 6px;
}
&__title {
margin: 0;
font-size: 15px;
font-weight: 800;
letter-spacing: 0.01em;
color: #fff;
line-height: 1.15;
::ng-deep mifosx-long-text {
color: #fff;
}
}
&__meta {
display: flex;
flex-wrap: wrap;
gap: 2px 18px;
margin-top: 1px;
}
&__meta-item {
display: inline-flex;
align-items: center;
gap: 6px;
font-size: 12px;
color: rgb(255 255 255 / 90%);
}
&__meta-label {
font-size: 10px;
text-transform: uppercase;
letter-spacing: 0.07em;
color: rgb(255 255 255 / 60%);
font-weight: 600;
}
&__meta-value {
font-weight: 600;
color: #fff;
}
&__kpis {
display: flex;
gap: 8px;
align-items: stretch;
position: relative;
z-index: 1;
}
&__actions {
display: flex;
align-items: center;
position: relative;
z-index: 1;
button {
width: 32px !important;
height: 32px !important;
min-width: 32px !important;
padding: 0 !important;
border-radius: 50% !important;
color: #fff !important;
background: rgb(255 255 255 / 10%) !important;
border: 1px solid rgb(255 255 255 / 22%) !important;
display: inline-flex !important;
align-items: center;
justify-content: center;
line-height: 1;
transition:
background 0.15s ease,
border-color 0.15s ease,
transform 0.12s ease;
::ng-deep fa-icon {
font-size: 14px;
line-height: 1;
display: inline-flex;
align-items: center;
justify-content: center;
}
::ng-deep .mat-mdc-button-persistent-ripple,
::ng-deep .mat-mdc-button-touch-target {
border-radius: 50% !important;
}
}
button:hover {
background: rgb(255 255 255 / 22%) !important;
border-color: rgb(255 255 255 / 38%) !important;
}
button:active {
transform: scale(0.94);
}
}
}
/* Balance KPI tiles */
.cur-kpi {
display: flex;
flex-direction: column;
justify-content: center;
min-width: 130px;
padding: 7px 14px;
border-radius: 9px;
background: rgb(255 255 255 / 14%);
border: 1px solid rgb(255 255 255 / 22%);
backdrop-filter: blur(8px);
&__label {
font-size: 9.5px;
text-transform: uppercase;
letter-spacing: 0.07em;
font-weight: 700;
color: rgb(255 255 255 / 75%);
}
&__value {
margin-top: 2px;
font-size: 15px;
font-weight: 800;
color: #fff;
font-variant-numeric: tabular-nums;
letter-spacing: -0.01em;
}
&--available {
background: rgb(16 185 129 / 22%);
border-color: rgb(16 185 129 / 35%);
}
}
/* Status pill */
.cur-status-pill {
display: inline-flex;
align-items: center;
gap: 5px;
height: 18px;
padding: 0 8px;
border-radius: 999px;
background: rgb(16 185 129 / 24%);
border: 1px solid rgb(16 185 129 / 40%);
font-size: 10px;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.06em;
color: #d1fae5;
&__dot {
width: 5px;
height: 5px;
border-radius: 50%;
background: #10b981;
box-shadow: 0 0 0 2px rgb(16 185 129 / 30%);
}
&--warn {
background: rgb(245 158 11 / 24%);
border-color: rgb(245 158 11 / 40%);
color: #fef3c7;
.cur-status-pill__dot {
background: #f59e0b;
box-shadow: 0 0 0 3px rgb(245 158 11 / 30%);
}
}
&.statussubmitted,
&.statuspending {
background: rgb(245 158 11 / 24%);
border-color: rgb(245 158 11 / 40%);
color: #fef3c7;
.cur-status-pill__dot {
background: #f59e0b;
}
}
&.statusclosed,
&.statusrejected,
&.statuswithdrawn {
background: rgb(239 68 68 / 24%);
border-color: rgb(239 68 68 / 40%);
color: #fee2e2;
.cur-status-pill__dot {
background: #ef4444;
}
}
}
/* ─── Pill-style tab navigation ─────────────────────────────────────── */
.cur-tabs {
margin-top: 18px;
background: #fff;
border: 1px solid $border;
border-radius: 14px;
padding: 6px;
box-shadow: 0 1px 3px rgb(15 23 42 / 4%);
overflow: hidden;
::ng-deep {
.mat-mdc-tab-link-container {
border-bottom: none !important;
}
.mat-mdc-tab-links {
gap: 4px;
flex-wrap: wrap;
}
.mdc-tab-indicator__content--underline,
.mat-mdc-tab-header-pagination-controls-enabled .mdc-tab-indicator__content--underline {
display: none !important;
}
}
}
.cur-tab {
display: inline-flex !important;
align-items: center;
gap: 8px;
height: 38px !important;
min-width: 0 !important;
padding: 0 16px !important;
border-radius: 10px !important;
font-family: Inter, 'DM Sans', Roboto, sans-serif !important;
font-size: 13px !important;
font-weight: 600 !important;
letter-spacing: 0.01em !important;
color: #64748b !important;
background: transparent !important;
transition: all 0.15s ease;
white-space: nowrap;
border: 1px solid transparent !important;
opacity: 1 !important;
&__ic {
font-size: 12px;
color: inherit;
}
&:hover:not(.mdc-tab--active) {
background: #f1f5f9 !important;
color: $navy !important;
}
&.mdc-tab--active,
&.mat-mdc-tab-link-active {
background: linear-gradient(135deg, $blue 0%, $blue-d 100%) !important;
color: #fff !important;
box-shadow: 0 2px 6px -1px rgb(30 64 175 / 35%);
border-color: $blue-d !important;
::ng-deep .mdc-tab__text-label,
::ng-deep .mat-mdc-tab-link-content,
::ng-deep span,
.cur-tab__ic {
color: #fff !important;
opacity: 1 !important;
}
}
}
.cur-body {
margin-top: 16px;
}
/* ─── Responsive ────────────────────────────────────────────────────── */
@media (width <= 1100px) {
.cur-hero {
grid-template-columns: 1fr;
&__kpis {
width: 100%;
flex-wrap: wrap;
}
&__actions {
position: absolute;
top: 18px;
right: 18px;
}
}
.cur-kpi {
flex: 1 1 auto;
min-width: 0;
}
}
@media (width <= 700px) {
.cur-view {
padding: 12px;
}
.cur-hero {
padding: 18px;
&__title {
font-size: 18px;
}
&__avatar {
width: 52px;
height: 52px;
font-size: 22px;
border-radius: 12px;
}
}
.cur-tab {
padding: 0 12px !important;
font-size: 12px !important;
}
}
@@ -13,355 +13,390 @@
}
@if (!isLoading) {
<div class="current-account-tables layout-row gap-2percent">
<div class="flex-49 layout-column gap-4percent">
@if (!currentAccountData.status.rejected && !currentAccountData.status.submittedAndPendingApproval) {
<div>
<h4 class="table-headers">{{ 'labels.heading.Current Account Details' | translate }}</h4>
<table>
<tbody>
<tr>
<td>{{ 'labels.inputs.External Id' | translate }}</td>
<div class="gt-grid">
<!-- ─── LEFT COLUMN ──────────────────────────────────────────── -->
<div class="gt-col">
<!-- Current Account Details -->
<section class="nx-card">
<header class="nx-card__head">
<fa-icon icon="info-circle" class="nx-card__ico"></fa-icon>
<h3 class="nx-card__title">{{ 'labels.heading.Current Account Details' | translate }}</h3>
</header>
<div class="nx-card__body">
@if (!currentAccountData.status.rejected && !currentAccountData.status.submittedAndPendingApproval) {
<div class="nx-row">
<span class="nx-row__label">{{ 'labels.inputs.External Id' | translate }}</span>
<span class="nx-row__value">
@if (currentAccountData.externalId) {
<td>
<mifosx-external-identifier
externalId="{{ currentAccountData.externalId }}"
></mifosx-external-identifier>
</td>
}
@if (!currentAccountData.externalId) {
<td>{{ 'labels.inputs.Not Provided' | translate }}</td>
}
</tr>
<tr>
<td>{{ 'labels.inputs.Activated On' | translate }}</td>
<td>
{{
currentAccountData.timeline.activatedOnDate
? (currentAccountData.timeline.activatedOnDate | dateFormat)
: 'Not Activated'
}}
</td>
</tr>
<tr>
<td>{{ 'labels.inputs.Field Officer' | translate }}</td>
<td>{{ currentAccountData.fieldOfficerName ? currentAccountData.fieldOfficerName : 'Unassigned' }}</td>
</tr>
<tr>
<td>{{ 'labels.inputs.Currency' | translate }}</td>
<td>{{ currentAccountData.currency.name }} [{{ currentAccountData.currency.code }}]</td>
</tr>
<tr>
<td>{{ 'labels.inputs.Nominal Interest Rate' | translate }}</td>
<td class="r-amount">
@if (currentAccountData.nominalAnnualInterestRate) {
<span>{{ currentAccountData.nominalAnnualInterestRate | formatNumber }} %</span>
}
@if (!currentAccountData.nominalAnnualInterestRate) {
<span>0 %</span>
}
</td>
</tr>
</tbody>
</table>
</div>
} @else {
<div>
<h4 class="table-headers">{{ 'labels.heading.Current Account Details' | translate }}</h4>
<table>
<tbody>
<tr>
<td>{{ 'labels.inputs.Field Officer' | translate }}</td>
<td>{{ currentAccountData.fieldOfficerName ? currentAccountData.fieldOfficerName : 'Unassigned' }}</td>
</tr>
<tr>
<td>{{ 'labels.inputs.Balance' | translate }}</td>
<td class="r-amount">
{{ currentAccountData.summary.accountBalance | currency: currency.code : 'symbol-narrow' : '1.2-2' }}
</td>
</tr>
</tbody>
</table>
</div>
}
@if (!currentAccountData.status.rejected && !currentAccountData.status.submittedAndPendingApproval) {
<div>
<h4 class="table-headers">{{ 'labels.heading.Performance History' | translate }}</h4>
<table>
<tbody>
@if (currentAccountData.summary.totalDeposits) {
<tr>
<td>{{ 'labels.inputs.Total Deposits' | translate }}</td>
<td class="r-amount">
{{ currentAccountData.summary.totalDeposits | currency: currency.code : 'symbol-narrow' : '1.2-2' }}
</td>
</tr>
}
@if (currentAccountData.summary.totalInterestEarned >= 0) {
<tr>
<td>{{ 'labels.inputs.Total Interest Earned' | translate }}</td>
<td class="r-amount">
{{
currentAccountData.summary.totalInterestEarned
| currency: currency.code : 'symbol-narrow' : '1.2-2'
}}
</td>
</tr>
}
</tbody>
</table>
</div>
}
</div>
@if (isActive) {
<div class="flex-49">
<h4 class="table-headers">{{ 'labels.heading.Account Summary' | translate }}</h4>
<table>
<tbody>
@if (currentAccountData.summary.totalWithdrawals) {
<tr>
<td>{{ 'labels.inputs.Total Withdrawls' | translate }}</td>
<td class="r-amount">
{{
currentAccountData.summary.totalWithdrawals | currency: currency.code : 'symbol-narrow' : '1.2-2'
}}
</td>
</tr>
}
@if (currentAccountData.summary.totalWithdrawalFees) {
<tr>
<td>{{ 'labels.inputs.Withdrawals Fees' | translate }}</td>
<td class="r-amount">
{{
currentAccountData.summary.totalWithdrawalFees | currency: currency.code : 'symbol-narrow' : '1.2-2'
}}
</td>
</tr>
}
@if (currentAccountData.summary.totalAnnualFees) {
<tr>
<td>{{ 'labels.inputs.Annual Fees' | translate }}</td>
<td class="r-amount">
{{ currentAccountData.summary.totalAnnualFees | currency: currency.code : 'symbol-narrow' : '1.2-2' }}
</td>
</tr>
}
@if (currentAccountData.summary.totalInterestEarned >= 0) {
<tr>
<td>{{ 'labels.inputs.Interest Earned' | translate }}</td>
<td class="r-amount">
{{
currentAccountData.summary.totalInterestEarned | currency: currency.code : 'symbol-narrow' : '1.2-2'
}}
</td>
</tr>
}
@if (currentAccountData.summary.totalInterestPosted) {
<tr>
<td>{{ 'labels.inputs.Interest Posted' | translate }}</td>
<td class="r-amount">
{{
currentAccountData.summary.totalInterestPosted | currency: currency.code : 'symbol-narrow' : '1.2-2'
}}
</td>
</tr>
}
@if (currentAccountData.summary.interestNotPosted >= 0) {
<tr>
<td>{{ 'labels.inputs.Interest Earned Not Posted' | translate }}</td>
<td class="r-amount">
{{
currentAccountData.summary.interestNotPosted | currency: currency.code : 'symbol-narrow' : '1.2-2'
}}
</td>
</tr>
}
@if (currentAccountData.summary.totalOverdraftInterestDerived) {
<tr>
<td>{{ 'labels.inputs.Interest On Overdraft' | translate }}</td>
<td class="r-amount">
{{
currentAccountData.summary.totalOverdraftInterestDerived
| currency: currency.code : 'symbol-narrow' : '1.2-2'
}}
</td>
</tr>
}
@if (currentAccountData.summary.interestNotPosted < 0) {
<tr>
<td>{{ 'labels.inputs.Overdraft Interest Not Posted' | translate }}</td>
<td class="r-amount">
{{
currentAccountData.summary.interestNotPosted | currency: currency.code : 'symbol-narrow' : '1.2-2'
}}
</td>
</tr>
}
@if (currentAccountData.nominalAnnualInterestRate) {
<tr>
<td>{{ 'labels.inputs.Nominal Interest Rate' | translate }}</td>
<td class="r-amount">{{ currentAccountData.nominalAnnualInterestRate | formatNumber }} %</td>
</tr>
}
<tr>
<td>{{ 'labels.inputs.Interest Compounding Period' | translate }}</td>
<td>{{ currentAccountData.interestCompoundingPeriodType.value | translateKey: 'catalogs' }}</td>
</tr>
<tr>
<td>{{ 'labels.inputs.Interest Posting Period' | translate }}</td>
<td>{{ currentAccountData.interestPostingPeriodType.value | translateKey: 'catalogs' }}</td>
</tr>
<tr>
<td>{{ 'labels.inputs.Interest Calculated Using' | translate }}</td>
<td>{{ currentAccountData.interestCalculationType.value | translateKey: 'catalogs' }}</td>
</tr>
<tr>
<td>{{ 'labels.inputs.Days in Year' | translate }}</td>
<td>{{ currentAccountData.interestCalculationDaysInYearType.value | translateKey: 'catalogs' }}</td>
</tr>
@if (currentAccountData.withdrawalFee) {
<tr>
<td>{{ 'labels.inputs.Withdrawal Fee' | translate }}</td>
<td class="r-amount">
{{ currentAccountData.withdrawalFee.amount | currency: currency.code : 'symbol-narrow' : '1.2-2' }}
</td>
</tr>
}
@if (currentAccountData.lastActiveTransactionDate) {
<tr>
<td>{{ 'labels.inputs.Last Active Transaction Date' | translate }}</td>
<td>{{ currentAccountData.lastActiveTransactionDate | dateFormat }}</td>
</tr>
}
@if (!(currentAccountData.subStatus.id === 0)) {
<tr>
<td>{{ 'labels.inputs.Substatus' | translate }}</td>
<td>{{ currentAccountData.subStatus.value }}</td>
</tr>
}
@if (currentAccountData.daysToInactive) {
<tr>
<td>{{ 'labels.inputs.Days to Inactive' | translate }}</td>
<td>{{ currentAccountData.daysToInactive }}</td>
</tr>
}
@if (currentAccountData.daysToDormancy) {
<tr>
<td>{{ 'labels.inputs.Days to Dormancy' | translate }}</td>
<td>{{ currentAccountData.daysToDormancy }}</td>
</tr>
}
@if (currentAccountData.daysToEscheat) {
<tr>
<td>{{ 'labels.inputs.Days to Escheat' | translate }}</td>
<td>{{ currentAccountData.daysToEscheat }}</td>
</tr>
}
@if (currentAccountData.annualFee) {
<tr>
<td>{{ 'labels.inputs.Annual Fee' | translate }}</td>
<td class="r-amount">
{{ currentAccountData.annualFee.amount | currency: currency.code : 'symbol-narrow' : '1.2-2' }}
</td>
</tr>
}
@if (currentAccountData.allowOverdraft) {
<tr>
<td>{{ 'labels.inputs.Overdraft Limit' | translate }}</td>
<td class="r-amount">
{{ currentAccountData.overdraftLimit | currency: currency.code : 'symbol-narrow' : '1.2-2' }}
</td>
</tr>
}
@if (currentAccountData.allowOverdraft) {
<tr>
<td>{{ 'labels.inputs.Minimum Overdraft Required for Interest Calculation' | translate }}</td>
<td>
{{
currentAccountData.minOverdraftForInterestCalculation
| currency: currency.code : 'symbol-narrow' : '1.2-2'
}}
</td>
</tr>
}
@if (currentAccountData.minBalanceForInterestCalculation) {
<tr>
<td>{{ 'labels.inputs.Min Balance Required for Interest Calculation' | translate }}</td>
<td>{{ currentAccountData.minBalanceForInterestCalculation | formatNumber }}</td>
</tr>
}
@if (currentAccountData.minRequiredBalance) {
<tr>
<td>{{ 'labels.inputs.Minimum Required Balance' | translate }}</td>
<td>{{ currentAccountData.minRequiredBalance | formatNumber }}</td>
</tr>
}
@if (currentAccountData.enforceMinRequiredBalance) {
<tr>
<td>{{ 'labels.inputs.Enforce Minimum Required Balance' | translate }}</td>
<td>{{ currentAccountData.enforceMinRequiredBalance | yesNo }}</td>
</tr>
}
@if (currentAccountData.summary.lastInterestCalculationDate) {
<tr>
<td>{{ 'labels.inputs.Interest Recalculation Date' | translate }}</td>
<td>{{ currentAccountData.summary.lastInterestCalculationDate | dateFormat }}</td>
</tr>
}
@if (currentAccountData.onHoldFunds) {
<tr>
<td>{{ 'labels.inputs.On Hold Funds' | translate }}</td>
<td>
<a *mifosxHasPermission="'READ_CURRENTACCOUNT'">{{
currentAccountData.onHoldFunds | currency: currency.code : 'symbol-narrow' : '1.2-2'
}}</a>
</td>
</tr>
}
@if (currentAccountData.withHoldTax) {
<tr>
<td>{{ 'labels.inputs.Withhold Tax Group' | translate }}</td>
<td>{{ currentAccountData.taxGroup.name }}</td>
</tr>
}
</tbody>
</table>
</div>
} @else {
<div class="flex-49">
<h4 class="table-headers">{{ 'labels.heading.Account Summary' | translate }}</h4>
<table>
<tbody>
<tr>
<td>{{ 'labels.inputs.External Id' | translate }}</td>
@if (currentAccountData.externalId) {
<td>
<mifosx-external-identifier
externalId="{{ currentAccountData.externalId }}"
></mifosx-external-identifier>
</td>
}
@if (!currentAccountData.externalId) {
<td>{{ 'labels.inputs.Not Provided' | translate }}</td>
}
</tr>
<tr>
<td>{{ 'labels.inputs.Activated On' | translate }}</td>
<td>
} @else {
{{ 'labels.inputs.Not Provided' | translate }}
}
</span>
</div>
<div class="nx-row">
<span class="nx-row__label">{{ 'labels.inputs.Activated On' | translate }}</span>
<span class="nx-row__value">
{{
currentAccountData.timeline.activatedOnDate
? (currentAccountData.timeline.activatedOnDate | dateFormat)
: 'Not Activated'
}}
</td>
</tr>
<tr>
<td>{{ 'labels.inputs.Currency' | translate }}</td>
<td>{{ currentAccountData.currency.name }} [{{ currentAccountData.currency.code }}]</td>
</tr>
</tbody>
</table>
</div>
}
</span>
</div>
<div class="nx-row">
<span class="nx-row__label">{{ 'labels.inputs.Field Officer' | translate }}</span>
<span class="nx-row__value">
{{ currentAccountData.fieldOfficerName ? currentAccountData.fieldOfficerName : 'Unassigned' }}
</span>
</div>
<div class="nx-row">
<span class="nx-row__label">{{ 'labels.inputs.Currency' | translate }}</span>
<span class="nx-row__value">
{{ currentAccountData.currency.name }}
<span class="nx-mono">[{{ currentAccountData.currency.code }}]</span>
</span>
</div>
<div class="nx-row">
<span class="nx-row__label">{{ 'labels.inputs.Nominal Interest Rate' | translate }}</span>
<span class="nx-row__value nx-row__value--amount">
@if (currentAccountData.nominalAnnualInterestRate) {
{{ currentAccountData.nominalAnnualInterestRate | formatNumber }} %
} @else {
0 %
}
</span>
</div>
} @else {
<div class="nx-row">
<span class="nx-row__label">{{ 'labels.inputs.Field Officer' | translate }}</span>
<span class="nx-row__value">
{{ currentAccountData.fieldOfficerName ? currentAccountData.fieldOfficerName : 'Unassigned' }}
</span>
</div>
<div class="nx-row">
<span class="nx-row__label">{{ 'labels.inputs.Balance' | translate }}</span>
<span class="nx-row__value nx-row__value--amount">
{{ currentAccountData.summary.accountBalance | currency: currency.code : 'symbol-narrow' : '1.2-2' }}
</span>
</div>
}
</div>
</section>
<!-- Performance History -->
@if (!currentAccountData.status.rejected && !currentAccountData.status.submittedAndPendingApproval) {
<section class="nx-card">
<header class="nx-card__head">
<fa-icon icon="chart-line" class="nx-card__ico"></fa-icon>
<h3 class="nx-card__title">{{ 'labels.heading.Performance History' | translate }}</h3>
</header>
<div class="nx-card__body">
@if (currentAccountData.summary.totalDeposits) {
<div class="nx-row">
<span class="nx-row__label">{{ 'labels.inputs.Total Deposits' | translate }}</span>
<span class="nx-row__value nx-row__value--amount nx-row__value--positive">
{{ currentAccountData.summary.totalDeposits | currency: currency.code : 'symbol-narrow' : '1.2-2' }}
</span>
</div>
}
@if (currentAccountData.summary.totalInterestEarned >= 0) {
<div class="nx-row">
<span class="nx-row__label">{{ 'labels.inputs.Total Interest Earned' | translate }}</span>
<span class="nx-row__value nx-row__value--amount">
{{
currentAccountData.summary.totalInterestEarned | currency: currency.code : 'symbol-narrow' : '1.2-2'
}}
</span>
</div>
}
@if (!currentAccountData.summary.totalDeposits && !(currentAccountData.summary.totalInterestEarned >= 0)) {
<div class="nx-empty">
<fa-icon icon="inbox" class="nx-empty__ico"></fa-icon>
<span>No activity yet.</span>
</div>
}
</div>
</section>
}
</div>
<!-- ─── RIGHT COLUMN ─────────────────────────────────────────── -->
<div class="gt-col">
@if (isActive) {
<section class="nx-card">
<header class="nx-card__head">
<fa-icon icon="file-invoice" class="nx-card__ico"></fa-icon>
<h3 class="nx-card__title">{{ 'labels.heading.Account Summary' | translate }}</h3>
</header>
<div class="nx-card__body">
@if (currentAccountData.summary.totalWithdrawals) {
<div class="nx-row">
<span class="nx-row__label">{{ 'labels.inputs.Total Withdrawls' | translate }}</span>
<span class="nx-row__value nx-row__value--amount nx-row__value--negative">
{{
currentAccountData.summary.totalWithdrawals | currency: currency.code : 'symbol-narrow' : '1.2-2'
}}
</span>
</div>
}
@if (currentAccountData.summary.totalWithdrawalFees) {
<div class="nx-row">
<span class="nx-row__label">{{ 'labels.inputs.Withdrawals Fees' | translate }}</span>
<span class="nx-row__value nx-row__value--amount">
{{
currentAccountData.summary.totalWithdrawalFees | currency: currency.code : 'symbol-narrow' : '1.2-2'
}}
</span>
</div>
}
@if (currentAccountData.summary.totalAnnualFees) {
<div class="nx-row">
<span class="nx-row__label">{{ 'labels.inputs.Annual Fees' | translate }}</span>
<span class="nx-row__value nx-row__value--amount">
{{ currentAccountData.summary.totalAnnualFees | currency: currency.code : 'symbol-narrow' : '1.2-2' }}
</span>
</div>
}
@if (currentAccountData.summary.totalInterestEarned >= 0) {
<div class="nx-row">
<span class="nx-row__label">{{ 'labels.inputs.Interest Earned' | translate }}</span>
<span class="nx-row__value nx-row__value--amount">
{{
currentAccountData.summary.totalInterestEarned | currency: currency.code : 'symbol-narrow' : '1.2-2'
}}
</span>
</div>
}
@if (currentAccountData.summary.totalInterestPosted) {
<div class="nx-row">
<span class="nx-row__label">{{ 'labels.inputs.Interest Posted' | translate }}</span>
<span class="nx-row__value nx-row__value--amount">
{{
currentAccountData.summary.totalInterestPosted | currency: currency.code : 'symbol-narrow' : '1.2-2'
}}
</span>
</div>
}
@if (currentAccountData.summary.interestNotPosted >= 0) {
<div class="nx-row">
<span class="nx-row__label">{{ 'labels.inputs.Interest Earned Not Posted' | translate }}</span>
<span class="nx-row__value nx-row__value--amount">
{{
currentAccountData.summary.interestNotPosted | currency: currency.code : 'symbol-narrow' : '1.2-2'
}}
</span>
</div>
}
@if (currentAccountData.summary.totalOverdraftInterestDerived) {
<div class="nx-row">
<span class="nx-row__label">{{ 'labels.inputs.Interest On Overdraft' | translate }}</span>
<span class="nx-row__value nx-row__value--amount">
{{
currentAccountData.summary.totalOverdraftInterestDerived
| currency: currency.code : 'symbol-narrow' : '1.2-2'
}}
</span>
</div>
}
@if (currentAccountData.summary.interestNotPosted < 0) {
<div class="nx-row">
<span class="nx-row__label">{{ 'labels.inputs.Overdraft Interest Not Posted' | translate }}</span>
<span class="nx-row__value nx-row__value--amount">
{{
currentAccountData.summary.interestNotPosted | currency: currency.code : 'symbol-narrow' : '1.2-2'
}}
</span>
</div>
}
@if (currentAccountData.nominalAnnualInterestRate) {
<div class="nx-row">
<span class="nx-row__label">{{ 'labels.inputs.Nominal Interest Rate' | translate }}</span>
<span class="nx-row__value nx-row__value--amount">
{{ currentAccountData.nominalAnnualInterestRate | formatNumber }} %
</span>
</div>
}
<div class="nx-row">
<span class="nx-row__label">{{ 'labels.inputs.Interest Compounding Period' | translate }}</span>
<span class="nx-row__value">
{{ currentAccountData.interestCompoundingPeriodType.value | translateKey: 'catalogs' }}
</span>
</div>
<div class="nx-row">
<span class="nx-row__label">{{ 'labels.inputs.Interest Posting Period' | translate }}</span>
<span class="nx-row__value">
{{ currentAccountData.interestPostingPeriodType.value | translateKey: 'catalogs' }}
</span>
</div>
<div class="nx-row">
<span class="nx-row__label">{{ 'labels.inputs.Interest Calculated Using' | translate }}</span>
<span class="nx-row__value">
{{ currentAccountData.interestCalculationType.value | translateKey: 'catalogs' }}
</span>
</div>
<div class="nx-row">
<span class="nx-row__label">{{ 'labels.inputs.Days in Year' | translate }}</span>
<span class="nx-row__value">
{{ currentAccountData.interestCalculationDaysInYearType.value | translateKey: 'catalogs' }}
</span>
</div>
@if (currentAccountData.withdrawalFee) {
<div class="nx-row">
<span class="nx-row__label">{{ 'labels.inputs.Withdrawal Fee' | translate }}</span>
<span class="nx-row__value nx-row__value--amount">
{{ currentAccountData.withdrawalFee.amount | currency: currency.code : 'symbol-narrow' : '1.2-2' }}
</span>
</div>
}
@if (currentAccountData.lastActiveTransactionDate) {
<div class="nx-row">
<span class="nx-row__label">{{ 'labels.inputs.Last Active Transaction Date' | translate }}</span>
<span class="nx-row__value">{{ currentAccountData.lastActiveTransactionDate | dateFormat }}</span>
</div>
}
@if (!(currentAccountData.subStatus.id === 0)) {
<div class="nx-row">
<span class="nx-row__label">{{ 'labels.inputs.Substatus' | translate }}</span>
<span class="nx-row__value">{{ currentAccountData.subStatus.value }}</span>
</div>
}
@if (currentAccountData.daysToInactive) {
<div class="nx-row">
<span class="nx-row__label">{{ 'labels.inputs.Days to Inactive' | translate }}</span>
<span class="nx-row__value">{{ currentAccountData.daysToInactive }}</span>
</div>
}
@if (currentAccountData.daysToDormancy) {
<div class="nx-row">
<span class="nx-row__label">{{ 'labels.inputs.Days to Dormancy' | translate }}</span>
<span class="nx-row__value">{{ currentAccountData.daysToDormancy }}</span>
</div>
}
@if (currentAccountData.daysToEscheat) {
<div class="nx-row">
<span class="nx-row__label">{{ 'labels.inputs.Days to Escheat' | translate }}</span>
<span class="nx-row__value">{{ currentAccountData.daysToEscheat }}</span>
</div>
}
@if (currentAccountData.annualFee) {
<div class="nx-row">
<span class="nx-row__label">{{ 'labels.inputs.Annual Fee' | translate }}</span>
<span class="nx-row__value nx-row__value--amount">
{{ currentAccountData.annualFee.amount | currency: currency.code : 'symbol-narrow' : '1.2-2' }}
</span>
</div>
}
@if (currentAccountData.allowOverdraft) {
<div class="nx-row">
<span class="nx-row__label">{{ 'labels.inputs.Overdraft Limit' | translate }}</span>
<span class="nx-row__value nx-row__value--amount">
{{ currentAccountData.overdraftLimit | currency: currency.code : 'symbol-narrow' : '1.2-2' }}
</span>
</div>
}
@if (currentAccountData.allowOverdraft) {
<div class="nx-row">
<span class="nx-row__label">
{{ 'labels.inputs.Minimum Overdraft Required for Interest Calculation' | translate }}
</span>
<span class="nx-row__value">
{{
currentAccountData.minOverdraftForInterestCalculation
| currency: currency.code : 'symbol-narrow' : '1.2-2'
}}
</span>
</div>
}
@if (currentAccountData.minBalanceForInterestCalculation) {
<div class="nx-row">
<span class="nx-row__label">
{{ 'labels.inputs.Min Balance Required for Interest Calculation' | translate }}
</span>
<span class="nx-row__value">
{{ currentAccountData.minBalanceForInterestCalculation | formatNumber }}
</span>
</div>
}
@if (currentAccountData.minRequiredBalance) {
<div class="nx-row">
<span class="nx-row__label">{{ 'labels.inputs.Minimum Required Balance' | translate }}</span>
<span class="nx-row__value">{{ currentAccountData.minRequiredBalance | formatNumber }}</span>
</div>
}
@if (currentAccountData.enforceMinRequiredBalance) {
<div class="nx-row">
<span class="nx-row__label">{{ 'labels.inputs.Enforce Minimum Required Balance' | translate }}</span>
<span class="nx-row__value">{{ currentAccountData.enforceMinRequiredBalance | yesNo }}</span>
</div>
}
@if (currentAccountData.summary.lastInterestCalculationDate) {
<div class="nx-row">
<span class="nx-row__label">{{ 'labels.inputs.Interest Recalculation Date' | translate }}</span>
<span class="nx-row__value">
{{ currentAccountData.summary.lastInterestCalculationDate | dateFormat }}
</span>
</div>
}
@if (currentAccountData.onHoldFunds) {
<div class="nx-row">
<span class="nx-row__label">{{ 'labels.inputs.On Hold Funds' | translate }}</span>
<span class="nx-row__value nx-row__value--amount">
<a *mifosxHasPermission="'READ_CURRENTACCOUNT'">
{{ currentAccountData.onHoldFunds | currency: currency.code : 'symbol-narrow' : '1.2-2' }}
</a>
</span>
</div>
}
@if (currentAccountData.withHoldTax) {
<div class="nx-row">
<span class="nx-row__label">{{ 'labels.inputs.Withhold Tax Group' | translate }}</span>
<span class="nx-row__value">{{ currentAccountData.taxGroup.name }}</span>
</div>
}
</div>
</section>
} @else {
<section class="nx-card">
<header class="nx-card__head">
<fa-icon icon="file-invoice" class="nx-card__ico"></fa-icon>
<h3 class="nx-card__title">{{ 'labels.heading.Account Summary' | translate }}</h3>
</header>
<div class="nx-card__body">
<div class="nx-row">
<span class="nx-row__label">{{ 'labels.inputs.External Id' | translate }}</span>
<span class="nx-row__value">
@if (currentAccountData.externalId) {
<mifosx-external-identifier
externalId="{{ currentAccountData.externalId }}"
></mifosx-external-identifier>
} @else {
{{ 'labels.inputs.Not Provided' | translate }}
}
</span>
</div>
<div class="nx-row">
<span class="nx-row__label">{{ 'labels.inputs.Activated On' | translate }}</span>
<span class="nx-row__value">
{{
currentAccountData.timeline.activatedOnDate
? (currentAccountData.timeline.activatedOnDate | dateFormat)
: 'Not Activated'
}}
</span>
</div>
<div class="nx-row">
<span class="nx-row__label">{{ 'labels.inputs.Currency' | translate }}</span>
<span class="nx-row__value">
{{ currentAccountData.currency.name }}
<span class="nx-mono">[{{ currentAccountData.currency.code }}]</span>
</span>
</div>
</div>
</section>
}
</div>
</div>
}
@@ -6,9 +6,15 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
$navy: #0d1b2a;
$slate: #475569;
$muted: #64748b;
$border: #e2e8f0;
$soft: #f8fafc;
/* ─── Loader keyframes ──────────────────────────────────────────────── */
$bar: 160px;
$foo: $bar * 8;
$color: #e2e4ec;
.loader {
margin-bottom: 39px;
@@ -79,164 +85,136 @@ $color: #e2e4ec;
}
}
.general-tab-container {
padding: 16px 20px;
width: 100%;
box-sizing: border-box;
position: relative;
background-color: var(--background-color, #f8f9fa);
color: var(--text-color, #333);
transition:
background-color 0.3s ease,
color 0.3s ease;
/* ─── Two-column grid ───────────────────────────────────────────────── */
.gt-grid {
display: grid;
grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
gap: 16px;
.page-header {
margin-bottom: 24px;
padding: 16px 0;
position: relative;
border-bottom: 1px solid var(--border-color, #e9ecef);
@media (width <= 1024px) {
grid-template-columns: 1fr;
}
}
.header-title {
font-size: 1.75rem;
font-weight: 600;
margin: 0 0 8px;
line-height: 1.2;
color: var(--primary-color, #007bff);
position: relative;
}
.gt-col {
display: flex;
flex-direction: column;
gap: 16px;
min-width: 0;
}
.header-subtitle {
font-size: 0.95rem;
margin: 0;
line-height: 1.4;
color: var(--text-secondary, #6c757d);
position: relative;
}
/* ─── Nexora section card ───────────────────────────────────────────── */
.nx-card {
background: #fff;
border: 1px solid $border;
border-radius: 14px;
box-shadow: 0 1px 3px rgb(15 23 42 / 4%);
overflow: hidden;
&__head {
display: flex;
align-items: center;
gap: 10px;
padding: 12px 18px;
background: $soft;
border-bottom: 1px solid $border;
}
.content-wrapper {
position: relative;
width: 100%;
}
.account-info-card {
background: var(--card-background, #fff);
&__ico {
width: 26px;
height: 26px;
display: inline-flex;
align-items: center;
justify-content: center;
border-radius: 8px;
box-shadow: 0 2px 4px rgb(0 0 0 / 10%);
overflow: hidden;
position: relative;
width: 100%;
max-width: 600px;
transition:
box-shadow 0.3s ease,
background-color 0.3s ease;
background: linear-gradient(135deg, #3b82f6 0%, #1e40af 100%);
color: #fff;
font-size: 12px;
}
.card-header {
padding: 16px 20px;
background: var(--card-header-bg, #f8f9fa);
border-bottom: 1px solid var(--border-color, #e9ecef);
position: relative;
&__title {
margin: 0;
font-size: 13.5px;
font-weight: 700;
color: $navy;
letter-spacing: 0.01em;
}
h3 {
margin: 0;
font-size: 1.1rem;
font-weight: 600;
line-height: 1.3;
color: var(--text-color, #333);
}
&__body {
padding: 4px 18px 10px;
}
}
/* ─── Key/Value row ─────────────────────────────────────────────────── */
.nx-row {
display: grid;
grid-template-columns: minmax(0, 1fr) auto;
gap: 12px;
align-items: center;
min-height: 38px;
padding: 8px 0;
border-bottom: 1px dashed #eef2f7;
font-size: 13px;
&:last-child {
border-bottom: none;
}
&__label {
color: $slate;
font-weight: 500;
}
&__value {
color: $navy;
font-weight: 600;
text-align: right;
word-break: break-word;
&--amount {
font-variant-numeric: tabular-nums;
font-weight: 700;
}
.card-content {
padding: 20px;
position: relative;
&--positive {
color: #047857;
}
.info-row {
display: flex;
justify-content: space-between;
align-items: baseline;
padding: 10px 0;
border-bottom: 1px solid var(--border-light, #f1f3f4);
position: relative;
min-height: 24px;
&:last-child {
border-bottom: none;
}
.label {
font-weight: 500;
color: var(--text-secondary, #6c757d);
flex: 0 0 auto;
margin-right: 16px;
white-space: nowrap;
line-height: 1.4;
}
.value {
font-weight: 600;
color: var(--text-color, #333);
text-align: right;
flex: 1 1 auto;
line-height: 1.4;
&.currency {
color: var(--success-color, #28a745);
font-size: 1.05rem;
}
}
}
&--negative {
color: #b91c1c;
}
}
}
// Dark mode styles
[data-theme='dark'] .general-tab-container,
.dark-theme .general-tab-container {
--background-color: #1a1a1a;
--text-color: #e9ecef;
--text-secondary: #adb5bd;
--primary-color: #4dabf7;
--card-background: #2d3748;
--card-header-bg: #374151;
--border-color: #4a5568;
--border-light: #2d3748;
--success-color: #68d391;
.nx-muted {
color: $muted;
font-weight: 500;
font-style: italic;
}
// Responsive adjustments for mobile
@media (width <= 768px) {
.general-tab-container {
padding: 12px 16px;
.nx-mono {
font-family: 'JetBrains Mono', 'Courier New', monospace;
font-size: 12.5px;
color: $muted;
margin-left: 4px;
}
.page-header {
margin-bottom: 20px;
padding: 12px 0;
/* ─── Empty state inside a card ─────────────────────────────────────── */
.nx-empty {
display: flex;
flex-direction: column;
align-items: center;
gap: 8px;
padding: 24px 16px;
color: $muted;
.header-title {
font-size: 1.5rem;
}
&__ico {
font-size: 28px;
color: #cbd5e1;
}
.header-subtitle {
font-size: 0.9rem;
}
}
.account-info-card {
.card-header {
padding: 14px 16px;
}
.card-content {
padding: 16px;
.info-row {
padding: 8px 0;
.label {
margin-right: 12px;
}
}
}
}
span {
font-size: 13px;
font-weight: 500;
}
}
@@ -7,8 +7,8 @@
-->
<div class="passbook-container">
<!-- Date Range Filter -->
<div class="passbook-filter-bar">
<!-- Filter Bar -->
<div class="passbook-filter-bar no-print">
<form [formGroup]="dateRangeForm" class="passbook-filter-form">
<mat-form-field appearance="outline" class="passbook-date-field" (click)="fromDatePicker.open()">
<mat-label>{{ 'labels.inputs.From Date' | translate }}</mat-label>
@@ -45,105 +45,152 @@
</form>
</div>
<!-- Passbook Preview (Printable Content) -->
<div class="passbook-preview" #passbookContent>
<div class="passbook-header">
<h2>{{ 'labels.heading.Current Account Passbook' | translate }}</h2>
<h3>{{ productName }}</h3>
</div>
<!-- Account Info -->
<div class="passbook-info-grid">
<div class="passbook-info-item">
<div class="label">{{ 'labels.inputs.Account Number' | translate }}</div>
<div class="value">{{ accountNo }}</div>
<!-- Bank Statement -->
<div class="statement-wrapper" #passbookContent>
<!-- Bank Header -->
<div class="bank-header">
<div class="bank-logo-area">
<div class="bank-logo-circle">
<mat-icon>account_balance</mat-icon>
</div>
<div class="bank-name-block">
<div class="bank-title">LincPay Co-operative Bank Ltd.</div>
<div class="bank-subtitle">Current Account Statement</div>
</div>
</div>
<div class="passbook-info-item">
<div class="label">{{ 'labels.inputs.Account Holder' | translate }}</div>
<div class="value">{{ clientName }}</div>
</div>
<div class="passbook-info-item">
<div class="label">{{ 'labels.inputs.Current Account Product' | translate }}</div>
<div class="value">{{ productName }}</div>
</div>
<div class="passbook-info-item">
<div class="label">{{ 'labels.inputs.Opening Date' | translate }}</div>
<div class="value">{{ activatedOnDate | dateFormat }}</div>
</div>
<div class="passbook-info-item">
<div class="label">{{ 'labels.inputs.Status' | translate }}</div>
<div class="value">{{ status }}</div>
</div>
<div class="passbook-info-item">
<div class="label">{{ 'labels.inputs.Interest Rate' | translate }}</div>
<div class="value">{{ interestRate }}%</div>
<div class="statement-label">
<div class="stmt-label-text">STATEMENT OF ACCOUNT</div>
<div class="stmt-period">As on: {{ maxDate | dateFormat }}</div>
</div>
</div>
<!-- Balance Summary -->
<div class="passbook-balance-row">
<div class="balance-item">
<span>{{ 'labels.inputs.Current Balance' | translate }}: </span>
<b>{{ accountBalance | currency: currency?.code : 'symbol-narrow' : '1.2-2' }}</b>
<!-- Account Details -->
<div class="account-details-band">
<div class="account-details-grid">
<div class="detail-block">
<div class="detail-label">Account Holder</div>
<div class="detail-value bold">{{ clientName | uppercase }}</div>
</div>
<div class="detail-block">
<div class="detail-label">Account Number</div>
<div class="detail-value mono bold">{{ accountNo }}</div>
</div>
<div class="detail-block">
<div class="detail-label">Product</div>
<div class="detail-value">{{ productName }}</div>
</div>
<div class="detail-block">
<div class="detail-label">Account Opening Date</div>
<div class="detail-value">{{ activatedOnDate | dateFormat }}</div>
</div>
<div class="detail-block">
<div class="detail-label">Status</div>
<div class="detail-value status-badge" [class.active]="status === 'Active'">{{ status }}</div>
</div>
<div class="detail-block">
<div class="detail-label">Interest Rate (p.a.)</div>
<div class="detail-value">{{ interestRate }}%</div>
</div>
</div>
<div class="balance-item">
<span>{{ 'labels.inputs.Available Balance' | translate }}: </span>
<b>{{ availableBalance | currency: currency?.code : 'symbol-narrow' : '1.2-2' }}</b>
</div>
<!-- Balance Summary Strip -->
<div class="balance-summary-strip">
<div class="balance-box">
<div class="balance-box-label">Current Balance</div>
<div class="balance-box-value">{{ accountBalance | currency: currency?.code : 'symbol-narrow' : '1.2-2' }}</div>
</div>
<div class="balance-divider"></div>
<div class="balance-box">
<div class="balance-box-label">Available Balance</div>
<div class="balance-box-value">
{{ availableBalance | currency: currency?.code : 'symbol-narrow' : '1.2-2' }}
</div>
</div>
<div class="balance-divider"></div>
<div class="balance-box">
<div class="balance-box-label">Total Transactions</div>
<div class="balance-box-value">{{ filteredTransactions.length }}</div>
</div>
</div>
<!-- Transactions Table -->
@if (filteredTransactions.length > 0) {
<table>
<table class="statement-table">
<thead>
<tr>
<th>#</th>
<th>{{ 'labels.inputs.Date' | translate }}</th>
<th>{{ 'labels.inputs.Description' | translate }}</th>
<th class="text-right">{{ 'labels.inputs.Debit' | translate }}</th>
<th class="text-right">{{ 'labels.inputs.Credit' | translate }}</th>
<th class="text-right">{{ 'labels.inputs.Balance' | translate }}</th>
<th class="col-sr">#</th>
<th class="col-date">Date</th>
<th class="col-desc">Description</th>
<th class="col-ref">Ref No.</th>
<th class="col-amount text-right">Debit (Dr)</th>
<th class="col-amount text-right">Credit (Cr)</th>
<th class="col-balance text-right">Balance (&#8377;)</th>
</tr>
</thead>
<tbody>
@for (txn of filteredTransactions; track txn.id; let i = $index) {
<tr [class.reversed]="txn.reversed">
<td>{{ i + 1 }}</td>
<td>{{ txn.date | dateFormat }}</td>
<td>{{ txn.transactionType.value }}</td>
<td class="text-right">
<tr
[class.reversed-row]="txn.reversed"
[class.debit-row]="isDebit(txn.transactionType) && !txn.reversed"
[class.credit-row]="!isDebit(txn.transactionType) && !txn.reversed"
>
<td class="col-sr text-center">{{ i + 1 }}</td>
<td class="col-date">{{ txn.date | dateFormat }}</td>
<td class="col-desc">
<span class="txn-desc">{{ txn.transactionType.value }}</span>
@if (txn.reversed) {
<span class="reversed-badge">REVERSED</span>
}
</td>
<td class="col-ref text-muted mono">{{ txn.id }}</td>
<td class="col-amount text-right debit-amount">
@if (isDebit(txn.transactionType)) {
{{ txn.amount | currency: currency?.code : 'symbol-narrow' : '1.2-2' }}
}
</td>
<td class="text-right">
<td class="col-amount text-right credit-amount">
@if (!isDebit(txn.transactionType)) {
{{ txn.amount | currency: currency?.code : 'symbol-narrow' : '1.2-2' }}
}
</td>
<td class="text-right">
<td class="col-balance text-right balance-amount">
{{ txn.runningBalance | currency: currency?.code : 'symbol-narrow' : '1.2-2' }}
</td>
</tr>
}
</tbody>
<tfoot>
<tr class="total-row">
<td colspan="4" class="text-right total-label">Closing Balance</td>
<td class="text-right total-label"></td>
<td class="text-right total-label"></td>
<td class="text-right closing-balance">
{{ accountBalance | currency: currency?.code : 'symbol-narrow' : '1.2-2' }}
</td>
</tr>
</tfoot>
</table>
} @else {
<div class="passbook-no-data">
<mat-icon>info_outline</mat-icon>
<span>{{
isFiltered
? ('labels.text.No transactions found for the selected date range.' | translate)
: 'No transactions found.'
}}</span>
<div class="no-data-block">
<mat-icon>inbox</mat-icon>
<p>
{{
isFiltered
? ('labels.text.No transactions found for the selected date range.' | translate)
: 'No transactions found.'
}}
</p>
</div>
}
<!-- Footer -->
<div class="passbook-footer">
{{ 'labels.text.Generated on' | translate }}: {{ maxDate | dateFormat }}
&nbsp;|&nbsp;
{{ 'labels.text.Total Transactions' | translate }}: {{ filteredTransactions.length }}
<!-- Statement Footer -->
<div class="statement-footer">
<div class="footer-note">
This is a computer-generated statement and does not require a signature. For queries, contact your branch or
call customer care.
</div>
<div class="footer-meta">Generated on: {{ maxDate | dateFormat }} &nbsp;|&nbsp; Account No: {{ accountNo }}</div>
</div>
</div>
</div>
@@ -6,13 +6,31 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
// ─── Variables ──────────────────────────────────────────────────────────────
$bank-primary: #1a3a6b; // deep navy (like SBI/PNB)
$bank-accent: #c8a951; // gold trim
$debit-color: #c0392b;
$credit-color: #1a7a3f;
$balance-color: #1a3a6b;
$border-color: #d0d7e3;
$row-alt: #f4f7fb;
$header-bg: #1a3a6b;
$header-text: #fff;
// ─── Container ──────────────────────────────────────────────────────────────
.passbook-container {
padding: 16px;
background: #eef1f7;
min-height: 100%;
}
// Filter Bar
// ─── Filter Bar ─────────────────────────────────────────────────────────────
.passbook-filter-bar {
margin-bottom: 16px;
background: #fff;
border-radius: 8px;
padding: 12px 16px;
border: 1px solid $border-color;
}
.passbook-filter-form {
@@ -30,147 +48,388 @@
flex: 1;
}
// Passbook Preview
.passbook-preview {
border: 1px solid #e0e0e0;
border-radius: 8px;
padding: 24px;
// ─── Statement Wrapper ───────────────────────────────────────────────────────
.statement-wrapper {
background: #fff;
border: 1px solid $border-color;
border-radius: 8px;
overflow: hidden;
box-shadow: 0 2px 12px rgb(0 0 0 / 8%);
font-family: 'Segoe UI', Arial, sans-serif;
}
.passbook-header {
text-align: center;
margin-bottom: 20px;
border-bottom: 2px solid #1976d2;
padding-bottom: 12px;
// ─── Bank Header ─────────────────────────────────────────────────────────────
.bank-header {
background: $bank-primary;
padding: 18px 24px;
display: flex;
justify-content: space-between;
align-items: center;
border-bottom: 4px solid $bank-accent;
}
h2 {
color: #1976d2;
font-size: 20px;
margin: 0 0 4px;
font-weight: 600;
}
.bank-logo-area {
display: flex;
align-items: center;
gap: 14px;
}
h3 {
font-size: 14px;
font-weight: 400;
color: #666;
margin: 0;
.bank-logo-circle {
width: 52px;
height: 52px;
border-radius: 50%;
background: $bank-accent;
display: flex;
align-items: center;
justify-content: center;
mat-icon {
color: $bank-primary;
font-size: 28px;
width: 28px;
height: 28px;
}
}
.passbook-info-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 12px 20px;
margin-bottom: 16px;
padding: 12px;
background: #f5f5f5;
border-radius: 6px;
}
.bank-name-block {
.bank-title {
color: #fff;
font-size: 18px;
font-weight: 700;
letter-spacing: 0.3px;
}
.passbook-info-item {
.label {
font-size: 10px;
font-weight: 600;
color: #888;
.bank-subtitle {
color: $bank-accent;
font-size: 11px;
font-weight: 500;
letter-spacing: 1.5px;
text-transform: uppercase;
letter-spacing: 0.5px;
}
.value {
font-size: 13px;
color: #333;
margin-top: 2px;
}
}
.passbook-balance-row {
display: flex;
justify-content: space-between;
margin-bottom: 16px;
padding: 10px 14px;
background: #e3f2fd;
border-radius: 6px;
font-size: 14px;
.statement-label {
text-align: right;
.balance-item {
b {
color: #1976d2;
.stmt-label-text {
color: $bank-accent;
font-size: 13px;
font-weight: 700;
letter-spacing: 2px;
text-transform: uppercase;
}
.stmt-period {
color: rgb(255 255 255 / 75%);
font-size: 11px;
margin-top: 4px;
}
}
// ─── Account Details Band ────────────────────────────────────────────────────
.account-details-band {
background: #f0f3f9;
border-bottom: 1px solid $border-color;
padding: 14px 24px;
}
.account-details-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 12px 24px;
}
.detail-block {
.detail-label {
font-size: 9px;
font-weight: 700;
color: #7a8aaa;
text-transform: uppercase;
letter-spacing: 0.8px;
margin-bottom: 3px;
}
.detail-value {
font-size: 13px;
color: #222;
&.bold {
font-weight: 600;
}
&.mono {
font-family: 'Courier New', monospace;
letter-spacing: 1px;
}
&.status-badge {
display: inline-block;
padding: 1px 8px;
border-radius: 10px;
font-size: 11px;
font-weight: 600;
background: #eee;
color: #555;
&.active {
background: #d4edda;
color: #155724;
}
}
}
}
// Transactions Table
table {
// ─── Balance Summary Strip ───────────────────────────────────────────────────
.balance-summary-strip {
display: flex;
align-items: stretch;
background: $bank-primary;
padding: 0;
}
.balance-box {
flex: 1;
padding: 12px 20px;
text-align: center;
.balance-box-label {
font-size: 10px;
font-weight: 600;
color: rgb(255 255 255 / 65%);
text-transform: uppercase;
letter-spacing: 1px;
margin-bottom: 4px;
}
.balance-box-value {
font-size: 16px;
font-weight: 700;
color: $bank-accent;
}
}
.balance-divider {
width: 1px;
background: rgb(255 255 255 / 15%);
margin: 8px 0;
}
// ─── Transactions Table ───────────────────────────────────────────────────────
.statement-table {
width: 100%;
border-collapse: collapse;
font-size: 13px;
font-size: 12.5px;
th {
background: #1976d2;
color: #fff;
padding: 8px 10px;
text-align: left;
font-weight: 600;
font-size: 12px;
thead tr {
background: #e8edf5;
border-top: 1px solid $border-color;
border-bottom: 2px solid $bank-primary;
}
td {
padding: 6px 10px;
border-bottom: 1px solid #e0e0e0;
thead th {
padding: 9px 10px;
font-size: 11px;
font-weight: 700;
color: $bank-primary;
text-transform: uppercase;
letter-spacing: 0.4px;
white-space: nowrap;
}
tr:nth-child(even) {
background: #fafafa;
tbody tr {
border-bottom: 1px solid #e8ecf3;
transition: background 0.1s;
&:nth-child(even) {
background: $row-alt;
}
&:hover {
background: #dce6f5;
}
&.reversed-row {
opacity: 0.5;
text-decoration: line-through;
}
}
tr:hover {
background: #f0f0f0;
tbody td {
padding: 7px 10px;
vertical-align: middle;
color: #333;
}
.reversed {
text-decoration: line-through;
color: #999;
tfoot .total-row {
background: #e8edf5;
border-top: 2px solid $bank-primary;
td {
padding: 9px 10px;
}
}
}
// ─── Column widths ────────────────────────────────────────────────────────────
.col-sr {
width: 38px;
}
.col-date {
width: 110px;
white-space: nowrap;
}
.col-desc {
min-width: 160px;
}
.col-ref {
width: 80px;
}
.col-amount {
width: 110px;
}
.col-balance {
width: 120px;
}
// ─── Amount colors ────────────────────────────────────────────────────────────
.debit-amount {
color: $debit-color;
font-weight: 500;
}
.credit-amount {
color: $credit-color;
font-weight: 500;
}
.balance-amount {
color: $balance-color;
font-weight: 600;
font-family: 'Courier New', monospace;
}
.total-label {
font-size: 12px;
font-weight: 700;
color: $bank-primary;
text-transform: uppercase;
letter-spacing: 0.5px;
}
.closing-balance {
font-size: 14px;
font-weight: 700;
color: $bank-primary;
font-family: 'Courier New', monospace;
}
// ─── Badges & misc ────────────────────────────────────────────────────────────
.txn-desc {
font-weight: 500;
}
.reversed-badge {
display: inline-block;
margin-left: 8px;
font-size: 9px;
font-weight: 700;
color: #fff;
background: #e74c3c;
padding: 1px 5px;
border-radius: 3px;
letter-spacing: 0.5px;
}
.text-right {
text-align: right;
}
.passbook-no-data {
.text-center {
text-align: center;
}
.text-muted {
color: #999;
}
.mono {
font-family: 'Courier New', monospace;
font-size: 11px;
}
// ─── No Data ──────────────────────────────────────────────────────────────────
.no-data-block {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 8px;
padding: 32px;
color: #888;
font-size: 14px;
padding: 48px;
color: #aaa;
mat-icon {
color: #bbb;
font-size: 48px;
width: 48px;
height: 48px;
margin-bottom: 12px;
}
p {
font-size: 14px;
margin: 0;
}
}
.passbook-footer {
margin-top: 16px;
font-size: 11px;
color: #999;
// ─── Statement Footer ─────────────────────────────────────────────────────────
.statement-footer {
background: #f0f3f9;
border-top: 1px solid $border-color;
padding: 12px 24px;
text-align: center;
border-top: 1px solid #e0e0e0;
padding-top: 10px;
.footer-note {
font-size: 10px;
color: #888;
margin-bottom: 4px;
font-style: italic;
}
.footer-meta {
font-size: 10px;
color: #aaa;
letter-spacing: 0.3px;
}
}
// Responsive
// ─── Responsive ───────────────────────────────────────────────────────────────
@media (width <= 768px) {
.passbook-info-grid {
.account-details-grid {
grid-template-columns: 1fr 1fr;
}
.passbook-balance-row {
.bank-header {
flex-direction: column;
gap: 8px;
gap: 12px;
align-items: flex-start;
}
.statement-label {
text-align: left;
}
.balance-summary-strip {
flex-direction: column;
}
.balance-divider {
width: auto;
height: 1px;
margin: 0 20px;
}
.passbook-filter-form {
@@ -188,19 +447,35 @@ table {
}
@media (width <= 480px) {
.passbook-info-grid {
.account-details-grid {
grid-template-columns: 1fr;
}
}
// Print-specific styles
// ─── Print ────────────────────────────────────────────────────────────────────
@media print {
.passbook-filter-bar {
.no-print {
display: none !important;
}
.passbook-preview {
border: none;
.passbook-container {
background: #fff;
padding: 0;
}
.statement-wrapper {
border: none;
box-shadow: none;
border-radius: 0;
}
.bank-header {
-webkit-print-color-adjust: exact;
print-color-adjust: exact;
}
.balance-summary-strip {
-webkit-print-color-adjust: exact;
print-color-adjust: exact;
}
}
@@ -6,23 +6,22 @@
file, You can obtain one at http://mozilla.org/MPL/2.0/.
-->
<div class="container">
<div class="mat-elevation-z1 m-b-25" [hidden]="!accountWithTransactions">
<div class="layout-row align-end">
@if (checkStatus()) {
<div class="action-button m-b-20 layout-row gap-20px">
<mat-checkbox [formControl]="hideReversedParam" (click)="hideReversed()" class="accruals">
{{ 'labels.inputs.Hide Reversed' | translate }}</mat-checkbox
>
<mat-checkbox [formControl]="hideAccrualsParam" (click)="hideAccruals()" class="accruals">
{{ 'labels.inputs.Hide Accruals' | translate }}</mat-checkbox
>
<button mat-raised-button color="primary" [routerLink]="['export']">
{{ 'labels.buttons.Export' | translate }}
</button>
</div>
}
</div>
<div class="txn-tab">
<div class="txn-tab__card" [hidden]="!accountWithTransactions">
@if (checkStatus()) {
<div class="txn-tab__toolbar">
<mat-checkbox [formControl]="hideReversedParam" (click)="hideReversed()" class="txn-tab__check">
{{ 'labels.inputs.Hide Reversed' | translate }}
</mat-checkbox>
<mat-checkbox [formControl]="hideAccrualsParam" (click)="hideAccruals()" class="txn-tab__check">
{{ 'labels.inputs.Hide Accruals' | translate }}
</mat-checkbox>
<span class="txn-tab__spacer"></span>
<button mat-flat-button class="txn-tab__export" [routerLink]="['export']">
{{ 'labels.buttons.Export' | translate }}
</button>
</div>
}
<table mat-table [dataSource]="dataSource" matSort>
<ng-container matColumnDef="row">
@@ -176,10 +175,11 @@
<mat-paginator [pageSize]="50" [pageSizeOptions]="[50, 100, 200]" showFirstLastButtons></mat-paginator>
</div>
<div class="alert" [hidden]="accountWithTransactions">
<div class="message">
<i class="fa fa-exclamation-circle alert-check"></i>
<div class="txn-tab__empty" [hidden]="accountWithTransactions">
<fa-icon icon="inbox" class="txn-tab__empty-ico"></fa-icon>
<div class="txn-tab__empty-title">
{{ 'labels.text.No transaction was found' | translate }}
</div>
<div class="txn-tab__empty-sub">Posted transactions will appear here.</div>
</div>
</div>
@@ -6,37 +6,151 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
@use 'assets/styles/helper';
$navy: #0d1b2a;
$slate: #475569;
$muted: #64748b;
$border: #e2e8f0;
$soft: #f8fafc;
$blue: #1e40af;
.tab-container {
padding: 1%;
margin: 1%;
.txn-tab {
width: 100%;
}
h3 {
margin: 1% auto;
.txn-tab__card {
background: #fff;
border: 1px solid $border;
border-radius: 14px;
box-shadow: 0 1px 3px rgb(15 23 42 / 4%);
overflow: hidden;
}
/* ─── Toolbar ─────────────────────────────────────────────────────── */
.txn-tab__toolbar {
display: flex;
align-items: center;
gap: 18px;
padding: 12px 18px;
background: $soft;
border-bottom: 1px solid $border;
flex-wrap: wrap;
}
.txn-tab__check {
font-size: 13px;
color: $slate;
font-weight: 500;
}
.txn-tab__spacer {
flex: 1;
}
.txn-tab__export {
height: 36px !important;
border-radius: 8px !important;
font-weight: 600 !important;
font-size: 13px !important;
padding: 0 16px !important;
background: linear-gradient(135deg, #1e88e5 0%, $blue 100%) !important;
color: #fff !important;
}
/* ─── Table ───────────────────────────────────────────────────────── */
table {
width: 100%;
}
::ng-deep .txn-tab__card {
.mat-mdc-header-row {
background: $soft;
}
.action-button {
margin-left: auto;
.mat-mdc-header-cell {
font-size: 11.5px;
font-weight: 800;
color: $slate;
text-transform: uppercase;
letter-spacing: 0.06em;
padding: 12px 14px;
border-bottom: 1px solid $border;
}
table {
width: 100%;
.mat-mdc-cell {
font-size: 13px;
color: $navy;
padding: 11px 14px;
border-bottom: 1px solid #f1f5f9;
}
.account-action-button {
min-width: 26px;
padding: 0 0 3px;
margin: 0 10%;
line-height: 25px;
.mat-mdc-row:hover {
background: #f8fafc;
cursor: pointer;
}
.accruals {
padding-top: 1%;
}
}
/* Tabular numerics on the money columns (Debit / Credit / Balance) +
right-align them. */
.mat-column-debit,
.mat-column-credit,
.mat-column-balance {
text-align: right;
font-variant-numeric: tabular-nums;
}
.mat-column-actions {
text-align: center;
width: 80px;
}
/* Compact paginator */
.mat-mdc-paginator {
border-top: 1px solid $border;
background: $soft;
min-height: 48px;
}
/* Row tint variants — strike-through reversed, dim accrual, blue transfer */
.strike td {
text-decoration: line-through;
color: $muted;
}
.accrual td {
color: $muted;
font-style: italic;
}
.transfer td {
color: #6d28d9;
}
}
.select-row:hover {
cursor: pointer;
font-weight: 800;
/* ─── Empty state ─────────────────────────────────────────────────── */
.txn-tab__empty {
background: #fff;
border: 1px solid $border;
border-radius: 14px;
box-shadow: 0 1px 3px rgb(15 23 42 / 4%);
padding: 56px 24px;
text-align: center;
color: $muted;
}
.txn-tab__empty-ico {
font-size: 34px;
color: #cbd5e1;
margin-bottom: 8px;
}
.txn-tab__empty-title {
font-size: 14.5px;
font-weight: 700;
color: $slate;
margin-top: 6px;
}
.txn-tab__empty-sub {
font-size: 12.5px;
color: $muted;
margin-top: 2px;
}
@@ -6,21 +6,18 @@
file, You can obtain one at http://mozilla.org/MPL/2.0/.
-->
<div class="tab-container mat-typography">
<div class="layout-row align-start">
<div class="m-b-10">
<h3>{{ 'labels.heading.All Charges' | translate }}</h3>
</div>
@if (this.chargesData) {
<div class="action-button m-b-20">
<button mat-raised-button color="primary" (click)="toggleCharges()">
<div class="chg-tab">
<div class="chg-tab__card">
<div class="chg-tab__toolbar">
<h3 class="chg-tab__title">{{ 'labels.heading.All Charges' | translate }}</h3>
<span class="chg-tab__spacer"></span>
@if (this.chargesData) {
<button mat-flat-button class="chg-tab__toggle" (click)="toggleCharges()">
{{ showInactiveCharges ? 'View Active Charges' : 'View Inactive Charges' }}
</button>
</div>
}
</div>
}
</div>
<div class="mat-elevation-z1 m-b-25">
<table mat-table #chargesTable [dataSource]="dataSource">
<ng-container matColumnDef="name">
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.name' | translate }}</th>
@@ -6,26 +6,106 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
.tab-container {
padding: 1%;
margin: 1%;
$navy: #0d1b2a;
$slate: #475569;
$muted: #64748b;
$border: #e2e8f0;
$soft: #f8fafc;
$blue: #1e40af;
h3 {
margin: 1% auto;
.chg-tab {
width: 100%;
}
.chg-tab__card {
background: #fff;
border: 1px solid $border;
border-radius: 14px;
box-shadow: 0 1px 3px rgb(15 23 42 / 4%);
overflow: hidden;
}
.chg-tab__toolbar {
display: flex;
align-items: center;
gap: 14px;
padding: 12px 18px;
background: $soft;
border-bottom: 1px solid $border;
}
.chg-tab__title {
margin: 0;
font-size: 14px;
font-weight: 700;
color: $navy;
letter-spacing: 0.01em;
}
.chg-tab__spacer {
flex: 1;
}
.chg-tab__toggle {
height: 34px !important;
border-radius: 8px !important;
font-weight: 600 !important;
font-size: 13px !important;
padding: 0 14px !important;
background: linear-gradient(135deg, #1e88e5 0%, $blue 100%) !important;
color: #fff !important;
}
table {
width: 100%;
}
::ng-deep .chg-tab__card {
.mat-mdc-header-row {
background: $soft;
}
.action-button {
margin-left: auto;
.mat-mdc-header-cell {
font-size: 11.5px;
font-weight: 800;
color: $slate;
text-transform: uppercase;
letter-spacing: 0.06em;
padding: 12px 14px;
border-bottom: 1px solid $border;
}
table {
width: 100%;
.mat-mdc-cell {
font-size: 13px;
color: $navy;
padding: 11px 14px;
border-bottom: 1px solid #f1f5f9;
}
.mat-mdc-row:hover {
background: #f8fafc;
cursor: pointer;
}
.mat-column-due,
.mat-column-paid,
.mat-column-waived,
.mat-column-outstanding {
text-align: right;
font-variant-numeric: tabular-nums;
}
.mat-column-actions {
text-align: center;
width: 140px;
.account-action-button {
min-width: 26px;
padding: 0 6px;
margin: 4px;
line-height: 25px;
min-width: 30px !important;
padding: 0 8px !important;
margin: 0 3px !important;
height: 30px !important;
line-height: 30px !important;
border-radius: 7px !important;
}
}
}
@@ -6,191 +6,191 @@
file, You can obtain one at http://mozilla.org/MPL/2.0/.
-->
<mat-card class="account-card">
<mat-card-header class="header layout-column">
<mat-card-title-group class="header-title-group">
<div class="profile-image-container">
<div>
<img
mat-card-md-image
class="profile-image"
matTooltip="{{ 'tooltips.Fixed Deposits Account' | translate }}"
[src]="'assets/images/fd_account_placeholder.png'"
/>
<div class="fd-view">
<!-- ─── Gradient hero header ─────────────────────────────────────── -->
<header class="fd-hero">
<div class="fd-hero__left">
<div class="fd-hero__avatar">
<fa-icon icon="piggy-bank"></fa-icon>
</div>
<div class="fd-hero__info">
<div class="fd-hero__status-row">
<span
class="fd-status-pill"
[ngClass]="fixedDepositsAccountData.status.code | statusLookup"
[matTooltip]="fixedDepositsAccountData.status.value"
>
<span class="fd-status-pill__dot"></span>
{{ fixedDepositsAccountData.status.value }}
</span>
</div>
<h2 class="fd-hero__title">
<mifosx-long-text textValue="{{ fixedDepositsAccountData.depositProductName }}"></mifosx-long-text>
</h2>
<div class="fd-hero__meta">
<span class="fd-hero__meta-item">
<span class="fd-hero__meta-label">{{ 'labels.inputs.Account Number' | translate }}</span>
<mifosx-account-number
accountNo="{{ fixedDepositsAccountData.accountNo }}"
display="left"
></mifosx-account-number>
</span>
<span class="fd-hero__meta-item">
<span class="fd-hero__meta-label">
{{ 'labels.text.' + entityType | translate }} {{ 'labels.inputs.name' | translate }}
</span>
<span class="fd-hero__meta-value">
{{ fixedDepositsAccountData.clientName || fixedDepositsAccountData.groupName }}
@if (fixedDepositsAccountData.clientAccountNo) {
<mifosx-account-number
accountNo="{{ fixedDepositsAccountData.clientAccountNo }}"
></mifosx-account-number>
}
</span>
</span>
</div>
</div>
</div>
<div class="mat-typography account-card-title">
<mat-card-title>
<div class="layout-row responsive-column">
<div class="flex-60">
<h3>
<i
class="fa fa-stop"
[ngClass]="fixedDepositsAccountData.status.code | statusLookup"
[matTooltip]="fixedDepositsAccountData.status.value"
></i>
{{ 'labels.inputs.Fixed Deposit Product' | translate }} :
{{ fixedDepositsAccountData.depositProductName }}
<mifosx-account-number accountNo="{{ fixedDepositsAccountData.accountNo }}"></mifosx-account-number>
</h3>
<span class="account-overview">
<span class="m-r-10">{{ 'labels.text.' + entityType | translate }} :</span>
{{ fixedDepositsAccountData.clientName || fixedDepositsAccountData.groupName }}
@if (fixedDepositsAccountData.clientAccountNo) {
<span class="m-l-10">
<mifosx-account-number
accountNo="{{ fixedDepositsAccountData.clientAccountNo }}"
></mifosx-account-number>
</span>
}
</span>
</div>
@if (
!fixedDepositsAccountData.status.rejected && !fixedDepositsAccountData.status.submittedAndPendingApproval
) {
<div class="flex-30">
<h3 class="flex-100">{{ 'labels.heading.Account Overview' | translate }}</h3>
<table class="account-overview flex-100">
<tbody>
<tr>
<td class="flex-70">{{ 'labels.inputs.Current Balance' | translate }}</td>
<td class="r-amount flex-30">
{{
fixedDepositsAccountData.summary.accountBalance
| currency: currency.code : 'symbol-narrow' : '1.2-2'
}}
</td>
</tr>
<tr>
<td class="flex-70">{{ 'labels.inputs.Deposit Amount' | translate }}</td>
<td class="r-amount flex-30">
{{
fixedDepositsAccountData.depositAmount | currency: currency.code : 'symbol-narrow' : '1.2-2'
}}
</td>
</tr>
</tbody>
</table>
</div>
}
<div class="flex-5">
<button
mat-icon-button
[matMenuTriggerFor]="accountMenu"
aria-label="Fixed Deposit account actions"
yPosition="below"
>
<mat-icon matListIcon class="actions-menu">
<fa-icon icon="bars" size="sm"></fa-icon>
</mat-icon>
</button>
</div>
</div>
</mat-card-title>
<!-- Balance KPI strip -->
@if (!fixedDepositsAccountData.status.rejected && !fixedDepositsAccountData.status.submittedAndPendingApproval) {
<div class="fd-hero__kpis">
<div class="fd-kpi">
<span class="fd-kpi__label">{{ 'labels.inputs.Current Balance' | translate }}</span>
<span class="fd-kpi__value">
{{ fixedDepositsAccountData.summary.accountBalance | currency: currency.code : 'symbol-narrow' : '1.2-2' }}
</span>
</div>
<div class="fd-kpi fd-kpi--deposit">
<span class="fd-kpi__label">{{ 'labels.inputs.Deposit Amount' | translate }}</span>
<span class="fd-kpi__value">
{{ fixedDepositsAccountData.depositAmount | currency: currency.code : 'symbol-narrow' : '1.2-2' }}
</span>
</div>
</div>
}
<mat-menu #accountMenu="matMenu">
@for (button of buttonConfig.singleButtons; track button) {
<button mat-menu-item (click)="doAction(button.name)">
<i ngClass="{{ button.icon }}"></i> {{ button.name | translateKey: 'menus' }}
</button>
}
<!-- Action menu (kebab) -->
<div class="fd-hero__actions">
<button mat-icon-button [matMenuTriggerFor]="accountMenu" aria-label="Account actions" yPosition="below">
<fa-icon icon="ellipsis-v"></fa-icon>
</button>
</div>
@if (buttonConfig.options.length) {
<button mat-menu-item [matMenuTriggerFor]="More">{{ 'More' | translateKey: 'menus' }}</button>
<mat-menu #More="matMenu">
@for (option of buttonConfig.options; track option) {
<span>
<button mat-menu-item (click)="doAction(option.name)">{{ option.name | translateKey: 'menus' }}</button>
</span>
}
</mat-menu>
}
</mat-menu>
</mat-card-title-group>
</mat-card-header>
<mat-card-content class="content">
<nav mat-tab-nav-bar class="navigation-tabs" [tabPanel]="tabPanel">
<a
mat-tab-link
[routerLink]="['./general']"
routerLinkActive
#general="routerLinkActive"
[active]="general.isActive"
>
{{ 'labels.inputs.General' | translate }}
</a>
<a
mat-tab-link
[routerLink]="['./interest-rate-chart']"
routerLinkActive
#IRC="routerLinkActive"
[active]="IRC.isActive"
>
{{ 'labels.inputs.Interest Rate Chart' | translate }}
</a>
@if (showTransactions) {
<span>
<a
mat-tab-link
[routerLink]="['./transactions']"
routerLinkActive
#transactions="routerLinkActive"
[active]="transactions.isActive"
>
{{ 'labels.inputs.Transactions' | translate }}
</a>
</span>
<mat-menu #accountMenu="matMenu">
@for (button of buttonConfig.singleButtons; track button) {
<button mat-menu-item (click)="doAction(button.name)">
<span>{{ button.name | translateKey: 'menus' }}</span>
</button>
}
<a
mat-tab-link
[routerLink]="['./charges']"
routerLinkActive
#charges="routerLinkActive"
[active]="charges.isActive"
>
{{ 'labels.inputs.Charges' | translate }}
</a>
@if (fixedDepositsAccountData.clientId) {
<a
mat-tab-link
[routerLink]="['./standing-instructions']"
routerLinkActive
#standingInstructions="routerLinkActive"
[active]="standingInstructions.isActive"
>
{{ 'labels.inputs.Standing Instructions' | translate }}
</a>
@if (buttonConfig.options.length) {
<button mat-menu-item [matMenuTriggerFor]="More">{{ 'More' | translateKey: 'menus' }}</button>
<mat-menu #More="matMenu">
@for (option of buttonConfig.options; track option) {
<span>
<button mat-menu-item (click)="doAction(option.name)">{{ option.name | translateKey: 'menus' }}</button>
</span>
}
</mat-menu>
}
<a
mat-tab-link
[routerLink]="['./nominees']"
routerLinkActive
#nominees="routerLinkActive"
[active]="nominees.isActive"
>
Nominees
</a>
@for (savingsDatatable of savingsDatatables; track savingsDatatable) {
<a
mat-tab-link
*mifosxHasPermission="'READ_' + savingsDatatable.registeredTableName"
[routerLink]="['./datatables', savingsDatatable.registeredTableName]"
routerLinkActive
#datatable="routerLinkActive"
[active]="datatable.isActive"
>
{{ savingsDatatable.registeredTableName }}
</a>
}
</nav>
</mat-menu>
</header>
<mat-tab-nav-panel #tabPanel>
<!-- ─── Pill-style tab navigation ─────────────────────────────────── -->
<nav mat-tab-nav-bar class="fd-tabs" [tabPanel]="tabPanel">
<a
mat-tab-link
class="fd-tab"
[routerLink]="['./general']"
routerLinkActive
#general="routerLinkActive"
[active]="general.isActive"
>
<fa-icon icon="info-circle" class="fd-tab__ic"></fa-icon>
<span>{{ 'labels.inputs.General' | translate }}</span>
</a>
<a
mat-tab-link
class="fd-tab"
[routerLink]="['./interest-rate-chart']"
routerLinkActive
#IRC="routerLinkActive"
[active]="IRC.isActive"
>
<fa-icon icon="chart-line" class="fd-tab__ic"></fa-icon>
<span>{{ 'labels.inputs.Interest Rate Chart' | translate }}</span>
</a>
@if (showTransactions) {
<a
mat-tab-link
class="fd-tab"
[routerLink]="['./transactions']"
routerLinkActive
#transactions="routerLinkActive"
[active]="transactions.isActive"
>
<fa-icon icon="exchange-alt" class="fd-tab__ic"></fa-icon>
<span>{{ 'labels.inputs.Transactions' | translate }}</span>
</a>
}
<a
mat-tab-link
class="fd-tab"
[routerLink]="['./charges']"
routerLinkActive
#charges="routerLinkActive"
[active]="charges.isActive"
>
<fa-icon icon="receipt" class="fd-tab__ic"></fa-icon>
<span>{{ 'labels.inputs.Charges' | translate }}</span>
</a>
@if (fixedDepositsAccountData.clientId) {
<a
mat-tab-link
class="fd-tab"
[routerLink]="['./standing-instructions']"
routerLinkActive
#standingInstructions="routerLinkActive"
[active]="standingInstructions.isActive"
>
<fa-icon icon="calendar-check" class="fd-tab__ic"></fa-icon>
<span>{{ 'labels.inputs.Standing Instructions' | translate }}</span>
</a>
}
<a
mat-tab-link
class="fd-tab"
[routerLink]="['./nominees']"
routerLinkActive
#nominees="routerLinkActive"
[active]="nominees.isActive"
>
<fa-icon icon="user-shield" class="fd-tab__ic"></fa-icon>
<span>Nominees</span>
</a>
@for (savingsDatatable of savingsDatatables; track savingsDatatable) {
<a
mat-tab-link
class="fd-tab"
*mifosxHasPermission="'READ_' + savingsDatatable.registeredTableName"
[routerLink]="['./datatables', savingsDatatable.registeredTableName]"
routerLinkActive
#datatable="routerLinkActive"
[active]="datatable.isActive"
>
<fa-icon icon="table" class="fd-tab__ic"></fa-icon>
<span>{{ savingsDatatable.registeredTableName }}</span>
</a>
}
</nav>
<mat-tab-nav-panel #tabPanel>
<div class="fd-body">
<router-outlet></router-outlet>
</mat-tab-nav-panel>
</mat-card-content>
</mat-card>
</div>
</mat-tab-nav-panel>
</div>
@@ -5,3 +5,381 @@
* 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/.
*/
$navy: #0d1b2a;
$blue: #1e88e5;
$blue-d: #1e40af;
$border: #e2e8f0;
.fd-view {
max-width: 1440px;
margin: 0 auto;
padding: 12px 20px 22px;
}
/* ─── Gradient hero header ──────────────────────────────────────────── */
.fd-hero {
position: relative;
display: grid;
grid-template-columns: minmax(0, 1fr) auto auto;
align-items: stretch;
gap: 14px;
padding: 12px 18px;
border-radius: 12px;
color: #fff;
background: linear-gradient(135deg, #1e3a8a 0%, #1e40af 45%, #4338ca 100%);
box-shadow: 0 6px 18px -10px rgb(15 23 42 / 35%);
overflow: hidden;
&::before {
content: '';
position: absolute;
inset: 0;
background: radial-gradient(circle at top right, rgb(255 255 255 / 12%) 0%, transparent 55%);
pointer-events: none;
}
&__left {
display: flex;
align-items: center;
gap: 12px;
min-width: 0;
position: relative;
z-index: 1;
}
&__avatar {
width: 40px;
height: 40px;
border-radius: 10px;
background: linear-gradient(135deg, rgb(255 255 255 / 22%) 0%, rgb(255 255 255 / 10%) 100%);
border: 1px solid rgb(255 255 255 / 28%);
display: flex;
align-items: center;
justify-content: center;
color: #fff;
font-size: 16px;
flex-shrink: 0;
box-shadow: 0 3px 10px -3px rgb(0 0 0 / 25%);
}
&__info {
display: flex;
flex-direction: column;
gap: 3px;
min-width: 0;
}
&__status-row {
display: flex;
align-items: center;
gap: 6px;
}
&__title {
margin: 0;
font-size: 15px;
font-weight: 800;
letter-spacing: 0.01em;
color: #fff;
line-height: 1.15;
::ng-deep mifosx-long-text {
color: #fff;
}
}
&__meta {
display: flex;
flex-wrap: wrap;
gap: 2px 18px;
margin-top: 1px;
}
&__meta-item {
display: inline-flex;
align-items: center;
gap: 6px;
font-size: 12px;
color: rgb(255 255 255 / 90%);
}
&__meta-label {
font-size: 10px;
text-transform: uppercase;
letter-spacing: 0.07em;
color: rgb(255 255 255 / 60%);
font-weight: 600;
}
&__meta-value {
font-weight: 600;
color: #fff;
}
&__kpis {
display: flex;
gap: 8px;
align-items: stretch;
position: relative;
z-index: 1;
}
&__actions {
display: flex;
align-items: center;
position: relative;
z-index: 1;
button {
width: 32px !important;
height: 32px !important;
min-width: 32px !important;
padding: 0 !important;
border-radius: 50% !important;
color: #fff !important;
background: rgb(255 255 255 / 10%) !important;
border: 1px solid rgb(255 255 255 / 22%) !important;
display: inline-flex !important;
align-items: center;
justify-content: center;
line-height: 1;
transition:
background 0.15s ease,
border-color 0.15s ease,
transform 0.12s ease;
::ng-deep fa-icon {
font-size: 14px;
line-height: 1;
display: inline-flex;
align-items: center;
justify-content: center;
}
::ng-deep .mat-mdc-button-persistent-ripple,
::ng-deep .mat-mdc-button-touch-target {
border-radius: 50% !important;
}
}
button:hover {
background: rgb(255 255 255 / 22%) !important;
border-color: rgb(255 255 255 / 38%) !important;
}
button:active {
transform: scale(0.94);
}
}
}
/* KPI tiles */
.fd-kpi {
display: flex;
flex-direction: column;
justify-content: center;
min-width: 130px;
padding: 7px 14px;
border-radius: 9px;
background: rgb(255 255 255 / 14%);
border: 1px solid rgb(255 255 255 / 22%);
backdrop-filter: blur(8px);
&__label {
font-size: 9.5px;
text-transform: uppercase;
letter-spacing: 0.07em;
font-weight: 700;
color: rgb(255 255 255 / 75%);
}
&__value {
margin-top: 2px;
font-size: 15px;
font-weight: 800;
color: #fff;
font-variant-numeric: tabular-nums;
letter-spacing: -0.01em;
}
&--deposit {
background: rgb(245 158 11 / 22%);
border-color: rgb(245 158 11 / 35%);
}
}
/* Status pill */
.fd-status-pill {
display: inline-flex;
align-items: center;
gap: 5px;
height: 18px;
padding: 0 8px;
border-radius: 999px;
background: rgb(16 185 129 / 24%);
border: 1px solid rgb(16 185 129 / 40%);
font-size: 10px;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.06em;
color: #d1fae5;
&__dot {
width: 5px;
height: 5px;
border-radius: 50%;
background: #10b981;
box-shadow: 0 0 0 2px rgb(16 185 129 / 30%);
}
&.statussubmitted,
&.statuspending {
background: rgb(245 158 11 / 24%);
border-color: rgb(245 158 11 / 40%);
color: #fef3c7;
.fd-status-pill__dot {
background: #f59e0b;
}
}
&.statusclosed,
&.statusrejected,
&.statuswithdrawn,
&.statusprematureclosed {
background: rgb(239 68 68 / 24%);
border-color: rgb(239 68 68 / 40%);
color: #fee2e2;
.fd-status-pill__dot {
background: #ef4444;
}
}
}
/* ─── Pill-style tab navigation ─────────────────────────────────────── */
.fd-tabs {
margin-top: 18px;
background: #fff;
border: 1px solid $border;
border-radius: 14px;
padding: 6px;
box-shadow: 0 1px 3px rgb(15 23 42 / 4%);
overflow: hidden;
::ng-deep {
.mat-mdc-tab-link-container {
border-bottom: none !important;
}
.mat-mdc-tab-links {
gap: 4px;
flex-wrap: wrap;
}
.mdc-tab-indicator__content--underline,
.mat-mdc-tab-header-pagination-controls-enabled .mdc-tab-indicator__content--underline {
display: none !important;
}
}
}
.fd-tab {
display: inline-flex !important;
align-items: center;
gap: 8px;
height: 38px !important;
min-width: 0 !important;
padding: 0 16px !important;
border-radius: 10px !important;
font-family: Inter, 'DM Sans', Roboto, sans-serif !important;
font-size: 13px !important;
font-weight: 600 !important;
letter-spacing: 0.01em !important;
color: #64748b !important;
background: transparent !important;
transition: all 0.15s ease;
white-space: nowrap;
border: 1px solid transparent !important;
opacity: 1 !important;
&__ic {
font-size: 12px;
color: inherit;
}
&:hover:not(.mdc-tab--active) {
background: #f1f5f9 !important;
color: $navy !important;
}
&.mdc-tab--active,
&.mat-mdc-tab-link-active {
background: linear-gradient(135deg, $blue 0%, $blue-d 100%) !important;
color: #fff !important;
box-shadow: 0 2px 6px -1px rgb(30 64 175 / 35%);
border-color: $blue-d !important;
::ng-deep .mdc-tab__text-label,
::ng-deep .mat-mdc-tab-link-content,
::ng-deep span,
.fd-tab__ic {
color: #fff !important;
opacity: 1 !important;
}
}
}
.fd-body {
margin-top: 16px;
}
/* ─── Responsive ────────────────────────────────────────────────────── */
@media (width <= 1100px) {
.fd-hero {
grid-template-columns: 1fr;
&__kpis {
width: 100%;
flex-wrap: wrap;
}
&__actions {
position: absolute;
top: 18px;
right: 18px;
}
}
.fd-kpi {
flex: 1 1 auto;
min-width: 0;
}
}
@media (width <= 700px) {
.fd-view {
padding: 12px;
}
.fd-hero {
padding: 18px;
&__title {
font-size: 18px;
}
&__avatar {
width: 52px;
height: 52px;
font-size: 22px;
border-radius: 12px;
}
}
.fd-tab {
padding: 0 12px !important;
font-size: 12px !important;
}
}
@@ -24,17 +24,10 @@ import { FixedDepositsButtonsConfiguration } from './fixed-deposits-buttons.conf
import { FixedDepositsService } from '../fixed-deposits.service';
import { SavingsService } from 'app/savings/savings.service';
import { Currency } from 'app/shared/models/general.model';
import {
MatCard,
MatCardHeader,
MatCardTitleGroup,
MatCardMdImage,
MatCardTitle,
MatCardContent
} from '@angular/material/card';
import { MatTooltip } from '@angular/material/tooltip';
import { NgClass, CurrencyPipe } from '@angular/common';
import { AccountNumberComponent } from '../../../shared/account-number/account-number.component';
import { LongTextComponent } from '../../../shared/long-text/long-text.component';
import { MatIconButton } from '@angular/material/button';
import { MatMenuTrigger, MatMenu, MatMenuItem } from '@angular/material/menu';
import { MatIcon } from '@angular/material/icon';
@@ -52,12 +45,9 @@ import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
styleUrls: ['./fixed-deposit-account-view.component.scss'],
imports: [
...STANDALONE_SHARED_IMPORTS,
MatCardHeader,
MatCardTitleGroup,
MatCardMdImage,
MatTooltip,
MatCardTitle,
NgClass,
LongTextComponent,
AccountNumberComponent,
MatIconButton,
MatMenuTrigger,
@@ -6,196 +6,211 @@
file, You can obtain one at http://mozilla.org/MPL/2.0/.
-->
<div class="fixed-deposits-account-tables layout-row gap-2percent">
<div class="flex-49 layout-column gap-4percent">
<div>
<h4 class="table-headers">{{ 'labels.heading.Fixed Deposit Details' | translate }}</h4>
<table>
<tbody>
<tr>
<td>{{ 'labels.inputs.External Id' | translate }}</td>
<td>
@if (fixedDepositsAccountData.externalId) {
<span>
<mifosx-external-identifier
externalId="{{ fixedDepositsAccountData.externalId }}"
completed="true"
></mifosx-external-identifier>
</span>
}
@if (!fixedDepositsAccountData.externalId) {
<span>
{{ 'labels.inputs.Not Available' | translate }}
</span>
}
</td>
</tr>
<tr>
<td>{{ 'labels.inputs.Activated On' | translate }}</td>
<td>
{{
fixedDepositsAccountData.timeline.activatedOnDate
? (fixedDepositsAccountData.timeline.activatedOnDate | dateFormat)
: ('labels.text.Not Activated' | translate)
}}
</td>
</tr>
@if (fixedDepositsAccountData.timeline.closedOnDate) {
<tr>
<td>{{ 'labels.inputs.Closed On' | translate }}</td>
<td>{{ fixedDepositsAccountData.timeline.closedOnDate | dateFormat }}</td>
</tr>
}
<tr>
<td>{{ 'labels.inputs.Field Officer' | translate }}</td>
<td>
{{
fixedDepositsAccountData.fieldOfficerName
? fixedDepositsAccountData.fieldOfficerName
: ('Unassigned' | translateKey: 'inputs')
}}
</td>
</tr>
<tr>
<td>{{ 'labels.inputs.Maturity Date' | translate }}</td>
<td>
{{ fixedDepositsAccountData.maturityDate ? (fixedDepositsAccountData.maturityDate | dateFormat) : 'N/A' }}
</td>
</tr>
<tr>
<td>{{ 'labels.inputs.Deposit Period' | translate }}</td>
<td>
{{ fixedDepositsAccountData.depositPeriod }}&nbsp;{{
fixedDepositsAccountData.depositPeriodFrequency.value | translateKey: 'catalogs'
}}
</td>
</tr>
@if (fixedDepositsAccountData.withHoldTax) {
<tr>
<td>{{ 'labels.inputs.Tax Group' | translate }}</td>
<td>{{ fixedDepositsAccountData.taxGroup.name }}</td>
</tr>
}
@if (fixedDepositsAccountData.onAccountClosure) {
<tr>
<td>{{ 'labels.inputs.Maturity Instructions' | translate }}</td>
<td>{{ fixedDepositsAccountData.onAccountClosure.value | translateKey: 'catalogs' }}</td>
</tr>
}
</tbody>
</table>
<div class="gt-grid">
<!-- ─── Left Column ──────────────────────────────────────────────── -->
<div class="gt-col">
<!-- Fixed Deposit Details -->
<div class="nx-card">
<div class="nx-card__head">
<span class="nx-card__ico"><fa-icon icon="file-alt"></fa-icon></span>
<h3 class="nx-card__title">{{ 'labels.heading.Fixed Deposit Details' | translate }}</h3>
</div>
<div class="nx-card__body">
<div class="nx-row">
<span class="nx-row__label">{{ 'labels.inputs.External Id' | translate }}</span>
<span class="nx-row__value">
@if (fixedDepositsAccountData.externalId) {
<mifosx-external-identifier
externalId="{{ fixedDepositsAccountData.externalId }}"
completed="true"
></mifosx-external-identifier>
} @else {
<span class="nx-muted">{{ 'labels.inputs.Not Available' | translate }}</span>
}
</span>
</div>
<div class="nx-row">
<span class="nx-row__label">{{ 'labels.inputs.Activated On' | translate }}</span>
<span class="nx-row__value">
{{
fixedDepositsAccountData.timeline.activatedOnDate
? (fixedDepositsAccountData.timeline.activatedOnDate | dateFormat)
: ('labels.text.Not Activated' | translate)
}}
</span>
</div>
@if (fixedDepositsAccountData.timeline.closedOnDate) {
<div class="nx-row">
<span class="nx-row__label">{{ 'labels.inputs.Closed On' | translate }}</span>
<span class="nx-row__value">{{ fixedDepositsAccountData.timeline.closedOnDate | dateFormat }}</span>
</div>
}
<div class="nx-row">
<span class="nx-row__label">{{ 'labels.inputs.Field Officer' | translate }}</span>
<span class="nx-row__value">
{{
fixedDepositsAccountData.fieldOfficerName
? fixedDepositsAccountData.fieldOfficerName
: ('Unassigned' | translateKey: 'inputs')
}}
</span>
</div>
<div class="nx-row">
<span class="nx-row__label">{{ 'labels.inputs.Maturity Date' | translate }}</span>
<span class="nx-row__value">
{{ fixedDepositsAccountData.maturityDate ? (fixedDepositsAccountData.maturityDate | dateFormat) : 'N/A' }}
</span>
</div>
<div class="nx-row">
<span class="nx-row__label">{{ 'labels.inputs.Deposit Period' | translate }}</span>
<span class="nx-row__value">
{{ fixedDepositsAccountData.depositPeriod }}&nbsp;{{
fixedDepositsAccountData.depositPeriodFrequency.value | translateKey: 'catalogs'
}}
</span>
</div>
@if (fixedDepositsAccountData.withHoldTax) {
<div class="nx-row">
<span class="nx-row__label">{{ 'labels.inputs.Tax Group' | translate }}</span>
<span class="nx-row__value">{{ fixedDepositsAccountData.taxGroup.name }}</span>
</div>
}
@if (fixedDepositsAccountData.onAccountClosure) {
<div class="nx-row">
<span class="nx-row__label">{{ 'labels.inputs.Maturity Instructions' | translate }}</span>
<span class="nx-row__value">{{
fixedDepositsAccountData.onAccountClosure.value | translateKey: 'catalogs'
}}</span>
</div>
}
</div>
</div>
<div>
<h4 class="table-headers">{{ 'labels.heading.Interest Details' | translate }}</h4>
<table>
<tbody>
<tr>
<td>{{ 'labels.inputs.Interest Rate' | translate }}</td>
<td>{{ fixedDepositsAccountData.nominalAnnualInterestRate }} %</td>
</tr>
<tr>
<td>{{ 'labels.inputs.Interest Compounding Period' | translate }}</td>
<td>{{ fixedDepositsAccountData.interestCompoundingPeriodType.value | translateKey: 'catalogs' }}</td>
</tr>
<tr>
<td>{{ 'labels.inputs.Interest Posting Period' | translate }}</td>
<td>{{ fixedDepositsAccountData.interestPostingPeriodType.value | translateKey: 'catalogs' }}</td>
</tr>
<tr>
<td>{{ 'labels.inputs.Interest Calculated Using' | translate }}</td>
<td>{{ fixedDepositsAccountData.interestCalculationType.value | translateKey: 'catalogs' }}</td>
</tr>
<tr>
<td>{{ 'labels.inputs.No. of Days in Year' | translate }}</td>
<td>{{ fixedDepositsAccountData.interestCalculationDaysInYearType.value | translateKey: 'catalogs' }}</td>
</tr>
@if (fixedDepositsAccountData.preClosurePenalApplicable) {
<tr>
<td>{{ 'labels.inputs.Pre-cloure Penal' | translate }}</td>
<td>
{{ fixedDepositsAccountData.preClosurePenalInterest }} % on
{{ fixedDepositsAccountData.preClosurePenalInterestOnType.value | translateKey: 'catalogs' }}
</td>
</tr>
}
</tbody>
</table>
<!-- Interest Details -->
<div class="nx-card">
<div class="nx-card__head">
<span class="nx-card__ico"><fa-icon icon="percentage"></fa-icon></span>
<h3 class="nx-card__title">{{ 'labels.heading.Interest Details' | translate }}</h3>
</div>
<div class="nx-card__body">
<div class="nx-row">
<span class="nx-row__label">{{ 'labels.inputs.Interest Rate' | translate }}</span>
<span class="nx-row__value">{{ fixedDepositsAccountData.nominalAnnualInterestRate }} %</span>
</div>
<div class="nx-row">
<span class="nx-row__label">{{ 'labels.inputs.Interest Compounding Period' | translate }}</span>
<span class="nx-row__value">{{
fixedDepositsAccountData.interestCompoundingPeriodType.value | translateKey: 'catalogs'
}}</span>
</div>
<div class="nx-row">
<span class="nx-row__label">{{ 'labels.inputs.Interest Posting Period' | translate }}</span>
<span class="nx-row__value">{{
fixedDepositsAccountData.interestPostingPeriodType.value | translateKey: 'catalogs'
}}</span>
</div>
<div class="nx-row">
<span class="nx-row__label">{{ 'labels.inputs.Interest Calculated Using' | translate }}</span>
<span class="nx-row__value">{{
fixedDepositsAccountData.interestCalculationType.value | translateKey: 'catalogs'
}}</span>
</div>
<div class="nx-row">
<span class="nx-row__label">{{ 'labels.inputs.No. of Days in Year' | translate }}</span>
<span class="nx-row__value">{{
fixedDepositsAccountData.interestCalculationDaysInYearType.value | translateKey: 'catalogs'
}}</span>
</div>
@if (fixedDepositsAccountData.preClosurePenalApplicable) {
<div class="nx-row">
<span class="nx-row__label">{{ 'labels.inputs.Pre-cloure Penal' | translate }}</span>
<span class="nx-row__value">
{{ fixedDepositsAccountData.preClosurePenalInterest }} % on
{{ fixedDepositsAccountData.preClosurePenalInterestOnType.value | translateKey: 'catalogs' }}
</span>
</div>
}
</div>
</div>
</div>
<div class="flex-49">
<!-- ─── Right Column ─────────────────────────────────────────────── -->
<div class="gt-col">
@if (!fixedDepositsAccountData.status.rejected && !fixedDepositsAccountData.status.submittedAndPendingApproval) {
<div>
<h4 class="table-headers">{{ 'labels.heading.Performance History' | translate }}</h4>
<table>
<tbody>
<tr>
<td>{{ 'labels.inputs.Principal Amount' | translate }}</td>
<td class="r-amount">
{{ fixedDepositsAccountData.depositAmount | currency: currency.code : 'symbol-narrow' : '1.2-2' }}
</td>
</tr>
<tr>
<td>{{ 'labels.inputs.Maturity Amount' | translate }}</td>
<td class="r-amount">
{{ fixedDepositsAccountData.maturityAmount | currency: currency.code : 'symbol-narrow' : '1.2-2' }}
</td>
</tr>
@if (fixedDepositsAccountData.summary.totalDeposits) {
<tr>
<td>{{ 'labels.inputs.Total Deposits' | translate }}</td>
<td class="r-amount">
{{
fixedDepositsAccountData.summary.totalDeposits | currency: currency.code : 'symbol-narrow' : '1.2-2'
}}
</td>
</tr>
}
@if (fixedDepositsAccountData.summary.totalWithdrawals) {
<tr>
<td>{{ 'labels.inputs.Total Withdrawals' | translate }}</td>
<td class="r-amount">
{{
fixedDepositsAccountData.summary.totalWithdrawals
| currency: currency.code : 'symbol-narrow' : '1.2-2'
}}
</td>
</tr>
}
@if (fixedDepositsAccountData.summary.totalInterestEarned >= 0) {
<tr>
<td>{{ 'labels.inputs.Total Interest Earned' | translate }}</td>
<td class="r-amount">
{{
fixedDepositsAccountData.summary.totalInterestEarned
| currency: currency.code : 'symbol-narrow' : '1.2-2'
}}
</td>
</tr>
}
</tbody>
</table>
<!-- Performance History -->
<div class="nx-card">
<div class="nx-card__head">
<span class="nx-card__ico"><fa-icon icon="chart-bar"></fa-icon></span>
<h3 class="nx-card__title">{{ 'labels.heading.Performance History' | translate }}</h3>
</div>
<div class="nx-card__body">
<div class="nx-row">
<span class="nx-row__label">{{ 'labels.inputs.Principal Amount' | translate }}</span>
<span class="nx-row__value nx-row__value--amount">
{{ fixedDepositsAccountData.depositAmount | currency: currency.code : 'symbol-narrow' : '1.2-2' }}
</span>
</div>
<div class="nx-row">
<span class="nx-row__label">{{ 'labels.inputs.Maturity Amount' | translate }}</span>
<span class="nx-row__value nx-row__value--amount nx-row__value--positive">
{{ fixedDepositsAccountData.maturityAmount | currency: currency.code : 'symbol-narrow' : '1.2-2' }}
</span>
</div>
@if (fixedDepositsAccountData.summary.totalDeposits) {
<div class="nx-row">
<span class="nx-row__label">{{ 'labels.inputs.Total Deposits' | translate }}</span>
<span class="nx-row__value nx-row__value--amount">
{{
fixedDepositsAccountData.summary.totalDeposits | currency: currency.code : 'symbol-narrow' : '1.2-2'
}}
</span>
</div>
}
@if (fixedDepositsAccountData.summary.totalWithdrawals) {
<div class="nx-row">
<span class="nx-row__label">{{ 'labels.inputs.Total Withdrawals' | translate }}</span>
<span class="nx-row__value nx-row__value--amount nx-row__value--negative">
{{
fixedDepositsAccountData.summary.totalWithdrawals
| currency: currency.code : 'symbol-narrow' : '1.2-2'
}}
</span>
</div>
}
@if (fixedDepositsAccountData.summary.totalInterestEarned >= 0) {
<div class="nx-row">
<span class="nx-row__label">{{ 'labels.inputs.Total Interest Earned' | translate }}</span>
<span class="nx-row__value nx-row__value--amount nx-row__value--positive">
{{
fixedDepositsAccountData.summary.totalInterestEarned
| currency: currency.code : 'symbol-narrow' : '1.2-2'
}}
</span>
</div>
}
</div>
</div>
} @else {
<div>
<h4 class="table-headers">{{ 'labels.heading.Other Details' | translate }}</h4>
<table>
<tbody>
<tr>
<td>{{ 'labels.inputs.Principal Amount' | translate }}</td>
<td class="r-amount">
{{ fixedDepositsAccountData.depositAmount | currency: currency.code : 'symbol-narrow' : '1.2-2' }}
</td>
</tr>
<tr>
<td>{{ 'labels.inputs.Maturity Amount' | translate }}</td>
<td class="r-amount">
{{ fixedDepositsAccountData.maturityAmount | currency: currency.code : 'symbol-narrow' : '1.2-2' }}
</td>
</tr>
</tbody>
</table>
<!-- Other Details (pending/rejected) -->
<div class="nx-card">
<div class="nx-card__head">
<span class="nx-card__ico"><fa-icon icon="info-circle"></fa-icon></span>
<h3 class="nx-card__title">{{ 'labels.heading.Other Details' | translate }}</h3>
</div>
<div class="nx-card__body">
<div class="nx-row">
<span class="nx-row__label">{{ 'labels.inputs.Principal Amount' | translate }}</span>
<span class="nx-row__value nx-row__value--amount">
{{ fixedDepositsAccountData.depositAmount | currency: currency.code : 'symbol-narrow' : '1.2-2' }}
</span>
</div>
<div class="nx-row">
<span class="nx-row__label">{{ 'labels.inputs.Maturity Amount' | translate }}</span>
<span class="nx-row__value nx-row__value--amount">
{{ fixedDepositsAccountData.maturityAmount | currency: currency.code : 'symbol-narrow' : '1.2-2' }}
</span>
</div>
</div>
</div>
}
</div>
@@ -6,6 +6,115 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
.fixed-deposits-account-tables {
padding: 1%;
$navy: #0d1b2a;
$slate: #475569;
$muted: #64748b;
$border: #e2e8f0;
$soft: #f8fafc;
/* ─── Two-column grid ───────────────────────────────────────────────── */
.gt-grid {
display: grid;
grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
gap: 16px;
@media (width <= 1024px) {
grid-template-columns: 1fr;
}
}
.gt-col {
display: flex;
flex-direction: column;
gap: 16px;
min-width: 0;
}
/* ─── Nexora section card ───────────────────────────────────────────── */
.nx-card {
background: #fff;
border: 1px solid $border;
border-radius: 14px;
box-shadow: 0 1px 3px rgb(15 23 42 / 4%);
overflow: hidden;
&__head {
display: flex;
align-items: center;
gap: 10px;
padding: 12px 18px;
background: $soft;
border-bottom: 1px solid $border;
}
&__ico {
width: 26px;
height: 26px;
display: inline-flex;
align-items: center;
justify-content: center;
border-radius: 8px;
background: linear-gradient(135deg, #3b82f6 0%, #1e40af 100%);
color: #fff;
font-size: 12px;
}
&__title {
margin: 0;
font-size: 13.5px;
font-weight: 700;
color: $navy;
letter-spacing: 0.01em;
}
&__body {
padding: 4px 18px 10px;
}
}
/* ─── Key/Value row ─────────────────────────────────────────────────── */
.nx-row {
display: grid;
grid-template-columns: minmax(0, 1fr) auto;
gap: 12px;
align-items: center;
min-height: 38px;
padding: 8px 0;
border-bottom: 1px dashed #eef2f7;
font-size: 13px;
&:last-child {
border-bottom: none;
}
&__label {
color: $slate;
font-weight: 500;
}
&__value {
color: $navy;
font-weight: 600;
text-align: right;
word-break: break-word;
&--amount {
font-variant-numeric: tabular-nums;
font-weight: 700;
}
&--positive {
color: #047857;
}
&--negative {
color: #b91c1c;
}
}
}
.nx-muted {
color: $muted;
font-weight: 500;
font-style: italic;
}
@@ -12,6 +12,7 @@ import { Currency } from 'app/shared/models/general.model';
import { CurrencyPipe } from '@angular/common';
import { ExternalIdentifierComponent } from '../../../../shared/external-identifier/external-identifier.component';
import { DateFormatPipe } from '../../../../pipes/date-format.pipe';
import { FaIconComponent } from '@fortawesome/angular-fontawesome';
import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
@Component({
@@ -21,6 +22,7 @@ import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
imports: [
...STANDALONE_SHARED_IMPORTS,
ExternalIdentifierComponent,
FaIconComponent,
CurrencyPipe,
DateFormatPipe
]
@@ -6,23 +6,19 @@
file, You can obtain one at http://mozilla.org/MPL/2.0/.
-->
<div class="tab-container mat-typography">
<div class="mat-elevation-z1 m-b-25">
<div class="layout-row align-start">
<div class="m-b-20">
<h3>{{ 'labels.heading.All Transactions' | translate }}</h3>
<div class="txn-tab">
<div class="txn-tab__card">
@if (checkStatus()) {
<div class="txn-tab__toolbar">
<mat-checkbox [formControl]="hideReversedParam" (click)="hideReversed()" class="txn-tab__check">
{{ 'labels.inputs.Hide Reversed' | translate }}
</mat-checkbox>
<mat-checkbox [formControl]="hideAccrualsParam" (click)="hideAccruals()" class="txn-tab__check">
{{ 'labels.inputs.Hide Accruals' | translate }}
</mat-checkbox>
<span class="txn-tab__spacer"></span>
</div>
@if (checkStatus()) {
<div class="action-button m-b-20 layout-row gap-20px">
<mat-checkbox [formControl]="hideReversedParam" (click)="hideReversed()" class="accruals">
{{ 'labels.inputs.Hide Reversed' | translate }}</mat-checkbox
>
<mat-checkbox [formControl]="hideAccrualsParam" (click)="hideAccruals()" class="accruals">
{{ 'labels.inputs.Hide Accruals' | translate }}</mat-checkbox
>
</div>
}
</div>
}
<table mat-table [dataSource]="dataSource">
<ng-container matColumnDef="row">
@@ -105,7 +101,7 @@
</ng-container>
<ng-container matColumnDef="actions">
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Actions' | translate }}</th>
<th mat-header-cell class="center" *matHeaderCellDef>{{ 'labels.inputs.Actions' | translate }}</th>
<td mat-cell class="center" *matCellDef="let transaction; let idx = index">
<button mat-icon-button [matMenuTriggerFor]="transactionMenu" aria-label="" class="action-button">
<mat-icon>more_vert</mat-icon>
@@ -6,48 +6,95 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
@use 'assets/styles/helper';
@use 'assets/styles/colours' as *;
$navy: #0d1b2a;
$slate: #475569;
$muted: #64748b;
$border: #e2e8f0;
$soft: #f8fafc;
$blue: #1e40af;
.tab-container {
padding: 1%;
margin: 1%;
h3 {
margin: 1% auto;
}
.action-button {
margin-left: auto;
}
table {
width: 100%;
color: $black;
.account-action-button {
min-width: 26px;
padding: 0 0 3px;
margin: 0 2%;
line-height: 25px;
}
.strike {
text-decoration: line-through;
color: $status-active-overdue;
}
.linked {
color: $status-approved;
}
.accrual {
color: $msg-default;
}
}
.txn-tab {
width: 100%;
}
.select-row:hover {
cursor: pointer;
font-weight: 800;
.txn-tab__card {
background: #fff;
border: 1px solid $border;
border-radius: 14px;
box-shadow: 0 1px 3px rgb(15 23 42 / 4%);
overflow: hidden;
}
.txn-tab__toolbar {
display: flex;
align-items: center;
gap: 18px;
padding: 12px 18px;
background: $soft;
border-bottom: 1px solid $border;
flex-wrap: wrap;
}
.txn-tab__check {
font-size: 13px;
color: $slate;
font-weight: 500;
}
.txn-tab__spacer {
flex: 1;
}
table {
width: 100%;
}
::ng-deep .txn-tab__card {
.mat-mdc-header-row {
background: $soft;
}
.mat-mdc-header-cell {
font-size: 11.5px;
font-weight: 800;
color: $slate;
text-transform: uppercase;
letter-spacing: 0.06em;
padding: 12px 14px;
border-bottom: 1px solid $border;
}
.mat-mdc-cell {
font-size: 13px;
color: $navy;
padding: 11px 14px;
border-bottom: 1px solid #f1f5f9;
}
.mat-mdc-row:hover {
background: #f8fafc;
cursor: pointer;
}
.mat-column-debit,
.mat-column-credit,
.mat-column-balance {
text-align: right;
font-variant-numeric: tabular-nums;
}
.mat-column-actions {
text-align: center;
width: 80px;
}
.strike td {
text-decoration: line-through;
color: $muted;
}
.accrual td {
color: $muted;
font-style: italic;
}
}
@@ -6,14 +6,13 @@
file, You can obtain one at http://mozilla.org/MPL/2.0/.
-->
<div class="tab-container mat-typography">
<div class="layout-row align-start">
<div class="m-b-10">
<h3>{{ 'labels.heading.All Charges' | translate }}</h3>
<div class="chg-tab">
<div class="chg-tab__card">
<div class="chg-tab__toolbar">
<h3 class="chg-tab__title">{{ 'labels.heading.All Charges' | translate }}</h3>
<span class="chg-tab__spacer"></span>
</div>
</div>
<div class="mat-elevation-z1 m-b-25">
<table mat-table #chargesTable [dataSource]="dataSource">
<ng-container matColumnDef="name">
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.name' | translate }}</th>
@@ -6,26 +6,96 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
.tab-container {
padding: 1%;
margin: 1%;
$navy: #0d1b2a;
$slate: #475569;
$muted: #64748b;
$border: #e2e8f0;
$soft: #f8fafc;
$blue: #1e40af;
h3 {
margin: 1% auto;
.chg-tab {
width: 100%;
}
.chg-tab__card {
background: #fff;
border: 1px solid $border;
border-radius: 14px;
box-shadow: 0 1px 3px rgb(15 23 42 / 4%);
overflow: hidden;
}
.chg-tab__toolbar {
display: flex;
align-items: center;
gap: 14px;
padding: 12px 18px;
background: $soft;
border-bottom: 1px solid $border;
}
.chg-tab__title {
margin: 0;
font-size: 14px;
font-weight: 700;
color: $navy;
letter-spacing: 0.01em;
}
.chg-tab__spacer {
flex: 1;
}
table {
width: 100%;
}
::ng-deep .chg-tab__card {
.mat-mdc-header-row {
background: $soft;
}
.action-button {
margin-left: auto;
.mat-mdc-header-cell {
font-size: 11.5px;
font-weight: 800;
color: $slate;
text-transform: uppercase;
letter-spacing: 0.06em;
padding: 12px 14px;
border-bottom: 1px solid $border;
}
table {
width: 100%;
.mat-mdc-cell {
font-size: 13px;
color: $navy;
padding: 11px 14px;
border-bottom: 1px solid #f1f5f9;
}
.mat-mdc-row:hover {
background: #f8fafc;
cursor: pointer;
}
.mat-column-due,
.mat-column-paid,
.mat-column-waived,
.mat-column-outstanding {
text-align: right;
font-variant-numeric: tabular-nums;
}
.mat-column-actions {
text-align: center;
width: 140px;
.account-action-button {
min-width: 26px;
padding: 0 6px;
margin: 4px;
line-height: 25px;
min-width: 30px !important;
padding: 0 8px !important;
margin: 0 3px !important;
height: 30px !important;
line-height: 30px !important;
border-radius: 7px !important;
}
}
}
@@ -6,503 +6,442 @@
file, You can obtain one at http://mozilla.org/MPL/2.0/.
-->
<div class="recurring-deposits-account-tables layout-row gap-2percent">
@if (
!recurringDepositsAccountData.status.rejected && !recurringDepositsAccountData.status.submittedAndPendingApproval
) {
<div class="flex-49">
<h4 class="table-headers">{{ 'labels.inputs.Account Details' | translate }}</h4>
<table>
<tbody>
<tr>
<td>{{ 'labels.inputs.External Id' | translate }}</td>
<td>
<div class="gt-grid">
<!-- ─── Left Column ──────────────────────────────────────────────── -->
<div class="gt-col">
@if (
!recurringDepositsAccountData.status.rejected && !recurringDepositsAccountData.status.submittedAndPendingApproval
) {
<!-- ACTIVE: Account Details -->
<div class="nx-card">
<div class="nx-card__head">
<span class="nx-card__ico"><fa-icon icon="file-alt"></fa-icon></span>
<h3 class="nx-card__title">{{ 'labels.inputs.Account Details' | translate }}</h3>
</div>
<div class="nx-card__body">
<div class="nx-row">
<span class="nx-row__label">{{ 'labels.inputs.External Id' | translate }}</span>
<span class="nx-row__value">
@if (recurringDepositsAccountData.externalId) {
<span>
<mifosx-external-identifier
externalId="{{ recurringDepositsAccountData.externalId }}"
completed="true"
></mifosx-external-identifier>
</span>
<mifosx-external-identifier
externalId="{{ recurringDepositsAccountData.externalId }}"
completed="true"
></mifosx-external-identifier>
} @else {
<span class="nx-muted">{{ 'labels.inputs.Not Available' | translate }}</span>
}
@if (!recurringDepositsAccountData.externalId) {
<span>
{{ 'labels.inputs.Not Available' | translate }}
</span>
</span>
</div>
<div class="nx-row">
<span class="nx-row__label">{{ 'labels.inputs.Field Officer' | translate }}</span>
<span class="nx-row__value">
@if (recurringDepositsAccountData.fieldOfficerName) {
{{ recurringDepositsAccountData.fieldOfficerName }}
} @else {
<span class="nx-muted">{{ 'labels.inputs.Unassigned' | translate }}</span>
}
</td>
</tr>
<tr>
<td>{{ 'labels.inputs.Field Officer' | translate }}</td>
<td>
{{ recurringDepositsAccountData.fieldOfficerName }}
@if (!recurringDepositsAccountData.fieldOfficerName) {
<span>{{ 'labels.inputs.Unassigned' | translate }}</span>
}
</td>
</tr>
<tr>
<td>{{ 'labels.inputs.Activation date' | translate }}</td>
<td>{{ recurringDepositsAccountData.timeline.activatedOnDate | dateFormat }}</td>
</tr>
<tr>
<td>{{ 'labels.inputs.Maturity Date' | translate }}</td>
<td>
<span>{{ recurringDepositsAccountData.maturityDate | dateFormat }}</span>
</td>
</tr>
</span>
</div>
<div class="nx-row">
<span class="nx-row__label">{{ 'labels.inputs.Activation date' | translate }}</span>
<span class="nx-row__value">{{ recurringDepositsAccountData.timeline.activatedOnDate | dateFormat }}</span>
</div>
<div class="nx-row">
<span class="nx-row__label">{{ 'labels.inputs.Maturity Date' | translate }}</span>
<span class="nx-row__value">{{ recurringDepositsAccountData.maturityDate | dateFormat }}</span>
</div>
@if (
recurringDepositsAccountData.timeline.closedOnDate || recurringDepositsAccountData.summary.totalWitddrawals
) {
<tr>
@if (recurringDepositsAccountData.timeline.closedOnDate) {
<td>
{{ 'labels.inputs.Closed on Date' | translate }}
</td>
}
<td>
<span>{{ recurringDepositsAccountData.timeline.closedOnDate | dateFormat }}</span>
</td>
</tr>
<div class="nx-row">
<span class="nx-row__label">{{ 'labels.inputs.Closed on Date' | translate }}</span>
<span class="nx-row__value">{{ recurringDepositsAccountData.timeline.closedOnDate | dateFormat }}</span>
</div>
}
<tr></tr>
<tr>
<td>{{ 'labels.inputs.Deposit Frequency' | translate }}</td>
<td>
<div class="nx-row">
<span class="nx-row__label">{{ 'labels.inputs.Deposit Frequency' | translate }}</span>
<span class="nx-row__value">
<span class="m-r-5">{{ recurringDepositsAccountData.recurringFrequency }}</span>
{{ recurringDepositsAccountData.recurringFrequencyType.value | translateKey: 'catalogs' }}
</td>
</tr>
</span>
</div>
@if (recurringDepositsAccountData.minBalanceForInterestCalculation) {
<tr>
<td>{{ 'labels.inputs.Balance Required for Interest Calculation' | translate }}</td>
<td>
<span>{{ recurringDepositsAccountData.minBalanceForInterestCalculation }}</span>
</td>
</tr>
<div class="nx-row">
<span class="nx-row__label">{{
'labels.inputs.Balance Required for Interest Calculation' | translate
}}</span>
<span class="nx-row__value">{{ recurringDepositsAccountData.minBalanceForInterestCalculation }}</span>
</div>
}
@if (recurringDepositsAccountData.witdHoldTax) {
<tr>
<td>{{ 'labels.inputs.Witdhold Tax Group' | translate }}</td>
<td>
<span>{{ recurringDepositsAccountData.taxGroup.name }}</span>
</td>
</tr>
<div class="nx-row">
<span class="nx-row__label">{{ 'labels.inputs.Witdhold Tax Group' | translate }}</span>
<span class="nx-row__value">{{ recurringDepositsAccountData.taxGroup.name }}</span>
</div>
}
</tbody>
</table>
</div>
}
</div>
</div>
@if (
!recurringDepositsAccountData.status.rejected && !recurringDepositsAccountData.status.submittedAndPendingApproval
) {
<div class="flex-49">
<h4 class="table-headers">{{ 'labels.heading.Performance History' | translate }}</h4>
<table>
<tbody>
<tr>
<td>{{ 'labels.inputs.Principal Amount' | translate }}</td>
<td class="r-amount">
<span>{{
recurringDepositsAccountData.depositAmount | currency: currency.code : 'symbol-narrow' : '1.2-2'
}}</span>
</td>
</tr>
<tr>
<td>{{ 'labels.inputs.Maturity Amount' | translate }}</td>
<td class="r-amount">
<span>{{
recurringDepositsAccountData.maturityAmount | currency: currency.code : 'symbol-narrow' : '1.2-2'
}}</span>
</td>
</tr>
<tr>
<td>{{ 'labels.inputs.Recurring Deposit Amount' | translate }}</td>
<td class="r-amount">
<span>{{
recurringDepositsAccountData?.mandatoryRecommendedDepositAmount
| currency: currency.code : 'symbol-narrow' : '1.2-2'
}}</span>
</td>
</tr>
<tr>
<td>{{ 'labels.inputs.Deposits till Date' | translate }}</td>
<td class="r-amount">
@if (recurringDepositsAccountData.summary.totalDeposits) {
<span>{{
recurringDepositsAccountData.summary.totalDeposits
| currency: currency.code : 'symbol-narrow' : '1.2-2'
}}</span>
}
@if (!recurringDepositsAccountData.summary.totalDeposits) {
<span>{{ 0 | currency: currency.code : 'symbol-narrow' : '1.2-2' }}</span>
}
</td>
</tr>
<tr>
<td>{{ 'labels.inputs.Withdrwals till Date' | translate }}</td>
<td class="r-amount">
@if (recurringDepositsAccountData.summary.totalWithdrawals) {
<span>{{
recurringDepositsAccountData.summary.totalWithdrawals
| currency: currency.code : 'symbol-narrow' : '1.2-2'
}}</span>
}
@if (!recurringDepositsAccountData.summary.totalWithdrawals) {
<span>{{ 0 | currency: currency.code : 'symbol-narrow' : '1.2-2' }}</span>
}
</td>
</tr>
@if (recurringDepositsAccountData.summary.totalInterestEarned >= 0) {
<tr>
<td>{{ 'labels.inputs.Interest Earned' | translate }}</td>
<td class="r-amount">
<span>{{
recurringDepositsAccountData.summary.totalInterestEarned
| currency: currency.code : 'symbol-narrow' : '1.2-2'
}}</span>
</td>
</tr>
<!-- ACTIVE: Interest Details -->
<div class="nx-card">
<div class="nx-card__head">
<span class="nx-card__ico"><fa-icon icon="percentage"></fa-icon></span>
<h3 class="nx-card__title">{{ 'labels.heading.Interest Details' | translate }}</h3>
</div>
<div class="nx-card__body">
<div class="nx-row">
<span class="nx-row__label">{{ 'labels.inputs.Interest Rate' | translate }}</span>
<span class="nx-row__value"
>{{ recurringDepositsAccountData.nominalAnnualInterestRate | formatNumber }} %</span
>
</div>
<div class="nx-row">
<span class="nx-row__label">{{ 'labels.inputs.Interest Compounding Period' | translate }}</span>
<span class="nx-row__value">{{
recurringDepositsAccountData.interestCompoundingPeriodType.value | translateKey: 'catalogs'
}}</span>
</div>
<div class="nx-row">
<span class="nx-row__label">{{ 'labels.inputs.Interest Posting Period' | translate }}</span>
<span class="nx-row__value">{{
recurringDepositsAccountData.interestPostingPeriodType.value | translateKey: 'catalogs'
}}</span>
</div>
<div class="nx-row">
<span class="nx-row__label">{{ 'labels.inputs.Interest calculated using' | translate }}</span>
<span class="nx-row__value">{{
recurringDepositsAccountData.interestCalculationType.value | translateKey: 'catalogs'
}}</span>
</div>
<div class="nx-row">
<span class="nx-row__label">{{ 'labels.inputs.Days in Year' | translate }}</span>
<span class="nx-row__value">{{
recurringDepositsAccountData.interestCalculationDaysInYearType.value | translateKey: 'catalogs'
}}</span>
</div>
@if (recurringDepositsAccountData.preClosurePenalApplicable) {
<div class="nx-row">
<span class="nx-row__label">{{ 'labels.inputs.Pre-closure penal Interest (less)' | translate }}</span>
<span class="nx-row__value">
{{ recurringDepositsAccountData.preClosurePenalInterest }} % on
{{ recurringDepositsAccountData.preClosurePenalInterestOnType.value | translateKey: 'catalogs' }}
</span>
</div>
}
</tbody>
</table>
</div>
}
@if (
recurringDepositsAccountData.status.rejected || recurringDepositsAccountData.status.submittedAndPendingApproval
) {
<div class="flex-49">
<table>
<tbody>
<tr>
<td>{{ 'labels.inputs.External Id' | translate }}</td>
<td>
</div>
</div>
} @else {
<!-- PENDING/REJECTED: Account Details -->
<div class="nx-card">
<div class="nx-card__head">
<span class="nx-card__ico"><fa-icon icon="file-alt"></fa-icon></span>
<h3 class="nx-card__title">{{ 'labels.inputs.Account Details' | translate }}</h3>
</div>
<div class="nx-card__body">
<div class="nx-row">
<span class="nx-row__label">{{ 'labels.inputs.External Id' | translate }}</span>
<span class="nx-row__value">
@if (recurringDepositsAccountData.externalId) {
<span>
<mifosx-external-identifier
externalId="{{ recurringDepositsAccountData.externalId }}"
completed="true"
></mifosx-external-identifier>
</span>
<mifosx-external-identifier
externalId="{{ recurringDepositsAccountData.externalId }}"
completed="true"
></mifosx-external-identifier>
} @else {
<span class="nx-muted">{{ 'labels.inputs.Not Available' | translate }}</span>
}
@if (!recurringDepositsAccountData.externalId) {
<span>
{{ 'labels.inputs.Not Available' | translate }}
</span>
}
</td>
</tr>
<tr>
<td>{{ 'labels.inputs.Date of Deposit' | translate }}</td>
<td>
</span>
</div>
<div class="nx-row">
<span class="nx-row__label">{{ 'labels.inputs.Date of Deposit' | translate }}</span>
<span class="nx-row__value">
@if (!recurringDepositsAccountData.expectedFirstDepositOnDate) {
<span
>{{ recurringDepositsAccountData.timeline.activatedOnDate | dateFormat }}
@if (!recurringDepositsAccountData.timeline.activatedOnDate) {
<span>{{ 'labels.inputs.Not Activated' | translate }}</span>
}
</span>
{{ recurringDepositsAccountData.timeline.activatedOnDate | dateFormat }}
@if (!recurringDepositsAccountData.timeline.activatedOnDate) {
<span class="nx-muted">{{ 'labels.inputs.Not Activated' | translate }}</span>
}
} @else {
{{ recurringDepositsAccountData.expectedFirstDepositOnDate | dateFormat }}
}
@if (recurringDepositsAccountData.expectedFirstDepositOnDate) {
<span
>{{ recurringDepositsAccountData.expectedFirstDepositOnDate | dateFormat }}
@if (!recurringDepositsAccountData.expectedFirstDepositOnDate) {
<span>{{ 'labels.inputs.Not Activated' | translate }}</span>
}
</span>
}
</td>
</tr>
<tr>
<td>{{ 'labels.inputs.Period' | translate }}</td>
<td>
<span
>{{ recurringDepositsAccountData.depositPeriod }}&nbsp;{{
recurringDepositsAccountData.depositPeriodFrequency.value
}}</span
>
</td>
</tr>
</span>
</div>
<div class="nx-row">
<span class="nx-row__label">{{ 'labels.inputs.Period' | translate }}</span>
<span class="nx-row__value">
{{ recurringDepositsAccountData.depositPeriod }}&nbsp;{{
recurringDepositsAccountData.depositPeriodFrequency.value
}}
</span>
</div>
@if (recurringDepositsAccountData.maturityDate) {
<tr>
<td>{{ 'labels.inputs.Maturity Date' | translate }}</td>
<td>
<span>{{ recurringDepositsAccountData.maturityDate | dateFormat }}</span>
</td>
</tr>
<div class="nx-row">
<span class="nx-row__label">{{ 'labels.inputs.Maturity Date' | translate }}</span>
<span class="nx-row__value">{{ recurringDepositsAccountData.maturityDate | dateFormat }}</span>
</div>
}
<tr>
<td>{{ 'labels.inputs.Total Deposits' | translate }}</td>
<td class="r-amount">
<div class="nx-row">
<span class="nx-row__label">{{ 'labels.inputs.Total Deposits' | translate }}</span>
<span class="nx-row__value nx-row__value--amount">
@if (recurringDepositsAccountData.summary.totalDeposits) {
<span>{{
{{
recurringDepositsAccountData.summary.totalDeposits
| currency: currency.code : 'symbol-narrow' : '1.2-2'
}}</span>
}}
} @else {
{{ 0 | currency: currency.code : 'symbol-narrow' : '1.2-2' }}
}
@if (!recurringDepositsAccountData.summary.totalDeposits) {
<span>{{ 0 | currency: currency.code : 'symbol-narrow' : '1.2-2' }}</span>
}
</td>
</tr>
<tr>
<td>{{ 'labels.inputs.Actual Available Balance' | translate }}</td>
<td class="r-amount">
</span>
</div>
<div class="nx-row">
<span class="nx-row__label">{{ 'labels.inputs.Actual Available Balance' | translate }}</span>
<span class="nx-row__value nx-row__value--amount">
{{
recurringDepositsAccountData.summary.accountBalance
| currency: currency.code : 'symbol-narrow' : '1.2-2'
}}
</td>
</tr>
<tr>
<td>{{ 'labels.inputs.Recurring Deposit Amount' | translate }}</td>
<td class="r-amount">
</span>
</div>
<div class="nx-row">
<span class="nx-row__label">{{ 'labels.inputs.Recurring Deposit Amount' | translate }}</span>
<span class="nx-row__value nx-row__value--amount">
{{
recurringDepositsAccountData.mandatoryRecommendedDepositAmount
| currency: currency.code : 'symbol-narrow' : '1.2-2'
}}
</td>
</tr>
</span>
</div>
@if (recurringDepositsAccountData.summary.totalInterestEarned) {
<tr>
<td>{{ 'labels.inputs.Interests Earned' | translate }}</td>
<td>
<span
>{{ recurringDepositsAccountData.currency.displaySymbol }}&nbsp;{{
recurringDepositsAccountData.summary.totalInterestEarned
}}</span
>
</td>
</tr>
<div class="nx-row">
<span class="nx-row__label">{{ 'labels.inputs.Interests Earned' | translate }}</span>
<span class="nx-row__value nx-row__value--amount">
{{ recurringDepositsAccountData.currency.displaySymbol }}&nbsp;{{
recurringDepositsAccountData.summary.totalInterestEarned
}}
</span>
</div>
}
@if (recurringDepositsAccountData.summary.totalWithdrawals) {
<tr>
<td>{{ 'labels.inputs.Total Withdrawls' | translate }}</td>
<td class="r-amount">
<span>{{
<div class="nx-row">
<span class="nx-row__label">{{ 'labels.inputs.Total Withdrawls' | translate }}</span>
<span class="nx-row__value nx-row__value--amount">
{{
recurringDepositsAccountData.summary.totalWithdrawals
| currency: currency.code : 'symbol-narrow' : '1.2-2'
}}</span>
</td>
</tr>
}}
</span>
</div>
}
@if (recurringDepositsAccountData.minBalanceForInterestCalculation) {
<tr>
<td>{{ 'labels.inputs.Balance Required for Interest Calculation' | translate }}</td>
<td class="r-amount">
<span>{{ recurringDepositsAccountData.minBalanceForInterestCalculation | number }}</span>
</td>
</tr>
<div class="nx-row">
<span class="nx-row__label">{{
'labels.inputs.Balance Required for Interest Calculation' | translate
}}</span>
<span class="nx-row__value">{{
recurringDepositsAccountData.minBalanceForInterestCalculation | number
}}</span>
</div>
}
</tbody>
</table>
</div>
}
</div>
</div>
@if (
recurringDepositsAccountData.status.rejected || recurringDepositsAccountData.status.submittedAndPendingApproval
) {
<div class="flex-49">
<table>
<tbody>
<tr>
<td>{{ 'labels.inputs.Principal Amount' | translate }}</td>
<td class="r-amount">
@if (recurringDepositsAccountData.depositAmount) {
<span>{{
recurringDepositsAccountData.depositAmount | currency: currency.code : 'symbol-narrow' : '1.2-2'
}}</span>
}
@if (!recurringDepositsAccountData.depositAmount) {
<span>{{ 0 | currency: currency.code : 'symbol-narrow' : '1.2-2' }}</span>
}
</td>
</tr>
<tr>
<td>{{ 'labels.inputs.Rate of Interest' | translate }}</td>
<td class="r-amount">
<span>{{ recurringDepositsAccountData.nominalAnnualInterestRate | formatNumber }} %</span>
</td>
</tr>
@if (recurringDepositsAccountData.maturityDate) {
<tr>
<td>{{ 'labels.inputs.Maturity Amount' | translate }}</td>
<td class="r-amount">
<span>{{
recurringDepositsAccountData.maturityAmount | currency: currency.code : 'symbol-narrow' : '1.2-2'
}}</span>
</td>
</tr>
<!-- PENDING/REJECTED: Interest Details -->
<div class="nx-card">
<div class="nx-card__head">
<span class="nx-card__ico"><fa-icon icon="percentage"></fa-icon></span>
<h3 class="nx-card__title">{{ 'labels.heading.Interest Details' | translate }}</h3>
</div>
<div class="nx-card__body">
<div class="nx-row">
<span class="nx-row__label">{{ 'labels.inputs.Interest Compounding Period' | translate }}</span>
<span class="nx-row__value">{{
recurringDepositsAccountData.interestCompoundingPeriodType.value | translateKey: 'catalogs'
}}</span>
</div>
<div class="nx-row">
<span class="nx-row__label">{{ 'labels.inputs.Interest Posting Period' | translate }}</span>
<span class="nx-row__value">{{
recurringDepositsAccountData.interestPostingPeriodType.value | translateKey: 'catalogs'
}}</span>
</div>
<div class="nx-row">
<span class="nx-row__label">{{ 'labels.inputs.Interest calculated using' | translate }}</span>
<span class="nx-row__value">{{
recurringDepositsAccountData.interestCalculationType.value | translateKey: 'catalogs'
}}</span>
</div>
<div class="nx-row">
<span class="nx-row__label">{{ 'labels.inputs.Days in Year' | translate }}</span>
<span class="nx-row__value">{{
recurringDepositsAccountData.interestCalculationDaysInYearType.value | translateKey: 'catalogs'
}}</span>
</div>
@if (recurringDepositsAccountData.preClosurePenalApplicable) {
<div class="nx-row">
<span class="nx-row__label">{{ 'labels.inputs.Pre-closure penal Interest (less)' | translate }}</span>
<span class="nx-row__value">
{{ recurringDepositsAccountData.preClosurePenalInterest }} % on
{{ recurringDepositsAccountData.preClosurePenalInterestOnType.value | translateKey: 'catalogs' }}
</span>
</div>
}
<tr>
<td>{{ 'labels.inputs.Total Withdrawls' | translate }}</td>
<td class="r-amount">
@if (recurringDepositsAccountData.witdHoldTax) {
<div class="nx-row">
<span class="nx-row__label">{{ 'labels.inputs.Witdhold Tax Group' | translate }}</span>
<span class="nx-row__value">{{ recurringDepositsAccountData.taxGroup.name }}</span>
</div>
}
</div>
</div>
}
</div>
<!-- ─── Right Column ─────────────────────────────────────────────── -->
<div class="gt-col">
@if (
!recurringDepositsAccountData.status.rejected && !recurringDepositsAccountData.status.submittedAndPendingApproval
) {
<!-- ACTIVE: Performance History -->
<div class="nx-card">
<div class="nx-card__head">
<span class="nx-card__ico"><fa-icon icon="chart-bar"></fa-icon></span>
<h3 class="nx-card__title">{{ 'labels.heading.Performance History' | translate }}</h3>
</div>
<div class="nx-card__body">
<div class="nx-row">
<span class="nx-row__label">{{ 'labels.inputs.Principal Amount' | translate }}</span>
<span class="nx-row__value nx-row__value--amount">
{{ recurringDepositsAccountData.depositAmount | currency: currency.code : 'symbol-narrow' : '1.2-2' }}
</span>
</div>
<div class="nx-row">
<span class="nx-row__label">{{ 'labels.inputs.Maturity Amount' | translate }}</span>
<span class="nx-row__value nx-row__value--amount nx-row__value--positive">
{{ recurringDepositsAccountData.maturityAmount | currency: currency.code : 'symbol-narrow' : '1.2-2' }}
</span>
</div>
<div class="nx-row">
<span class="nx-row__label">{{ 'labels.inputs.Recurring Deposit Amount' | translate }}</span>
<span class="nx-row__value nx-row__value--amount">
{{
recurringDepositsAccountData?.mandatoryRecommendedDepositAmount
| currency: currency.code : 'symbol-narrow' : '1.2-2'
}}
</span>
</div>
<div class="nx-row">
<span class="nx-row__label">{{ 'labels.inputs.Deposits till Date' | translate }}</span>
<span class="nx-row__value nx-row__value--amount">
@if (recurringDepositsAccountData.summary.totalDeposits) {
{{
recurringDepositsAccountData.summary.totalDeposits
| currency: currency.code : 'symbol-narrow' : '1.2-2'
}}
} @else {
{{ 0 | currency: currency.code : 'symbol-narrow' : '1.2-2' }}
}
</span>
</div>
<div class="nx-row">
<span class="nx-row__label">{{ 'labels.inputs.Withdrwals till Date' | translate }}</span>
<span class="nx-row__value nx-row__value--amount nx-row__value--negative">
@if (recurringDepositsAccountData.summary.totalWithdrawals) {
<span>{{
{{
recurringDepositsAccountData.summary.totalWithdrawals
| currency: currency.code : 'symbol-narrow' : '1.2-2'
}}</span>
}}
} @else {
{{ 0 | currency: currency.code : 'symbol-narrow' : '1.2-2' }}
}
@if (!recurringDepositsAccountData.summary.totalWithdrawals) {
<span>{{ 0 | currency: currency.code : 'symbol-narrow' : '1.2-2' }}</span>
}
</td>
</tr>
@if (recurringDepositsAccountData.timeline.closedOnDate) {
<tr>
<td>{{ 'labels.inputs.Closed on Date' | translate }}</td>
<td>
{{ recurringDepositsAccountData.timeline.closedOnDate | dateFormat }}
</td>
</tr>
</span>
</div>
@if (recurringDepositsAccountData.summary.totalInterestEarned >= 0) {
<div class="nx-row">
<span class="nx-row__label">{{ 'labels.inputs.Interest Earned' | translate }}</span>
<span class="nx-row__value nx-row__value--amount nx-row__value--positive">
{{
recurringDepositsAccountData.summary.totalInterestEarned
| currency: currency.code : 'symbol-narrow' : '1.2-2'
}}
</span>
</div>
}
<tr>
<td>{{ 'labels.inputs.Deposits Frequency' | translate }}</td>
<td>
</div>
</div>
} @else {
<!-- PENDING/REJECTED: Financial Summary -->
<div class="nx-card">
<div class="nx-card__head">
<span class="nx-card__ico"><fa-icon icon="info-circle"></fa-icon></span>
<h3 class="nx-card__title">{{ 'labels.heading.Other Details' | translate }}</h3>
</div>
<div class="nx-card__body">
<div class="nx-row">
<span class="nx-row__label">{{ 'labels.inputs.Principal Amount' | translate }}</span>
<span class="nx-row__value nx-row__value--amount">
@if (recurringDepositsAccountData.depositAmount) {
{{ recurringDepositsAccountData.depositAmount | currency: currency.code : 'symbol-narrow' : '1.2-2' }}
} @else {
{{ 0 | currency: currency.code : 'symbol-narrow' : '1.2-2' }}
}
</span>
</div>
<div class="nx-row">
<span class="nx-row__label">{{ 'labels.inputs.Rate of Interest' | translate }}</span>
<span class="nx-row__value"
>{{ recurringDepositsAccountData.nominalAnnualInterestRate | formatNumber }} %</span
>
</div>
@if (recurringDepositsAccountData.maturityDate) {
<div class="nx-row">
<span class="nx-row__label">{{ 'labels.inputs.Maturity Amount' | translate }}</span>
<span class="nx-row__value nx-row__value--amount">
{{ recurringDepositsAccountData.maturityAmount | currency: currency.code : 'symbol-narrow' : '1.2-2' }}
</span>
</div>
}
<div class="nx-row">
<span class="nx-row__label">{{ 'labels.inputs.Total Withdrawls' | translate }}</span>
<span class="nx-row__value nx-row__value--amount">
@if (recurringDepositsAccountData.summary.totalWithdrawals) {
{{
recurringDepositsAccountData.summary.totalWithdrawals
| currency: currency.code : 'symbol-narrow' : '1.2-2'
}}
} @else {
{{ 0 | currency: currency.code : 'symbol-narrow' : '1.2-2' }}
}
</span>
</div>
@if (recurringDepositsAccountData.timeline.closedOnDate) {
<div class="nx-row">
<span class="nx-row__label">{{ 'labels.inputs.Closed on Date' | translate }}</span>
<span class="nx-row__value">{{ recurringDepositsAccountData.timeline.closedOnDate | dateFormat }}</span>
</div>
}
<div class="nx-row">
<span class="nx-row__label">{{ 'labels.inputs.Deposits Frequency' | translate }}</span>
<span class="nx-row__value">
{{ recurringDepositsAccountData.recurringFrequency }}&nbsp;{{
recurringDepositsAccountData.recurringFrequencyType.value | translateKey: 'catalogs'
}}
</td>
</tr>
</span>
</div>
@if (recurringDepositsAccountData.summary.totalInterestEarned) {
<tr>
<td>{{ 'labels.inputs.Interests Posted' | translate }}</td>
<td class="r-amount">
<div class="nx-row">
<span class="nx-row__label">{{ 'labels.inputs.Interests Posted' | translate }}</span>
<span class="nx-row__value nx-row__value--amount">
@if (recurringDepositsAccountData.summary.totalInterestPosted) {
<span>{{
{{
recurringDepositsAccountData.summary.totalInterestPosted
| currency: currency.code : 'symbol-narrow' : '1.2-2'
}}</span>
}}
} @else {
{{ 0 | currency: currency.code : 'symbol-narrow' : '1.2-2' }}
}
@if (!recurringDepositsAccountData.summary.totalInterestPosted) {
<span>{{ 0 | currency: currency.code : 'symbol-narrow' : '1.2-2' }}</span>
}
</td>
</tr>
</span>
</div>
}
</tbody>
</table>
</div>
}
</div>
<div class="recurring-deposits-account-tables layout-row gap-2percent">
@if (
!recurringDepositsAccountData.status.rejected && !recurringDepositsAccountData.status.submittedAndPendingApproval
) {
<div class="flex-49">
<h4 class="table-headers">{{ 'labels.heading.Interest Details' | translate }}</h4>
<table>
<tbody>
<tr>
<td>{{ 'labels.inputs.Interest Rate' | translate }}</td>
<td class="r-amount">{{ recurringDepositsAccountData.nominalAnnualInterestRate | formatNumber }} %</td>
</tr>
<tr>
<td>{{ 'labels.inputs.Interest Compounding Period' | translate }}</td>
<td>
<span>{{
recurringDepositsAccountData.interestCompoundingPeriodType.value | translateKey: 'catalogs'
}}</span>
</td>
</tr>
<tr>
<td>{{ 'labels.inputs.Interest Posting Period' | translate }}</td>
<td>
<span>{{ recurringDepositsAccountData.interestPostingPeriodType.value | translateKey: 'catalogs' }}</span>
</td>
</tr>
<tr>
<td>{{ 'labels.inputs.Interest calculated using' | translate }}</td>
<td>
<span>{{ recurringDepositsAccountData.interestCalculationType.value | translateKey: 'catalogs' }}</span>
</td>
</tr>
<tr>
<td>{{ 'labels.inputs.Days in Year' | translate }}</td>
<td>
<span>{{
recurringDepositsAccountData.interestCalculationDaysInYearType.value | translateKey: 'catalogs'
}}</span>
</td>
</tr>
@if (recurringDepositsAccountData.preClosurePenalApplicable) {
<tr>
<td>{{ 'labels.inputs.Pre-closure penal Interest (less)' | translate }}</td>
<td>
<span
>{{ recurringDepositsAccountData.preClosurePenalInterest }} % on
{{
recurringDepositsAccountData.preClosurePenalInterestOnType.value | translateKey: 'catalogs'
}}</span
>
</td>
</tr>
}
</tbody>
</table>
</div>
}
@if (
recurringDepositsAccountData.status.rejected || recurringDepositsAccountData.status.submittedAndPendingApproval
) {
<div class="flex-49">
<table>
<tbody>
<tr>
<td>{{ 'labels.inputs.Interest Compounding Period' | translate }}</td>
<td>
<span>{{
recurringDepositsAccountData.interestCompoundingPeriodType.value | translateKey: 'catalogs'
}}</span>
</td>
</tr>
<tr>
<td>{{ 'labels.inputs.Interest Posting Period' | translate }}</td>
<td>
<span>{{ recurringDepositsAccountData.interestPostingPeriodType.value | translateKey: 'catalogs' }}</span>
</td>
</tr>
<tr>
<td>{{ 'labels.inputs.Interest calculated using' | translate }}</td>
<td>
<span>{{ recurringDepositsAccountData.interestCalculationType.value | translateKey: 'catalogs' }}</span>
</td>
</tr>
<tr>
<td>{{ 'labels.inputs.Days in Year' | translate }}</td>
<td>
<span>{{
recurringDepositsAccountData.interestCalculationDaysInYearType.value | translateKey: 'catalogs'
}}</span>
</td>
</tr>
@if (recurringDepositsAccountData.preClosurePenalApplicable) {
<tr>
<td>{{ 'labels.inputs.Pre-closure penal Interest (less)' | translate }}</td>
<td>
<span
>{{ recurringDepositsAccountData.preClosurePenalInterest }} % on
{{
recurringDepositsAccountData.preClosurePenalInterestOnType.value | translateKey: 'catalogs'
}}</span
>
</td>
</tr>
}
@if (recurringDepositsAccountData.witdHoldTax) {
<tr>
<td>{{ 'labels.inputs.Witdhold Tax Group' | translate }}</td>
<td>
<span>{{ recurringDepositsAccountData.taxGroup.name }}</span>
</td>
</tr>
}
</tbody>
</table>
</div>
}
</div>
</div>
}
</div>
</div>
@@ -6,6 +6,115 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
.recurring-deposits-account-tables {
padding: 1%;
$navy: #0d1b2a;
$slate: #475569;
$muted: #64748b;
$border: #e2e8f0;
$soft: #f8fafc;
/* ─── Two-column grid ───────────────────────────────────────────────── */
.gt-grid {
display: grid;
grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
gap: 16px;
@media (width <= 1024px) {
grid-template-columns: 1fr;
}
}
.gt-col {
display: flex;
flex-direction: column;
gap: 16px;
min-width: 0;
}
/* ─── Nexora section card ───────────────────────────────────────────── */
.nx-card {
background: #fff;
border: 1px solid $border;
border-radius: 14px;
box-shadow: 0 1px 3px rgb(15 23 42 / 4%);
overflow: hidden;
&__head {
display: flex;
align-items: center;
gap: 10px;
padding: 12px 18px;
background: $soft;
border-bottom: 1px solid $border;
}
&__ico {
width: 26px;
height: 26px;
display: inline-flex;
align-items: center;
justify-content: center;
border-radius: 8px;
background: linear-gradient(135deg, #3b82f6 0%, #1e40af 100%);
color: #fff;
font-size: 12px;
}
&__title {
margin: 0;
font-size: 13.5px;
font-weight: 700;
color: $navy;
letter-spacing: 0.01em;
}
&__body {
padding: 4px 18px 10px;
}
}
/* ─── Key/Value row ─────────────────────────────────────────────────── */
.nx-row {
display: grid;
grid-template-columns: minmax(0, 1fr) auto;
gap: 12px;
align-items: center;
min-height: 38px;
padding: 8px 0;
border-bottom: 1px dashed #eef2f7;
font-size: 13px;
&:last-child {
border-bottom: none;
}
&__label {
color: $slate;
font-weight: 500;
}
&__value {
color: $navy;
font-weight: 600;
text-align: right;
word-break: break-word;
&--amount {
font-variant-numeric: tabular-nums;
font-weight: 700;
}
&--positive {
color: #047857;
}
&--negative {
color: #b91c1c;
}
}
}
.nx-muted {
color: $muted;
font-weight: 500;
font-style: italic;
}
@@ -13,6 +13,7 @@ import { DecimalPipe, CurrencyPipe } from '@angular/common';
import { ExternalIdentifierComponent } from '../../../../shared/external-identifier/external-identifier.component';
import { DateFormatPipe } from '../../../../pipes/date-format.pipe';
import { FormatNumberPipe } from '../../../../pipes/format-number.pipe';
import { FaIconComponent } from '@fortawesome/angular-fontawesome';
import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
@Component({
@@ -22,6 +23,7 @@ import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
imports: [
...STANDALONE_SHARED_IMPORTS,
ExternalIdentifierComponent,
FaIconComponent,
DecimalPipe,
CurrencyPipe,
DateFormatPipe,
@@ -6,196 +6,201 @@
file, You can obtain one at http://mozilla.org/MPL/2.0/.
-->
<mat-card class="account-card">
<mat-card-header class="header layout-column">
<mat-card-title-group class="header-title-group">
<div class="profile-image-container">
<div>
<img
mat-card-md-image
class="profile-image"
matTooltip="{{ 'tooltips.Recurring Deposits Account' | translate }}"
[src]="'assets/images/recurring-deposits_account_placeholder.png'"
/>
<div class="rd-view">
<!-- ─── Gradient hero header ─────────────────────────────────────── -->
<header class="rd-hero">
<div class="rd-hero__left">
<div class="rd-hero__avatar">
<fa-icon icon="calendar-alt"></fa-icon>
</div>
<div class="rd-hero__info">
<div class="rd-hero__status-row">
<span
class="rd-status-pill"
[ngClass]="recurringDepositsAccountData.status.code | statusLookup"
[matTooltip]="recurringDepositsAccountData.status.value"
>
<span class="rd-status-pill__dot"></span>
{{ recurringDepositsAccountData.status.value }}
</span>
</div>
<h2 class="rd-hero__title">
<mifosx-long-text textValue="{{ recurringDepositsAccountData.depositProductName }}"></mifosx-long-text>
</h2>
<div class="rd-hero__meta">
<span class="rd-hero__meta-item">
<span class="rd-hero__meta-label">{{ 'labels.inputs.Account Number' | translate }}</span>
<mifosx-account-number
accountNo="{{ recurringDepositsAccountData.accountNo }}"
display="left"
></mifosx-account-number>
</span>
<span class="rd-hero__meta-item">
<span class="rd-hero__meta-label">
{{ 'labels.text.' + entityType | translate }} {{ 'labels.inputs.name' | translate }}
</span>
<span class="rd-hero__meta-value">
{{ recurringDepositsAccountData.clientName || recurringDepositsAccountData.groupName }}
@if (recurringDepositsAccountData.clientAccountNo) {
<mifosx-account-number
accountNo="{{ recurringDepositsAccountData.clientAccountNo }}"
></mifosx-account-number>
}
</span>
</span>
</div>
</div>
</div>
<div class="mat-typography account-card-title">
<mat-card-title>
<div class="layout-row responsive-column">
<div class="flex-60">
<h3>
<i
class="fa fa-stop"
[ngClass]="recurringDepositsAccountData.status.code | statusLookup"
[matTooltip]="recurringDepositsAccountData.status.value"
></i>
{{ 'labels.inputs.Recurring Deposit' | translate }} :
{{ recurringDepositsAccountData.depositProductName }}
<mifosx-account-number accountNo="{{ recurringDepositsAccountData.accountNo }}"></mifosx-account-number>
</h3>
<span class="account-overview">
{{ 'labels.text.' + entityType | translate }} {{ 'labels.inputs.name' | translate }}:
{{ recurringDepositsAccountData.clientName || recurringDepositsAccountData.groupName }}
@if (recurringDepositsAccountData.clientAccountNo) {
<span class="m-l-10">
<mifosx-account-number
accountNo="{{ recurringDepositsAccountData.clientAccountNo }}"
></mifosx-account-number>
</span>
}
</span>
</div>
@if (
!recurringDepositsAccountData.status.rejected &&
!recurringDepositsAccountData.status.submittedAndPendingApproval
) {
<div class="flex-30">
<h3>{{ 'labels.heading.Account Overview' | translate }}</h3>
<table class="account-overview">
<tbody>
<tr>
<td>{{ 'labels.inputs.Current Balance' | translate }}</td>
<td class="r-amount">
{{
recurringDepositsAccountData.summary.accountBalance
| currency: currency.code : 'symbol-narrow' : '1.2-2'
}}
</td>
</tr>
<tr>
<td>{{ 'labels.inputs.Deposit Amount' | translate }}</td>
<td class="r-amount">
{{
recurringDepositsAccountData.depositAmount
| currency: currency.code : 'symbol-narrow' : '1.2-2'
}}
</td>
</tr>
<tr>
<td>{{ 'labels.inputs.Deposit Period' | translate }}</td>
<td class="r-amount">{{ recurringDepositsAccountData.depositPeriod | number }}</td>
</tr>
</tbody>
</table>
</div>
}
<div class="flex-5">
<button
mat-icon-button
[matMenuTriggerFor]="accountMenu"
aria-label="Loan account actions"
yPosition="below"
>
<mat-icon matListIcon class="actions-menu">
<fa-icon icon="bars" size="sm"></fa-icon>
</mat-icon>
</button>
</div>
</div>
</mat-card-title>
<!-- Balance KPI strip -->
@if (
!recurringDepositsAccountData.status.rejected && !recurringDepositsAccountData.status.submittedAndPendingApproval
) {
<div class="rd-hero__kpis">
<div class="rd-kpi">
<span class="rd-kpi__label">{{ 'labels.inputs.Current Balance' | translate }}</span>
<span class="rd-kpi__value">
{{
recurringDepositsAccountData.summary.accountBalance | currency: currency.code : 'symbol-narrow' : '1.2-2'
}}
</span>
</div>
<div class="rd-kpi rd-kpi--deposit">
<span class="rd-kpi__label">{{ 'labels.inputs.Deposit Amount' | translate }}</span>
<span class="rd-kpi__value">
{{ recurringDepositsAccountData.depositAmount | currency: currency.code : 'symbol-narrow' : '1.2-2' }}
</span>
</div>
<div class="rd-kpi rd-kpi--period">
<span class="rd-kpi__label">{{ 'labels.inputs.Deposit Period' | translate }}</span>
<span class="rd-kpi__value">{{ recurringDepositsAccountData.depositPeriod | number }}</span>
</div>
</div>
}
<mat-menu #accountMenu="matMenu">
@for (item of buttonConfig.singleButtons; track item) {
<button mat-menu-item *mifosxHasPermission="item.taskPermissionName" (click)="doAction(item.name)">
<i ngClass="{{ item.icon }}"></i> {{ 'labels.menus.' + item.name | translate }}
</button>
}
<!-- Action menu (kebab) -->
<div class="rd-hero__actions">
<button mat-icon-button [matMenuTriggerFor]="accountMenu" aria-label="Account actions" yPosition="below">
<fa-icon icon="ellipsis-v"></fa-icon>
</button>
</div>
@if (buttonConfig.options.length) {
<button mat-menu-item [matMenuTriggerFor]="More">{{ 'labels.menus.More' | translate }}</button>
<mat-menu #More="matMenu">
@for (option of buttonConfig.options; track option) {
<span>
<button mat-menu-item *mifosxHasPermission="option.taskPermissionName" (click)="doAction(option.name)">
{{ 'labels.menus.' + option.name | translate }}
</button>
</span>
}
</mat-menu>
}
</mat-menu>
</mat-card-title-group>
</mat-card-header>
<mat-menu #accountMenu="matMenu">
@for (item of buttonConfig.singleButtons; track item) {
<button mat-menu-item *mifosxHasPermission="item.taskPermissionName" (click)="doAction(item.name)">
<span>{{ 'labels.menus.' + item.name | translate }}</span>
</button>
}
@if (buttonConfig.options.length) {
<button mat-menu-item [matMenuTriggerFor]="More">{{ 'labels.menus.More' | translate }}</button>
<mat-menu #More="matMenu">
@for (option of buttonConfig.options; track option) {
<span>
<button mat-menu-item *mifosxHasPermission="option.taskPermissionName" (click)="doAction(option.name)">
{{ 'labels.menus.' + option.name | translate }}
</button>
</span>
}
</mat-menu>
}
</mat-menu>
</header>
<mat-card-content class="content">
<nav mat-tab-nav-bar class="navigation-tabs" [tabPanel]="tabPanel">
<!-- ─── Pill-style tab navigation ─────────────────────────────────── -->
<nav mat-tab-nav-bar class="rd-tabs" [tabPanel]="tabPanel">
<a
mat-tab-link
class="rd-tab"
[routerLink]="['./general']"
routerLinkActive
#general="routerLinkActive"
[active]="general.isActive"
>
<fa-icon icon="info-circle" class="rd-tab__ic"></fa-icon>
<span>{{ 'labels.inputs.General' | translate }}</span>
</a>
<a
mat-tab-link
class="rd-tab"
[routerLink]="['./interest-rate-chart']"
routerLinkActive
#IRC="routerLinkActive"
[active]="IRC.isActive"
>
<fa-icon icon="chart-line" class="rd-tab__ic"></fa-icon>
<span>{{ 'labels.inputs.Interest Rate Chart' | translate }}</span>
</a>
@if (showTransactions) {
<a
mat-tab-link
[routerLink]="['./general']"
class="rd-tab"
[routerLink]="['./transactions']"
routerLinkActive
#general="routerLinkActive"
[active]="general.isActive"
#transactions="routerLinkActive"
[active]="transactions.isActive"
>
{{ 'labels.inputs.General' | translate }}
<fa-icon icon="exchange-alt" class="rd-tab__ic"></fa-icon>
<span>{{ 'labels.inputs.Transactions' | translate }}</span>
</a>
}
<a
mat-tab-link
class="rd-tab"
[routerLink]="['./charges']"
routerLinkActive
#charges="routerLinkActive"
[active]="charges.isActive"
>
<fa-icon icon="receipt" class="rd-tab__ic"></fa-icon>
<span>{{ 'labels.inputs.Charges' | translate }}</span>
</a>
@if (recurringDepositsAccountData.clientId) {
<a
mat-tab-link
[routerLink]="['./interest-rate-chart']"
class="rd-tab"
[routerLink]="['./standing-instructions-tab']"
routerLinkActive
#IRC="routerLinkActive"
[active]="IRC.isActive"
#standingInstructions="routerLinkActive"
[active]="standingInstructions.isActive"
>
{{ 'labels.inputs.Interest Rate Chart' | translate }}
<fa-icon icon="calendar-check" class="rd-tab__ic"></fa-icon>
<span>{{ 'labels.inputs.Standing Instructions' | translate }}</span>
</a>
@if (showTransactions) {
<a
mat-tab-link
[routerLink]="['./transactions']"
routerLinkActive
#transactions="routerLinkActive"
[active]="transactions.isActive"
>
{{ 'labels.inputs.Transactions' | translate }}
</a>
}
}
<a
mat-tab-link
class="rd-tab"
[routerLink]="['./nominees']"
routerLinkActive
#nominees="routerLinkActive"
[active]="nominees.isActive"
>
<fa-icon icon="user-shield" class="rd-tab__ic"></fa-icon>
<span>Nominees</span>
</a>
@for (savingsDatatable of savingsDatatables; track savingsDatatable) {
<a
mat-tab-link
[routerLink]="['./charges']"
class="rd-tab"
*mifosxHasPermission="'READ_' + savingsDatatable.registeredTableName"
[routerLink]="['./datatables', savingsDatatable.registeredTableName]"
routerLinkActive
#charges="routerLinkActive"
[active]="charges.isActive"
#datatable="routerLinkActive"
[active]="datatable.isActive"
>
{{ 'labels.inputs.Charges' | translate }}
<fa-icon icon="table" class="rd-tab__ic"></fa-icon>
<span>{{ savingsDatatable.registeredTableName }}</span>
</a>
@if (recurringDepositsAccountData.clientId) {
<a
mat-tab-link
[routerLink]="['./standing-instructions-tab']"
routerLinkActive
#standingInstructions="routerLinkActive"
[active]="standingInstructions.isActive"
>
{{ 'labels.inputs.Standing Instructions' | translate }}
</a>
}
<a
mat-tab-link
[routerLink]="['./nominees']"
routerLinkActive
#nominees="routerLinkActive"
[active]="nominees.isActive"
>
Nominees
</a>
@for (savingsDatatable of savingsDatatables; track savingsDatatable) {
<a
mat-tab-link
*mifosxHasPermission="'READ_' + savingsDatatable.registeredTableName"
[routerLink]="['./datatables', savingsDatatable.registeredTableName]"
routerLinkActive
#datatable="routerLinkActive"
[active]="datatable.isActive"
>
{{ savingsDatatable.registeredTableName }}
</a>
}
</nav>
<mat-tab-nav-panel #tabPanel>
}
</nav>
<mat-tab-nav-panel #tabPanel>
<div class="rd-body">
<router-outlet></router-outlet>
</mat-tab-nav-panel>
</mat-card-content>
</mat-card>
</div>
</mat-tab-nav-panel>
</div>
@@ -5,3 +5,386 @@
* 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/.
*/
$navy: #0d1b2a;
$blue: #1e88e5;
$blue-d: #1e40af;
$border: #e2e8f0;
.rd-view {
max-width: 1440px;
margin: 0 auto;
padding: 12px 20px 22px;
}
/* ─── Gradient hero header ──────────────────────────────────────────── */
.rd-hero {
position: relative;
display: grid;
grid-template-columns: minmax(0, 1fr) auto auto;
align-items: stretch;
gap: 14px;
padding: 12px 18px;
border-radius: 12px;
color: #fff;
background: linear-gradient(135deg, #1e3a8a 0%, #1e40af 45%, #4338ca 100%);
box-shadow: 0 6px 18px -10px rgb(15 23 42 / 35%);
overflow: hidden;
&::before {
content: '';
position: absolute;
inset: 0;
background: radial-gradient(circle at top right, rgb(255 255 255 / 12%) 0%, transparent 55%);
pointer-events: none;
}
&__left {
display: flex;
align-items: center;
gap: 12px;
min-width: 0;
position: relative;
z-index: 1;
}
&__avatar {
width: 40px;
height: 40px;
border-radius: 10px;
background: linear-gradient(135deg, rgb(255 255 255 / 22%) 0%, rgb(255 255 255 / 10%) 100%);
border: 1px solid rgb(255 255 255 / 28%);
display: flex;
align-items: center;
justify-content: center;
color: #fff;
font-size: 16px;
flex-shrink: 0;
box-shadow: 0 3px 10px -3px rgb(0 0 0 / 25%);
}
&__info {
display: flex;
flex-direction: column;
gap: 3px;
min-width: 0;
}
&__status-row {
display: flex;
align-items: center;
gap: 6px;
}
&__title {
margin: 0;
font-size: 15px;
font-weight: 800;
letter-spacing: 0.01em;
color: #fff;
line-height: 1.15;
::ng-deep mifosx-long-text {
color: #fff;
}
}
&__meta {
display: flex;
flex-wrap: wrap;
gap: 2px 18px;
margin-top: 1px;
}
&__meta-item {
display: inline-flex;
align-items: center;
gap: 6px;
font-size: 12px;
color: rgb(255 255 255 / 90%);
}
&__meta-label {
font-size: 10px;
text-transform: uppercase;
letter-spacing: 0.07em;
color: rgb(255 255 255 / 60%);
font-weight: 600;
}
&__meta-value {
font-weight: 600;
color: #fff;
}
&__kpis {
display: flex;
gap: 8px;
align-items: stretch;
position: relative;
z-index: 1;
}
&__actions {
display: flex;
align-items: center;
position: relative;
z-index: 1;
button {
width: 32px !important;
height: 32px !important;
min-width: 32px !important;
padding: 0 !important;
border-radius: 50% !important;
color: #fff !important;
background: rgb(255 255 255 / 10%) !important;
border: 1px solid rgb(255 255 255 / 22%) !important;
display: inline-flex !important;
align-items: center;
justify-content: center;
line-height: 1;
transition:
background 0.15s ease,
border-color 0.15s ease,
transform 0.12s ease;
::ng-deep fa-icon {
font-size: 14px;
line-height: 1;
display: inline-flex;
align-items: center;
justify-content: center;
}
::ng-deep .mat-mdc-button-persistent-ripple,
::ng-deep .mat-mdc-button-touch-target {
border-radius: 50% !important;
}
}
button:hover {
background: rgb(255 255 255 / 22%) !important;
border-color: rgb(255 255 255 / 38%) !important;
}
button:active {
transform: scale(0.94);
}
}
}
/* KPI tiles */
.rd-kpi {
display: flex;
flex-direction: column;
justify-content: center;
min-width: 110px;
padding: 7px 14px;
border-radius: 9px;
background: rgb(255 255 255 / 14%);
border: 1px solid rgb(255 255 255 / 22%);
backdrop-filter: blur(8px);
&__label {
font-size: 9.5px;
text-transform: uppercase;
letter-spacing: 0.07em;
font-weight: 700;
color: rgb(255 255 255 / 75%);
}
&__value {
margin-top: 2px;
font-size: 15px;
font-weight: 800;
color: #fff;
font-variant-numeric: tabular-nums;
letter-spacing: -0.01em;
}
&--deposit {
background: rgb(245 158 11 / 22%);
border-color: rgb(245 158 11 / 35%);
}
&--period {
background: rgb(139 92 246 / 22%);
border-color: rgb(139 92 246 / 35%);
}
}
/* Status pill */
.rd-status-pill {
display: inline-flex;
align-items: center;
gap: 5px;
height: 18px;
padding: 0 8px;
border-radius: 999px;
background: rgb(16 185 129 / 24%);
border: 1px solid rgb(16 185 129 / 40%);
font-size: 10px;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.06em;
color: #d1fae5;
&__dot {
width: 5px;
height: 5px;
border-radius: 50%;
background: #10b981;
box-shadow: 0 0 0 2px rgb(16 185 129 / 30%);
}
&.statussubmitted,
&.statuspending {
background: rgb(245 158 11 / 24%);
border-color: rgb(245 158 11 / 40%);
color: #fef3c7;
.rd-status-pill__dot {
background: #f59e0b;
}
}
&.statusclosed,
&.statusrejected,
&.statuswithdrawn,
&.statusprematureclosed {
background: rgb(239 68 68 / 24%);
border-color: rgb(239 68 68 / 40%);
color: #fee2e2;
.rd-status-pill__dot {
background: #ef4444;
}
}
}
/* ─── Pill-style tab navigation ─────────────────────────────────────── */
.rd-tabs {
margin-top: 18px;
background: #fff;
border: 1px solid $border;
border-radius: 14px;
padding: 6px;
box-shadow: 0 1px 3px rgb(15 23 42 / 4%);
overflow: hidden;
::ng-deep {
.mat-mdc-tab-link-container {
border-bottom: none !important;
}
.mat-mdc-tab-links {
gap: 4px;
flex-wrap: wrap;
}
.mdc-tab-indicator__content--underline,
.mat-mdc-tab-header-pagination-controls-enabled .mdc-tab-indicator__content--underline {
display: none !important;
}
}
}
.rd-tab {
display: inline-flex !important;
align-items: center;
gap: 8px;
height: 38px !important;
min-width: 0 !important;
padding: 0 16px !important;
border-radius: 10px !important;
font-family: Inter, 'DM Sans', Roboto, sans-serif !important;
font-size: 13px !important;
font-weight: 600 !important;
letter-spacing: 0.01em !important;
color: #64748b !important;
background: transparent !important;
transition: all 0.15s ease;
white-space: nowrap;
border: 1px solid transparent !important;
opacity: 1 !important;
&__ic {
font-size: 12px;
color: inherit;
}
&:hover:not(.mdc-tab--active) {
background: #f1f5f9 !important;
color: $navy !important;
}
&.mdc-tab--active,
&.mat-mdc-tab-link-active {
background: linear-gradient(135deg, $blue 0%, $blue-d 100%) !important;
color: #fff !important;
box-shadow: 0 2px 6px -1px rgb(30 64 175 / 35%);
border-color: $blue-d !important;
::ng-deep .mdc-tab__text-label,
::ng-deep .mat-mdc-tab-link-content,
::ng-deep span,
.rd-tab__ic {
color: #fff !important;
opacity: 1 !important;
}
}
}
.rd-body {
margin-top: 16px;
}
/* ─── Responsive ────────────────────────────────────────────────────── */
@media (width <= 1100px) {
.rd-hero {
grid-template-columns: 1fr;
&__kpis {
width: 100%;
flex-wrap: wrap;
}
&__actions {
position: absolute;
top: 18px;
right: 18px;
}
}
.rd-kpi {
flex: 1 1 auto;
min-width: 0;
}
}
@media (width <= 700px) {
.rd-view {
padding: 12px;
}
.rd-hero {
padding: 18px;
&__title {
font-size: 18px;
}
&__avatar {
width: 52px;
height: 52px;
font-size: 22px;
border-radius: 12px;
}
}
.rd-tab {
padding: 0 12px !important;
font-size: 12px !important;
}
}
@@ -23,17 +23,10 @@ import { DeleteDialogComponent } from 'app/shared/delete-dialog/delete-dialog.co
import { RecurringDepositConfirmationDialogComponent } from './custom-dialogs/recurring-deposit-confirmation-dialog/recurring-deposit-confirmation-dialog.component';
import { Currency } from 'app/shared/models/general.model';
import { TranslateService } from '@ngx-translate/core';
import {
MatCard,
MatCardHeader,
MatCardTitleGroup,
MatCardMdImage,
MatCardTitle,
MatCardContent
} from '@angular/material/card';
import { MatTooltip } from '@angular/material/tooltip';
import { NgClass, DecimalPipe, CurrencyPipe } from '@angular/common';
import { AccountNumberComponent } from '../../../shared/account-number/account-number.component';
import { LongTextComponent } from '../../../shared/long-text/long-text.component';
import { MatIconButton } from '@angular/material/button';
import { MatMenuTrigger, MatMenu, MatMenuItem } from '@angular/material/menu';
import { MatIcon } from '@angular/material/icon';
@@ -51,12 +44,9 @@ import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
styleUrls: ['./recurring-deposits-account-view.component.scss'],
imports: [
...STANDALONE_SHARED_IMPORTS,
MatCardHeader,
MatCardTitleGroup,
MatCardMdImage,
MatTooltip,
MatCardTitle,
NgClass,
LongTextComponent,
AccountNumberComponent,
MatIconButton,
MatMenuTrigger,
@@ -6,23 +6,19 @@
file, You can obtain one at http://mozilla.org/MPL/2.0/.
-->
<div class="tab-container mat-typography">
<div class="mat-elevation-z1 m-b-25">
<div class="layout-row align-start">
<div class="m-b-20">
<h3>{{ 'labels.heading.All Transactions' | translate }}</h3>
<div class="txn-tab">
<div class="txn-tab__card">
@if (checkStatus()) {
<div class="txn-tab__toolbar">
<mat-checkbox [formControl]="hideReversedParam" (click)="hideReversed()" class="txn-tab__check">
{{ 'labels.inputs.Hide Reversed' | translate }}
</mat-checkbox>
<mat-checkbox [formControl]="hideAccrualsParam" (click)="hideAccruals()" class="txn-tab__check">
{{ 'labels.inputs.Hide Accruals' | translate }}
</mat-checkbox>
<span class="txn-tab__spacer"></span>
</div>
@if (checkStatus()) {
<div class="action-button m-b-20 layout-row gap-20px">
<mat-checkbox [formControl]="hideReversedParam" (click)="hideReversed()" class="accruals">
{{ 'labels.inputs.Hide Reversed' | translate }}</mat-checkbox
>
<mat-checkbox [formControl]="hideAccrualsParam" (click)="hideAccruals()" class="accruals">
{{ 'labels.inputs.Hide Accruals' | translate }}</mat-checkbox
>
</div>
}
</div>
}
<table mat-table [dataSource]="dataSource">
<ng-container matColumnDef="row">
@@ -6,37 +6,95 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
@use 'assets/styles/helper';
$navy: #0d1b2a;
$slate: #475569;
$muted: #64748b;
$border: #e2e8f0;
$soft: #f8fafc;
$blue: #1e40af;
.tab-container {
padding: 1%;
margin: 1%;
h3 {
margin: 1% auto;
}
.action-button {
margin-left: auto;
}
table {
width: 100%;
.account-action-button {
min-width: 26px;
padding: 0 0 3px;
margin: 0 10%;
line-height: 25px;
.accruals {
padding-top: 1%;
}
}
}
.txn-tab {
width: 100%;
}
.select-row:hover {
cursor: pointer;
font-weight: 800;
.txn-tab__card {
background: #fff;
border: 1px solid $border;
border-radius: 14px;
box-shadow: 0 1px 3px rgb(15 23 42 / 4%);
overflow: hidden;
}
.txn-tab__toolbar {
display: flex;
align-items: center;
gap: 18px;
padding: 12px 18px;
background: $soft;
border-bottom: 1px solid $border;
flex-wrap: wrap;
}
.txn-tab__check {
font-size: 13px;
color: $slate;
font-weight: 500;
}
.txn-tab__spacer {
flex: 1;
}
table {
width: 100%;
}
::ng-deep .txn-tab__card {
.mat-mdc-header-row {
background: $soft;
}
.mat-mdc-header-cell {
font-size: 11.5px;
font-weight: 800;
color: $slate;
text-transform: uppercase;
letter-spacing: 0.06em;
padding: 12px 14px;
border-bottom: 1px solid $border;
}
.mat-mdc-cell {
font-size: 13px;
color: $navy;
padding: 11px 14px;
border-bottom: 1px solid #f1f5f9;
}
.mat-mdc-row:hover {
background: #f8fafc;
cursor: pointer;
}
.mat-column-debit,
.mat-column-credit,
.mat-column-balance {
text-align: right;
font-variant-numeric: tabular-nums;
}
.mat-column-actions {
text-align: center;
width: 80px;
}
.strike td {
text-decoration: line-through;
color: $muted;
}
.accrual td {
color: $muted;
font-style: italic;
}
}