feat(digital+toolbar): NEFT/RTGS module, AI menu, polished header icons

- Add /digital/neft-rtgs (entry) and /digital/neft-rtgs-report (analytics):
  KPIs, tabbed views (Trends, Bank-wise, Date-wise, Pending/Pass/Return,
  Branch/Mobile, User-wise), filterable transaction table with CSV export,
  and a non-dismissible Initiate dialog launched from a floating action.
- Make BANL verification mandatory before NEFT/RTGS submit; auto-fill and
  lock beneficiary name from the lookup response, invalidate match on
  A/C or IFSC edit, demo fallback for unknown accounts.
- Add Digital menu links (NEFT/RTGS + Report) in shell top-nav.
- Replace toolbar theme toggle with AI / Intelligence Modules mega menu:
  4 colour-coded columns covering Lending & Risk, Investment & Profit,
  Compliance & Advisory, Governance (9 modules total). Wire RBI
  Notifications Monitoring to /rbi-circulars.
- Unify notifications, settings, AI, and profile toolbar buttons into a
  26px ring style; colourful gradient bell SVG for notifications,
  custom gear+petals AI mark, online dot on the avatar.
- Trim ISO 27001 chip in the top bar.
- Add MPL header to generated .env.ts and its template in version.js.
- Bundles other pending working-tree changes (clients family/nominee
  tab tweaks, auth/2FA fixes, translations) into the same push.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Aditya
