feat(clerk): add dedicated nav pages, state restore, and fullscreen ESC fix

- Add ClerkUiStateService with full account state save/restore (no double network call on back navigation)
- Add Photo & Signature, Standing Instructions, Today's Activity as dedicated nav pages
- Remove photo/signature, standing instructions, linked accounts, today's activity panels from main dashboard
- Add ESC key to exit fullscreen; clicking Clerk nav immediately restores fullscreen without re-navigating
- Compact clerk dashboard UI with reduced font sizes and padding throughout
- Add CashierOnlyGuard for route protection

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Jaanu423
2026-06-09 12:49:06 +05:30
co-authored by Claude Sonnet 4.6
parent f7d9259eb7
commit 40a436dabe
20 changed files with 3708 additions and 1020 deletions
@@ -6,7 +6,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
import { Component, OnInit, inject } from '@angular/core';
import { Component, OnInit, OnDestroy, inject } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { HttpClient, HttpParams } from '@angular/common/http';
@@ -14,6 +14,8 @@ import { catchError } from 'rxjs/operators';
import { forkJoin, of } from 'rxjs';
import { MatDialog, MatDialogModule, MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { CashierService } from '../cashier.service';
import { AuthenticationService } from '../../core/authentication/authentication.service';
import { ClerkUiStateService, ClerkSavedAccount } from '../clerk-ui-state.service';
interface TxnRow {
typ: string;
@@ -621,10 +623,12 @@ export class ImagePreviewDialogComponent {
MatDialogModule
]
})
export class TransactionEntryComponent implements OnInit {
export class TransactionEntryComponent implements OnInit, OnDestroy {
private http = inject(HttpClient);
private cashierService = inject(CashierService);
private authService = inject(AuthenticationService);
private dialog = inject(MatDialog);
private clerkUiState = inject(ClerkUiStateService);
today = '';
rows: TxnRow[] = [];
@@ -748,6 +752,56 @@ export class TransactionEntryComponent implements OnInit {
.getPaymentTypes()
.pipe(catchError(() => of([])))
.subscribe((types: any[]) => (this.paymentTypes = types ?? []));
if (this.isClerk) {
const saved = this.clerkUiState.snapshot.savedAccount;
if (saved) {
this.accountDetails = saved.accountDetails;
this.clientDetails = saved.clientDetails;
this.kycDetails = saved.kycDetails;
this.nominees = saved.nominees;
this.jointHolders = saved.jointHolders;
this.clientAddresses = saved.clientAddresses;
this.nonPersonDetails = saved.nonPersonDetails;
this.clientPhotoUrl = saved.clientPhotoUrl;
this.signatureUrl = saved.signatureUrl;
this.accountNotes = saved.accountNotes;
this.standingInstructions = saved.standingInstructions;
this.todayTransactions = saved.todayTransactions;
this.linkedAccounts = saved.linkedAccounts;
this.sameDayNeftRtgs = saved.sameDayNeftRtgs;
this.sameDayChequeClearing = saved.sameDayChequeClearing;
this.selectedRow = saved.selectedRow;
if (saved.accountDetails) {
const acNo = saved.accountDetails.accountNo ?? '';
const acTyp = this.clerkUiState.snapshot.savedRow?.acTyp ?? '';
this.rows[0] = { ...this.rows[0], acNo, acTyp };
}
}
}
}
ngOnDestroy(): void {
if (this.isClerk && this.accountDetails) {
this.clerkUiState.saveFullState({
accountDetails: this.accountDetails,
clientDetails: this.clientDetails,
kycDetails: this.kycDetails,
nominees: this.nominees,
jointHolders: this.jointHolders,
clientAddresses: this.clientAddresses,
nonPersonDetails: this.nonPersonDetails,
clientPhotoUrl: this.clientPhotoUrl,
signatureUrl: this.signatureUrl,
accountNotes: this.accountNotes,
standingInstructions: this.standingInstructions,
todayTransactions: this.todayTransactions,
linkedAccounts: this.linkedAccounts,
sameDayNeftRtgs: this.sameDayNeftRtgs,
sameDayChequeClearing: this.sameDayChequeClearing,
selectedRow: this.selectedRow
});
}
}
private loadDenominations(): void {
@@ -1010,7 +1064,7 @@ export class TransactionEntryComponent implements OnInit {
// returns all accounts as an array (e.g. recurringdepositaccounts)
const match = allItems.find((x: any) => x.accountNo === acNo) ?? allItems[0];
if (match?.id) {
this.fetchAllDetails(match.id, acNo, cfg, token);
this.fetchAllDetails(match.id, acNo, cfg, token, acTyp);
return;
}
// Fallback: global search
@@ -1021,7 +1075,7 @@ export class TransactionEntryComponent implements OnInit {
if (token !== this.searchToken) return;
const list: any[] = Array.isArray(found) ? found : [];
if (list.length > 0 && list[0].entityId) {
this.fetchAllDetails(list[0].entityId, acNo, cfg, token);
this.fetchAllDetails(list[0].entityId, acNo, cfg, token, acTyp);
} else {
this.loadingAccount = false;
this.accountError = 'Account not found for: ' + acNo;
@@ -1107,7 +1161,8 @@ export class TransactionEntryComponent implements OnInit {
txnBase: string;
txnSearchSupported: boolean;
},
token: number
token: number,
acTyp: string = ''
): void {
this.http
.get<any>(`${cfg.detail}/${accountId}?associations=all`)
@@ -1127,6 +1182,7 @@ export class TransactionEntryComponent implements OnInit {
}
this.accountDetails = acc;
this.jointHolders = acc.jointHolders ?? [];
if (this.isClerk) this.clerkUiState.setFacilities(this.facilities, acNo, acTyp);
// Compute same-day compliance indicators from included transactions
this.computeSameDayNeftRtgs(acc.transactions);
this.computeSameDayChequeClearing(acc.transactions);
@@ -1263,11 +1319,6 @@ export class TransactionEntryComponent implements OnInit {
if (acc.withHoldTax)
alerts.push({ type: 'info', message: `TDS Applicable — Tax Group: ${acc.taxGroupData?.name ?? ''}` });
// Notes
(this.accountNotes ?? []).forEach((n: any) => {
if (n.note) alerts.push({ type: 'info', message: n.note });
});
// Sanctions (from KYC) — DB stores 'YES'/'NO' strings, not booleans
const isYes = (v: any) => v === true || v === 'YES' || v === 'yes' || v === 'Y';
if (isYes(this.kycDetails?.bannedEntityMatch))
@@ -1423,6 +1474,10 @@ export class TransactionEntryComponent implements OnInit {
if (this.selectedRow && !this.rows.includes(this.selectedRow)) this.selectedRow = null;
}
get isClerk(): boolean {
return this.authService.isClerkUser();
}
get isNonIndividual(): boolean {
// legalForm.id=1 → Individual, legalForm.id=2 → Non-Individual (Entity)
const lf = this.clientDetails?.legalForm?.id ?? this.clientDetails?.legalFormId;
@@ -1996,6 +2051,7 @@ export class TransactionEntryComponent implements OnInit {
}
onCancel(): void {
this.clerkUiState.clear();
this.accountDetails = null;
this.clientDetails = null;
this.kycDetails = null;