Files
cbsnexora/src/app/deposits/daily-transaction-entry/daily-transaction-entry.component.scss
T
niteshandClaude Sonnet 5.5 293b671966 feat(dds): upgrade DDS UI - agent, party, daily entry, approvals, product view
- Agent master: tabbed layout, customer search dialog, GL dropdown, account card, view page
- Party master: tabbed layout, expected party code, nominee cards, relation from code values
- Daily transaction entry: compact style, collections popup, Apply DDS Receipt placement
- Approvals: new review modal, Agent Approval tab, DDS routing into product/pending account
- DDS product: view page and list buttons; drop Auto Interest Post Day

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-28 10:50:03 +05:30

1169 lines
23 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/.
*/
/* stylelint-disable no-duplicate-selectors -- the compact-density pass below deliberately re-opens earlier rules; kept in cascade order */
$primary: #0f766e;
$primary-dark: #0f4f3c;
$primary-light: #e6f4ee;
$border: #e2e8f0;
$text-dark: #0f172a;
$text-mid: #475569;
$text-soft: #94a3b8;
$danger: #b91c1c;
.dte-container {
// Wider than a normal form page: a collection row carries six fields side by side, and a clerk
// keying a round should see the whole of it.
max-width: 1280px;
margin: 0 auto;
padding: 20px 16px;
}
.dte-header {
display: flex;
align-items: center;
gap: 14px;
margin-bottom: 20px;
&__icon {
display: grid;
place-items: center;
flex: 0 0 auto;
width: 46px;
height: 46px;
border-radius: 14px;
background: linear-gradient(135deg, $primary 0%, $primary-dark 100%);
color: #fff;
font-size: 19px;
box-shadow: 0 6px 16px rgb(15 79 60 / 22%);
}
&__text {
min-width: 0;
}
&__title {
margin: 0;
font-size: 22px;
font-weight: 600;
color: $text-dark;
}
&__sub {
margin: 2px 0 0;
font-size: 13px;
color: $text-mid;
}
}
.dte-back {
color: $primary;
}
.dte-card {
background: var(--md-sys-color-surface, #fff);
border: 1px solid $border;
border-radius: 14px;
box-shadow: 0 2px 10px rgb(15 23 42 / 6%);
padding: 20px 24px 24px;
}
.dte-info-panels {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 16px;
margin-bottom: 18px;
}
.dte-info-panel {
border: 1px solid $border;
border-radius: 12px;
padding: 14px 18px 12px;
background: linear-gradient(180deg, #f8fafc 0%, #fff 100%);
&__title {
display: flex;
align-items: center;
gap: 8px;
margin-bottom: 10px;
padding-bottom: 8px;
border-bottom: 1px solid $border;
font-weight: 700;
font-size: 12.5px;
text-transform: uppercase;
letter-spacing: 0.06em;
color: $primary-dark;
i {
display: grid;
place-items: center;
width: 26px;
height: 26px;
border-radius: 8px;
background: $primary-light;
color: $primary-dark;
font-size: 12px;
}
}
&__hint {
display: flex;
align-items: flex-start;
gap: 8px;
margin: 0;
color: $text-mid;
font-size: 13px;
line-height: 1.5;
i {
margin-top: 3px;
color: $text-soft;
}
}
dl {
display: grid;
grid-template-columns: minmax(96px, auto) 1fr;
gap: 0 14px;
margin: 0;
}
dt,
dd {
padding: 6px 0;
border-bottom: 1px dashed #edf2f7;
}
dt {
font-weight: 600;
color: $text-soft;
font-size: 12px;
text-transform: uppercase;
letter-spacing: 0.03em;
}
dd {
margin: 0;
font-size: 13.5px;
color: $text-dark;
word-break: break-word;
}
}
// The round at a glance: parties, entries, what to count, what the agent holds.
.dte-kpis {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(170px, 1fr));
gap: 12px;
margin-bottom: 18px;
}
.dte-kpi {
display: flex;
flex-direction: column;
gap: 3px;
padding: 12px 16px;
border: 1px solid $border;
border-radius: 12px;
background: #fff;
box-shadow: 0 1px 3px rgb(15 23 42 / 5%);
&__label {
display: flex;
align-items: center;
gap: 6px;
font-size: 11px;
font-weight: 700;
letter-spacing: 0.05em;
text-transform: uppercase;
color: $text-soft;
i {
color: $primary;
}
}
&__value {
font-size: 21px;
font-weight: 700;
color: $text-dark;
font-variant-numeric: tabular-nums;
}
&__hint {
font-size: 12px;
color: $text-mid;
}
&--accent {
background: $primary-light;
border-color: #b7ddd0;
.dte-kpi__value {
color: $primary-dark;
}
}
}
.dte-controls {
display: flex;
align-items: flex-start;
gap: 12px;
flex-wrap: wrap;
margin-bottom: 12px;
padding: 14px 16px 0;
border: 1px solid $border;
border-radius: 12px;
background: #f8fafc;
}
// A sensible floor for the fields laid out with flex in .dte-controls, where there is room to spare.
.dte-field {
min-width: 180px;
&--wide {
grid-column: span 2;
}
}
// Inside the collection grid the track decides the width, not the field. Left to its own 180px
// floor a field refuses to shrink into a narrower track and overruns the one beside it.
.dte-collection-row .dte-field {
width: 100%;
min-width: 0;
}
.dte-section {
margin-top: 16px;
padding: 16px 18px 18px;
border: 1px solid $border;
border-radius: 12px;
background: #fff;
// A very narrow window scrolls the grid sideways rather than the whole page.
overflow-x: auto;
&__header {
display: flex;
align-items: center;
gap: 10px;
margin-bottom: 14px;
padding-bottom: 12px;
border-bottom: 1px solid $border;
> i {
display: grid;
place-items: center;
width: 30px;
height: 30px;
border-radius: 9px;
background: $primary-light;
color: $primary-dark;
font-size: 13px;
}
h3 {
margin: 0;
font-size: 16px;
font-weight: 600;
color: $text-dark;
flex: 1;
}
}
}
.dte-add-row {
color: $primary;
}
.dte-empty {
margin: 0;
padding: 18px 16px;
border: 1.5px dashed #cbd5e1;
border-radius: 10px;
background: #f8fafc;
color: $text-mid;
font-size: 14px;
text-align: center;
}
.dte-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 8px 16px;
// Withdraw Deposite: the two accounts each in a card of their own, then the amount and description.
&--transfer {
grid-template-columns: 1fr;
}
}
.dte-party-card {
padding: 12px 16px 2px;
border: 1px solid $border;
border-radius: 12px;
background: #f8fafc;
&__title {
display: flex;
align-items: center;
gap: 8px;
margin-bottom: 10px;
font-size: 12px;
font-weight: 700;
letter-spacing: 0.05em;
text-transform: uppercase;
color: $text-mid;
}
&--to &__title i {
color: #16a34a;
}
&--from &__title i {
color: #d97706;
}
&__fields {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 0 16px;
}
}
@media (width <= 900px) {
.dte-party-card__fields {
grid-template-columns: 1fr;
}
}
/*
* One collection per row. Fixed columns rather than an even split, so the row holds its shape as
* fields come and go - Cheque No and Token No only appear for a non-cash entry - and the party
* picker gets the width its "code · name · daily amount" needs.
*/
.dte-collection-row {
display: grid;
grid-template-columns:
minmax(0, 2.2fr) /* Party */
minmax(0, 1fr) /* TRTP */
minmax(0, 1fr) /* AC Type */
minmax(0, 1.4fr) /* A/C No. */
minmax(0, 1fr) /* Amount */
minmax(0, 1.6fr) /* Description */
auto; /* remove */
gap: 8px 12px;
align-items: start;
padding: 10px 0;
border-top: 1px dashed rgb(0 0 0 / 10%);
&:first-of-type {
border-top: none;
}
// Cheque No and Token No push Description and the remove button onto a second line, which is
// what a non-cash entry should look like rather than a row running off the screen.
&:has(.dte-field--instrument) {
grid-template-columns:
minmax(0, 2.2fr) minmax(0, 1fr) minmax(0, 1fr) minmax(0, 1.4fr) minmax(0, 1fr)
minmax(0, 1fr) minmax(0, 1fr) minmax(0, 1.6fr) auto;
}
}
.dte-collection-row .dte-field--wide {
grid-column: span 2;
}
@media (width <= 1200px) {
.dte-collection-row,
.dte-collection-row:has(.dte-field--instrument) {
grid-template-columns: repeat(3, minmax(0, 1fr)) auto;
}
}
.dte-remove-row {
color: #b91c1c;
margin-top: 8px;
}
.dte-action-bar {
display: flex;
justify-content: flex-end;
gap: 12px;
margin-top: 20px;
padding: 14px 16px;
border: 1px solid $border;
border-radius: 12px;
background: #f8fafc;
flex-wrap: wrap;
}
@media (width <= 1000px) {
.dte-info-panels {
grid-template-columns: 1fr;
}
.dte-grid {
grid-template-columns: repeat(2, 1fr);
}
.dte-collection-row {
grid-template-columns: repeat(2, 1fr);
}
}
/* Resolved name under an Agent / Party Code field — the check that the right record was found. */
.dte-hint--found {
color: #15803d;
font-weight: 500;
}
.dte-hint--missing {
color: #b91c1c;
font-weight: 500;
}
/* Marks an agent in the list who is no longer collecting - only offered for settlement. */
.dte-agent-off {
color: #b45309;
font-size: 12px;
font-style: italic;
margin-left: 6px;
}
/* The figure the clerk actually verifies against the passbook before saving. */
.dte-after {
font-weight: 600;
color: #0f766e;
}
/* What the agent should be handing over for this round. */
.dte-total {
display: flex;
justify-content: flex-end;
align-items: baseline;
gap: 14px;
margin-top: 6px;
padding: 12px 14px;
border-radius: 10px;
background: $primary-light;
font-size: 0.88rem;
color: $text-mid;
strong {
font-size: 1.15rem;
color: $primary-dark;
font-variant-numeric: tabular-nums;
}
}
/* Marks a transaction type the system does not handle yet, so it reads as pending rather than broken. */
.dte-soon {
font-size: 0.78rem;
font-style: italic;
color: rgb(0 0 0 / 45%);
}
/*
* Marks a party this agent has already collected from today, on the row where it is picked.
*
* Amber, not red: a customer paying twice in a day is legitimate - arrears, or two days at once -
* so this is a second look, not an error. Blocking it would stop real work; saying nothing lets a
* whole round be credited twice.
*/
.dte-hint--again {
color: #b45309;
font-weight: 500;
}
/*
* Where a party stands against its instalments. Three states rather than one colour, because they
* call for three different things from the clerk: ahead means take nothing, behind means take more
* than the daily amount, level means take the daily amount as usual.
*
* Behind is amber, not red - falling a few days behind is ordinary in daily collection and is the
* agent's job to recover, not an error. Red here would cry wolf on most rows.
*/
.dte-hint--ahead,
.dte-sched--ahead {
color: #15803d;
font-weight: 500;
}
.dte-hint--behind,
.dte-sched--behind {
color: #b45309;
font-weight: 500;
}
.dte-hint--level,
.dte-sched--level {
color: rgb(0 0 0 / 55%);
}
/* In the party panel the schedule line carries more weight than the fields around it. */
.dte-sched {
font-weight: 500;
}
/* Says why a party in the list cannot be picked, beside the party rather than in a tooltip. */
.dte-covered {
color: #15803d;
font-size: 0.8rem;
}
/*
* An amount that looks like a slip. Red rather than the amber used for arrears: arrears are ordinary
* and the agent's job to recover, while this is a figure that probably was not handed over at all,
* and once saved it cannot be taken back. It still does not block - the clerk decides.
*/
.dte-hint--overpay {
color: #b91c1c;
font-weight: 500;
}
/*
The agent's written-down round, as the counter reads it.
Laid out as lines rather than as the input grid this replaced: there is nothing to fill in here,
only a list to check a bag of cash against, and controls would invite editing something the clerk
has no business editing.
*/
.dte-pending-row {
display: flex;
align-items: center;
gap: 12px;
padding: 11px 12px;
border-bottom: 1px solid #eef2f6;
&:last-of-type {
border-bottom: none;
}
&:hover {
background: #f8fafc;
}
/* First, and in the quieter weight: the date is context for the line, not what is being read. */
&__date {
min-width: 96px;
color: $text-mid;
font-variant-numeric: tabular-nums;
}
&__code {
font-weight: 700;
min-width: 84px;
color: $primary-dark;
font-family: ui-monospace, 'SF Mono', 'Cascadia Mono', Consolas, monospace;
font-size: 13px;
}
&__name {
flex: 1;
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
color: $text-dark;
}
// The mode of payment as a small label rather than loose capitals.
&__mode {
padding: 2px 10px;
border-radius: 999px;
background: #eef2ff;
color: #3730a3;
font-size: 11px;
font-weight: 600;
letter-spacing: 0.04em;
text-transform: uppercase;
white-space: nowrap;
}
/* Amounts are read down the column and compared, so the digits have to line up. */
&__amount {
font-variant-numeric: tabular-nums;
font-weight: 700;
min-width: 96px;
text-align: right;
color: $text-dark;
}
}
/* A line that can be clicked to show its party above, and the one currently shown. */
.dte-pending-row--pick {
cursor: pointer;
}
.dte-pending-row--active {
background: #eef6f3;
box-shadow: inset 3px 0 0 $primary;
}
/* Names the columns of the lines below, in the same widths so they sit over them. */
.dte-pending-head {
display: flex;
align-items: center;
gap: 12px;
padding: 8px 12px;
border-radius: 8px;
background: #f1f5f9;
span {
font-size: 11px;
font-weight: 700;
letter-spacing: 0.05em;
text-transform: uppercase;
color: $text-soft;
}
.dte-pending-row__mode {
padding: 0;
background: none;
color: $text-soft;
}
}
/* The one figure the clerk acts on, said as a sentence so it is not read as another column total. */
.dte-expected {
margin: 14px 0 0;
padding: 10px 14px;
border-left: 3px solid $primary;
border-radius: 0 8px 8px 0;
background: #f0fdf9;
font-size: 14px;
line-height: 1.5;
color: $text-dark;
strong {
font-variant-numeric: tabular-nums;
}
}
/* Quieter than the figure above it: a note about how the screen behaves, not part of the count. */
.dte-expected--note {
border-left-color: #cbd5e1;
background: #f8fafc;
font-size: 13px;
color: $text-mid;
margin-top: 8px;
}
.dte-apply-receipt {
margin-left: 8px;
}
/* Agent Tran: what the agent owes, what the cashier has taken in, and what is left. */
.dte-money {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 12px;
margin: 12px 0;
&__box {
padding: 14px 18px;
border: 1px solid $border;
border-radius: 12px;
display: flex;
flex-direction: column;
gap: 6px;
background: #fff;
box-shadow: 0 1px 3px rgb(15 23 42 / 5%);
font-variant-numeric: tabular-nums;
strong {
font-size: 22px;
color: $text-dark;
}
&--owed {
background: #eff6ff;
border-color: #bfdbfe;
strong {
color: #1d4ed8;
}
}
&--received {
background: #f0fdf4;
border-color: #bbf7d0;
strong {
color: #15803d;
}
}
&--due {
border-color: rgb(198 40 40 / 40%);
background: #fef2f2;
strong {
color: $danger;
}
}
}
&__label {
display: flex;
align-items: center;
gap: 7px;
font-size: 11px;
font-weight: 700;
color: $text-mid;
text-transform: uppercase;
letter-spacing: 0.05em;
}
}
@media (width <= 720px) {
.dte-money {
grid-template-columns: 1fr;
}
}
.dte-subhead {
display: flex;
align-items: center;
gap: 8px;
margin: 20px 0 8px;
font-size: 13px;
font-weight: 700;
letter-spacing: 0.04em;
text-transform: uppercase;
color: $text-mid;
i {
color: $primary;
}
}
.dte-pending-row--day {
cursor: pointer;
background: #f8fafc;
border-radius: 8px;
margin-top: 4px;
&:hover {
background: #eef6f3;
}
}
.dte-day-lines {
margin: 0 0 6px 24px;
max-height: 280px;
overflow-y: auto;
border-left: 3px solid #b7ddd0;
padding-left: 10px;
}
.dte-status {
font-size: 12px;
font-weight: 600;
padding: 3px 11px;
border-radius: 999px;
background: rgb(237 108 2 / 12%);
color: #b45309;
&--ok {
background: rgb(46 125 50 / 12%);
color: #2e7d32;
}
}
.dte-list-arrow {
cursor: pointer;
}
.dte-same-party {
color: #c62828;
font-weight: 600;
}
// ── Compact density ─────────────────────────────────────────────────────────────────────────────────
// The whole screen a step smaller: type, spacing and the height of every field. It is a working screen
// kept open all day, and at full size a round needed scrolling to be read. Everything above states the
// look; this states the size, so one place says how big the page is.
.dte-container {
max-width: 1200px;
padding: 10px 12px;
font-size: 13px;
// Material sizes every field on the page from these.
--mat-form-field-container-height: 38px;
--mat-form-field-container-vertical-padding: 8px;
--mat-form-field-container-text-size: 13px;
--mat-form-field-container-text-line-height: 20px;
--mat-form-field-outlined-label-text-populated-size: 11px;
--mat-form-field-subscript-text-size: 11px;
--mat-select-trigger-text-size: 13px;
// The datepicker's toggle and a field's icon buttons default to a 48px touch target, which reopens
// the height just saved - and is why the Date box stood taller than the two beside it.
::ng-deep .mat-mdc-form-field-icon-suffix .mat-mdc-icon-button.mat-mdc-button-base {
--mat-icon-button-state-layer-size: 30px;
width: 30px;
height: 30px;
padding: 4px;
}
::ng-deep .mat-mdc-form-field-icon-suffix .mat-datepicker-toggle svg {
width: 16px;
height: 16px;
}
::ng-deep .mat-mdc-form-field-hint,
::ng-deep .mat-mdc-form-field-error {
font-size: 11px;
}
::ng-deep .mat-mdc-button-base {
font-size: 12.5px;
}
}
.dte-header {
gap: 10px;
margin-bottom: 10px;
&__icon {
width: 36px;
height: 36px;
border-radius: 11px;
font-size: 15px;
}
&__title {
font-size: 18px;
}
&__sub {
font-size: 11.5px;
}
}
.dte-card {
padding: 12px 14px 14px;
border-radius: 12px;
}
.dte-info-panels {
gap: 10px;
margin-bottom: 10px;
}
.dte-info-panel {
padding: 8px 12px 6px;
border-radius: 10px;
&__title {
gap: 7px;
margin-bottom: 6px;
padding-bottom: 6px;
font-size: 11px;
i {
width: 22px;
height: 22px;
border-radius: 7px;
font-size: 10.5px;
}
}
&__hint {
font-size: 12px;
}
dl {
gap: 0 12px;
}
dt,
dd {
padding: 3px 0;
}
dt {
font-size: 10.5px;
}
dd {
font-size: 12.5px;
}
}
.dte-kpis {
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
gap: 8px;
margin-bottom: 10px;
}
.dte-kpi {
gap: 1px;
padding: 7px 12px;
border-radius: 10px;
&__label {
font-size: 10px;
}
&__value {
font-size: 16px;
}
&__hint {
font-size: 11px;
}
}
// The three controls the same size, whatever each holds: one width, one height, side by side.
.dte-controls {
align-items: flex-start;
gap: 10px;
margin-bottom: 8px;
padding: 10px 12px;
border-radius: 10px;
.dte-field {
flex: 0 0 240px;
width: 240px;
min-width: 0;
}
}
// The button that opens the round: the same height as the fields beside it.
.dte-view-btn {
height: 38px;
margin-left: auto;
padding: 0 14px;
border-radius: 8px;
color: $primary-dark;
border-color: #b7ddd0;
background: $primary-light;
i {
margin-right: 6px;
}
&__count {
margin-left: 8px;
padding: 1px 8px;
border-radius: 999px;
background: $primary;
color: #fff;
font-size: 11px;
font-weight: 700;
}
}
.dte-section {
margin-top: 10px;
padding: 10px 12px 12px;
border-radius: 10px;
&__header {
gap: 8px;
margin-bottom: 8px;
padding-bottom: 8px;
> i {
width: 24px;
height: 24px;
border-radius: 7px;
font-size: 11px;
}
h3 {
font-size: 14px;
}
}
}
.dte-empty {
padding: 12px;
font-size: 12.5px;
}
.dte-money {
gap: 8px;
margin: 8px 0;
&__box {
padding: 8px 12px;
gap: 3px;
border-radius: 10px;
strong {
font-size: 17px;
}
}
&__label {
font-size: 10px;
}
}
.dte-subhead {
margin: 12px 0 6px;
font-size: 11.5px;
}
.dte-expected {
margin-top: 8px;
padding: 7px 10px;
font-size: 12.5px;
}
.dte-expected--note {
font-size: 12px;
}
.dte-pending-row {
padding: 6px 10px;
font-size: 12.5px;
&__code {
font-size: 12px;
}
&__mode {
font-size: 10.5px;
}
}
.dte-pending-head {
padding: 6px 10px;
span {
font-size: 10px;
}
}
.dte-party-card {
padding: 8px 12px 0;
border-radius: 10px;
&__title {
margin-bottom: 6px;
font-size: 11px;
}
}
.dte-action-bar {
gap: 8px;
margin-top: 12px;
padding: 8px 12px;
border-radius: 10px;
}
// ── Agent Tran summary tiles and the actions beside the Date ────────────────────────────────────────
// To take in blue, Received in green, Still to take in red once something is left: the colours the
// Agent's cash section used, now on the summary tiles.
.dte-kpi {
&--owed {
background: #eff6ff;
border-color: #bfdbfe;
.dte-kpi__value {
color: #1d4ed8;
}
}
&--received {
background: #f0fdf4;
border-color: #bbf7d0;
.dte-kpi__value {
color: #15803d;
}
}
&--due {
background: #fef2f2;
border-color: rgb(198 40 40 / 40%);
.dte-kpi__value {
color: $danger;
}
}
}
// Notes under the summary: the GL the cash goes into, and anything that looks wrong.
.dte-notes {
display: grid;
gap: 6px;
margin: 0 0 8px;
.dte-expected {
margin: 0;
}
}
// Refresh and Apply DDS Receipt, at the end of the row and the same height as the fields beside them.
.dte-agent-actions {
display: flex;
align-items: center;
gap: 8px;
margin-left: auto;
.dte-refresh-btn,
.dte-apply-btn {
height: 38px;
padding: 0 14px;
border-radius: 8px;
font-size: 12.5px;
i {
margin-right: 6px;
}
}
.dte-refresh-btn {
color: $primary;
}
}
// ── Party Information, wider and two pairs to a line ───────────────────────────────────────────────
// A party carries twice what an agent does, so listing it one pair to a line made the card the taller of
// the two and pushed the rest of the screen down. It takes the larger share of the row and sets its
// details out as two columns of label and value, which brings it level with the agent's card.
.dte-info-panels {
grid-template-columns: minmax(0, 1fr) minmax(0, 1.8fr);
}
.dte-info-panel--party {
dl {
grid-template-columns: minmax(64px, auto) minmax(0, 1fr) minmax(64px, auto) minmax(0, 1fr);
gap: 0 14px;
}
// The schedule is a sentence, so it takes the rest of the line rather than being squeezed into half of it.
dd.dte-sched {
grid-column: span 3;
}
}
@media (width <= 1000px) {
.dte-info-panels {
grid-template-columns: 1fr;
}
}
@media (width <= 640px) {
.dte-info-panel--party dl {
grid-template-columns: minmax(64px, auto) minmax(0, 1fr);
dd.dte-sched {
grid-column: span 1;
}
}
}