feat: Nexora grouped action menu for Current Account, FD, and RD views

- Non-individual clients: hide Nomination Facility & Nominees in create flow,
  skip nominee validation on submit, hide Nominees tab in account view
- Current Account view: replace flat kebab menu with Nexora grouped menu
  (Transact / Restrict / Interest / Admin / Danger zone), scrollable body,
  pinned header, colour-coded icons; add kindOf/iconOf/hasKind helpers
- Fixed Deposit view: same grouped menu using shared cur-action-menu styles
- Recurring Deposit view: same grouped menu with permission directives
- main.scss: add cur-action-menu / cur-menu global styles with MDC flex fix
  for Material 20 (.mat-mdc-menu-item-text)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Jaanu423
2026-05-25 13:11:02 +05:30
co-authored by Claude Sonnet 4.6
parent 38c4a7d9e9
commit 547cfd581e
11 changed files with 838 additions and 78 deletions
@@ -60,6 +60,7 @@
<mifosx-current-account-currency-step
[currentAccountProductTemplate]="currentAccountProductTemplate"
[primaryClientId]="currentAccountTemplate?.clientId ?? null"
[isNonIndividualClient]="currentAccountDetailsStep.isNonIndividualClient"
></mifosx-current-account-currency-step>
}
@@ -143,6 +143,7 @@ export class CreateCurrentAccountComponent {
return;
}
if (
!this.currentAccountDetailsStep.isNonIndividualClient &&
this.currentAccountProductTemplate?.nominationFacility &&
(this.currentAccountCurrencyStep?.nominees ?? []).length === 0
) {
@@ -63,16 +63,18 @@
<mifosx-dormancy-alert-panel [productTemplate]="currentAccountProductTemplate"></mifosx-dormancy-alert-panel>
}
<div class="sac-alert">
<div class="sac-alert__content">
<h4>Nomination Facility</h4>
<p>Allow account holders to designate individuals to receive funds in the event of account holder's death.</p>
@if (!isNonIndividualClient) {
<div class="sac-alert">
<div class="sac-alert__content">
<h4>Nomination Facility</h4>
<p>Allow account holders to designate individuals to receive funds in the event of account holder's death.</p>
</div>
<span class="sac-badge" [class.sac-badge--yes]="currentAccountProductTemplate?.nominationFacility">
{{ currentAccountProductTemplate?.nominationFacility ? 'Yes' : 'No' }}
</span>
</div>
<span class="sac-badge" [class.sac-badge--yes]="currentAccountProductTemplate?.nominationFacility">
{{ currentAccountProductTemplate?.nominationFacility ? 'Yes' : 'No' }}
</span>
</div>
@if (currentAccountProductTemplate?.nominationFacility) {
}
@if (!isNonIndividualClient && currentAccountProductTemplate?.nominationFacility) {
<div class="nom-section">
<div class="nom-header">
<div class="nom-header__left"><i class="fas fa-user-friends"></i><span>Nominees</span></div>
@@ -28,6 +28,7 @@ import { FormsModule } from '@angular/forms';
export class CurrentAccountCurrencyStepComponent implements OnChanges {
@Input() currentAccountProductTemplate: any;
@Input() primaryClientId: number | null = null;
@Input() isNonIndividualClient = false;
@ViewChild(NomineeFormComponent) nomineeFormComp: NomineeFormComponent;
nominees: any[] = [];
@@ -91,28 +91,183 @@
</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>
}
<mat-menu #accountMenu="matMenu" class="cur-action-menu" xPosition="before">
<div class="cur-menu" (click)="$event.stopPropagation()">
<header class="cur-menu__head">
<fa-icon icon="bolt" class="cur-menu__head-ico"></fa-icon>
<div>
<div class="cur-menu__head-title">Account Actions</div>
<div class="cur-menu__head-sub">Manage deposits, blocks, interest &amp; more</div>
</div>
</header>
@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>
<div class="cur-menu__body">
<!-- Transact: singleButtons + options of kind 'transact' -->
@if (hasKind('transact')) {
<div class="cur-menu__group">
<div class="cur-menu__group-title">Transact</div>
@for (item of buttonConfig.singleButtons; track item.name) {
@if (kindOf(item.name) === 'transact') {
<button
mat-menu-item
class="cur-menu__item cur-menu__item--transact"
*mifosxHasPermission="item.taskPermissionName"
(click)="doAction(item.name)"
>
<span class="cur-menu__ico"><fa-icon [icon]="iconOf(item.name, item.icon)"></fa-icon></span>
<span class="cur-menu__label">{{ 'labels.menus.' + item.name | translate }}</span>
</button>
}
}
@for (option of buttonConfig.options; track option.name) {
@if (kindOf(option.name) === 'transact') {
<button
mat-menu-item
class="cur-menu__item cur-menu__item--transact"
*mifosxHasPermission="option.taskPermissionName"
(click)="doAction(option.name)"
>
<span class="cur-menu__ico"><fa-icon [icon]="iconOf(option.name)"></fa-icon></span>
<span class="cur-menu__label">{{ 'labels.menus.' + option.name | translate }}</span>
</button>
}
}
</div>
}
</mat-menu>
}
<!-- Restrict: singleButtons + options of kind 'block' -->
@if (hasKind('block')) {
<div class="cur-menu__group">
<div class="cur-menu__group-title">Restrict</div>
@for (item of buttonConfig.singleButtons; track item.name) {
@if (kindOf(item.name) === 'block') {
<button
mat-menu-item
class="cur-menu__item cur-menu__item--block"
*mifosxHasPermission="item.taskPermissionName"
(click)="doAction(item.name)"
>
<span class="cur-menu__ico"><fa-icon [icon]="iconOf(item.name, item.icon)"></fa-icon></span>
<span class="cur-menu__label">{{ 'labels.menus.' + item.name | translate }}</span>
</button>
}
}
@for (option of buttonConfig.options; track option.name) {
@if (kindOf(option.name) === 'block') {
<button
mat-menu-item
class="cur-menu__item cur-menu__item--block"
*mifosxHasPermission="option.taskPermissionName"
(click)="doAction(option.name)"
>
<span class="cur-menu__ico"><fa-icon [icon]="iconOf(option.name)"></fa-icon></span>
<span class="cur-menu__label">{{ 'labels.menus.' + option.name | translate }}</span>
</button>
}
}
</div>
}
<!-- Interest: singleButtons + options of kind 'interest' -->
@if (hasKind('interest')) {
<div class="cur-menu__group">
<div class="cur-menu__group-title">Interest</div>
@for (item of buttonConfig.singleButtons; track item.name) {
@if (kindOf(item.name) === 'interest') {
<button
mat-menu-item
class="cur-menu__item cur-menu__item--interest"
*mifosxHasPermission="item.taskPermissionName"
(click)="doAction(item.name)"
>
<span class="cur-menu__ico"><fa-icon [icon]="iconOf(item.name, item.icon)"></fa-icon></span>
<span class="cur-menu__label">{{ 'labels.menus.' + item.name | translate }}</span>
</button>
}
}
@for (option of buttonConfig.options; track option.name) {
@if (kindOf(option.name) === 'interest') {
<button
mat-menu-item
class="cur-menu__item cur-menu__item--interest"
*mifosxHasPermission="option.taskPermissionName"
(click)="doAction(option.name)"
>
<span class="cur-menu__ico"><fa-icon [icon]="iconOf(option.name)"></fa-icon></span>
<span class="cur-menu__label">{{ 'labels.menus.' + option.name | translate }}</span>
</button>
}
}
</div>
}
<!-- Admin: singleButtons + options of kind 'admin', hidden only when blocked -->
@if (hasKind('admin') && !currentAccountData.subStatus.block) {
<div class="cur-menu__group">
<div class="cur-menu__group-title">Admin</div>
@for (item of buttonConfig.singleButtons; track item.name) {
@if (kindOf(item.name) === 'admin') {
<button
mat-menu-item
class="cur-menu__item cur-menu__item--admin"
*mifosxHasPermission="item.taskPermissionName"
(click)="doAction(item.name)"
>
<span class="cur-menu__ico"><fa-icon [icon]="iconOf(item.name, item.icon)"></fa-icon></span>
<span class="cur-menu__label">{{ 'labels.menus.' + item.name | translate }}</span>
</button>
}
}
@for (option of buttonConfig.options; track option.name) {
@if (kindOf(option.name) === 'admin') {
<button
mat-menu-item
class="cur-menu__item cur-menu__item--admin"
*mifosxHasPermission="option.taskPermissionName"
(click)="doAction(option.name)"
>
<span class="cur-menu__ico"><fa-icon [icon]="iconOf(option.name)"></fa-icon></span>
<span class="cur-menu__label">{{ 'labels.menus.' + option.name | translate }}</span>
</button>
}
}
</div>
}
<!-- Danger zone: singleButtons + options of kind 'danger' -->
@if (hasKind('danger')) {
<div class="cur-menu__group cur-menu__group--danger">
<div class="cur-menu__group-title">Danger zone</div>
@for (item of buttonConfig.singleButtons; track item.name) {
@if (kindOf(item.name) === 'danger') {
<button
mat-menu-item
class="cur-menu__item cur-menu__item--danger"
*mifosxHasPermission="item.taskPermissionName"
(click)="doAction(item.name)"
>
<span class="cur-menu__ico"><fa-icon [icon]="iconOf(item.name, item.icon)"></fa-icon></span>
<span class="cur-menu__label">{{ 'labels.menus.' + item.name | translate }}</span>
</button>
}
}
@for (option of buttonConfig.options; track option.name) {
@if (kindOf(option.name) === 'danger') {
<button
mat-menu-item
class="cur-menu__item cur-menu__item--danger"
*mifosxHasPermission="option.taskPermissionName"
(click)="doAction(option.name)"
>
<span class="cur-menu__ico"><fa-icon [icon]="iconOf(option.name)"></fa-icon></span>
<span class="cur-menu__label">{{ 'labels.menus.' + option.name | translate }}</span>
</button>
}
}
</div>
}
</div>
</div>
</mat-menu>
</header>
@@ -201,17 +356,19 @@
</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>
@if (!isNonIndividualClient) {
<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
@@ -20,6 +20,7 @@ import { ToggleWithholdTaxDialogComponent } from './custom-dialogs/toggle-withho
/** Custom Buttons Configuration */
import { CurrentAccountButtonsConfiguration } from './current-accounts-buttons.config';
import { CurrentAccountsService } from '../current-accounts.service';
import { ClientsService } from 'app/clients/clients.service';
import { ConfirmationDialogComponent } from 'app/shared/confirmation-dialog/confirmation-dialog.component';
import { Currency } from 'app/shared/models/general.model';
import { TranslateService } from '@ngx-translate/core';
@@ -40,7 +41,6 @@ import { LongTextComponent } from '../../shared/long-text/long-text.component';
import { AccountNumberComponent } from '../../shared/account-number/account-number.component';
import { MatIconButton } from '@angular/material/button';
import { MatMenuTrigger, MatMenu, MatMenuItem } from '@angular/material/menu';
import { MatIcon } from '@angular/material/icon';
import { FaIconComponent } from '@fortawesome/angular-fontawesome';
import { MatTabNav, MatTabLink, MatTabNavPanel } from '@angular/material/tabs';
import { StatusLookupPipe } from '../../pipes/status-lookup.pipe';
@@ -65,7 +65,6 @@ import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
AccountNumberComponent,
MatIconButton,
MatMenuTrigger,
MatIcon,
FaIconComponent,
MatMenu,
MatMenuItem,
@@ -82,6 +81,7 @@ export class CurrentAccountViewComponent implements OnInit {
private route = inject(ActivatedRoute);
private router = inject(Router);
private currentAccountsService = inject(CurrentAccountsService);
private clientsService = inject(ClientsService);
private translateService = inject(TranslateService);
dialog = inject(MatDialog);
@@ -95,8 +95,87 @@ export class CurrentAccountViewComponent implements OnInit {
entityType: string;
isActive = false;
isNonIndividualClient = false;
currency: Currency;
private readonly ACTION_KIND: Record<string, 'transact' | 'block' | 'interest' | 'admin' | 'danger'> = {
Deposit: 'transact',
Withdrawal: 'transact',
'Hold Amount': 'transact',
'Transfer Funds': 'transact',
'Interbank Transfer': 'transact',
'Block Deposit': 'block',
'Unblock Deposit': 'block',
'Block Withdrawal': 'block',
'Unblock Withdrawal': 'block',
'Block Account': 'block',
'Unblock Account': 'block',
'Calculate Interest': 'interest',
'Post Interest': 'interest',
'Post Interest As On': 'interest',
'Enable Withhold Tax': 'interest',
'Disable Withhold Tax': 'interest',
'Add Charge': 'admin',
'Apply Annual Fees': 'admin',
'Modify Application': 'admin',
'Assign Staff': 'admin',
'Unassign Staff': 'admin',
Approve: 'admin',
Activate: 'admin',
'Undo Approval': 'admin',
Close: 'danger',
Reject: 'danger',
Delete: 'danger',
'Withdrawn by Client': 'danger'
};
private readonly ACTION_ICON: Record<string, string> = {
Deposit: 'arrow-down',
Withdrawal: 'arrow-up',
'Hold Amount': 'pause',
'Transfer Funds': 'exchange-alt',
'Interbank Transfer': 'random',
'Block Deposit': 'lock',
'Unblock Deposit': 'lock-open',
'Block Withdrawal': 'lock',
'Unblock Withdrawal': 'lock-open',
'Block Account': 'lock',
'Unblock Account': 'lock-open',
'Calculate Interest': 'calculator',
'Post Interest': 'percentage',
'Post Interest As On': 'calendar-day',
'Enable Withhold Tax': 'percentage',
'Disable Withhold Tax': 'percentage',
'Add Charge': 'plus-circle',
'Apply Annual Fees': 'receipt',
'Modify Application': 'edit',
'Assign Staff': 'user-plus',
'Unassign Staff': 'user-slash',
Approve: 'check-circle',
Activate: 'check',
'Undo Approval': 'undo',
Close: 'times-circle',
Reject: 'ban',
Delete: 'trash',
'Withdrawn by Client': 'user-slash'
};
kindOf(name: string): 'transact' | 'block' | 'interest' | 'admin' | 'danger' {
return this.ACTION_KIND[name] ?? 'admin';
}
iconOf(name: string, fallback?: string): string {
return this.ACTION_ICON[name] ?? fallback ?? 'circle';
}
hasKind(kind: 'transact' | 'block' | 'interest' | 'admin' | 'danger'): boolean {
const all = [
...(this.buttonConfig?.singleButtons ?? []),
...(this.buttonConfig?.options ?? [])
];
return all.some((item) => this.kindOf(item.name) === kind);
}
/**
* Fetches current account data from `resolve`
* @param {ActivatedRoute} route Activated Route
@@ -120,6 +199,11 @@ export class CurrentAccountViewComponent implements OnInit {
ngOnInit() {
this.setConditionalButtons();
if (this.currentAccountData.clientId) {
this.clientsService.getClientData(String(this.currentAccountData.clientId)).subscribe((client: any) => {
this.isNonIndividualClient = client?.legalForm?.id === 2;
});
}
}
/**
@@ -81,22 +81,102 @@
</button>
</div>
<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>
}
@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 #accountMenu="matMenu" class="cur-action-menu" xPosition="before">
<div class="cur-menu" (click)="$event.stopPropagation()">
<header class="cur-menu__head">
<fa-icon icon="bolt" class="cur-menu__head-ico"></fa-icon>
<div>
<div class="cur-menu__head-title">Account Actions</div>
<div class="cur-menu__head-sub">Manage deposits, interest &amp; more</div>
</div>
</header>
<div class="cur-menu__body">
@if (hasKind('transact')) {
<div class="cur-menu__group">
<div class="cur-menu__group-title">Transact</div>
@for (item of buttonConfig.singleButtons; track item.name) {
@if (kindOf(item.name) === 'transact') {
<button mat-menu-item class="cur-menu__item cur-menu__item--transact" (click)="doAction(item.name)">
<span class="cur-menu__ico"><fa-icon [icon]="iconOf(item.name, item.icon)"></fa-icon></span>
<span class="cur-menu__label">{{ item.name | translateKey: 'menus' }}</span>
</button>
}
}
@for (option of buttonConfig.options; track option.name) {
@if (kindOf(option.name) === 'transact') {
<button mat-menu-item class="cur-menu__item cur-menu__item--transact" (click)="doAction(option.name)">
<span class="cur-menu__ico"><fa-icon [icon]="iconOf(option.name)"></fa-icon></span>
<span class="cur-menu__label">{{ option.name | translateKey: 'menus' }}</span>
</button>
}
}
</div>
}
</mat-menu>
}
@if (hasKind('interest')) {
<div class="cur-menu__group">
<div class="cur-menu__group-title">Interest</div>
@for (item of buttonConfig.singleButtons; track item.name) {
@if (kindOf(item.name) === 'interest') {
<button mat-menu-item class="cur-menu__item cur-menu__item--interest" (click)="doAction(item.name)">
<span class="cur-menu__ico"><fa-icon [icon]="iconOf(item.name, item.icon)"></fa-icon></span>
<span class="cur-menu__label">{{ item.name | translateKey: 'menus' }}</span>
</button>
}
}
@for (option of buttonConfig.options; track option.name) {
@if (kindOf(option.name) === 'interest') {
<button mat-menu-item class="cur-menu__item cur-menu__item--interest" (click)="doAction(option.name)">
<span class="cur-menu__ico"><fa-icon [icon]="iconOf(option.name)"></fa-icon></span>
<span class="cur-menu__label">{{ option.name | translateKey: 'menus' }}</span>
</button>
}
}
</div>
}
@if (hasKind('admin')) {
<div class="cur-menu__group">
<div class="cur-menu__group-title">Admin</div>
@for (item of buttonConfig.singleButtons; track item.name) {
@if (kindOf(item.name) === 'admin') {
<button mat-menu-item class="cur-menu__item cur-menu__item--admin" (click)="doAction(item.name)">
<span class="cur-menu__ico"><fa-icon [icon]="iconOf(item.name, item.icon)"></fa-icon></span>
<span class="cur-menu__label">{{ item.name | translateKey: 'menus' }}</span>
</button>
}
}
@for (option of buttonConfig.options; track option.name) {
@if (kindOf(option.name) === 'admin') {
<button mat-menu-item class="cur-menu__item cur-menu__item--admin" (click)="doAction(option.name)">
<span class="cur-menu__ico"><fa-icon [icon]="iconOf(option.name)"></fa-icon></span>
<span class="cur-menu__label">{{ option.name | translateKey: 'menus' }}</span>
</button>
}
}
</div>
}
@if (hasKind('danger')) {
<div class="cur-menu__group cur-menu__group--danger">
<div class="cur-menu__group-title">Danger zone</div>
@for (item of buttonConfig.singleButtons; track item.name) {
@if (kindOf(item.name) === 'danger') {
<button mat-menu-item class="cur-menu__item cur-menu__item--danger" (click)="doAction(item.name)">
<span class="cur-menu__ico"><fa-icon [icon]="iconOf(item.name, item.icon)"></fa-icon></span>
<span class="cur-menu__label">{{ item.name | translateKey: 'menus' }}</span>
</button>
}
}
@for (option of buttonConfig.options; track option.name) {
@if (kindOf(option.name) === 'danger') {
<button mat-menu-item class="cur-menu__item cur-menu__item--danger" (click)="doAction(option.name)">
<span class="cur-menu__ico"><fa-icon [icon]="iconOf(option.name)"></fa-icon></span>
<span class="cur-menu__label">{{ option.name | translateKey: 'menus' }}</span>
</button>
}
}
</div>
}
</div>
</div>
</mat-menu>
</header>
@@ -30,7 +30,6 @@ import { AccountNumberComponent } from '../../../shared/account-number/account-n
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';
import { FaIconComponent } from '@fortawesome/angular-fontawesome';
import { MatTabNav, MatTabLink, MatTabNavPanel } from '@angular/material/tabs';
import { StatusLookupPipe } from '../../../pipes/status-lookup.pipe';
@@ -51,7 +50,6 @@ import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
AccountNumberComponent,
MatIconButton,
MatMenuTrigger,
MatIcon,
FaIconComponent,
MatMenu,
MatMenuItem,
@@ -82,6 +80,66 @@ export class FixedDepositAccountViewComponent implements OnInit {
currency: Currency;
showTransactions = false;
private readonly ACTION_KIND: Record<string, 'transact' | 'block' | 'interest' | 'admin' | 'danger'> = {
Deposit: 'transact',
Withdrawal: 'transact',
'Transfer Funds': 'transact',
'Calculate Interest': 'interest',
'Post Interest': 'interest',
'Enable Withhold Tax': 'interest',
'Disable Withhold Tax': 'interest',
'Premature Close': 'danger',
'Undo Activation': 'admin',
Close: 'danger',
'Modify Application': 'admin',
Approve: 'admin',
'Undo Approval': 'admin',
Activate: 'admin',
'Add Charge': 'admin',
'Apply Annual Fees': 'admin',
Reject: 'danger',
'Withdrawn by Client': 'danger',
Delete: 'danger'
};
private readonly ACTION_ICON: Record<string, string> = {
Deposit: 'arrow-down',
Withdrawal: 'arrow-up',
'Transfer Funds': 'exchange-alt',
'Calculate Interest': 'calculator',
'Post Interest': 'percentage',
'Enable Withhold Tax': 'percentage',
'Disable Withhold Tax': 'percentage',
'Premature Close': 'times-circle',
'Undo Activation': 'undo',
Close: 'times-circle',
'Modify Application': 'edit',
Approve: 'check-circle',
'Undo Approval': 'undo',
Activate: 'check',
'Add Charge': 'plus-circle',
'Apply Annual Fees': 'receipt',
Reject: 'ban',
'Withdrawn by Client': 'user-slash',
Delete: 'trash'
};
kindOf(name: string): 'transact' | 'block' | 'interest' | 'admin' | 'danger' {
return this.ACTION_KIND[name] ?? 'admin';
}
iconOf(name: string, fallback?: string): string {
return this.ACTION_ICON[name] ?? fallback ?? 'circle';
}
hasKind(kind: 'transact' | 'block' | 'interest' | 'admin' | 'danger'): boolean {
const all = [
...(this.buttonConfig?.singleButtons ?? []),
...(this.buttonConfig?.options ?? [])
];
return all.some((item) => this.kindOf(item.name) === kind);
}
/**
* Fetches fixed deposits account data from `resolve`
* @param {ActivatedRoute} route Activated Route
@@ -89,24 +89,142 @@
</button>
</div>
<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 #accountMenu="matMenu" class="cur-action-menu" xPosition="before">
<div class="cur-menu" (click)="$event.stopPropagation()">
<header class="cur-menu__head">
<fa-icon icon="bolt" class="cur-menu__head-ico"></fa-icon>
<div>
<div class="cur-menu__head-title">Account Actions</div>
<div class="cur-menu__head-sub">Manage deposits, interest &amp; more</div>
</div>
</header>
<div class="cur-menu__body">
@if (hasKind('transact')) {
<div class="cur-menu__group">
<div class="cur-menu__group-title">Transact</div>
@for (item of buttonConfig.singleButtons; track item.name) {
@if (kindOf(item.name) === 'transact') {
<button
mat-menu-item
class="cur-menu__item cur-menu__item--transact"
*mifosxHasPermission="item.taskPermissionName"
(click)="doAction(item.name)"
>
<span class="cur-menu__ico"><fa-icon [icon]="iconOf(item.name, item.icon)"></fa-icon></span>
<span class="cur-menu__label">{{ 'labels.menus.' + item.name | translate }}</span>
</button>
}
}
@for (option of buttonConfig.options; track option.name) {
@if (kindOf(option.name) === 'transact') {
<button
mat-menu-item
class="cur-menu__item cur-menu__item--transact"
*mifosxHasPermission="option.taskPermissionName"
(click)="doAction(option.name)"
>
<span class="cur-menu__ico"><fa-icon [icon]="iconOf(option.name)"></fa-icon></span>
<span class="cur-menu__label">{{ 'labels.menus.' + option.name | translate }}</span>
</button>
}
}
</div>
}
</mat-menu>
}
@if (hasKind('interest')) {
<div class="cur-menu__group">
<div class="cur-menu__group-title">Interest</div>
@for (item of buttonConfig.singleButtons; track item.name) {
@if (kindOf(item.name) === 'interest') {
<button
mat-menu-item
class="cur-menu__item cur-menu__item--interest"
*mifosxHasPermission="item.taskPermissionName"
(click)="doAction(item.name)"
>
<span class="cur-menu__ico"><fa-icon [icon]="iconOf(item.name, item.icon)"></fa-icon></span>
<span class="cur-menu__label">{{ 'labels.menus.' + item.name | translate }}</span>
</button>
}
}
@for (option of buttonConfig.options; track option.name) {
@if (kindOf(option.name) === 'interest') {
<button
mat-menu-item
class="cur-menu__item cur-menu__item--interest"
*mifosxHasPermission="option.taskPermissionName"
(click)="doAction(option.name)"
>
<span class="cur-menu__ico"><fa-icon [icon]="iconOf(option.name)"></fa-icon></span>
<span class="cur-menu__label">{{ 'labels.menus.' + option.name | translate }}</span>
</button>
}
}
</div>
}
@if (hasKind('admin')) {
<div class="cur-menu__group">
<div class="cur-menu__group-title">Admin</div>
@for (item of buttonConfig.singleButtons; track item.name) {
@if (kindOf(item.name) === 'admin') {
<button
mat-menu-item
class="cur-menu__item cur-menu__item--admin"
*mifosxHasPermission="item.taskPermissionName"
(click)="doAction(item.name)"
>
<span class="cur-menu__ico"><fa-icon [icon]="iconOf(item.name, item.icon)"></fa-icon></span>
<span class="cur-menu__label">{{ 'labels.menus.' + item.name | translate }}</span>
</button>
}
}
@for (option of buttonConfig.options; track option.name) {
@if (kindOf(option.name) === 'admin') {
<button
mat-menu-item
class="cur-menu__item cur-menu__item--admin"
*mifosxHasPermission="option.taskPermissionName"
(click)="doAction(option.name)"
>
<span class="cur-menu__ico"><fa-icon [icon]="iconOf(option.name)"></fa-icon></span>
<span class="cur-menu__label">{{ 'labels.menus.' + option.name | translate }}</span>
</button>
}
}
</div>
}
@if (hasKind('danger')) {
<div class="cur-menu__group cur-menu__group--danger">
<div class="cur-menu__group-title">Danger zone</div>
@for (item of buttonConfig.singleButtons; track item.name) {
@if (kindOf(item.name) === 'danger') {
<button
mat-menu-item
class="cur-menu__item cur-menu__item--danger"
*mifosxHasPermission="item.taskPermissionName"
(click)="doAction(item.name)"
>
<span class="cur-menu__ico"><fa-icon [icon]="iconOf(item.name, item.icon)"></fa-icon></span>
<span class="cur-menu__label">{{ 'labels.menus.' + item.name | translate }}</span>
</button>
}
}
@for (option of buttonConfig.options; track option.name) {
@if (kindOf(option.name) === 'danger') {
<button
mat-menu-item
class="cur-menu__item cur-menu__item--danger"
*mifosxHasPermission="option.taskPermissionName"
(click)="doAction(option.name)"
>
<span class="cur-menu__ico"><fa-icon [icon]="iconOf(option.name)"></fa-icon></span>
<span class="cur-menu__label">{{ 'labels.menus.' + option.name | translate }}</span>
</button>
}
}
</div>
}
</div>
</div>
</mat-menu>
</header>
@@ -29,7 +29,6 @@ import { AccountNumberComponent } from '../../../shared/account-number/account-n
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';
import { FaIconComponent } from '@fortawesome/angular-fontawesome';
import { MatTabNav, MatTabLink, MatTabNavPanel } from '@angular/material/tabs';
import { StatusLookupPipe } from '../../../pipes/status-lookup.pipe';
@@ -50,7 +49,6 @@ import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
AccountNumberComponent,
MatIconButton,
MatMenuTrigger,
MatIcon,
FaIconComponent,
MatMenu,
MatMenuItem,
@@ -85,6 +83,66 @@ export class RecurringDepositsAccountViewComponent implements OnInit {
entityType: string;
currency: Currency;
showTransactions = false;
private readonly ACTION_KIND: Record<string, 'transact' | 'block' | 'interest' | 'admin' | 'danger'> = {
Deposit: 'transact',
Withdrawal: 'transact',
'Transfer Funds': 'transact',
'Calculate Interest': 'interest',
'Post Interest': 'interest',
'Enable Withhold Tax': 'interest',
'Disable Withhold Tax': 'interest',
'Premature Close': 'danger',
'Undo Activation': 'admin',
Close: 'danger',
'Modify Application': 'admin',
Approve: 'admin',
'Undo Approval': 'admin',
Activate: 'admin',
'Add Charge': 'admin',
'Apply Annual Fees': 'admin',
Reject: 'danger',
'Withdrawn by Client': 'danger',
Delete: 'danger'
};
private readonly ACTION_ICON: Record<string, string> = {
Deposit: 'arrow-down',
Withdrawal: 'arrow-up',
'Transfer Funds': 'exchange-alt',
'Calculate Interest': 'calculator',
'Post Interest': 'percentage',
'Enable Withhold Tax': 'percentage',
'Disable Withhold Tax': 'percentage',
'Premature Close': 'times-circle',
'Undo Activation': 'undo',
Close: 'times-circle',
'Modify Application': 'edit',
Approve: 'check-circle',
'Undo Approval': 'undo',
Activate: 'check',
'Add Charge': 'plus-circle',
'Apply Annual Fees': 'receipt',
Reject: 'ban',
'Withdrawn by Client': 'user-slash',
Delete: 'trash'
};
kindOf(name: string): 'transact' | 'block' | 'interest' | 'admin' | 'danger' {
return this.ACTION_KIND[name] ?? 'admin';
}
iconOf(name: string, fallback?: string): string {
return this.ACTION_ICON[name] ?? fallback ?? 'circle';
}
hasKind(kind: 'transact' | 'block' | 'interest' | 'admin' | 'danger'): boolean {
const all = [
...(this.buttonConfig?.singleButtons ?? []),
...(this.buttonConfig?.options ?? [])
];
return all.some((item) => this.kindOf(item.name) === kind);
}
/**
* Fetches recurringDeposits account data from `resolve`
* @param {ActivatedRoute} route Activated Route