Files
cbsnexora/src/app/cashier/cash-exchange-entry/cash-exchange-entry.component.scss
T
niteshandClaude Sonnet 5.5 915deddb74 feat(cashier): cashier-only cash operations, exchange PAN/denomination checks and Branch Cash Position
- Cash Exchange Entry: customer details with PAN (mandatory above Rs 50,000), cashier card,
  denomination-wise validation against the master and real-time impact preview.
- Inter-Cashier Transfer: sender fixed to the logged-in cashier, both sides' details and
  denomination holdings shown, blocked until both are allocated cash.
- Reconciliation, exchange and inter-cashier operate on the logged-in cashier only.
- New Branch Cash Position screen (vault + cashier drawers, denomination-wise).
- Customer search dialog shows the KYC PAN.
- Cashier menu cleanup: Excess/Closing report and Exchange Detail removed, nav compacted to one row.
- Reverse actions removed from cash transfer, settlement, exchange and inter-cashier.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-13 18:41:24 +05:30

1147 lines
18 KiB
SCSS

/**
* 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/.
*/
:host {
display: block;
}
.cx {
padding: 20px 24px;
display: flex;
flex-direction: column;
gap: 16px;
}
.cx-head {
display: flex;
align-items: flex-start;
justify-content: space-between;
gap: 16px;
flex-wrap: wrap;
}
.cx-eyebrow {
font-size: 11px;
font-weight: 700;
letter-spacing: 0.1em;
color: #0f4f3c;
}
.cx-title {
font-size: 26px;
font-weight: 600;
margin: 4px 0 2px;
color: #1a1a1a;
}
.cx-sub {
font-size: 13px;
color: #64748b;
margin: 0;
}
.cx-toolbar {
display: flex;
align-items: flex-end;
gap: 10px;
}
.cx-field {
display: flex;
flex-direction: column;
gap: 4px;
font-size: 11px;
font-weight: 600;
color: #64748b;
input {
height: 36px;
padding: 0 10px;
border: 1px solid #e2e8f0;
border-radius: 8px;
font-size: 13px;
color: #0d1b2a;
}
&--note {
flex: 1 1 auto;
}
}
/* ── Customer: existing client or walk-in, plus PAN ── */
.cx-cust {
background: #fff;
border: 1px solid #e2e8f0;
border-radius: 14px;
box-shadow: 0 1px 2px rgb(15 23 42 / 4%);
overflow: hidden;
&__head {
display: flex;
align-items: center;
justify-content: space-between;
flex-wrap: wrap;
gap: 12px;
padding: 12px 16px;
background: linear-gradient(180deg, #f8fafc, #f1f5f9);
border-bottom: 1px solid #e2e8f0;
}
&__title {
display: flex;
align-items: center;
gap: 10px;
}
&__title-icon {
display: grid;
place-items: center;
width: 36px;
height: 36px;
border-radius: 10px;
background: #e6f4ee;
color: #0f4f3c;
mat-icon {
font-size: 20px;
width: 20px;
height: 20px;
}
}
&__title-text {
font-size: 14px;
font-weight: 700;
color: #0f172a;
}
&__title-sub {
font-size: 12px;
color: #64748b;
}
&__modes {
display: inline-flex;
padding: 3px;
background: #e2e8f0;
border-radius: 10px;
}
&__mode {
display: inline-flex;
align-items: center;
gap: 6px;
height: 32px;
padding: 0 14px;
border: 0;
border-radius: 8px;
background: transparent;
font-size: 13px;
font-weight: 600;
color: #475569;
cursor: pointer;
transition:
background 0.15s ease,
color 0.15s ease;
mat-icon {
font-size: 18px;
width: 18px;
height: 18px;
}
&:hover {
color: #0f172a;
}
&--active,
&--active:hover {
background: #fff;
color: #0f4f3c;
box-shadow: 0 1px 3px rgb(15 23 42 / 15%);
}
}
&__body {
display: grid;
grid-template-columns: minmax(0, 1.7fr) minmax(260px, 1fr);
gap: 16px;
padding: 16px;
}
&__who {
min-width: 0;
}
/* Nothing picked yet, or loading */
&__empty {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 4px;
min-height: 150px;
padding: 20px;
text-align: center;
background: #f8fafc;
border: 1.5px dashed #cbd5e1;
border-radius: 12px;
}
&__empty-icon {
font-size: 36px;
width: 36px;
height: 36px;
color: #94a3b8;
}
&__empty-title {
font-size: 14px;
font-weight: 600;
color: #334155;
}
&__empty-sub {
margin-bottom: 8px;
font-size: 12px;
color: #64748b;
}
/* A picked client */
&__card {
padding: 14px;
background: #fff;
border: 1px solid #e2e8f0;
border-radius: 12px;
}
&__card-top {
display: flex;
align-items: center;
flex-wrap: wrap;
gap: 12px;
}
&__card-id {
flex: 1 1 180px;
min-width: 0;
}
&__name {
font-size: 16px;
font-weight: 700;
color: #0f172a;
overflow-wrap: anywhere;
}
&__badges {
display: flex;
align-items: center;
flex-wrap: wrap;
gap: 6px;
margin-top: 4px;
}
&__card-actions {
display: flex;
gap: 4px;
}
&__grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
gap: 12px 16px;
margin-top: 12px;
padding-top: 12px;
border-top: 1px dashed #e2e8f0;
span {
display: block;
margin-bottom: 2px;
font-size: 11px;
color: #64748b;
}
strong {
font-size: 13px;
font-weight: 600;
color: #0f172a;
}
}
/* A walk-in, identified at the counter */
&__walkin {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 12px;
.cx-field {
font-size: 12px;
}
.cx-field input {
width: 100%;
height: 40px;
box-sizing: border-box;
}
}
.cx-field input:focus {
outline: none;
border-color: #1f7a4d;
box-shadow: 0 0 0 3px rgb(31 122 77 / 15%);
}
}
.cx-avatar {
display: grid;
place-items: center;
flex: 0 0 auto;
width: 44px;
height: 44px;
border-radius: 50%;
background: linear-gradient(135deg, #1f7a4d, #0f4f3c);
font-size: 15px;
font-weight: 700;
letter-spacing: 0.02em;
color: #fff;
&--sm {
width: 40px;
height: 40px;
font-size: 14px;
}
}
/* The cashier at the counter, or why there is none */
.cx-cashier {
display: flex;
align-items: center;
flex-wrap: wrap;
gap: 14px 20px;
padding: 12px 16px;
background: #fff;
border: 1px solid #e2e8f0;
border-left: 4px solid #1f7a4d;
border-radius: 14px;
box-shadow: 0 1px 2px rgb(15 23 42 / 4%);
&__id {
flex: 1 1 220px;
min-width: 0;
}
&__label {
font-size: 11px;
font-weight: 600;
letter-spacing: 0.04em;
text-transform: uppercase;
color: #64748b;
}
&__name {
font-size: 16px;
font-weight: 700;
color: #0f172a;
}
&__flow {
display: flex;
flex-wrap: wrap;
gap: 4px 14px;
margin-top: 2px;
font-size: 12px;
color: #475569;
span {
display: inline-flex;
align-items: center;
gap: 2px;
}
mat-icon {
font-size: 14px;
width: 14px;
height: 14px;
color: #94a3b8;
}
}
&__stats {
display: flex;
flex-wrap: wrap;
gap: 10px 28px;
span {
display: block;
font-size: 11px;
color: #64748b;
}
strong {
font-size: 14px;
font-weight: 600;
color: #0f172a;
}
}
&--none {
background: #fffbeb;
border-color: #fcd34d;
border-left-color: #d97706;
mat-icon {
color: #d97706;
}
.cx-cashier__label {
font-weight: 500;
letter-spacing: 0;
text-transform: none;
color: #92400e;
}
}
}
.cx-kyc {
display: inline-flex;
align-items: center;
gap: 4px;
padding: 2px 8px;
border-radius: 999px;
font-size: 11px;
font-weight: 600;
mat-icon {
font-size: 14px;
width: 14px;
height: 14px;
}
&--ok {
background: #dcfce7;
color: #166534;
}
&--warn {
background: #fef3c7;
color: #92400e;
}
&--sm {
margin-left: 6px;
padding: 1px 6px;
font-size: 10px;
}
}
.cx-chip-tag {
padding: 2px 8px;
border-radius: 6px;
background: #eef2ff;
font-size: 11px;
font-weight: 600;
color: #3730a3;
}
.cx-link-btn {
display: inline-flex;
align-items: center;
gap: 4px;
height: 30px;
padding: 0 10px;
border: 0;
border-radius: 8px;
background: transparent;
font-size: 12px;
font-weight: 600;
color: #1e40af;
cursor: pointer;
mat-icon {
font-size: 16px;
width: 16px;
height: 16px;
}
&:hover {
background: #eff6ff;
}
&--danger {
color: #b91c1c;
&:hover {
background: #fef2f2;
}
}
}
/* PAN: neutral until needed, amber when required and missing, red when malformed, green when valid */
.cx-pan {
display: flex;
flex-direction: column;
align-self: start;
gap: 8px;
padding: 14px;
background: #f8fafc;
border: 1px solid #e2e8f0;
border-radius: 12px;
&__head {
display: flex;
align-items: center;
gap: 8px;
mat-icon {
font-size: 20px;
width: 20px;
height: 20px;
color: #475569;
}
}
&__label {
font-size: 13px;
font-weight: 700;
color: #0f172a;
}
&__tag {
margin-left: auto;
padding: 2px 8px;
border-radius: 999px;
background: #e2e8f0;
font-size: 11px;
font-weight: 600;
color: #475569;
&--on {
background: #fef3c7;
color: #92400e;
}
}
&__input {
position: relative;
input {
width: 100%;
height: 44px;
box-sizing: border-box;
padding: 0 40px 0 12px;
background: #fff;
border: 1.5px solid #cbd5e1;
border-radius: 10px;
font-family: ui-monospace, sfmono-regular, menlo, consolas, monospace;
font-size: 18px;
font-weight: 600;
letter-spacing: 0.18em;
text-transform: uppercase;
color: #0f172a;
&::placeholder {
font-weight: 500;
color: #cbd5e1;
}
&:focus {
outline: none;
border-color: #1f7a4d;
box-shadow: 0 0 0 3px rgb(31 122 77 / 15%);
}
}
}
&__state {
position: absolute;
top: 50%;
right: 10px;
transform: translateY(-50%);
font-size: 22px;
width: 22px;
height: 22px;
&--ok {
color: #16a34a;
}
&--bad {
color: #dc2626;
}
}
&__hint {
display: flex;
align-items: center;
gap: 4px;
font-size: 12px;
color: #64748b;
mat-icon {
font-size: 14px;
width: 14px;
height: 14px;
}
}
&--required {
background: #fffbeb;
border-color: #fcd34d;
.cx-pan__input input {
border-color: #f59e0b;
}
.cx-pan__hint {
font-weight: 600;
color: #92400e;
}
}
&--invalid {
background: #fef2f2;
border-color: #fecaca;
.cx-pan__input input {
border-color: #ef4444;
}
.cx-pan__hint {
font-weight: 600;
color: #b91c1c;
}
}
&--valid {
background: #f0fdf4;
border-color: #bbf7d0;
.cx-pan__input input {
border-color: #22c55e;
}
.cx-pan__hint {
color: #166534;
}
}
}
.cx-spin {
animation: cx-spin 1s linear infinite;
}
@keyframes cx-spin {
to {
transform: rotate(360deg);
}
}
@media (width <= 900px) {
.cx-cust__body {
grid-template-columns: 1fr;
}
}
@media (width <= 600px) {
.cx-cust__walkin {
grid-template-columns: 1fr;
}
}
.cx-warn--pan {
background: #eff6ff;
border-color: #bfdbfe;
color: #1e40af;
mat-icon {
color: #2563eb;
}
}
.cx-overdraft-card {
background: #fff5f5;
border: 1.5px solid #fca5a5;
border-left: 4px solid #ef4444;
border-radius: 10px;
overflow: hidden;
&__title {
display: flex;
align-items: center;
gap: 8px;
padding: 9px 14px;
font-size: 13px;
font-weight: 700;
color: #7f1d1d;
background: #fef2f2;
border-bottom: 1px solid #fecaca;
}
&__ico {
color: #ef4444;
font-size: 18px;
height: 18px;
width: 18px;
flex-shrink: 0;
}
&__rows {
display: flex;
flex-direction: column;
padding: 4px 0;
}
&__row {
display: flex;
align-items: center;
gap: 10px;
padding: 7px 14px;
font-size: 12.5px;
color: #7f1d1d;
&:not(:last-child) {
border-bottom: 1px dashed #fecaca;
}
}
&__chip {
display: inline-flex;
align-items: center;
justify-content: center;
padding: 3px 10px;
background: linear-gradient(135deg, #fee2e2, #fecaca);
border: 1px solid #fca5a5;
border-radius: 6px;
font-size: 12px;
font-weight: 800;
color: #991b1b;
min-width: 52px;
letter-spacing: 0.01em;
}
&__stat {
display: flex;
align-items: center;
gap: 4px;
strong {
font-weight: 800;
font-variant-numeric: tabular-nums;
}
}
&__lbl {
font-size: 11px;
color: #b91c1c;
font-weight: 600;
}
&__dot {
color: #fca5a5;
font-weight: 700;
font-size: 14px;
}
&__short {
margin-left: auto;
padding: 2px 9px;
background: #fff;
border: 1.5px solid #f87171;
border-radius: 999px;
font-size: 11px;
font-weight: 800;
color: #b91c1c;
}
}
.cx-cols {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 14px;
}
.cx-col {
background: #fff;
border: 1px solid #e2e8f0;
border-radius: 12px;
overflow: hidden;
}
.cx-col-head {
display: flex;
align-items: center;
gap: 10px;
padding: 10px 14px;
color: #fff;
mat-icon {
font-size: 18px;
height: 18px;
width: 18px;
}
.cx-col-title {
flex: 1 1 auto;
font-weight: 700;
font-size: 13px;
}
.cx-col-total {
font-weight: 800;
font-variant-numeric: tabular-nums;
}
}
.cx-col--received .cx-col-head {
background: linear-gradient(135deg, #047857, #059669);
}
.cx-col--given .cx-col-head {
background: linear-gradient(135deg, #b45309, #d97706);
}
.cx-list {
padding: 6px 0;
max-height: 360px;
overflow-y: auto;
}
.cx-subhead {
font-size: 10px;
font-weight: 800;
text-transform: uppercase;
letter-spacing: 0.07em;
color: #94a3b8;
padding: 8px 14px 4px;
}
.cx-row {
display: grid;
grid-template-columns: 64px 1fr 96px;
align-items: center;
gap: 8px;
padding: 6px 14px;
border-bottom: 1px dotted #eef2f7;
}
/* Both sides: the cashier's count of each denomination sits between the counter and the amount. */
.cx-row--stock {
grid-template-columns: 64px 120px 1fr 96px;
}
.cx-row--over {
background: #fef2f2;
}
.cx-avail {
padding-left: 6px;
font-size: 12px;
color: #64748b;
strong {
font-weight: 700;
color: #0f172a;
}
/* the count after this exchange: green when it goes up, red when it goes down */
.cx-after {
display: inline-flex;
align-items: center;
gap: 3px;
margin-left: 6px;
font-weight: 600;
mat-icon {
font-size: 16px;
width: 16px;
height: 16px;
}
small {
font-size: 11px;
}
}
&--up .cx-after,
&--up .cx-after strong {
color: #15803d;
}
&--down .cx-after,
&--down .cx-after strong {
color: #b45309;
}
&--over,
&--over strong,
&--over .cx-after,
&--over .cx-after strong {
color: #b91c1c;
}
}
/* The drawer after the whole exchange, one chip per denomination it changes */
.cx-impact {
padding: 12px 14px;
background: #fff;
border: 1px solid #e2e8f0;
border-radius: 12px;
&__title {
display: flex;
align-items: center;
gap: 6px;
margin-bottom: 8px;
font-size: 13px;
font-weight: 700;
color: #0f172a;
mat-icon {
font-size: 18px;
width: 18px;
height: 18px;
color: #475569;
}
}
&__chips {
display: flex;
flex-wrap: wrap;
gap: 8px;
}
&__chip {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 5px 10px;
border-radius: 8px;
background: #f1f5f9;
font-size: 12px;
color: #334155;
b {
font-weight: 700;
}
em {
font-style: normal;
font-weight: 700;
}
&--up {
background: #dcfce7;
color: #166534;
}
&--down {
background: #fef3c7;
color: #92400e;
}
}
&__note {
margin-top: 8px;
font-size: 11px;
color: #64748b;
}
}
.cx-chip {
display: inline-flex;
align-items: center;
justify-content: center;
min-width: 52px;
height: 26px;
padding: 0 8px;
border-radius: 6px;
font-size: 12px;
font-weight: 700;
background: linear-gradient(135deg, #ecfeff, #cffafe);
color: #155e75;
border: 1px solid #a5f3fc;
&--coin {
border-radius: 50%;
min-width: 34px;
width: 34px;
height: 34px;
padding: 0;
background: linear-gradient(135deg, #fef9c3, #fde68a);
color: #854d0e;
border: 1px solid #fcd34d;
}
}
.cx-step {
display: flex;
align-items: stretch;
height: 30px;
width: 120px;
border: 1px solid #e2e8f0;
border-radius: 7px;
overflow: hidden;
button {
width: 30px;
background: #f8fafc;
border: none;
font-size: 16px;
font-weight: 700;
color: #475569;
cursor: pointer;
&:disabled {
opacity: 0.4;
cursor: not-allowed;
}
}
input {
flex: 1 1 auto;
width: 100%;
border: none;
outline: none;
text-align: center;
font-size: 13px;
font-weight: 700;
color: #0d1b2a;
appearance: textfield;
&::-webkit-outer-spin-button,
&::-webkit-inner-spin-button {
appearance: none;
margin: 0;
}
}
}
.cx-amt {
text-align: right;
font-size: 13px;
font-weight: 700;
color: #0d1b2a;
font-variant-numeric: tabular-nums;
&--zero {
color: #cbd5e1;
font-weight: 600;
}
}
.cx-summary {
display: flex;
align-items: center;
justify-content: center;
gap: 22px;
padding: 14px;
background: #f8fafc;
border: 1px solid #e2e8f0;
border-radius: 10px;
mat-icon {
color: #94a3b8;
}
}
.cx-sum {
display: flex;
flex-direction: column;
align-items: center;
gap: 2px;
span {
font-size: 10px;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.06em;
color: #64748b;
}
strong {
font-size: 16px;
font-weight: 800;
color: #0d1b2a;
font-variant-numeric: tabular-nums;
}
}
.cx-bad {
color: #b91c1c;
}
.cx-good {
color: #047857;
}
.cx-warn {
display: flex;
align-items: center;
gap: 8px;
padding: 10px 14px;
border-radius: 8px;
background: #fffbeb;
border: 1px solid #fde68a;
color: #92400e;
font-size: 13px;
mat-icon {
font-size: 18px;
height: 18px;
width: 18px;
}
}
.cx-actions {
display: flex;
align-items: flex-end;
gap: 12px;
}
.cx-btn {
display: inline-flex;
align-items: center;
gap: 6px;
height: 40px;
padding: 0 18px;
border-radius: 999px;
font-size: 13px;
font-weight: 600;
cursor: pointer;
border: 1px solid transparent;
mat-icon {
font-size: 18px;
height: 18px;
width: 18px;
}
&--ghost {
background: #fff;
border-color: #e2e8f0;
color: #475569;
}
&--primary {
background: linear-gradient(135deg, #1f7a4d, #0f4f3c);
color: #fff;
&:disabled {
opacity: 0.45;
cursor: not-allowed;
}
}
}
@media (width <= 820px) {
.cx-cols {
grid-template-columns: 1fr;
}
}