feat: add CFCFRMS module, bank menus, mock backend, and dashboard redesign
- CFCFRMS cyber fraud complaint management with dashboard, complaints, escalation, report suspect/mule/fraud txn, FMR submission, CFR lookup - Mock HTTP interceptor with 20 dummy complaints and all endpoints - Simulator service for I4C/MHA integration testing - 3-row menu bar with 22 items (7+8+8 layout) - Mega menus for General Ledger, Sub Ledger, Deposits, Share, Others, HRM, RBI with search and grouped columns - Dropdowns for Cashier, Clearing, NPA, CKYC, Advances, Letter Mgmt, Insurance, Digital - Dashboard redesign with regulatory, financial health, deposits, risk/NPA sections using static banking data - All translations added to en-US.json - FontAwesome icons registered for all new menu items Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
0a32d7135e
commit
b3a4cce0cf
@@ -0,0 +1,162 @@
|
||||
<!--
|
||||
Copyright since 2025 Mifos Initiative
|
||||
|
||||
This Source Code Form is subject to the terms of the Mozilla Public
|
||||
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/.
|
||||
-->
|
||||
|
||||
<div class="complaint-list-page">
|
||||
<div class="page-header">
|
||||
<div>
|
||||
<h2>{{ 'labels.menus.Complaint Management' | translate }}</h2>
|
||||
<p class="subtitle">{{ 'labels.text.I4C/MHA CFCFRMS Complaint Tickets' | translate }}</p>
|
||||
</div>
|
||||
<button mat-raised-button color="accent" (click)="exportCsv()" [disabled]="complaints.length === 0">
|
||||
<fa-icon icon="file-csv" class="mr-05"></fa-icon> {{ 'labels.text.Export CSV' | translate }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Filter Chips -->
|
||||
<div class="filter-chips">
|
||||
<button mat-stroked-button [class.active-chip]="activeChip === 'ALL'" (click)="onChipClick('ALL')">
|
||||
{{ 'labels.text.All' | translate }}
|
||||
</button>
|
||||
<button mat-stroked-button [class.active-chip]="activeChip === 'PENDING'" (click)="onChipClick('PENDING')">
|
||||
<span class="chip-dot pending"></span> {{ 'labels.text.Pending' | translate }}
|
||||
</button>
|
||||
<button mat-stroked-button [class.active-chip]="activeChip === 'ACTIVE'" (click)="onChipClick('ACTIVE')">
|
||||
<span class="chip-dot active"></span> {{ 'labels.text.Active' | translate }}
|
||||
</button>
|
||||
<button mat-stroked-button [class.active-chip]="activeChip === 'RESOLVED'" (click)="onChipClick('RESOLVED')">
|
||||
<span class="chip-dot resolved"></span> {{ 'labels.text.Resolved' | translate }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Filter Bar -->
|
||||
<mat-card class="filter-bar">
|
||||
<mat-card-content>
|
||||
<div class="filter-row">
|
||||
<mat-form-field appearance="outline" class="filter-field">
|
||||
<mat-label>{{ 'labels.text.Search by Ticket ID / Mobile' | translate }}</mat-label>
|
||||
<input
|
||||
matInput
|
||||
[(ngModel)]="searchQuery"
|
||||
(keyup.enter)="onSearch()"
|
||||
placeholder="{{ 'labels.text.Enter ticket ID or mobile...' | translate }}"
|
||||
/>
|
||||
<button mat-icon-button matSuffix (click)="onSearch()">
|
||||
<fa-icon icon="search"></fa-icon>
|
||||
</button>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field appearance="outline" class="filter-field">
|
||||
<mat-label>{{ 'labels.text.Status' | translate }}</mat-label>
|
||||
<mat-select [(ngModel)]="statusFilter" (selectionChange)="onStatusFilterChange()">
|
||||
<mat-option value="">{{ 'labels.text.All Statuses' | translate }}</mat-option>
|
||||
@for (s of statuses; track s) {
|
||||
<mat-option [value]="s">{{ s.replace('_', ' ') }}</mat-option>
|
||||
}
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field appearance="outline" class="filter-field">
|
||||
<mat-label>{{ 'labels.text.From Date' | translate }}</mat-label>
|
||||
<input matInput [matDatepicker]="fromPicker" [(ngModel)]="dateFrom" (dateChange)="onSearch()" />
|
||||
<mat-datepicker-toggle matSuffix [for]="fromPicker"></mat-datepicker-toggle>
|
||||
<mat-datepicker #fromPicker></mat-datepicker>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field appearance="outline" class="filter-field">
|
||||
<mat-label>{{ 'labels.text.To Date' | translate }}</mat-label>
|
||||
<input matInput [matDatepicker]="toPicker" [(ngModel)]="dateTo" (dateChange)="onSearch()" />
|
||||
<mat-datepicker-toggle matSuffix [for]="toPicker"></mat-datepicker-toggle>
|
||||
<mat-datepicker #toPicker></mat-datepicker>
|
||||
</mat-form-field>
|
||||
|
||||
<button mat-icon-button matTooltip="{{ 'labels.text.Clear Filters' | translate }}" (click)="clearFilters()">
|
||||
<fa-icon icon="times-circle"></fa-icon>
|
||||
</button>
|
||||
</div>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
|
||||
<!-- Table -->
|
||||
<mat-card class="table-card">
|
||||
<mat-card-content>
|
||||
@if (isLoading) {
|
||||
<div class="loading-container">
|
||||
<mat-spinner diameter="40"></mat-spinner>
|
||||
</div>
|
||||
} @else if (complaints.length === 0) {
|
||||
<div class="empty-state">
|
||||
<fa-icon icon="inbox" size="3x"></fa-icon>
|
||||
<p>{{ 'labels.text.No complaints match your filters' | translate }}</p>
|
||||
<button mat-stroked-button (click)="clearFilters()">{{ 'labels.text.Clear Filters' | translate }}</button>
|
||||
</div>
|
||||
} @else {
|
||||
<div class="table-responsive">
|
||||
<table mat-table [dataSource]="complaints" class="full-width">
|
||||
<ng-container matColumnDef="ticketId">
|
||||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.text.Ticket ID' | translate }}</th>
|
||||
<td mat-cell *matCellDef="let row">
|
||||
<a [routerLink]="['/cfcfrms/complaints', row.ticketId]" class="ticket-link">{{ row.ticketId }}</a>
|
||||
</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="complainantName">
|
||||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.text.Complainant' | translate }}</th>
|
||||
<td mat-cell *matCellDef="let row">{{ row.complainantName }}</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="complainantMobile">
|
||||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.text.Mobile' | translate }}</th>
|
||||
<td mat-cell *matCellDef="let row">{{ row.complainantMobile }}</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="fraudAmount">
|
||||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.text.Fraud Amount' | translate }}</th>
|
||||
<td mat-cell *matCellDef="let row" class="amount-cell">{{ formatCurrency(row.fraudAmount) }}</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="suspectAccountNumber">
|
||||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.text.Suspect Account' | translate }}</th>
|
||||
<td mat-cell *matCellDef="let row" class="mono">{{ row.suspectAccountNumber }}</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="status">
|
||||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.text.Status' | translate }}</th>
|
||||
<td mat-cell *matCellDef="let row">
|
||||
<cfcfrms-status-badge [status]="row.status"></cfcfrms-status-badge>
|
||||
</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="createdAt">
|
||||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.text.Created At' | translate }}</th>
|
||||
<td mat-cell *matCellDef="let row">{{ formatDate(row.createdAt) }}</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="actions">
|
||||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.text.Actions' | translate }}</th>
|
||||
<td mat-cell *matCellDef="let row">
|
||||
<a
|
||||
mat-icon-button
|
||||
color="primary"
|
||||
[routerLink]="['/cfcfrms/complaints', row.ticketId]"
|
||||
matTooltip="{{ 'labels.text.View Details' | translate }}"
|
||||
>
|
||||
<fa-icon icon="eye"></fa-icon>
|
||||
</a>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
|
||||
<tr mat-row *matRowDef="let row; columns: displayedColumns"></tr>
|
||||
</table>
|
||||
</div>
|
||||
<mat-paginator
|
||||
[length]="totalElements"
|
||||
[pageSize]="pageSize"
|
||||
[pageIndex]="pageIndex"
|
||||
[pageSizeOptions]="[10, 25, 50]"
|
||||
(page)="onPageChange($event)"
|
||||
showFirstLastButtons
|
||||
>
|
||||
</mat-paginator>
|
||||
}
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
</div>
|
||||
Reference in New Issue
Block a user