- Savings account general tab now shows the real lien/user hold (savingsAmountOnHold) alongside the existing guarantor hold (onHoldFunds). - Cashier transaction-entry "Transfer Hold" / "Tr. Hold" now bind to the real freezeAmount instead of a hardcoded 0.00 placeholder; "Freeze Amount" itself reverted to 0.00 per request. withdrawableAmount no longer double-subtracts the hold for savings accounts (whose availableBalance is already hold-adjusted server-side), while current accounts (whose availableBalance is not) still get the client-side subtraction they need. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
1715 lines
78 KiB
HTML
1715 lines
78 KiB
HTML
<!--
|
||
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="te-root">
|
||
<!-- ── Clerk edit: rejection reason banner ───────────────────────────── -->
|
||
@if (editMode && editRequest?.rejection_reason) {
|
||
<div class="te-reject-banner">
|
||
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||
<circle cx="12" cy="12" r="10" />
|
||
<line x1="12" y1="8" x2="12" y2="12" />
|
||
<line x1="12" y1="16" x2="12.01" y2="16" />
|
||
</svg>
|
||
<div>
|
||
<strong>This request was rejected by the manager.</strong>
|
||
<span>Reason: {{ editRequest.rejection_reason }}</span>
|
||
<small>Update the details below and click “Resubmit for Approval”.</small>
|
||
</div>
|
||
</div>
|
||
}
|
||
|
||
<!-- ── Cashier Day-Status Blocking Banners ───────────────────────────── -->
|
||
@if (previousDayUnsettled) {
|
||
<div class="te-day-block te-day-block--danger">
|
||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5">
|
||
<circle cx="12" cy="12" r="10" />
|
||
<line x1="12" y1="8" x2="12" y2="12" />
|
||
<line x1="12" y1="16" x2="12.01" y2="16" />
|
||
</svg>
|
||
<span>
|
||
<strong>TRANSACTIONS BLOCKED</strong> — Previous day cash settlement is pending
|
||
@if (unsettledSince) {
|
||
<em>(since {{ unsettledSince }})</em>
|
||
}
|
||
. Complete Cash Settlement before processing any transactions.
|
||
</span>
|
||
</div>
|
||
}
|
||
@if (!morningTransferReceived && !previousDayUnsettled) {
|
||
<div class="te-day-block te-day-block--warning">
|
||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5">
|
||
<path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z" />
|
||
<line x1="12" y1="9" x2="12" y2="13" />
|
||
<line x1="12" y1="17" x2="12.01" y2="17" />
|
||
</svg>
|
||
<span>
|
||
<strong>NO CASH ALLOCATED</strong> — Morning transfer not received. Request a morning transfer from the vault
|
||
before processing cash transactions.
|
||
</span>
|
||
</div>
|
||
}
|
||
|
||
<!-- ── ENTRY GRID shown when no account loaded yet ────────────────── -->
|
||
@if (!accountDetails && !loadingAccount) {
|
||
<div class="ws-wrap">
|
||
<!-- ── Header ── -->
|
||
<div class="ws-header">
|
||
<div class="ws-header__left">
|
||
<div class="ws-header__icon">
|
||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||
<rect x="2" y="3" width="20" height="14" rx="2" />
|
||
<line x1="8" y1="21" x2="16" y2="21" />
|
||
<line x1="12" y1="17" x2="12" y2="21" />
|
||
</svg>
|
||
</div>
|
||
<span class="ws-header__title">Transaction Entry</span>
|
||
</div>
|
||
<div class="ws-header__right">
|
||
<span class="ws-header__effdt">
|
||
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||
<rect x="3" y="4" width="18" height="18" rx="2" />
|
||
<line x1="16" y1="2" x2="16" y2="6" />
|
||
<line x1="8" y1="2" x2="8" y2="6" />
|
||
<line x1="3" y1="10" x2="21" y2="10" />
|
||
</svg>
|
||
Eff. Date
|
||
<input class="ws-header__date" type="date" [value]="todayIso" readonly />
|
||
</span>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ── Row cards ── -->
|
||
<div class="ws-rows">
|
||
@for (row of rows; track $index; let i = $index) {
|
||
<div class="ws-card" [class.ws-card--active]="selectedRow === row" (click)="onRowSelect(row, i)">
|
||
<div class="ws-card__badge">{{ i + 1 }}</div>
|
||
@if (isClerk && rows.length > 1) {
|
||
<button
|
||
class="ws-card__del"
|
||
type="button"
|
||
(click)="$event.stopPropagation(); deleteRow(i)"
|
||
title="Remove this row"
|
||
>
|
||
×
|
||
</button>
|
||
}
|
||
|
||
<div class="ws-card__body">
|
||
<!-- ── Row 1: Classification | Account ── -->
|
||
<div class="ws-group-row">
|
||
<div class="ws-group">
|
||
<span class="ws-caption">Classification</span>
|
||
<div class="ws-group__fields">
|
||
<div class="ws-f ws-f--type">
|
||
<label class="ws-f__lbl" title="Transaction Type">TYPE</label>
|
||
<select
|
||
class="ws-f__ctl"
|
||
[(ngModel)]="row.typ"
|
||
(click)="$event.stopPropagation()"
|
||
(change)="onTypChange(row)"
|
||
>
|
||
<option value="~">~</option>
|
||
<option value="Withdrawal Slip">Withdrawal Slip</option>
|
||
<option value="Cheque">Cheque</option>
|
||
<option value="Disbursement">Disbursement</option>
|
||
<option value="Enter Bank Transfer">Enter Bank Transfer</option>
|
||
<option value="DD">DD (Demand Draft)</option>
|
||
</select>
|
||
</div>
|
||
<div class="ws-f ws-f--branch">
|
||
<label class="ws-f__lbl" title="Branch">BRANCH</label>
|
||
<select class="ws-f__ctl" [(ngModel)]="row.br" (click)="$event.stopPropagation()">
|
||
<option [ngValue]="1">001 · Main</option>
|
||
<option [ngValue]="2">002 · M.G. Road</option>
|
||
<option [ngValue]="14">014 · Station Rd</option>
|
||
<option [ngValue]="27">027 · Industrial</option>
|
||
</select>
|
||
</div>
|
||
<div class="ws-f ws-f--paytype">
|
||
<label class="ws-f__lbl ws-f__lbl--accent" title="Transaction Type">TXN TP.</label>
|
||
<select
|
||
class="ws-f__ctl ws-f__ctl--accent"
|
||
[(ngModel)]="row.paymentTypeId"
|
||
(click)="$event.stopPropagation()"
|
||
[class.ws-f__ctl--req]="!row.paymentTypeId"
|
||
>
|
||
<option [ngValue]="null">— Select —</option>
|
||
@for (pt of paymentTypes; track pt.id) {
|
||
<option [ngValue]="pt.id">{{ pt.name }}</option>
|
||
}
|
||
</select>
|
||
</div>
|
||
<div class="ws-f ws-f--actyp">
|
||
<label class="ws-f__lbl" title="Account Type">AC. TP.</label>
|
||
<select
|
||
class="ws-f__ctl"
|
||
[(ngModel)]="row.acTyp"
|
||
(click)="$event.stopPropagation()"
|
||
(change)="
|
||
row.acTyp === 'GL' &&
|
||
row.typ !== 'Disbursement' &&
|
||
row.typ !== 'Enter Bank Transfer' &&
|
||
(row.dbCr = 'Cr')
|
||
"
|
||
>
|
||
@for (a of acTypOptions; track a) {
|
||
<option [value]="a">{{ a }}</option>
|
||
}
|
||
</select>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="ws-vdivider"></div>
|
||
|
||
<div class="ws-group ws-group--flex">
|
||
<span class="ws-caption">Account</span>
|
||
<div class="ws-group__fields">
|
||
<div class="ws-f ws-f--acno">
|
||
<label class="ws-f__lbl" title="Account Number">A/C NO.</label>
|
||
<input
|
||
class="ws-f__ctl ws-f__ctl--mono"
|
||
[value]="row.acNo"
|
||
(input)="onAcNoInput(row, i, $any($event.target).value)"
|
||
(click)="$event.stopPropagation()"
|
||
(blur)="onAcNoBlur(row, i)"
|
||
placeholder="Account number"
|
||
/>
|
||
</div>
|
||
<div class="ws-f ws-f--ecsno">
|
||
<label class="ws-f__lbl" title="ECS Account Number">ECS A/C NO.</label>
|
||
<input
|
||
class="ws-f__ctl ws-f__ctl--mono"
|
||
[(ngModel)]="row.ecsAcNo"
|
||
(click)="$event.stopPropagation()"
|
||
placeholder="ECS account"
|
||
/>
|
||
</div>
|
||
<div class="ws-f ws-f--dbcr">
|
||
<label class="ws-f__lbl" title="Debit / Credit">DR./CR.</label>
|
||
<div class="ws-seg" (click)="$event.stopPropagation()">
|
||
<button
|
||
type="button"
|
||
class="ws-seg__btn ws-seg__btn--dr"
|
||
[class.ws-seg__btn--active-dr]="row.dbCr === 'Dr'"
|
||
[disabled]="
|
||
row.acTyp === 'GL' && row.typ !== 'Disbursement' && row.typ !== 'Enter Bank Transfer'
|
||
"
|
||
(click)="row.dbCr = 'Dr'"
|
||
>
|
||
DR
|
||
</button>
|
||
<button
|
||
type="button"
|
||
class="ws-seg__btn ws-seg__btn--cr"
|
||
[class.ws-seg__btn--active-cr]="row.dbCr === 'Cr'"
|
||
(click)="row.dbCr = 'Cr'"
|
||
>
|
||
CR
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ── Row 2: Payment instrument | Narration ── -->
|
||
<div class="ws-group-row ws-group-row--mt">
|
||
<div class="ws-group">
|
||
<span class="ws-caption">Payment instrument</span>
|
||
<div class="ws-group__fields">
|
||
<div class="ws-f ws-f--amt">
|
||
<label class="ws-f__lbl" title="Transaction Amount">
|
||
AMOUNT
|
||
@if (!isClerk && activeWithdrawalRequest) {
|
||
<span class="ws-amt-locked-badge" title="Amount locked — approved withdrawal">🔒</span>
|
||
}
|
||
</label>
|
||
<div
|
||
class="ws-amt-wrap"
|
||
[class.ws-amt-wrap--cash]="isCashPayment && selectedRow === row"
|
||
[class.ws-amt-wrap--locked]="!isClerk && !!activeWithdrawalRequest"
|
||
>
|
||
<span class="ws-amt-prefix">₹</span>
|
||
<input
|
||
class="ws-f__ctl ws-amt-input"
|
||
type="number"
|
||
[value]="row.amount ?? ''"
|
||
(input)="row.amount = +$any($event.target).value"
|
||
(click)="$event.stopPropagation()"
|
||
placeholder="0.00"
|
||
[readonly]="!isClerk && !!activeWithdrawalRequest"
|
||
[class.ws-amt-input--locked]="!isClerk && !!activeWithdrawalRequest"
|
||
/>
|
||
</div>
|
||
</div>
|
||
@if (row.typ !== 'Withdrawal Slip') {
|
||
<div class="ws-f ws-f--cheqno">
|
||
<label class="ws-f__lbl" title="Cheque Number">CHEQUE NO.</label>
|
||
<input
|
||
class="ws-f__ctl ws-f__ctl--mono"
|
||
[(ngModel)]="row.chequeNo"
|
||
(click)="$event.stopPropagation()"
|
||
placeholder="—"
|
||
/>
|
||
</div>
|
||
<div class="ws-f ws-f--cheqdt">
|
||
<label class="ws-f__lbl" title="Cheque Date">CHEQ. DATE</label>
|
||
<input
|
||
class="ws-f__ctl"
|
||
[(ngModel)]="row.cheqDate"
|
||
placeholder="dd-mm-yyyy"
|
||
(click)="$event.stopPropagation()"
|
||
/>
|
||
</div>
|
||
}
|
||
<div class="ws-f ws-f--tok">
|
||
<label class="ws-f__lbl" title="Token Number">TOK. NO.</label>
|
||
<input
|
||
class="ws-f__ctl"
|
||
[(ngModel)]="row.tokNo"
|
||
[readonly]="!isClerk && !!activeWithdrawalRequest"
|
||
[class.ws-f__ctl--readonly]="!isClerk && !!activeWithdrawalRequest"
|
||
placeholder="Enter token"
|
||
(blur)="onTokNoBlur(row)"
|
||
(click)="$event.stopPropagation()"
|
||
/>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="ws-vdivider"></div>
|
||
|
||
<div class="ws-group ws-group--flex">
|
||
<span class="ws-caption">Narration</span>
|
||
<div class="ws-group__fields">
|
||
<div class="ws-f ws-f--desc">
|
||
<label class="ws-f__lbl" title="Description / Narration">DESCRIPTION</label>
|
||
<input
|
||
class="ws-f__ctl"
|
||
[(ngModel)]="row.description"
|
||
(click)="$event.stopPropagation()"
|
||
placeholder="Narration / remarks"
|
||
/>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
}
|
||
</div>
|
||
</div>
|
||
}
|
||
|
||
<!-- ── APPROVED WITHDRAWAL BANNER (cashier disburse flow) ─────────── -->
|
||
@if (!isClerk && loadingWithdrawal) {
|
||
<div class="ws-withdrawal-banner ws-withdrawal-banner--loading">
|
||
<div class="ws-withdrawal-banner__spinner"></div>
|
||
<span>Looking up approved withdrawal request…</span>
|
||
</div>
|
||
}
|
||
@if (!isClerk && activeWithdrawalRequest) {
|
||
<div class="ws-withdrawal-banner">
|
||
<mat-icon class="ws-withdrawal-banner__icon">check_circle</mat-icon>
|
||
<div class="ws-withdrawal-banner__content">
|
||
<div class="ws-withdrawal-banner__title">Approved Withdrawal Request Linked</div>
|
||
<div class="ws-withdrawal-banner__details">
|
||
<span
|
||
>Token: <strong>{{ activeWithdrawalRequest.tok_no }}</strong></span
|
||
>
|
||
<span class="ws-wb-sep">·</span>
|
||
<span
|
||
>Amount: <strong>₹{{ activeWithdrawalRequest.amount | number: '1.2-2' }}</strong></span
|
||
>
|
||
<span class="ws-wb-sep">·</span>
|
||
<span
|
||
>Client: <strong>{{ activeWithdrawalRequest.client_name }}</strong></span
|
||
>
|
||
<span class="ws-wb-sep">·</span>
|
||
<span>By Clerk: {{ activeWithdrawalRequest.clerk_name }}</span>
|
||
<span class="ws-wb-sep">·</span>
|
||
<span>Approved by: {{ activeWithdrawalRequest.manager_name || '—' }}</span>
|
||
</div>
|
||
<div class="ws-withdrawal-banner__note">
|
||
After transaction succeeds, this request will be automatically marked <strong>WITHDRAWN</strong>.
|
||
</div>
|
||
</div>
|
||
<button type="button" class="ws-withdrawal-banner__clear" (click)="activeWithdrawalRequest = null" title="Unlink">
|
||
<mat-icon>close</mat-icon>
|
||
</button>
|
||
</div>
|
||
}
|
||
|
||
<!-- ── STATUS ────────────────────────────────────────────────────────── -->
|
||
@if (loadingAccount) {
|
||
<div class="te-loading">
|
||
<div class="te-loading__spinner">
|
||
<svg width="56" height="56" viewBox="0 0 56 56" fill="none">
|
||
<circle cx="28" cy="28" r="24" stroke="#e2e8f0" stroke-width="4" />
|
||
<circle
|
||
cx="28"
|
||
cy="28"
|
||
r="24"
|
||
stroke="url(#te-grad)"
|
||
stroke-width="4"
|
||
stroke-linecap="round"
|
||
stroke-dasharray="40 112"
|
||
class="te-loading__arc"
|
||
/>
|
||
<defs>
|
||
<linearGradient id="te-grad" x1="0" y1="0" x2="56" y2="56" gradientUnits="userSpaceOnUse">
|
||
<stop offset="0%" stop-color="#2563eb" />
|
||
<stop offset="100%" stop-color="#7c3aed" />
|
||
</linearGradient>
|
||
</defs>
|
||
</svg>
|
||
<div class="te-loading__pulse"></div>
|
||
</div>
|
||
<span class="te-loading__title">Loading Account</span>
|
||
<span class="te-loading__sub">Fetching account details, please wait…</span>
|
||
</div>
|
||
}
|
||
@if (accountError && !loadingAccount) {
|
||
<div class="te-error">{{ accountError }}</div>
|
||
}
|
||
|
||
<!-- ── ACCOUNT DETAIL ─────────────────────────────────────────────────── -->
|
||
@if (accountDetails && !loadingAccount) {
|
||
<div class="te-detail">
|
||
<!-- Customer info panels — Clerk role only -->
|
||
@if (isClerk) {
|
||
<!-- ROW 1: Personal | Joint + Nominees | Other + Balances -->
|
||
<div class="te-detail-top">
|
||
<!-- Personal Information -->
|
||
<div class="te-panel">
|
||
<div class="te-panel__header">
|
||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||
<circle cx="12" cy="8" r="4" />
|
||
<path d="M4 20c0-4 3.6-7 8-7s8 3 8 7" />
|
||
</svg>
|
||
Personal Information
|
||
</div>
|
||
<div class="te-panel__body">
|
||
<div class="te-field">
|
||
<span class="te-label">Branch</span><span class="te-value">{{ officeName }}</span>
|
||
</div>
|
||
<div class="te-field">
|
||
<span class="te-label">A/c No.</span
|
||
><span class="te-value te-mono">{{ accountDetails.accountNo }}</span>
|
||
</div>
|
||
<div class="te-field">
|
||
<span class="te-label">Name</span><span class="te-value te-value-name">{{ clientName }}</span>
|
||
</div>
|
||
<div class="te-field">
|
||
<span class="te-label">S/D/W/H/O</span><span class="te-value">{{ guardianName }}</span>
|
||
</div>
|
||
<div class="te-field">
|
||
<span class="te-label">Address</span><span class="te-value te-value-wrap">{{ clientAddress }}</span>
|
||
</div>
|
||
<div class="te-field">
|
||
<span class="te-label">Email</span><span class="te-value">{{ clientEmail }}</span>
|
||
</div>
|
||
<div class="te-field-row">
|
||
<div class="te-field">
|
||
<span class="te-label">Last Txn</span><span class="te-value">{{ lastTransactionDate }}</span>
|
||
</div>
|
||
<div class="te-field">
|
||
<span class="te-label">Type</span
|
||
><span class="te-value">{{ accountDetails.depositType?.value ?? selectedRow?.acTyp ?? '—' }}</span>
|
||
</div>
|
||
</div>
|
||
<div class="te-field-row">
|
||
<div class="te-field">
|
||
<span class="te-label">Opening Dt</span><span class="te-value">{{ openingDate }}</span>
|
||
</div>
|
||
<div class="te-field">
|
||
<span class="te-label">Status</span
|
||
><span class="te-value">{{ accountDetails.status?.value ?? '—' }}</span>
|
||
</div>
|
||
</div>
|
||
<div class="te-field">
|
||
<span class="te-label">CIF No.</span><span class="te-value te-mono">{{ cifNumber }}</span>
|
||
</div>
|
||
@if (isNonIndividual) {
|
||
<div class="te-field">
|
||
<span class="te-label">Constitution</span>
|
||
<span class="te-value">{{
|
||
nonPersonDetails?.constitution?.name ?? nonPersonDetails?.constitutionType?.name ?? '—'
|
||
}}</span>
|
||
</div>
|
||
<div class="te-field">
|
||
<span class="te-label">Incorp. No.</span>
|
||
<span class="te-value te-mono">{{
|
||
nonPersonDetails?.incorpNo ?? nonPersonDetails?.incorporationNo ?? '—'
|
||
}}</span>
|
||
</div>
|
||
<div class="te-field">
|
||
<span class="te-label">Main Business</span>
|
||
<span class="te-value">{{
|
||
nonPersonDetails?.mainBusinessLine?.name ?? nonPersonDetails?.mainBusinessLineType?.name ?? '—'
|
||
}}</span>
|
||
</div>
|
||
}
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Joint Holders + Nominees -->
|
||
<div class="te-panel">
|
||
<div class="te-panel__header">
|
||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||
<path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2" />
|
||
<circle cx="9" cy="7" r="4" />
|
||
<path d="M23 21v-2a4 4 0 0 0-3-3.87" />
|
||
<path d="M16 3.13a4 4 0 0 1 0 7.75" />
|
||
</svg>
|
||
Joint A/c Holders
|
||
@if (accountDetails?.modeOfOperation) {
|
||
<span class="te-mandate-badge">{{ modeOfOperationLabel }}</span>
|
||
}
|
||
</div>
|
||
<div class="te-panel__body">
|
||
@if (jointHolders.length === 0) {
|
||
<span class="te-dim">No joint holders</span>
|
||
}
|
||
@for (jh of jointHolders; track jh.id ?? $index; let i = $index) {
|
||
<div
|
||
class="te-joint-row te-joint-row--clickable"
|
||
(click)="toggleHolder(i, jh)"
|
||
title="Click to view details"
|
||
>
|
||
<span class="te-joint-name">{{ jh.displayName }}</span>
|
||
<span class="te-badge" [class.te-badge--primary]="jh.isPrimary || jh.primary">
|
||
{{ jh.role ?? (jh.isPrimary || jh.primary ? 'Primary' : 'Co-Holder') }}
|
||
</span>
|
||
@if (jh.sharePercentage) {
|
||
<span class="te-dim">{{ jh.sharePercentage }}%</span>
|
||
}
|
||
@if (jh.signingAuthority) {
|
||
<span
|
||
class="te-sign-badge"
|
||
[class.te-sign-badge--mandatory]="jh.signingAuthority?.toLowerCase() === 'mandatory'"
|
||
[class.te-sign-badge--optional]="jh.signingAuthority?.toLowerCase() === 'optional'"
|
||
>
|
||
{{ jh.signingAuthority }}
|
||
</span>
|
||
}
|
||
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="#94a3b8" stroke-width="2">
|
||
<circle cx="11" cy="11" r="8" />
|
||
<line x1="21" y1="21" x2="16.65" y2="16.65" />
|
||
</svg>
|
||
</div>
|
||
}
|
||
|
||
<!-- Withdrawal rule alert -->
|
||
@if (withdrawalRule) {
|
||
<div
|
||
class="te-withdrawal-rule"
|
||
[class.te-withdrawal-rule--danger]="withdrawalRule.type === 'danger'"
|
||
[class.te-withdrawal-rule--warning]="withdrawalRule.type === 'warning'"
|
||
>
|
||
<span class="te-wr-icon">{{ withdrawalRule.icon }}</span>
|
||
<span class="te-wr-msg">{{ withdrawalRule.msg }}</span>
|
||
</div>
|
||
}
|
||
</div>
|
||
|
||
<!-- Nominees (Individual) / Non-Individual Account Details -->
|
||
@if (!isNonIndividual) {
|
||
<div class="te-panel__header te-panel__header--mt">
|
||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||
<path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z" />
|
||
</svg>
|
||
Nominees
|
||
</div>
|
||
<div class="te-panel__body">
|
||
@if (nominees.length === 0) {
|
||
<span class="te-dim">No nominees</span>
|
||
}
|
||
@for (n of nominees; track n.id ?? $index) {
|
||
<div class="te-joint-row">
|
||
<span class="te-joint-name">{{ n.nomineeDisplayName ?? '—' }}</span>
|
||
<span class="te-dim">{{ n.relationshipName ?? '—' }}</span>
|
||
@if (n.percentageShare) {
|
||
<span class="te-badge">{{ n.percentageShare }}%</span>
|
||
}
|
||
@if (n.nomineeIsMinor) {
|
||
<span class="te-badge" style="background: #fff7ed; color: #c2410c; border-color: #fed7aa"
|
||
>Minor</span
|
||
>
|
||
}
|
||
</div>
|
||
}
|
||
</div>
|
||
}
|
||
|
||
@if (isNonIndividual) {
|
||
<div class="te-panel__header te-panel__header--mt">
|
||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||
<rect x="2" y="7" width="20" height="14" rx="2" />
|
||
<path d="M16 7V5a2 2 0 0 0-2-2h-4a2 2 0 0 0-2 2v2" />
|
||
</svg>
|
||
Non-Individual Account Details
|
||
@if (accountDetails?.modeOfOperation) {
|
||
<span class="te-mandate-badge">{{ modeOfOperationLabel }}</span>
|
||
}
|
||
</div>
|
||
<div class="te-panel__body">
|
||
@if (!accountDetails?.partners?.length) {
|
||
<span class="te-dim">No partners / members linked</span>
|
||
}
|
||
@for (p of accountDetails?.partners ?? []; track p.id; let i = $index) {
|
||
<div
|
||
class="te-joint-row te-joint-row--clickable"
|
||
(click)="
|
||
toggleHolder(i + 1000, {
|
||
clientId: p.clientId,
|
||
displayName: p.clientName,
|
||
cifNumber: p.clientCif,
|
||
sharePercentage: p.sharePercentage,
|
||
signingAuthority: p.isMandatory ? 'Mandatory' : 'Optional',
|
||
role: 'Partner',
|
||
isPrimary: false
|
||
})
|
||
"
|
||
title="Click to view details"
|
||
>
|
||
<span class="te-joint-name">{{ p.clientName ?? '—' }}</span>
|
||
<span class="te-dim te-mono" style="font-size: 10px">{{ p.clientCif ?? '' }}</span>
|
||
@if (p.sharePercentage) {
|
||
<span class="te-dim">{{ p.sharePercentage }}%</span>
|
||
}
|
||
@if (p.isMandatory) {
|
||
<span class="te-sign-badge te-sign-badge--mandatory">Mandatory</span>
|
||
} @else {
|
||
<span class="te-sign-badge te-sign-badge--optional">Optional</span>
|
||
}
|
||
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="#94a3b8" stroke-width="2">
|
||
<circle cx="11" cy="11" r="8" />
|
||
<line x1="21" y1="21" x2="16.65" y2="16.65" />
|
||
</svg>
|
||
</div>
|
||
}
|
||
@if (withdrawalRule && isNonIndividual) {
|
||
<div
|
||
class="te-withdrawal-rule"
|
||
[class.te-withdrawal-rule--danger]="withdrawalRule.type === 'danger'"
|
||
[class.te-withdrawal-rule--warning]="withdrawalRule.type === 'warning'"
|
||
>
|
||
<span class="te-wr-icon">{{ withdrawalRule.icon }}</span>
|
||
<span class="te-wr-msg">{{ withdrawalRule.msg }}</span>
|
||
</div>
|
||
}
|
||
</div>
|
||
}
|
||
</div>
|
||
|
||
<!-- Other Info + KYC + Account Balances -->
|
||
<div class="te-panel">
|
||
<div class="te-panel__header">
|
||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||
<rect x="3" y="4" width="18" height="18" rx="2" />
|
||
<line x1="16" y1="2" x2="16" y2="6" />
|
||
<line x1="8" y1="2" x2="8" y2="6" />
|
||
<line x1="3" y1="10" x2="21" y2="10" />
|
||
</svg>
|
||
Other Information
|
||
</div>
|
||
<div class="te-panel__body">
|
||
<div class="te-field">
|
||
<span class="te-label">Phone No.</span><span class="te-value">{{ clientPhone }}</span>
|
||
</div>
|
||
<div class="te-field">
|
||
<span class="te-label">Client ID</span
|
||
><span class="te-value te-mono">{{ accountDetails.clientId }}</span>
|
||
</div>
|
||
<div class="te-field">
|
||
<span class="te-label">PAN</span><span class="te-value te-mono">{{ panNumber }}</span>
|
||
</div>
|
||
<div class="te-field">
|
||
<span class="te-label">KYC Status</span>
|
||
<span class="te-value" [style.color]="kycDetails?.kycStatus === 300 ? '#059669' : '#f59e0b'">{{
|
||
kycStatus
|
||
}}</span>
|
||
</div>
|
||
<div class="te-field">
|
||
<span class="te-label">CKYC No.</span><span class="te-value te-mono">{{ ckycNumber }}</span>
|
||
</div>
|
||
<div class="te-field">
|
||
<span class="te-label">Aadhaar</span><span class="te-value te-mono">{{ aadhaarNumber }}</span>
|
||
</div>
|
||
<div class="te-field">
|
||
<span class="te-label">Product</span>
|
||
<span class="te-value">{{
|
||
accountDetails.savingsProductName ||
|
||
accountDetails.depositProductName ||
|
||
accountDetails.productName ||
|
||
'—'
|
||
}}</span>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="te-panel__header te-panel__header--mt">
|
||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||
<line x1="12" y1="1" x2="12" y2="23" />
|
||
<path d="M17 5H9.5a3.5 3.5 0 0 0 0 7h5a3.5 3.5 0 0 1 0 7H6" />
|
||
</svg>
|
||
Account Information
|
||
</div>
|
||
<div class="te-panel__body">
|
||
<div class="te-balance-row">
|
||
<span class="te-bal-num">1.</span>
|
||
<span class="te-bal-label">{{ accountDetails?.glCode ? 'GL Balance' : 'Current Balance' }}</span>
|
||
<span class="te-bal-value te-bal-value--cr"
|
||
>{{ currencySymbol }}{{ currentBalance | number: '1.2-2' }}</span
|
||
>
|
||
</div>
|
||
<div class="te-balance-row">
|
||
<span class="te-bal-num">2.</span><span class="te-bal-label">Today's Clg. Amount</span>
|
||
<span class="te-bal-value">{{ currencySymbol }}{{ todayClgAmount | number: '1.2-2' }}</span>
|
||
</div>
|
||
<div class="te-balance-row">
|
||
<span class="te-bal-num">3.</span><span class="te-bal-label">Clear Balance (2-3)</span>
|
||
<span class="te-bal-value te-bal-value--cr"
|
||
>{{ currencySymbol }}{{ clearBalance | number: '1.2-2' }}</span
|
||
>
|
||
</div>
|
||
<div class="te-balance-row">
|
||
<span class="te-bal-num">4.</span><span class="te-bal-label">Freeze Amount</span>
|
||
<span class="te-bal-value">{{ currencySymbol }}0.00</span>
|
||
</div>
|
||
<div class="te-balance-row">
|
||
<span class="te-bal-num">5.</span><span class="te-bal-label">Withdrawable Amt.</span>
|
||
<span class="te-bal-value te-bal-value--cr"
|
||
>{{ currencySymbol }}{{ withdrawableAmount | number: '1.2-2' }}</span
|
||
>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ── ALERTS PANEL ─────────────────────────────────────────────── -->
|
||
@if (accountAlerts.length > 0) {
|
||
<div class="te-alerts-wrap">
|
||
@for (alert of accountAlerts; track $index) {
|
||
<div
|
||
class="te-alert"
|
||
[class.te-alert--danger]="alert.type === 'danger'"
|
||
[class.te-alert--warning]="alert.type === 'warning'"
|
||
[class.te-alert--info]="alert.type === 'info'"
|
||
>
|
||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||
@if (alert.type === 'danger') {
|
||
<path
|
||
d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"
|
||
/>
|
||
<line x1="12" y1="9" x2="12" y2="13" />
|
||
<line x1="12" y1="17" x2="12.01" y2="17" />
|
||
}
|
||
@if (alert.type === 'warning') {
|
||
<circle cx="12" cy="12" r="10" />
|
||
<line x1="12" y1="8" x2="12" y2="12" />
|
||
<line x1="12" y1="16" x2="12.01" y2="16" />
|
||
}
|
||
@if (alert.type === 'info') {
|
||
<circle cx="12" cy="12" r="10" />
|
||
<line x1="12" y1="16" x2="12" y2="12" />
|
||
<line x1="12" y1="8" x2="12.01" y2="8" />
|
||
}
|
||
</svg>
|
||
<span>{{ alert.message }}</span>
|
||
</div>
|
||
}
|
||
</div>
|
||
}
|
||
|
||
<!-- Balance info — Clerk mode (below full panels) -->
|
||
<div class="te-balance-bar">
|
||
<div class="te-bal-item">
|
||
<span class="te-bal-item__label">Net Balance</span>
|
||
<span class="te-bal-item__val te-bal-item__val--cr te-bal-item__val--big">
|
||
{{ currencySymbol }}{{ clearBalance | number: '1.2-2' }} CR
|
||
</span>
|
||
</div>
|
||
<div class="te-bal-item">
|
||
<span class="te-bal-item__label">Opening</span>
|
||
<span class="te-bal-item__val">{{ currencySymbol }}{{ currentBalance | number: '1.2-2' }}</span>
|
||
</div>
|
||
<div class="te-bal-item">
|
||
<span class="te-bal-item__label">Cash Hold</span>
|
||
<span class="te-bal-item__val">0.00</span>
|
||
</div>
|
||
<div class="te-bal-item">
|
||
<span class="te-bal-item__label">Transfer Hold</span>
|
||
<span class="te-bal-item__val">{{ freezeAmount | number: '1.2-2' }}</span>
|
||
</div>
|
||
<div class="te-bal-item">
|
||
<span class="te-bal-item__label">Clearing Hold</span>
|
||
<span class="te-bal-item__val">0.00</span>
|
||
</div>
|
||
<div class="te-bal-item">
|
||
<span class="te-bal-item__label">After Trans.</span>
|
||
<span class="te-bal-item__val te-bal-item__val--cr"
|
||
>{{ currencySymbol }}{{ clearBalance | number: '1.2-2' }}</span
|
||
>
|
||
</div>
|
||
<div class="te-bal-item">
|
||
<span class="te-bal-item__label">Rate</span>
|
||
<span class="te-bal-item__val">{{ accountDetails.nominalAnnualInterestRate ?? 0 | number: '1.2-2' }}%</span>
|
||
</div>
|
||
<div class="te-bal-item">
|
||
<span class="te-bal-item__label">Currency</span>
|
||
<span class="te-bal-item__val">{{ accountDetails.currency?.code ?? 'INR' }}</span>
|
||
</div>
|
||
</div>
|
||
} @else {
|
||
<!-- Cashier: all account info in one compact strip -->
|
||
<div class="te-acs">
|
||
<!-- Identity -->
|
||
<div class="te-acs-id">
|
||
@if (clientPhotoUrl) {
|
||
<img
|
||
[src]="clientPhotoUrl"
|
||
class="te-acs-photo te-acs-photo--clickable"
|
||
alt="Photo"
|
||
title="Click to enlarge"
|
||
(click)="previewImage(clientPhotoUrl, 'Client Photo')"
|
||
/>
|
||
} @else {
|
||
<div class="te-acs-photo te-acs-photo--ph">
|
||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="#94a3b8" stroke-width="1.5">
|
||
<circle cx="12" cy="8" r="4" />
|
||
<path d="M4 20c0-4 3.6-7 8-7s8 3 8 7" />
|
||
</svg>
|
||
</div>
|
||
}
|
||
<div class="te-acs-nameblock">
|
||
<span class="te-acs-name">{{
|
||
accountDetails?.glCode
|
||
? (accountDetails?.name ?? '—')
|
||
: (clientDetails?.displayName ?? accountDetails?.clientName ?? '—')
|
||
}}</span>
|
||
<span class="te-acs-acno">{{ accountDetails?.glCode ?? accountDetails?.accountNo }}</span>
|
||
</div>
|
||
</div>
|
||
<div class="te-acs-sep"></div>
|
||
<!-- Account meta -->
|
||
<div class="te-acs-meta">
|
||
<span
|
||
class="te-acs-tag"
|
||
[class.te-acs-tag--active]="accountDetails?.status?.value === 'Active'"
|
||
[class.te-acs-tag--blocked]="accountDetails?.status?.value === 'Blocked'"
|
||
[class.te-acs-tag--dormant]="accountDetails?.subStatus?.value === 'Dormant'"
|
||
>{{
|
||
accountDetails?.subStatus?.value === 'Dormant' ? 'Dormant' : (accountDetails?.status?.value ?? '—')
|
||
}}</span
|
||
>
|
||
<span class="te-acs-kv"
|
||
><b>A/c</b>
|
||
{{
|
||
accountDetails?.depositType?.value ||
|
||
(selectedRow?.acTyp === 'CA'
|
||
? 'Current'
|
||
: selectedRow?.acTyp === 'SD'
|
||
? 'Savings'
|
||
: selectedRow?.acTyp === 'FD'
|
||
? 'Fixed Dep.'
|
||
: selectedRow?.acTyp === 'RD'
|
||
? 'Recurring'
|
||
: selectedRow?.acTyp === 'GL'
|
||
? (accountDetails?.type?.value ?? 'GL Account')
|
||
: selectedRow?.acTyp === 'SHR'
|
||
? 'Share'
|
||
: selectedRow?.acTyp === 'LA'
|
||
? 'Loan'
|
||
: '—')
|
||
}}</span
|
||
>
|
||
<span class="te-acs-kv"><b>Branch</b> {{ officeName ?? '—' }}</span>
|
||
<span class="te-acs-kv"
|
||
><b>Product</b>
|
||
{{
|
||
accountDetails?.savingsProductName ||
|
||
accountDetails?.depositProductName ||
|
||
accountDetails?.productName ||
|
||
'—'
|
||
}}</span
|
||
>
|
||
<span class="te-acs-kv"
|
||
><b>Rate</b> {{ accountDetails?.nominalAnnualInterestRate ?? 0 | number: '1.2-2' }}%</span
|
||
>
|
||
<span class="te-acs-kv"><b>CCY</b> {{ accountDetails?.currency?.code ?? 'INR' }}</span>
|
||
</div>
|
||
<div class="te-acs-sep"></div>
|
||
<!-- Balance figures -->
|
||
<div class="te-acs-bals">
|
||
<div class="te-acs-b te-acs-b--main">
|
||
<span class="te-acs-b-l">Net Bal</span>
|
||
<span class="te-acs-b-v te-acs-b-v--cr">{{ currencySymbol }}{{ clearBalance | number: '1.2-2' }} CR</span>
|
||
</div>
|
||
<div class="te-acs-b">
|
||
<span class="te-acs-b-l">Opening</span>
|
||
<span class="te-acs-b-v">{{ currencySymbol }}{{ currentBalance | number: '1.2-2' }}</span>
|
||
</div>
|
||
<div class="te-acs-b">
|
||
<span class="te-acs-b-l">Cash Hold</span>
|
||
<span class="te-acs-b-v">0.00</span>
|
||
</div>
|
||
<div class="te-acs-b">
|
||
<span class="te-acs-b-l">Tr. Hold</span>
|
||
<span class="te-acs-b-v">{{ freezeAmount | number: '1.2-2' }}</span>
|
||
</div>
|
||
<div class="te-acs-b">
|
||
<span class="te-acs-b-l">Clr. Hold</span>
|
||
<span class="te-acs-b-v">0.00</span>
|
||
</div>
|
||
<div class="te-acs-b te-acs-b--after">
|
||
<span class="te-acs-b-l">After Trans.</span>
|
||
<span class="te-acs-b-v te-acs-b-v--cr">{{ currencySymbol }}{{ clearBalance | number: '1.2-2' }}</span>
|
||
</div>
|
||
</div>
|
||
<!-- Alerts -->
|
||
@if (accountAlerts.length > 0) {
|
||
<div class="te-acs-sep"></div>
|
||
<div class="te-acs-alerts">
|
||
@for (alert of accountAlerts; track alert.message) {
|
||
<span class="te-acs-alert te-acs-alert--{{ alert.type }}">
|
||
<svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5">
|
||
<circle cx="12" cy="12" r="10" />
|
||
<line x1="12" y1="8" x2="12" y2="12" />
|
||
<line x1="12" y1="16" x2="12.01" y2="16" />
|
||
</svg>
|
||
{{ alert.message }}
|
||
</span>
|
||
}
|
||
</div>
|
||
}
|
||
</div>
|
||
}
|
||
|
||
<!-- ── DENOMINATION + OPENING/CLOSING — Cashier only ───────────── -->
|
||
@if (!isClerk) {
|
||
<div class="te-denom-wrap">
|
||
<!-- Opening Balance -->
|
||
<div class="te-panel te-panel--denom">
|
||
<div class="te-panel__header">
|
||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||
<circle cx="12" cy="12" r="10" />
|
||
<polyline points="12 6 12 12 16 14" />
|
||
</svg>
|
||
Opening Position — {{ today }}
|
||
</div>
|
||
<div class="te-panel__body">
|
||
@if (!openingLoaded) {
|
||
<span class="te-dim">Loading…</span>
|
||
}
|
||
@if (openingLoaded && openingDenoms.length === 0) {
|
||
<span class="te-dim">No opening position found</span>
|
||
}
|
||
@if (openingLoaded && openingDenoms.length > 0) {
|
||
<table class="te-denom-table">
|
||
<thead>
|
||
<tr>
|
||
<th>Denomination</th>
|
||
<th class="num">Opening Count</th>
|
||
<th class="num">Amount</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
@for (d of sortedOpeningDenoms; track d.denominationId) {
|
||
@if (d.count > 0) {
|
||
<tr>
|
||
<td>
|
||
<span class="te-denom-chip" [class.te-denom-chip--coin]="d.kind === 'coin'"
|
||
>₹{{ d.denominationValue }}</span
|
||
>
|
||
</td>
|
||
<td class="num">{{ d.count }}</td>
|
||
<td class="num te-cr">₹{{ d.count * d.denominationValue | number: '1.2-2' }}</td>
|
||
</tr>
|
||
}
|
||
}
|
||
</tbody>
|
||
<tfoot>
|
||
<tr>
|
||
<td><strong>Total</strong></td>
|
||
<td></td>
|
||
<td class="num te-cr">
|
||
<strong>₹{{ openingTotal | number: '1.2-2' }}</strong>
|
||
</td>
|
||
</tr>
|
||
</tfoot>
|
||
</table>
|
||
}
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Denomination Entry -->
|
||
<div class="te-panel te-panel--denom">
|
||
<div class="te-panel__header">
|
||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||
<rect x="2" y="7" width="20" height="14" rx="2" />
|
||
<path d="M16 21V5a2 2 0 0 0-2-2h-4a2 2 0 0 0-2 2v16" />
|
||
</svg>
|
||
Denomination Entry
|
||
@if (denominationTotal > 0) {
|
||
<span class="te-mandate-badge">₹{{ denominationTotal | number: '1.2-2' }}</span>
|
||
}
|
||
@if (isCashPayment && denominationTotal <= 0) {
|
||
<span class="te-denom-required-badge">⚠ Required for Cash</span>
|
||
}
|
||
</div>
|
||
<div class="te-panel__body">
|
||
<table class="te-denom-table">
|
||
<thead>
|
||
<tr>
|
||
<th>Denomination</th>
|
||
<th class="num">Count</th>
|
||
<th class="num">Amount</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
@for (d of allDenominations; track d.id) {
|
||
<tr>
|
||
<td>
|
||
<span class="te-denom-chip" [class.te-denom-chip--coin]="d.kind === 'coin'">
|
||
{{ d.label }}
|
||
</span>
|
||
</td>
|
||
<td class="num">
|
||
<div class="te-denom-stepper">
|
||
<button type="button" (click)="bumpDenom(d, -1)" [disabled]="countFor(d) === 0">−</button>
|
||
<input
|
||
type="number"
|
||
min="0"
|
||
[value]="countFor(d) || ''"
|
||
placeholder="0"
|
||
(input)="onDenomChange(d, $any($event.target).value)"
|
||
/>
|
||
<button type="button" (click)="bumpDenom(d, 1)">+</button>
|
||
</div>
|
||
</td>
|
||
<td class="num te-cr">
|
||
{{ subtotalFor(d) > 0 ? (subtotalFor(d) | number: '1.2-2') : '—' }}
|
||
</td>
|
||
</tr>
|
||
}
|
||
</tbody>
|
||
<tfoot>
|
||
<tr>
|
||
<td><strong>Total</strong></td>
|
||
<td></td>
|
||
<td class="num te-cr">
|
||
<strong>₹{{ denominationTotal | number: '1.2-2' }}</strong>
|
||
</td>
|
||
</tr>
|
||
</tfoot>
|
||
</table>
|
||
|
||
@if (denominationTotal > 0) {
|
||
<div class="te-denom-actions">
|
||
<button class="te-btn te-btn--primary" type="button" (click)="applyDenominationTotal()">
|
||
Apply ₹{{ denominationTotal | number: '1.2-2' }} to Amount
|
||
</button>
|
||
<button class="te-btn" type="button" (click)="clearDenominations()">Clear</button>
|
||
</div>
|
||
}
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Closing Position -->
|
||
<div class="te-panel te-panel--denom">
|
||
<div class="te-panel__header">
|
||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||
<polyline points="20 6 9 17 4 12" />
|
||
</svg>
|
||
Closing Position (Preview)
|
||
@if (selectedRow?.dbCr === 'Dr' && hasWithdrawalDenomOverdraft) {
|
||
<span class="te-denom-overdraft-badge">⚠ Stock Overdraft</span>
|
||
}
|
||
</div>
|
||
<div class="te-panel__body">
|
||
@if (selectedRow?.dbCr === 'Dr' && hasWithdrawalDenomOverdraft) {
|
||
<div class="te-denom-overdraft-alert">
|
||
<div class="te-denom-overdraft-alert__title">
|
||
<svg
|
||
width="14"
|
||
height="14"
|
||
viewBox="0 0 24 24"
|
||
fill="none"
|
||
stroke="currentColor"
|
||
stroke-width="2.5"
|
||
>
|
||
<circle cx="12" cy="12" r="10" />
|
||
<line x1="12" y1="8" x2="12" y2="12" />
|
||
<line x1="12" y1="16" x2="12.01" y2="16" />
|
||
</svg>
|
||
Insufficient Denomination Stock — Cannot Proceed
|
||
</div>
|
||
<table class="te-denom-overdraft-alert__table">
|
||
<thead>
|
||
<tr>
|
||
<th>Denomination</th>
|
||
<th>Requested</th>
|
||
<th>Available</th>
|
||
<th>Short</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
@for (o of denominationOverdrafts; track o.label) {
|
||
<tr>
|
||
<td>{{ o.label }}</td>
|
||
<td class="te-dr">{{ o.requested }}</td>
|
||
<td>{{ o.available }}</td>
|
||
<td class="te-dr">
|
||
<strong>−{{ o.shortfall }}</strong>
|
||
</td>
|
||
</tr>
|
||
}
|
||
</tbody>
|
||
</table>
|
||
<div class="te-denom-overdraft-alert__hint">Adjust denomination or request vault replenishment.</div>
|
||
</div>
|
||
}
|
||
@if (closingDenoms.length === 0) {
|
||
<span class="te-dim">Enter denomination to see closing position</span>
|
||
}
|
||
@if (closingDenoms.length > 0) {
|
||
<table class="te-denom-table">
|
||
<thead>
|
||
<tr>
|
||
<th>Denomination</th>
|
||
<th class="num">Opening</th>
|
||
<th class="num">{{ selectedRow?.dbCr === 'Cr' ? '+Deposit' : '-Withdrawal' }}</th>
|
||
<th class="num">Closing</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
@for (d of sortedClosingDenoms; track d.denominationValue + d.kind) {
|
||
<tr [class.te-closing-row--neg]="d.closingCount < 0">
|
||
<td>
|
||
<span class="te-denom-chip" [class.te-denom-chip--coin]="d.kind === 'coin'"
|
||
>₹{{ d.denominationValue }}</span
|
||
>
|
||
</td>
|
||
<td class="num">{{ d.openCount }}</td>
|
||
<td class="num" [style.color]="selectedRow?.dbCr === 'Cr' ? '#059669' : '#dc2626'">
|
||
{{ selectedRow?.dbCr === 'Cr' ? '+' : '-' }}{{ d.entered }}
|
||
</td>
|
||
<td class="num" [class.te-cr]="d.closingCount >= 0" [class.te-dr]="d.closingCount < 0">
|
||
{{ d.closingCount }}
|
||
</td>
|
||
</tr>
|
||
}
|
||
</tbody>
|
||
<tfoot>
|
||
<tr>
|
||
<td><strong>Total</strong></td>
|
||
<td class="num">₹{{ openingTotal | number: '1.2-2' }}</td>
|
||
<td class="num" [style.color]="selectedRow?.dbCr === 'Cr' ? '#059669' : '#dc2626'">
|
||
{{ selectedRow?.dbCr === 'Cr' ? '+' : '-' }}₹{{ denominationTotal | number: '1.2-2' }}
|
||
</td>
|
||
<td class="num" [class.te-cr]="closingTotal >= 0" [class.te-dr]="closingTotal < 0">
|
||
<strong>₹{{ closingTotal | number: '1.2-2' }}</strong>
|
||
</td>
|
||
</tr>
|
||
</tfoot>
|
||
</table>
|
||
}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
}
|
||
|
||
<!-- ── ENTRY CARD (bottom — same design as top) ──────────────── -->
|
||
<div class="ws-wrap">
|
||
<div class="ws-header">
|
||
<div class="ws-header__left">
|
||
<div class="ws-header__icon">
|
||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||
<rect x="2" y="3" width="20" height="14" rx="2" />
|
||
<line x1="8" y1="21" x2="16" y2="21" />
|
||
<line x1="12" y1="17" x2="12" y2="21" />
|
||
</svg>
|
||
</div>
|
||
<span class="ws-header__title">Transaction Entry</span>
|
||
</div>
|
||
<div class="ws-header__right">
|
||
<span class="ws-header__effdt">
|
||
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||
<rect x="3" y="4" width="18" height="18" rx="2" />
|
||
<line x1="16" y1="2" x2="16" y2="6" />
|
||
<line x1="8" y1="2" x2="8" y2="6" />
|
||
<line x1="3" y1="10" x2="21" y2="10" />
|
||
</svg>
|
||
Eff. Date
|
||
<input class="ws-header__date" type="date" [value]="todayIso" readonly />
|
||
</span>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="ws-rows">
|
||
<fieldset class="ws-rows-fieldset" [disabled]="formReadOnly">
|
||
@for (row of rows; track $index; let i = $index) {
|
||
<div class="ws-card" [class.ws-card--active]="selectedRow === row" (click)="onRowSelect(row, i)">
|
||
<div class="ws-card__badge">{{ i + 1 }}</div>
|
||
<div class="ws-card__body">
|
||
<div class="ws-group-row">
|
||
<div class="ws-group">
|
||
<span class="ws-caption">Classification</span>
|
||
<div class="ws-group__fields">
|
||
<div class="ws-f ws-f--type">
|
||
<label class="ws-f__lbl">TYPE</label>
|
||
<select class="ws-f__ctl" [(ngModel)]="row.typ">
|
||
<option value="~">~</option>
|
||
<option value="Withdrawal Slip">Withdrawal Slip</option>
|
||
<option value="Cheque">Cheque</option>
|
||
<option value="Disbursement">Disbursement</option>
|
||
<option value="Enter Bank Transfer">Enter Bank Transfer</option>
|
||
<option value="DD">DD (Demand Draft)</option>
|
||
</select>
|
||
</div>
|
||
<div class="ws-f ws-f--branch">
|
||
<label class="ws-f__lbl">BRANCH</label>
|
||
<select class="ws-f__ctl" [(ngModel)]="row.br">
|
||
<option [ngValue]="1">001 · Main</option>
|
||
<option [ngValue]="2">002 · M.G. Road</option>
|
||
<option [ngValue]="14">014 · Station Rd</option>
|
||
<option [ngValue]="27">027 · Industrial</option>
|
||
</select>
|
||
</div>
|
||
<div class="ws-f ws-f--paytype">
|
||
<label class="ws-f__lbl ws-f__lbl--accent">TXN TP.</label>
|
||
<select
|
||
class="ws-f__ctl ws-f__ctl--accent"
|
||
[(ngModel)]="row.paymentTypeId"
|
||
[class.ws-f__ctl--req]="!row.paymentTypeId"
|
||
>
|
||
<option [ngValue]="null">— Select —</option>
|
||
@for (pt of paymentTypes; track pt.id) {
|
||
<option [ngValue]="pt.id">{{ pt.name }}</option>
|
||
}
|
||
</select>
|
||
</div>
|
||
<div class="ws-f ws-f--actyp">
|
||
<label class="ws-f__lbl">AC. TP.</label>
|
||
<select
|
||
class="ws-f__ctl"
|
||
[(ngModel)]="row.acTyp"
|
||
(change)="
|
||
row.acTyp === 'GL' &&
|
||
row.typ !== 'Disbursement' &&
|
||
row.typ !== 'Enter Bank Transfer' &&
|
||
(row.dbCr = 'Cr')
|
||
"
|
||
>
|
||
@for (a of acTypOptions; track a) {
|
||
<option [value]="a">{{ a }}</option>
|
||
}
|
||
</select>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="ws-vdivider"></div>
|
||
<div class="ws-group ws-group--flex">
|
||
<span class="ws-caption">Account</span>
|
||
<div class="ws-group__fields">
|
||
<div class="ws-f ws-f--acno">
|
||
<label class="ws-f__lbl">A/C NO.</label>
|
||
<input
|
||
class="ws-f__ctl ws-f__ctl--mono"
|
||
[value]="row.acNo"
|
||
(input)="onAcNoInput(row, i, $any($event.target).value)"
|
||
(blur)="onAcNoBlur(row, i)"
|
||
placeholder="Account number"
|
||
/>
|
||
</div>
|
||
<div class="ws-f ws-f--ecsno">
|
||
<label class="ws-f__lbl">ECS A/C NO.</label>
|
||
<input
|
||
class="ws-f__ctl ws-f__ctl--mono"
|
||
[(ngModel)]="row.ecsAcNo"
|
||
placeholder="ECS account"
|
||
/>
|
||
</div>
|
||
<div class="ws-f ws-f--dbcr">
|
||
<label class="ws-f__lbl">DR./CR.</label>
|
||
<div class="ws-seg">
|
||
<button
|
||
type="button"
|
||
class="ws-seg__btn ws-seg__btn--dr"
|
||
[class.ws-seg__btn--active-dr]="row.dbCr === 'Dr'"
|
||
[disabled]="
|
||
row.acTyp === 'GL' && row.typ !== 'Disbursement' && row.typ !== 'Enter Bank Transfer'
|
||
"
|
||
(click)="$event.stopPropagation(); row.dbCr = 'Dr'"
|
||
>
|
||
DR
|
||
</button>
|
||
<button
|
||
type="button"
|
||
class="ws-seg__btn ws-seg__btn--cr"
|
||
[class.ws-seg__btn--active-cr]="row.dbCr === 'Cr'"
|
||
(click)="$event.stopPropagation(); row.dbCr = 'Cr'"
|
||
>
|
||
CR
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="ws-group-row ws-group-row--mt">
|
||
<div class="ws-group">
|
||
<span class="ws-caption">Payment instrument</span>
|
||
<div class="ws-group__fields">
|
||
<div class="ws-f ws-f--amt">
|
||
<label class="ws-f__lbl">
|
||
AMOUNT
|
||
@if (!isClerk && activeWithdrawalRequest) {
|
||
<span class="ws-amt-locked-badge" title="Amount locked — approved withdrawal">🔒</span>
|
||
}
|
||
</label>
|
||
<div
|
||
class="ws-amt-wrap"
|
||
[class.ws-amt-wrap--cash]="isCashPayment && selectedRow === row"
|
||
[class.ws-amt-wrap--locked]="!isClerk && !!activeWithdrawalRequest"
|
||
>
|
||
<span class="ws-amt-prefix">₹</span>
|
||
<input
|
||
class="ws-f__ctl ws-amt-input"
|
||
type="number"
|
||
[value]="row.amount ?? ''"
|
||
(input)="row.amount = +$any($event.target).value"
|
||
(click)="$event.stopPropagation()"
|
||
placeholder="0.00"
|
||
[readonly]="!isClerk && !!activeWithdrawalRequest"
|
||
[class.ws-amt-input--locked]="!isClerk && !!activeWithdrawalRequest"
|
||
/>
|
||
</div>
|
||
</div>
|
||
<div class="ws-f ws-f--cheqno">
|
||
<label class="ws-f__lbl">CHEQUE NO.</label>
|
||
<input class="ws-f__ctl ws-f__ctl--mono" [(ngModel)]="row.chequeNo" placeholder="—" />
|
||
</div>
|
||
<div class="ws-f ws-f--cheqdt">
|
||
<label class="ws-f__lbl">CHEQ. DATE</label>
|
||
<input class="ws-f__ctl" [(ngModel)]="row.cheqDate" placeholder="dd-mm-yyyy" />
|
||
</div>
|
||
<div class="ws-f ws-f--tok">
|
||
<label class="ws-f__lbl">TOK. NO.</label>
|
||
<input
|
||
class="ws-f__ctl"
|
||
[(ngModel)]="row.tokNo"
|
||
[readonly]="!isClerk && !!activeWithdrawalRequest"
|
||
[class.ws-f__ctl--readonly]="!isClerk && !!activeWithdrawalRequest"
|
||
placeholder="Enter token"
|
||
(blur)="onTokNoBlur(row)"
|
||
/>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="ws-vdivider"></div>
|
||
<div class="ws-group ws-group--flex">
|
||
<span class="ws-caption">Narration</span>
|
||
<div class="ws-group__fields">
|
||
<div class="ws-f ws-f--desc">
|
||
<label class="ws-f__lbl">DESCRIPTION</label>
|
||
<input class="ws-f__ctl" [(ngModel)]="row.description" placeholder="Narration / remarks" />
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
}
|
||
</fieldset>
|
||
</div>
|
||
|
||
<!-- Action buttons footer -->
|
||
@if (reviewMode) {
|
||
<div class="ws-footer">
|
||
<div class="ws-footer__stats">
|
||
<button class="ws-btn ws-btn--ghost ws-btn--exit" type="button" (click)="backToInbox()">
|
||
← Back to Inbox
|
||
</button>
|
||
@if (reviewRequest) {
|
||
<span class="ws-review-status" [attr.data-status]="reviewRequest.status">
|
||
{{ reviewRequest.status }}
|
||
</span>
|
||
}
|
||
</div>
|
||
<div class="ws-footer__actions">
|
||
@if (reviewRequest?.status === 'PENDING') {
|
||
<button class="ws-btn ws-btn--reject" type="button" (click)="openRejectReview()" [disabled]="saving">
|
||
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5">
|
||
<line x1="18" y1="6" x2="6" y2="18" />
|
||
<line x1="6" y1="6" x2="18" y2="18" />
|
||
</svg>
|
||
Reject
|
||
</button>
|
||
<button class="ws-btn ws-btn--post" type="button" (click)="approveReview()" [disabled]="saving">
|
||
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5">
|
||
<polyline points="20 6 9 17 4 12" />
|
||
</svg>
|
||
{{ saving ? 'Processing…' : 'Approve & Post' }}
|
||
</button>
|
||
}
|
||
</div>
|
||
</div>
|
||
} @else if (editMode) {
|
||
<div class="ws-footer">
|
||
<div class="ws-footer__stats">
|
||
<button class="ws-btn ws-btn--ghost ws-btn--exit" type="button" (click)="cancelEdit()">
|
||
← My Requests
|
||
</button>
|
||
@if (editRequest) {
|
||
<span class="ws-review-status" [attr.data-status]="editRequest.status">
|
||
{{ editResubmittable ? 'EDITING REJECTED' : editRequest.status }}
|
||
</span>
|
||
}
|
||
</div>
|
||
<div class="ws-footer__actions">
|
||
@if (editResubmittable) {
|
||
<button class="ws-btn ws-btn--post" type="button" (click)="resubmitEdit()" [disabled]="saving">
|
||
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5">
|
||
<polyline points="20 6 9 17 4 12" />
|
||
</svg>
|
||
{{ saving ? 'Resubmitting…' : 'Resubmit for Approval' }}
|
||
</button>
|
||
}
|
||
</div>
|
||
</div>
|
||
} @else {
|
||
@if (isClerkCashDeposit) {
|
||
<div class="te-day-block te-day-block--warning">
|
||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5">
|
||
<path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z" />
|
||
<line x1="12" y1="9" x2="12" y2="13" />
|
||
<line x1="12" y1="17" x2="12.01" y2="17" />
|
||
</svg>
|
||
<span>
|
||
<strong>CASH DEPOSIT NOT PERMITTED</strong> — Cash deposits can only be processed by the cashier.
|
||
Notes/coins denomination is managed at the cashier vault.
|
||
</span>
|
||
</div>
|
||
}
|
||
<div class="ws-footer">
|
||
<div class="ws-footer__stats">
|
||
<button class="ws-btn ws-btn--ghost ws-btn--exit" type="button" (click)="onExit()">Exit</button>
|
||
<button class="ws-btn ws-btn--ghost" type="button" (click)="onCancel()">Cancel</button>
|
||
<button class="ws-btn ws-btn--ghost" type="button">Drop</button>
|
||
<button class="ws-btn ws-btn--ghost" type="button" (click)="onSearch()">Search</button>
|
||
<button class="ws-btn ws-btn--mode" type="button">Change Tr.Mode</button>
|
||
@if (isClerk) {
|
||
<button class="ws-btn ws-btn--add" type="button" (click)="addRow()" title="Add another transaction row">
|
||
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5">
|
||
<line x1="12" y1="5" x2="12" y2="19" />
|
||
<line x1="5" y1="12" x2="19" y2="12" />
|
||
</svg>
|
||
Add
|
||
</button>
|
||
<button
|
||
class="ws-btn ws-btn--ghost"
|
||
type="button"
|
||
(click)="router.navigate(['/clerk/my-disbursement-requests'])"
|
||
title="View my disbursement requests"
|
||
>
|
||
My Disbursements
|
||
</button>
|
||
}
|
||
</div>
|
||
<div class="ws-footer__actions">
|
||
<button
|
||
class="ws-btn ws-btn--post"
|
||
type="button"
|
||
(click)="onSave()"
|
||
[disabled]="saving || isClerkCashDeposit"
|
||
>
|
||
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5">
|
||
<polyline points="20 6 9 17 4 12" />
|
||
</svg>
|
||
{{ saving ? 'Posting…' : 'Save' }}
|
||
</button>
|
||
</div>
|
||
</div>
|
||
}
|
||
</div>
|
||
</div>
|
||
}
|
||
</div>
|
||
|
||
<!-- ── REJECT DIALOG (manager review) ───────────────────────────────────── -->
|
||
@if (showRejectDialog) {
|
||
<div class="te-confirm-overlay" (click)="cancelRejectReview()">
|
||
<div class="te-confirm-box" (click)="$event.stopPropagation()">
|
||
<div class="te-confirm-header te-confirm-header--withdrawal">
|
||
<span class="te-confirm-icon">✕</span>
|
||
<span>Reject Withdrawal Request</span>
|
||
</div>
|
||
<div class="te-confirm-body">
|
||
<p class="ws-reject-hint">
|
||
Please provide a reason. The request will be sent back to the clerk for correction and resubmission.
|
||
</p>
|
||
<textarea
|
||
class="ws-reject-textarea"
|
||
[(ngModel)]="rejectionReason"
|
||
rows="4"
|
||
placeholder="e.g. Amount does not match the withdrawal slip; please verify and resubmit."
|
||
></textarea>
|
||
</div>
|
||
<div class="te-confirm-actions">
|
||
<button class="te-btn" type="button" (click)="cancelRejectReview()">Cancel</button>
|
||
<button class="te-btn te-btn--danger" type="button" [disabled]="saving" (click)="confirmRejectReview()">
|
||
{{ saving ? 'Rejecting…' : 'Confirm Reject' }}
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
}
|
||
|
||
<!-- ── CONFIRMATION DIALOG ──────────────────────────────────────────────── -->
|
||
@if (showConfirmDialog && confirmData) {
|
||
<div class="te-confirm-overlay" (click)="showConfirmDialog = false">
|
||
<div class="te-confirm-box" (click)="$event.stopPropagation()">
|
||
<div
|
||
class="te-confirm-header"
|
||
[class.te-confirm-header--deposit]="confirmData.type === 'DEPOSIT'"
|
||
[class.te-confirm-header--withdrawal]="confirmData.type === 'WITHDRAWAL'"
|
||
[class.te-confirm-header--disbursement]="confirmData.isDisbursement || confirmData.isDepositRequest"
|
||
>
|
||
<span class="te-confirm-icon">{{ confirmData.type === 'WITHDRAWAL' ? '⬆' : '⬇' }}</span>
|
||
<span>Confirm {{ confirmData.isDisbursement ? 'CASH DISBURSEMENT' : confirmData.type }}</span>
|
||
</div>
|
||
<div class="te-confirm-body">
|
||
<div class="te-confirm-row">
|
||
<span class="te-confirm-label">Account</span
|
||
><span class="te-confirm-val te-mono">{{ confirmData.accountNo }}</span>
|
||
</div>
|
||
<div class="te-confirm-row">
|
||
<span class="te-confirm-label">Customer</span><span class="te-confirm-val">{{ confirmData.clientName }}</span>
|
||
</div>
|
||
<div class="te-confirm-row">
|
||
<span class="te-confirm-label">Payment Type</span
|
||
><span class="te-confirm-val">{{ confirmData.paymentType }}</span>
|
||
</div>
|
||
@if (confirmData.chequeNo) {
|
||
<div class="te-confirm-row">
|
||
<span class="te-confirm-label">Cheque No.</span
|
||
><span class="te-confirm-val te-mono">{{ confirmData.chequeNo }}</span>
|
||
</div>
|
||
}
|
||
@if (confirmData.tokNo) {
|
||
<div class="te-confirm-row">
|
||
<span class="te-confirm-label">Token No.</span
|
||
><span class="te-confirm-val te-mono"
|
||
><strong>{{ confirmData.tokNo }}</strong></span
|
||
>
|
||
</div>
|
||
}
|
||
<div class="te-confirm-divider"></div>
|
||
|
||
@if (confirmData.isDisbursement) {
|
||
<!-- Token-based disbursement: balance was already deducted at manager approval -->
|
||
<div class="te-confirm-row">
|
||
<span class="te-confirm-label">Current Balance</span>
|
||
<span class="te-confirm-val"
|
||
>{{ confirmData.symbol }}{{ confirmData.currentBalance | number: '1.2-2' }}</span
|
||
>
|
||
</div>
|
||
<div class="te-confirm-row te-confirm-row--amount">
|
||
<span class="te-confirm-label">Disburse Amount</span>
|
||
<span class="te-confirm-val te-cr">{{ confirmData.symbol }}{{ confirmData.amount | number: '1.2-2' }}</span>
|
||
</div>
|
||
<div class="te-confirm-row te-confirm-disbursed-note">
|
||
<span class="te-confirm-disbursed-badge"
|
||
>Balance already deducted at manager approval — no further deduction</span
|
||
>
|
||
</div>
|
||
} @else if (confirmData.isDepositRequest) {
|
||
<!-- Maker-checker deposit: balance credits only after manager approval -->
|
||
<div class="te-confirm-row">
|
||
<span class="te-confirm-label">Current Balance</span>
|
||
<span class="te-confirm-val"
|
||
>{{ confirmData.symbol }}{{ confirmData.currentBalance | number: '1.2-2' }}</span
|
||
>
|
||
</div>
|
||
<div class="te-confirm-row te-confirm-row--amount">
|
||
<span class="te-confirm-label">Deposit Amount</span>
|
||
<span class="te-confirm-val te-cr"
|
||
>+{{ confirmData.symbol }}{{ confirmData.amount | number: '1.2-2' }}</span
|
||
>
|
||
</div>
|
||
<div class="te-confirm-row te-confirm-disbursed-note">
|
||
<span class="te-confirm-disbursed-badge"
|
||
>Sent for manager approval — balance will be credited only after approval</span
|
||
>
|
||
</div>
|
||
} @else {
|
||
<div class="te-confirm-row">
|
||
<span class="te-confirm-label">Current Balance</span
|
||
><span class="te-confirm-val"
|
||
>{{ confirmData.symbol }}{{ confirmData.currentBalance | number: '1.2-2' }}</span
|
||
>
|
||
</div>
|
||
<div class="te-confirm-row te-confirm-row--amount">
|
||
<span class="te-confirm-label">{{ confirmData.type === 'DEPOSIT' ? 'Deposit' : 'Withdrawal' }} Amount</span>
|
||
<span
|
||
class="te-confirm-val"
|
||
[class.te-cr]="confirmData.type === 'DEPOSIT'"
|
||
[class.te-dr]="confirmData.type === 'WITHDRAWAL'"
|
||
>
|
||
{{ confirmData.type === 'DEPOSIT' ? '+' : '−' }}{{ confirmData.symbol
|
||
}}{{ confirmData.amount | number: '1.2-2' }}
|
||
</span>
|
||
</div>
|
||
<div class="te-confirm-row te-confirm-row--after">
|
||
<span class="te-confirm-label">Balance After</span>
|
||
<span
|
||
class="te-confirm-val"
|
||
[class.te-cr]="confirmData.balanceAfter >= 0"
|
||
[class.te-dr]="confirmData.balanceAfter < 0"
|
||
>
|
||
<strong>{{ confirmData.symbol }}{{ confirmData.balanceAfter | number: '1.2-2' }}</strong>
|
||
</span>
|
||
</div>
|
||
}
|
||
</div>
|
||
<div class="te-confirm-actions">
|
||
<button class="te-btn" type="button" (click)="showConfirmDialog = false">Cancel</button>
|
||
<button class="te-btn te-btn--primary" type="button" [disabled]="saving" (click)="confirmSubmit()">
|
||
{{
|
||
saving
|
||
? 'Posting…'
|
||
: confirmData.isDisbursement
|
||
? 'Confirm DISBURSEMENT'
|
||
: confirmData.isDepositRequest
|
||
? 'Submit for Approval'
|
||
: 'Confirm ' + confirmData.type
|
||
}}
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
}
|
||
|
||
<!-- ── Custom Alert / Confirm Modal ──────────────────────────────────────── -->
|
||
@if (alertModal) {
|
||
<div class="te-alert-overlay" (click)="closeAlert()">
|
||
<div class="te-alert-box te-alert-box--{{ alertModal.type }}" (click)="$event.stopPropagation()">
|
||
<!-- Left sidebar -->
|
||
<div class="te-alert-box__sidebar">
|
||
<div class="te-alert-box__sidebar-icon">
|
||
@if (alertModal.type === 'error') {
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round">
|
||
<circle cx="12" cy="12" r="10" />
|
||
<line x1="15" y1="9" x2="9" y2="15" />
|
||
<line x1="9" y1="9" x2="15" y2="15" />
|
||
</svg>
|
||
}
|
||
@if (alertModal.type === 'warning') {
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round">
|
||
<path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z" />
|
||
<line x1="12" y1="9" x2="12" y2="13" />
|
||
<line x1="12" y1="17" x2="12.01" y2="17" />
|
||
</svg>
|
||
}
|
||
@if (alertModal.type === 'info') {
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round">
|
||
<circle cx="12" cy="12" r="10" />
|
||
<line x1="12" y1="8" x2="12" y2="12" />
|
||
<line x1="12" y1="16" x2="12.01" y2="16" />
|
||
</svg>
|
||
}
|
||
@if (alertModal.type === 'success') {
|
||
<svg
|
||
viewBox="0 0 24 24"
|
||
fill="none"
|
||
stroke="currentColor"
|
||
stroke-width="2.5"
|
||
stroke-linecap="round"
|
||
stroke-linejoin="round"
|
||
>
|
||
<circle cx="12" cy="12" r="10" />
|
||
<polyline points="9 12 11 14 15 10" />
|
||
</svg>
|
||
}
|
||
</div>
|
||
<span class="te-alert-box__sidebar-type">
|
||
{{
|
||
alertModal.type === 'error'
|
||
? 'ERROR'
|
||
: alertModal.type === 'warning'
|
||
? 'WARN'
|
||
: alertModal.type === 'success'
|
||
? 'OK'
|
||
: 'INFO'
|
||
}}
|
||
</span>
|
||
</div>
|
||
|
||
<!-- Right content -->
|
||
<div class="te-alert-box__content">
|
||
<div class="te-alert-box__content-head">
|
||
<h3>{{ alertModal.title }}</h3>
|
||
<button class="te-alert-box__close" (click)="closeAlert()">
|
||
<svg
|
||
width="14"
|
||
height="14"
|
||
viewBox="0 0 24 24"
|
||
fill="none"
|
||
stroke="currentColor"
|
||
stroke-width="2.5"
|
||
stroke-linecap="round"
|
||
>
|
||
<line x1="18" y1="6" x2="6" y2="18" />
|
||
<line x1="6" y1="6" x2="18" y2="18" />
|
||
</svg>
|
||
</button>
|
||
</div>
|
||
|
||
<div class="te-alert-box__lines">
|
||
@for (line of alertModal.lines; track $index) {
|
||
@if ($index === 0) {
|
||
<p class="te-alert-box__desc">{{ line }}</p>
|
||
} @else if (line.includes(':') && $index < alertModal.lines.length - 1) {
|
||
<div class="te-alert-box__kv">
|
||
<span class="te-alert-box__kv-k">{{ line.split(':')[0].trim() }}</span>
|
||
<span class="te-alert-box__kv-v">{{ line.split(':').slice(1).join(':').trim() }}</span>
|
||
</div>
|
||
} @else {
|
||
<p class="te-alert-box__foot">{{ line }}</p>
|
||
}
|
||
}
|
||
</div>
|
||
|
||
<div class="te-alert-box__actions">
|
||
@if (alertModal.onConfirm) {
|
||
<button class="te-alert-btn te-alert-btn--ghost" type="button" (click)="closeAlert()">Cancel</button>
|
||
<button
|
||
class="te-alert-btn te-alert-btn--solid te-alert-btn--{{ alertModal.type }}"
|
||
type="button"
|
||
(click)="confirmAlert()"
|
||
>
|
||
{{ alertModal.confirmLabel ?? 'Confirm' }}
|
||
</button>
|
||
} @else {
|
||
<button
|
||
class="te-alert-btn te-alert-btn--solid te-alert-btn--{{ alertModal.type }}"
|
||
type="button"
|
||
(click)="closeAlert()"
|
||
>
|
||
{{ alertModal.type === 'success' ? 'Done' : 'OK, Got It' }}
|
||
</button>
|
||
}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
}
|