2026-05-15 11:50:26 +05:30
co-authored by Claude Opus 4.7
parent 6714897688
commit 595054b0b8
35 changed files with 4409 additions and 194 deletions
+6 -1
View File
@@ -947,7 +947,12 @@
</mat-menu>
<mat-menu #digitalMenu="matMenu" [overlapTrigger]="false" class="nav-dropdown">
<button mat-menu-item tabindex="0"><fa-icon icon="exchange" class="menu-item-icon"></fa-icon> NEFT / RTGS</button>
<button mat-menu-item routerLink="/digital/neft-rtgs" tabindex="0">
<fa-icon icon="exchange" class="menu-item-icon"></fa-icon> NEFT / RTGS
</button>
<button mat-menu-item routerLink="/digital/neft-rtgs-report" tabindex="0">
<fa-icon icon="chart-line" class="menu-item-icon"></fa-icon> NEFT / RTGS Report
</button>
<button mat-menu-item tabindex="0"><fa-icon icon="bolt" class="menu-item-icon"></fa-icon> IMPS / BANL</button>
<button mat-menu-item tabindex="0"><fa-icon icon="mobile-alt" class="menu-item-icon"></fa-icon> UPI</button>
<button mat-menu-item tabindex="0">
@@ -32,7 +32,7 @@
<i class="fas fa-lock t-chip-icon t-chip-icon--green"></i>SSL/TLS&nbsp;<strong>256-bit</strong>
</div>
<div class="t-chip t-chip--secure">
<i class="fas fa-certificate t-chip-icon t-chip-icon--blue"></i>ISO 27001&nbsp;<strong>Certified</strong>
<i class="fas fa-certificate t-chip-icon t-chip-icon--blue"></i>ISO&nbsp;<strong>27001</strong>
</div>
<div class="t-chip t-chip--timer">
<i class="fas fa-clock t-chip-icon t-chip-icon--amber"></i>Session&nbsp;<strong>{{ sessionTimer }}</strong>
@@ -45,26 +45,206 @@
<mifosx-notifications-tray #notificationsTray class="hide-lt-md" tabindex="0"></mifosx-notifications-tray>
</div>
<div class="t-action-btn" [routerLink]="['/settings']" matTooltip="{{ 'tooltips.Settings' | translate }}">
<div
class="t-action-btn"
[routerLink]="['/settings']"
matTooltip="{{ 'tooltips.Settings' | translate }}"
tabindex="0"
>
<fa-icon icon="cog"></fa-icon>
</div>
<div #themeToggle class="t-action-btn">
<mifosx-theme-toggle tabindex="0"></mifosx-theme-toggle>
<div
class="t-action-btn t-action-btn--ai"
[matMenuTriggerFor]="aiMenu"
matTooltip="AI / Intelligence Modules"
tabindex="0"
>
<!-- Custom AI mark: teal petals + blue cog + AI label -->
<svg class="t-ai-svg" viewBox="0 0 64 64" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
<!-- Petals (4 ellipses rotated to form a 4-leaf flower) -->
<g class="t-ai-petals" fill="#5eccba" fill-opacity="0.92">
<ellipse cx="32" cy="32" rx="22" ry="11" transform="rotate(0 32 32)" />
<ellipse cx="32" cy="32" rx="22" ry="11" transform="rotate(45 32 32)" />
<ellipse cx="32" cy="32" rx="22" ry="11" transform="rotate(90 32 32)" />
<ellipse cx="32" cy="32" rx="22" ry="11" transform="rotate(135 32 32)" />
</g>
<!-- Cog: 8-tooth gear shape via path -->
<path
class="t-ai-cog"
fill="#1e88e5"
d="M32 12.5l3.4 1.6 2.6-2.6 3 2 .2 3.7 3.6 1 1.9 3.2-2 3.1 1.9 3.2-1.9 3.2 2 3.1-1.9 3.2-3.6 1-.2 3.7-3 2-2.6-2.6-3.4 1.6-3.4-1.6-2.6 2.6-3-2-.2-3.7-3.6-1-1.9-3.2 2-3.1-1.9-3.2 1.9-3.2-2-3.1 1.9-3.2 3.6-1 .2-3.7 3-2 2.6 2.6L32 12.5z"
/>
<!-- Inner circle (AI label background) -->
<circle cx="32" cy="32" r="9" fill="#bfe2ff" />
<!-- AI text -->
<text
x="32"
y="36"
text-anchor="middle"
font-family="'Inter', 'DM Sans', system-ui, sans-serif"
font-size="11"
font-weight="800"
fill="#0b1e3d"
letter-spacing="0.3"
>
AI
</text>
</svg>
<span class="t-action-dot"></span>
</div>
<div class="t-divider"></div>
<div #appMenu class="t-action-btn t-user-btn" [matMenuTriggerFor]="applicationMenu" matTooltip="Account">
<div
#appMenu
class="t-action-btn t-user-btn"
[matMenuTriggerFor]="applicationMenu"
matTooltip="Account"
tabindex="0"
>
<img
src="assets/images/user_placeholder.png"
[alt]="'labels.headings.User Profile' | translate"
class="t-user-img"
/>
<span class="t-user-status"></span>
</div>
</div>
</div>
<!-- AI / Intelligence Modules Menu -->
<mat-menu #aiMenu="matMenu" [overlapTrigger]="false" class="ai-menu" xPosition="before">
<div class="ai-menu-header">
<svg class="ai-menu-header-svg" viewBox="0 0 64 64" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
<g fill="#5eccba" fill-opacity="0.92">
<ellipse cx="32" cy="32" rx="22" ry="11" />
<ellipse cx="32" cy="32" rx="22" ry="11" transform="rotate(45 32 32)" />
<ellipse cx="32" cy="32" rx="22" ry="11" transform="rotate(90 32 32)" />
<ellipse cx="32" cy="32" rx="22" ry="11" transform="rotate(135 32 32)" />
</g>
<path
fill="#1e88e5"
d="M32 12.5l3.4 1.6 2.6-2.6 3 2 .2 3.7 3.6 1 1.9 3.2-2 3.1 1.9 3.2-1.9 3.2 2 3.1-1.9 3.2-3.6 1-.2 3.7-3 2-2.6-2.6-3.4 1.6-3.4-1.6-2.6 2.6-3-2-.2-3.7-3.6-1-1.9-3.2 2-3.1-1.9-3.2 1.9-3.2-2-3.1 1.9-3.2 3.6-1 .2-3.7 3-2 2.6 2.6L32 12.5z"
/>
<circle cx="32" cy="32" r="9" fill="#bfe2ff" />
<text
x="32"
y="36"
text-anchor="middle"
font-family="'Inter', 'DM Sans', system-ui, sans-serif"
font-size="11"
font-weight="800"
fill="#0b1e3d"
>
AI
</text>
</svg>
<div>
<div class="ai-menu-title">AI / Intelligence Modules</div>
<div class="ai-menu-sub">Smart banking · Powered by ML &amp; LLMs</div>
</div>
</div>
<!-- 4-column mega layout -->
<div class="ai-menu-cols">
<div class="ai-col ai-col--red">
<div class="ai-col-h">
<fa-icon icon="hand-holding-usd" class="ai-col-h-ic"></fa-icon>
<span>Lending &amp; Risk</span>
</div>
<button mat-menu-item class="ai-card" tabindex="0">
<span class="ai-card-ic"><fa-icon icon="hand-holding-usd"></fa-icon></span>
<span class="ai-card-body">
<span class="ai-card-t">Loan Eligibility</span>
<span class="ai-card-d">Score applicants &amp; recommend sanction limit</span>
</span>
</button>
<button mat-menu-item class="ai-card" tabindex="0">
<span class="ai-card-ic"><fa-icon icon="exclamation-triangle"></fa-icon></span>
<span class="ai-card-body">
<span class="ai-card-t">Probable NPA Prediction</span>
<span class="ai-card-d">Flag likely NPA slippages in next 90 days</span>
</span>
</button>
<button mat-menu-item class="ai-card" tabindex="0">
<span class="ai-card-ic"><fa-icon icon="arrow-up"></fa-icon></span>
<span class="ai-card-body">
<span class="ai-card-t">Income Increase Analysis</span>
<span class="ai-card-d">Detect rising-income segments to up-sell</span>
</span>
</button>
</div>
<div class="ai-col ai-col--teal">
<div class="ai-col-h">
<fa-icon icon="chart-pie" class="ai-col-h-ic"></fa-icon>
<span>Investment &amp; Profit</span>
</div>
<button mat-menu-item class="ai-card" tabindex="0">
<span class="ai-card-ic"><fa-icon icon="chart-pie"></fa-icon></span>
<span class="ai-card-body">
<span class="ai-card-t">Profit Distribution Analysis</span>
<span class="ai-card-d">Branch / product / segment profitability split</span>
</span>
</button>
<button mat-menu-item class="ai-card" tabindex="0">
<span class="ai-card-ic"><fa-icon icon="chart-bar"></fa-icon></span>
<span class="ai-card-body">
<span class="ai-card-t">Area of Investment Analysis</span>
<span class="ai-card-d">Optimal asset / sector deployment mix</span>
</span>
</button>
</div>
<div class="ai-col ai-col--indigo">
<div class="ai-col-h">
<fa-icon icon="shield-alt" class="ai-col-h-ic"></fa-icon>
<span>Compliance &amp; Advisory</span>
</div>
<button mat-menu-item class="ai-card" routerLink="/rbi-circulars" tabindex="0">
<span class="ai-card-ic"><fa-icon icon="university"></fa-icon></span>
<span class="ai-card-body">
<span class="ai-card-t">RBI Notifications Monitoring</span>
<span class="ai-card-d">Auto-track circulars relevant to you</span>
</span>
</button>
<button mat-menu-item class="ai-card" tabindex="0">
<span class="ai-card-ic"><fa-icon icon="shield-alt"></fa-icon></span>
<span class="ai-card-body">
<span class="ai-card-t">CERT-In Advisory Monitoring</span>
<span class="ai-card-d">Cybersecurity advisories mapped to CBS</span>
</span>
</button>
<button mat-menu-item class="ai-card" tabindex="0">
<span class="ai-card-ic"><fa-icon icon="calculator"></fa-icon></span>
<span class="ai-card-body">
<span class="ai-card-t">Advance Tax Calculation</span>
<span class="ai-card-d">Project quarterly liability per customer</span>
</span>
</button>
</div>
<div class="ai-col ai-col--pink">
<div class="ai-col-h">
<fa-icon icon="users" class="ai-col-h-ic"></fa-icon>
<span>Governance</span>
</div>
<button mat-menu-item class="ai-card" tabindex="0">
<span class="ai-card-ic"><fa-icon icon="users"></fa-icon></span>
<span class="ai-card-body">
<span class="ai-card-t">Election Management</span>
<span class="ai-card-d">Cooperative board elections end-to-end</span>
</span>
</button>
</div>
</div>
<div class="ai-menu-footer">
<fa-icon icon="info-circle"></fa-icon>
<span>Modules are powered by ML models &amp; LLMs. Some are in beta.</span>
</div>
</mat-menu>
<!-- Sub Ledger Mega Menu -->
@if (subLedgerOpen) {
<div class="subledger-backdrop" (click)="subLedgerOpen = false"></div>
+362 -61
View File
@@ -160,12 +160,15 @@ $top-h: 32px;
box-shadow: 0 0 0 2.5px rgb(16 185 129 / 25%);
}
// ===== Right Actions — Unified =====
$btn-size: 30px;
$icon-size: 14px;
$icon-color: rgb(255 255 255 / 55%);
// ===== Right Actions — Unified, polished =====
$btn-size: 26px;
$icon-size: 13px;
$icon-color: rgb(255 255 255 / 70%);
$icon-hover: #fff;
$icon-hover-bg: rgb(255 255 255 / 10%);
$icon-bg: rgb(255 255 255 / 6%);
$icon-border: rgb(255 255 255 / 10%);
$icon-hover-bg: rgb(255 255 255 / 14%);
$icon-hover-border: rgb(255 255 255 / 28%);
.t-actions {
display: flex;
@@ -173,41 +176,58 @@ $icon-hover-bg: rgb(255 255 255 / 10%);
margin-left: auto;
height: $top-h;
flex-shrink: 0;
gap: 2px;
padding-right: 8px;
gap: 6px;
padding-right: 12px;
}
.t-divider {
width: 1px;
height: 16px;
background: rgb(255 255 255 / 12%);
height: 18px;
background: rgb(255 255 255 / 14%);
margin: 0 4px;
}
.t-action-btn {
position: relative;
width: $btn-size;
height: $btn-size;
display: flex;
display: inline-flex;
align-items: center;
justify-content: center;
cursor: pointer;
color: $icon-color;
border-radius: 6px;
transition: all 0.15s;
background: $icon-bg;
border: 1px solid $icon-border;
border-radius: 50%;
transition:
color 0.16s ease,
background 0.16s ease,
border-color 0.16s ease,
transform 0.16s ease;
flex-shrink: 0;
outline: none;
&:hover {
&:hover,
&:focus-visible {
color: $icon-hover;
background: $icon-hover-bg;
border-color: $icon-hover-border;
}
// Direct fa-icon (Settings)
fa-icon {
&:active {
transform: scale(0.94);
}
// Direct fa-icon (Settings, AI)
> fa-icon {
font-size: $icon-size;
line-height: 1;
}
// Notifications tray — force same size
// Notifications tray — kill its own button chrome; outer pill is the visual
::ng-deep mifosx-notifications-tray {
width: $btn-size;
height: $btn-size;
display: flex;
align-items: center;
justify-content: center;
@@ -216,67 +236,103 @@ $icon-hover-bg: rgb(255 255 255 / 10%);
width: $btn-size;
height: $btn-size;
padding: 0;
color: $icon-color;
color: inherit;
background: transparent !important;
border-radius: 50%;
line-height: $btn-size;
display: inline-flex;
align-items: center;
justify-content: center;
// Suppress Material's ripple/state layer so we don't get a double hover
.mat-mdc-button-persistent-ripple,
.mat-mdc-button-ripple,
.mat-mdc-focus-indicator {
display: none !important;
}
&:hover {
color: $icon-hover;
background: transparent; // parent handles hover bg
color: inherit;
background: transparent !important;
}
}
.mat-icon,
mat-icon {
font-size: $icon-size;
width: $icon-size;
height: $icon-size;
line-height: $icon-size;
}
// Colorful bell SVG — same 20px box as the AI icon
.t-notif-svg {
width: 20px;
height: 20px;
display: block;
filter: drop-shadow(0 1px 2px rgb(11 30 61 / 35%));
}
.mat-badge-content {
font-size: 8px;
width: 14px;
height: 14px;
line-height: 14px;
top: 2px !important;
right: 2px !important;
}
}
// Theme toggle — force same size
::ng-deep mifosx-theme-toggle {
display: flex;
align-items: center;
justify-content: center;
button {
width: $btn-size;
height: $btn-size;
padding: 0;
color: $icon-color;
line-height: $btn-size;
&:hover {
color: $icon-hover;
background: transparent;
}
mat-icon,
.mat-icon {
font-size: $icon-size;
width: $icon-size;
height: $icon-size;
line-height: $icon-size;
}
width: 13px;
height: 13px;
line-height: 13px;
top: -2px !important;
right: -2px !important;
background: #ef4444 !important;
color: #fff !important;
font-weight: 700;
border: 1.5px solid $brand-dark;
}
}
}
// ===== User Button — Same size, round avatar =====
// ===== AI button — full-color SVG mark, ringed pill =====
.t-action-btn--ai {
background: rgb(255 255 255 / 9%);
border-color: rgb(94 204 186 / 50%);
padding: 0;
overflow: visible;
&:hover,
&:focus-visible {
background: rgb(255 255 255 / 14%);
border-color: rgb(94 204 186 / 85%);
box-shadow: 0 0 0 3px rgb(94 204 186 / 22%);
}
}
.t-ai-svg {
width: 20px;
height: 20px;
display: block;
filter: drop-shadow(0 1px 2px rgb(11 30 61 / 35%));
}
.t-action-dot {
position: absolute;
top: 1px;
right: 1px;
width: 6px;
height: 6px;
border-radius: 50%;
background: #34d399;
border: 1.5px solid $brand-dark;
box-shadow: 0 0 0 0 rgb(52 211 153 / 50%);
animation: t-ai-pulse 2.2s ease-in-out infinite;
}
@keyframes t-ai-pulse {
0%,
100% {
box-shadow: 0 0 0 0 rgb(52 211 153 / 45%);
}
50% {
box-shadow: 0 0 0 5px rgb(52 211 153 / 0%);
}
}
// ===== User Button — Same pill, round avatar inside =====
.t-user-btn {
width: $btn-size;
height: $btn-size;
padding: 0;
overflow: visible;
background: transparent;
}
.t-user-img {
@@ -284,7 +340,252 @@ $icon-hover-bg: rgb(255 255 255 / 10%);
height: 22px;
border-radius: 50%;
object-fit: cover;
border: 1.5px solid rgb(255 255 255 / 20%);
border: 1.5px solid rgb(255 255 255 / 22%);
transition: border-color 0.16s ease;
}
.t-user-btn:hover .t-user-img,
.t-user-btn:focus-visible .t-user-img {
border-color: rgb(255 255 255 / 55%);
}
.t-user-status {
position: absolute;
bottom: 0;
right: 0;
width: 7px;
height: 7px;
border-radius: 50%;
background: #10b981;
border: 1.5px solid $brand-dark;
}
// ===== AI Mega Menu Panel =====
::ng-deep .ai-menu {
&.mat-mdc-menu-panel {
min-width: 880px !important;
max-width: 96vw !important;
max-height: 80vh;
border-radius: 14px !important;
padding: 0 !important;
box-shadow:
0 18px 36px rgb(11 30 61 / 22%),
0 6px 14px rgb(11 30 61 / 14%) !important;
overflow: hidden;
background: #fff !important;
}
.mat-mdc-menu-content {
padding: 0 !important;
max-height: 80vh;
overflow-y: auto;
}
}
.ai-menu-header {
display: flex;
align-items: center;
gap: 12px;
padding: 14px 16px 12px;
background: linear-gradient(135deg, #0b1e3d 0%, #1d4ed8 100%);
color: #fff;
.ai-menu-header-svg {
width: 36px;
height: 36px;
flex-shrink: 0;
filter: drop-shadow(0 1px 3px rgb(0 0 0 / 35%));
}
.ai-menu-title {
font-size: 14px;
font-weight: 700;
letter-spacing: 0.01em;
}
.ai-menu-sub {
font-size: 11px;
color: rgb(255 255 255 / 80%);
margin-top: 2px;
}
}
// ===== Mega Layout — 4 colour-coded columns =====
.ai-menu-cols {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 0;
padding: 14px 14px 6px;
background: #f8fafc;
align-items: stretch;
}
.ai-col {
display: flex;
flex-direction: column;
gap: 6px;
padding: 8px 8px 12px;
background: #fff;
border-radius: 12px;
margin: 0 6px;
box-shadow: 0 1px 2px rgb(15 23 42 / 5%);
border-top: 3px solid var(--c-color);
--c-color: #4f46e5;
--c-bg: #eef2ff;
--c-hover: #4f46e5;
}
.ai-col--red {
--c-color: #c0392b;
--c-bg: #fee2e2;
--c-hover: #c0392b;
}
.ai-col--teal {
--c-color: #0e7e6b;
--c-bg: #ccfbf1;
--c-hover: #0e7e6b;
}
.ai-col--indigo {
--c-color: #3730a3;
--c-bg: #e0e7ff;
--c-hover: #3730a3;
}
.ai-col--pink {
--c-color: #be185d;
--c-bg: #fce7f3;
--c-hover: #be185d;
}
.ai-col-h {
display: flex;
align-items: center;
gap: 7px;
padding: 6px 8px 10px;
margin-bottom: 4px;
font-size: 10.5px;
font-weight: 800;
text-transform: uppercase;
letter-spacing: 0.08em;
color: var(--c-color);
border-bottom: 1px dashed rgb(15 23 42 / 10%);
}
.ai-col-h-ic {
font-size: 11px;
}
// Compact card per AI module
.ai-card.mat-mdc-menu-item {
display: flex !important;
align-items: flex-start;
gap: 10px;
padding: 9px 10px;
min-height: 0;
height: auto;
border-radius: 9px;
font-family: inherit;
line-height: 1.3;
white-space: normal;
transition: background 0.16s ease;
.ai-card-ic {
width: 32px;
height: 32px;
display: inline-flex;
align-items: center;
justify-content: center;
border-radius: 8px;
font-size: 13px;
flex-shrink: 0;
background: var(--c-bg);
color: var(--c-color);
transition:
background 0.16s ease,
color 0.16s ease,
transform 0.16s ease;
}
.ai-card-body {
display: flex;
flex-direction: column;
flex: 1;
min-width: 0;
text-align: left;
}
.ai-card-t {
font-size: 12.5px;
font-weight: 600;
color: #0f172a;
line-height: 1.25;
}
.ai-card-d {
font-size: 10.5px;
color: #64748b;
margin-top: 2px;
line-height: 1.35;
}
&:hover {
background: var(--c-bg);
.ai-card-ic {
background: var(--c-hover);
color: #fff;
transform: scale(1.06);
}
.ai-card-t {
color: var(--c-color);
}
}
}
.ai-menu-footer {
display: flex;
align-items: center;
gap: 8px;
padding: 9px 18px 11px;
background: #f8fafc;
border-top: 1px solid #eef2f7;
font-size: 11px;
color: #64748b;
line-height: 1.4;
fa-icon {
font-size: 11px;
color: #1d4ed8;
}
}
// Responsive collapse
@media (width <= 1100px) {
::ng-deep .ai-menu.mat-mdc-menu-panel {
min-width: 680px !important;
}
.ai-menu-cols {
grid-template-columns: repeat(2, 1fr);
}
}
@media (width <= 720px) {
::ng-deep .ai-menu.mat-mdc-menu-panel {
min-width: 90vw !important;
}
.ai-menu-cols {
grid-template-columns: 1fr;
}
.ai-col {
margin: 4px 0;
}
}
// ===== Sub Ledger Mega Menu =====
@@ -47,7 +47,6 @@ import { SearchToolComponent } from '../../../shared/search-tool/search-tool.com
import { LanguageSelectorComponent } from '../../../shared/language-selector/language-selector.component';
import { MatIcon } from '@angular/material/icon';
import { NotificationsTrayComponent as NotificationsTrayComponent_1 } from '../../../shared/notifications-tray/notifications-tray.component';
import { ThemeToggleComponent } from '../../../shared/theme-toggle/theme-toggle.component';
import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
import { DocumentationLinksService } from 'app/shared/services/documentation-links.service';
@@ -69,7 +68,6 @@ import { DocumentationLinksService } from 'app/shared/services/documentation-lin
LanguageSelectorComponent,
MatIcon,
NotificationsTrayComponent_1,
ThemeToggleComponent,
MatMenu,
MatMenuItem
]