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:
co-authored by
Claude Sonnet 4.6
parent
38c4a7d9e9
commit
547cfd581e
+95
-15
@@ -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 & 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>
|
||||
|
||||
|
||||
+60
-2
@@ -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
|
||||
|
||||
+135
-17
@@ -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 & 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>
|
||||
|
||||
|
||||
+60
-2
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user