Files
cbsnexora/src/app/clerk/cheque-book/cheque-book-detail.component.ts
T
Jaanu423andClaude Sonnet 5 0cc79ccd57 feat(clerk): add clerk-facing Cheque Book screen and shell nav wiring
Adds a customer-account-scoped Cheque Book view under Clerk > Account
Facilities (list + leaf detail, with stop-payment/report-lost/mark-used/
return actions), reachable via a new hover dropdown on the Account
Facilities nav item. Also refreshes the Account Facilities page layout,
and carries over a few chequebook service/detail-dialog fixes from the
back-office module (used-leaf display, leaf-lookup dialog polish).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-15 16:08:43 +05:30

208 lines
8.0 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 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/.
*/
import { Component, OnInit, inject } from '@angular/core';
import { CommonModule } from '@angular/common';
import { ActivatedRoute, Router } from '@angular/router';
import { MatDialog } from '@angular/material/dialog';
import { ChequeBookService } from '../../chequebook/chequebook.service';
import { LeafDetailDialogComponent } from '../../chequebook/cheque-book-detail/leaf-detail-dialog/leaf-detail-dialog.component';
import { ClerkLeafActionDialogComponent, LeafAction } from './leaf-action-dialog/leaf-action-dialog.component';
/** Cheque-book detail for the Clerk Dashboard — a fullscreen clerk-styled view of one book, its leaf
* register, and the same maker-side leaf actions (stop-payment, revoke, report-lost, cancel-leaf,
* mark-used) as the back-office ChequeBookDetailComponent, so a clerk can act on a leaf without leaving
* the clerk shell/nav. Every action still goes through the existing maker-checker approval flow server-side.
* Each action opens as a popup (ClerkLeafActionDialogComponent) rather than an inline form so it never
* pushes the leaf grid around and gets the full dialog width to lay out its fields. */
@Component({
selector: 'mifosx-clerk-cheque-book-detail',
standalone: true,
imports: [CommonModule],
styleUrl: './cheque-book-detail.component.scss',
template: `
<div class="cb-page">
<!-- Header -->
<div class="cb-header">
<div class="cb-header__left">
<svg
class="cb-header__icon"
width="26"
height="26"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="1.8"
>
<rect x="1" y="4" width="22" height="16" rx="2" />
<line x1="1" y1="10" x2="23" y2="10" />
</svg>
<div>
<div class="cb-header__title">Cheque Book #{{ book.id }}</div>
<div class="cb-header__sub">
{{ book.accountNo || book.accountId }}
@if (book.clientName) {
&nbsp;&middot;&nbsp;{{ book.clientName }}
}
</div>
</div>
</div>
<button type="button" class="cb-btn" (click)="goBack()">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5">
<polyline points="15 18 9 12 15 6" />
</svg>
Back
</button>
</div>
@if (loading) {
<div class="cb-status-msg">Loading cheque book&hellip;</div>
} @else {
<div class="cb-body">
<!-- Info card -->
<div class="cb-info-card">
<div class="cb-info-grid">
<div>
<span class="cb-info-grid__k">Account Type</span>
<span class="cb-info-grid__v">{{ book.accountType }}</span>
</div>
<div>
<span class="cb-info-grid__k">Office</span>
<span class="cb-info-grid__v">{{ book.officeName }}</span>
</div>
<div>
<span class="cb-info-grid__k">Series</span>
<span class="cb-info-grid__v">{{ book.seriesCode ?? '&#8212;' }}</span>
</div>
<div>
<span class="cb-info-grid__k">Leaf Range</span>
<span class="cb-info-grid__v">
{{ book.rangeStart ? book.rangeStart + ' – ' + book.rangeEnd : '&#8212;' }}
</span>
</div>
<div>
<span class="cb-info-grid__k">Quantity</span>
<span class="cb-info-grid__v">{{ book.noOfBooks }} book{{ book.noOfBooks === 1 ? '' : 's' }}</span>
</div>
<div>
<span class="cb-info-grid__k">Status</span>
<span
class="cb-pill"
[class.cb-pill--ok]="book.status === 'ISSUED'"
[class.cb-pill--bad]="book.status === 'REJECTED' || book.status === 'WITHDRAWN'"
[class.cb-pill--neutral]="
book.status !== 'ISSUED' && book.status !== 'REJECTED' && book.status !== 'WITHDRAWN'
"
>{{ book.status }}</span
>
</div>
<div>
<span class="cb-info-grid__k">Issue Date</span>
<span class="cb-info-grid__v">{{ book.issueDate ?? '&#8212;' }}</span>
</div>
</div>
</div>
<!-- Action toolbar — only for an issued book; each button pops up its own action dialog -->
@if (book.status === 'ISSUED') {
<div class="cb-action-toolbar">
<button type="button" class="cb-action-btn cb-action-btn--warn" (click)="openAction('stoppayment')">
Stop Payment
</button>
<button type="button" class="cb-action-btn" (click)="openAction('revokestoppayment')">
Revoke Stop Payment
</button>
<button type="button" class="cb-action-btn cb-action-btn--warn" (click)="openAction('reportlost')">
Report Lost
</button>
<button type="button" class="cb-action-btn" (click)="openAction('cancelleaf')">Cancel Leaf</button>
<button type="button" class="cb-action-btn cb-action-btn--primary" (click)="openAction('markused')">
Mark as Used
</button>
<button type="button" class="cb-action-btn cb-action-btn--warn" (click)="openAction('return')">
Flag Returned
</button>
</div>
}
<!-- Leaf grid -->
<div class="cb-info-card">
@if (leaves.length === 0) {
<div class="cb-no-books">No leaves to display</div>
} @else {
<div class="cb-leaf-grid">
@for (l of leaves; track l.leafSerialNo) {
<button
type="button"
class="cb-leaf-circle"
[class.cb-leaf-circle--ok]="l.status === 'ACTIVE'"
[class.cb-leaf-circle--used]="l.status === 'USED'"
[class.cb-leaf-circle--warn]="l.status === 'STOP_PAYMENT'"
[class.cb-leaf-circle--bad]="l.status === 'LOST'"
[class.cb-leaf-circle--returned]="l.status === 'RETURNED'"
(click)="selectLeaf(l)"
>
{{ l.leafSerialNo }}
</button>
}
</div>
}
</div>
</div>
}
</div>
`
})
export class ClerkChequeBookDetailComponent implements OnInit {
private route = inject(ActivatedRoute);
private router = inject(Router);
private chequeBookService = inject(ChequeBookService);
private dialog = inject(MatDialog);
bookId!: string;
book: any = {};
leaves: any[] = [];
loading = false;
ngOnInit(): void {
this.bookId = this.route.snapshot.paramMap.get('id') as string;
this.loadAll();
}
loadAll(): void {
this.loading = true;
this.chequeBookService.getBook(this.bookId).subscribe((data: any) => {
this.book = data || {};
this.loading = false;
});
this.chequeBookService.getLeaves(this.bookId).subscribe((data: any[]) => {
this.leaves = data || [];
});
}
selectLeaf(leaf: any): void {
this.dialog.open(LeafDetailDialogComponent, { data: leaf, width: '420px' });
}
openAction(action: LeafAction): void {
const ref = this.dialog.open(ClerkLeafActionDialogComponent, {
width: '480px',
data: { bookId: this.bookId, action }
});
ref.afterClosed().subscribe((result) => {
if (result?.success) {
this.loadAll();
}
});
}
goBack(): void {
this.router.navigate(['/clerk/account-facilities/cheque-book']);
}
}