Tower: upload laundry_management 19.0.19.0.4 (via marketplace)
This commit is contained in:
994
addons/laundry_management/static/src/scss/laundry_pos.scss
Normal file
994
addons/laundry_management/static/src/scss/laundry_pos.scss
Normal file
@@ -0,0 +1,994 @@
|
|||||||
|
// ============================================================================
|
||||||
|
// Laundry Management — POS / Backend Design System
|
||||||
|
// 8px grid, soft shadows, premium pill components.
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
// ── Tokens ─────────────────────────────────────────────────────────────────
|
||||||
|
$lm-space-1: 4px;
|
||||||
|
$lm-space-2: 8px;
|
||||||
|
$lm-space-3: 12px;
|
||||||
|
$lm-space-4: 16px;
|
||||||
|
$lm-space-5: 24px;
|
||||||
|
|
||||||
|
$lm-radius-sm: 6px;
|
||||||
|
$lm-radius-md: 10px;
|
||||||
|
$lm-radius-lg: 14px;
|
||||||
|
|
||||||
|
$lm-shadow-sm: 0 1px 2px rgba(15, 23, 42, 0.06), 0 1px 1px rgba(15, 23, 42, 0.04);
|
||||||
|
$lm-shadow-md: 0 4px 12px rgba(15, 23, 42, 0.08), 0 2px 4px rgba(15, 23, 42, 0.05);
|
||||||
|
$lm-shadow-lg: 0 10px 30px rgba(15, 23, 42, 0.12), 0 4px 8px rgba(15, 23, 42, 0.06);
|
||||||
|
|
||||||
|
$lm-color-urgent: #EF4444;
|
||||||
|
$lm-color-delivery: #10B981;
|
||||||
|
$lm-color-vip: #8B5CF6;
|
||||||
|
$lm-color-normal: #6B7280;
|
||||||
|
$lm-color-type: #4F46E5;
|
||||||
|
|
||||||
|
$lm-font-xs: 11px;
|
||||||
|
$lm-font-sm: 13px;
|
||||||
|
$lm-font-md: 14px;
|
||||||
|
$lm-font-lg: 16px;
|
||||||
|
$lm-font-xl: 18px;
|
||||||
|
|
||||||
|
$lm-bg-card: #FFFFFF;
|
||||||
|
$lm-bg-soft: #F8FAFC;
|
||||||
|
$lm-border: #E2E8F0;
|
||||||
|
$lm-text: #0F172A;
|
||||||
|
$lm-text-muted: #64748B;
|
||||||
|
|
||||||
|
// ── Pill component (shared across panel / kanban / receipt) ───────────────
|
||||||
|
.laundry-pill {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: $lm-space-1 $lm-space-3;
|
||||||
|
border-radius: 9999px;
|
||||||
|
font-size: $lm-font-sm;
|
||||||
|
font-weight: 600;
|
||||||
|
line-height: 1.2;
|
||||||
|
color: #fff;
|
||||||
|
background-color: $lm-color-normal;
|
||||||
|
box-shadow: $lm-shadow-sm;
|
||||||
|
white-space: nowrap;
|
||||||
|
transition: transform 0.12s ease, box-shadow 0.12s ease;
|
||||||
|
|
||||||
|
&:hover { transform: translateY(-1px); box-shadow: $lm-shadow-md; }
|
||||||
|
|
||||||
|
.fa { font-size: 0.95em; }
|
||||||
|
|
||||||
|
&--type { background-color: $lm-color-type; }
|
||||||
|
&--attr { background-color: $lm-color-normal; }
|
||||||
|
&--urgent { background-color: $lm-color-urgent; }
|
||||||
|
&--delivery { background-color: $lm-color-delivery; }
|
||||||
|
&--vip { background-color: $lm-color-vip; }
|
||||||
|
&--deferred { background-color: #F59E0B; }
|
||||||
|
|
||||||
|
// Semantic data overrides — win over default --attr
|
||||||
|
&[data-priority="urgent"] { background-color: $lm-color-urgent; }
|
||||||
|
&[data-delivery="1"] { background-color: $lm-color-delivery; }
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Settle Due Banner (POS Mode indicator) ────────────────────────────────
|
||||||
|
.laundry-settle-banner {
|
||||||
|
margin: $lm-space-2 $lm-space-3 0 $lm-space-3;
|
||||||
|
padding: $lm-space-3 $lm-space-4;
|
||||||
|
background: linear-gradient(135deg, #F59E0B 0%, #F97316 100%);
|
||||||
|
color: #fff;
|
||||||
|
border-radius: $lm-radius-md;
|
||||||
|
box-shadow: $lm-shadow-md;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: $lm-space-3;
|
||||||
|
animation: laundry-settle-banner-pulse 2.4s ease-in-out infinite;
|
||||||
|
|
||||||
|
&__lead {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: $lm-space-3;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__icon {
|
||||||
|
font-size: $lm-font-xl;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__text {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__title {
|
||||||
|
font-size: $lm-font-md;
|
||||||
|
font-weight: 800;
|
||||||
|
letter-spacing: 0.06em;
|
||||||
|
line-height: 1.15;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__meta {
|
||||||
|
display: flex;
|
||||||
|
align-items: baseline;
|
||||||
|
gap: $lm-space-3;
|
||||||
|
margin-top: 3px;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__partner {
|
||||||
|
font-size: $lm-font-sm;
|
||||||
|
font-weight: 600;
|
||||||
|
opacity: 0.95;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
max-width: 40ch;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__amount {
|
||||||
|
font-size: $lm-font-md;
|
||||||
|
font-weight: 800;
|
||||||
|
letter-spacing: 0.01em;
|
||||||
|
padding: 1px 8px;
|
||||||
|
background: rgba(255, 255, 255, 0.2);
|
||||||
|
border-radius: $lm-radius-sm;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__hint {
|
||||||
|
font-size: $lm-font-xs;
|
||||||
|
opacity: 0.85;
|
||||||
|
margin-top: 2px;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__exit {
|
||||||
|
background: rgba(255, 255, 255, 0.18);
|
||||||
|
color: #fff;
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.4);
|
||||||
|
padding: $lm-space-2 $lm-space-3;
|
||||||
|
border-radius: $lm-radius-sm;
|
||||||
|
font-size: $lm-font-sm;
|
||||||
|
font-weight: 600;
|
||||||
|
cursor: pointer;
|
||||||
|
flex-shrink: 0;
|
||||||
|
transition: background 0.15s ease, transform 0.12s ease;
|
||||||
|
|
||||||
|
&:hover { background: rgba(255, 255, 255, 0.3); transform: translateY(-1px); }
|
||||||
|
&:active { transform: translateY(0); }
|
||||||
|
&:focus-visible {
|
||||||
|
outline: 2px solid #fff;
|
||||||
|
outline-offset: 2px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes laundry-settle-banner-pulse {
|
||||||
|
0%, 100% { box-shadow: 0 4px 12px rgba(245, 158, 11, 0.35); }
|
||||||
|
50% { box-shadow: 0 4px 20px rgba(245, 158, 11, 0.65); }
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Global lock of POS chrome while settle-due mode is active ─────────────
|
||||||
|
// The banner component toggles `pos-laundry-settle-active` on <body>.
|
||||||
|
// CSS is a clarity layer, not the safety layer — JS (pos_store_patch /
|
||||||
|
// order_tabs_patch / navbar_patch) is the real gate. But here we also
|
||||||
|
// disable pointer-events on the peripherals because SINGLE-ORDER MODE
|
||||||
|
// guarantees the only tab is the settle order itself; everything else
|
||||||
|
// is visual chrome that shouldn't respond to clicks.
|
||||||
|
body.pos-laundry-settle-active {
|
||||||
|
// Every non-active tab (should be zero under single-order mode —
|
||||||
|
// but defense in depth): hatched, dim, unclickable.
|
||||||
|
.floating-order-container .btn:not(.active) {
|
||||||
|
pointer-events: none;
|
||||||
|
opacity: 0.35;
|
||||||
|
filter: saturate(0.3);
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
border-radius: inherit;
|
||||||
|
background: repeating-linear-gradient(
|
||||||
|
135deg,
|
||||||
|
rgba(245, 158, 11, 0.0) 0 6px,
|
||||||
|
rgba(245, 158, 11, 0.15) 6px 12px
|
||||||
|
);
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Active settle tab — keep visible & highlighted in warning orange.
|
||||||
|
.floating-order-container .btn.active {
|
||||||
|
outline: 2px solid #F59E0B;
|
||||||
|
outline-offset: 2px;
|
||||||
|
pointer-events: none; // the tab IS the current order; no switch needed
|
||||||
|
}
|
||||||
|
|
||||||
|
// "+" new-order button inside ListContainer — hard-disabled visually.
|
||||||
|
// JS addNewOrder patch also blocks it.
|
||||||
|
.list-container-add,
|
||||||
|
.o_list_container_add,
|
||||||
|
button[title*="Add a new order" i],
|
||||||
|
button[aria-label*="Add a new order" i] {
|
||||||
|
pointer-events: none;
|
||||||
|
opacity: 0.35;
|
||||||
|
filter: saturate(0.3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Order Context Panel (POS right-side card) ─────────────────────────────
|
||||||
|
.laundry-context-panel {
|
||||||
|
margin: $lm-space-2 $lm-space-3;
|
||||||
|
padding: $lm-space-3 $lm-space-4;
|
||||||
|
background: $lm-bg-card;
|
||||||
|
border: 1px solid $lm-border;
|
||||||
|
border-radius: $lm-radius-md;
|
||||||
|
box-shadow: $lm-shadow-sm;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: $lm-space-2;
|
||||||
|
transition: box-shadow 0.18s ease, transform 0.18s ease;
|
||||||
|
|
||||||
|
&:hover { box-shadow: $lm-shadow-md; }
|
||||||
|
|
||||||
|
&__header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__title {
|
||||||
|
font-size: $lm-font-xs;
|
||||||
|
font-weight: 700;
|
||||||
|
letter-spacing: 0.06em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: $lm-text-muted;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__edit {
|
||||||
|
background: transparent;
|
||||||
|
border: 0;
|
||||||
|
color: $lm-text-muted;
|
||||||
|
padding: $lm-space-1 $lm-space-2;
|
||||||
|
border-radius: $lm-radius-sm;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background 0.12s ease, color 0.12s ease;
|
||||||
|
|
||||||
|
&:hover { background: $lm-bg-soft; color: $lm-text; }
|
||||||
|
&:focus-visible {
|
||||||
|
outline: 2px solid rgba(99, 102, 241, 0.45);
|
||||||
|
outline-offset: 2px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__row {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: $lm-space-2;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__attrs .laundry-pill { font-size: $lm-font-xs; padding: 2px $lm-space-2; }
|
||||||
|
|
||||||
|
&__empty {
|
||||||
|
font-size: $lm-font-sm;
|
||||||
|
color: $lm-text-muted;
|
||||||
|
background: $lm-bg-soft;
|
||||||
|
padding: $lm-space-2 $lm-space-3;
|
||||||
|
border-radius: $lm-radius-sm;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__cta {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: $lm-space-2;
|
||||||
|
width: 100%;
|
||||||
|
padding: $lm-space-3 $lm-space-4;
|
||||||
|
background: linear-gradient(135deg, #4F46E5 0%, #6366F1 100%);
|
||||||
|
color: #fff;
|
||||||
|
font-size: $lm-font-md;
|
||||||
|
font-weight: 600;
|
||||||
|
border: 0;
|
||||||
|
border-radius: $lm-radius-md;
|
||||||
|
box-shadow: $lm-shadow-sm;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: transform 0.12s ease, box-shadow 0.12s ease, filter 0.12s ease;
|
||||||
|
|
||||||
|
.fa { font-size: 1.05em; }
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
transform: translateY(-1px);
|
||||||
|
box-shadow: $lm-shadow-md;
|
||||||
|
filter: brightness(1.05);
|
||||||
|
}
|
||||||
|
&:active { transform: translateY(0); filter: brightness(0.95); }
|
||||||
|
&:focus-visible {
|
||||||
|
outline: 2px solid rgba(99, 102, 241, 0.55);
|
||||||
|
outline-offset: 2px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__edit-label {
|
||||||
|
margin-inline-start: $lm-space-1;
|
||||||
|
font-size: $lm-font-xs;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__delivery {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: $lm-space-1;
|
||||||
|
padding-top: $lm-space-2;
|
||||||
|
border-top: 1px dashed $lm-border;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__delivery-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: $lm-space-2;
|
||||||
|
font-size: $lm-font-sm;
|
||||||
|
color: $lm-text;
|
||||||
|
|
||||||
|
.fa { color: $lm-color-delivery; width: 14px; text-align: center; }
|
||||||
|
}
|
||||||
|
|
||||||
|
&[data-empty="1"] { background: $lm-bg-soft; }
|
||||||
|
&[data-delivery="1"] { border-left: 3px solid $lm-color-delivery; }
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Popup polish (shared by all 3 laundry popups) ─────────────────────────
|
||||||
|
.modal .modal-dialog {
|
||||||
|
.btn.btn-outline-primary,
|
||||||
|
.btn.btn-primary {
|
||||||
|
border-radius: $lm-radius-md;
|
||||||
|
transition: transform 0.12s ease, box-shadow 0.12s ease,
|
||||||
|
background-color 0.12s ease, border-color 0.12s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn.btn-primary {
|
||||||
|
box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.18);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn.btn-outline-primary:hover {
|
||||||
|
transform: translateY(-1px);
|
||||||
|
box-shadow: $lm-shadow-md;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-footer .btn-primary,
|
||||||
|
.modal-footer .btn-secondary {
|
||||||
|
position: sticky;
|
||||||
|
bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Receipt details (printer-friendly) ────────────────────────────────────
|
||||||
|
.laundry-receipt-details {
|
||||||
|
margin-top: $lm-space-2;
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
&__sep {
|
||||||
|
border-top: 1px dashed #000;
|
||||||
|
margin: $lm-space-2 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__title {
|
||||||
|
font-weight: 700;
|
||||||
|
font-size: $lm-font-md;
|
||||||
|
margin-bottom: $lm-space-1;
|
||||||
|
letter-spacing: 0.04em;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__row {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: $lm-space-3;
|
||||||
|
font-size: $lm-font-sm;
|
||||||
|
margin: 2px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__label {
|
||||||
|
font-weight: 600;
|
||||||
|
color: #000;
|
||||||
|
min-width: 80px;
|
||||||
|
text-align: start;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__value {
|
||||||
|
text-align: end;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__chip {
|
||||||
|
display: inline-block;
|
||||||
|
padding: 0 $lm-space-2;
|
||||||
|
margin: 0 2px;
|
||||||
|
border: 1px solid #000;
|
||||||
|
border-radius: $lm-radius-sm;
|
||||||
|
font-size: $lm-font-xs;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// RTL safety
|
||||||
|
[dir="rtl"] .laundry-receipt-details__label { text-align: end; }
|
||||||
|
[dir="rtl"] .laundry-receipt-details__value { text-align: start; }
|
||||||
|
|
||||||
|
// ── Operational Control Board (laundry.order kanban) ──────────────────────
|
||||||
|
.laundry-board {
|
||||||
|
.laundry-board__card {
|
||||||
|
position: relative;
|
||||||
|
background: $lm-bg-card;
|
||||||
|
border: 1px solid $lm-border;
|
||||||
|
border-radius: $lm-radius-md;
|
||||||
|
box-shadow: $lm-shadow-sm;
|
||||||
|
padding: $lm-space-3 $lm-space-4 $lm-space-3 ($lm-space-4 + 4px);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: $lm-space-2;
|
||||||
|
transition: box-shadow 0.18s ease, transform 0.18s ease;
|
||||||
|
|
||||||
|
&:hover { box-shadow: $lm-shadow-md; transform: translateY(-1px); }
|
||||||
|
}
|
||||||
|
|
||||||
|
.laundry-board__strip {
|
||||||
|
position: absolute;
|
||||||
|
left: 0; top: 0; bottom: 0;
|
||||||
|
width: 4px;
|
||||||
|
border-top-left-radius: $lm-radius-md;
|
||||||
|
border-bottom-left-radius: $lm-radius-md;
|
||||||
|
background: $lm-color-normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
.laundry-board__card[data-state="intake"] .laundry-board__strip { background: #3B82F6; }
|
||||||
|
.laundry-board__card[data-state="processing"] .laundry-board__strip { background: #F59E0B; }
|
||||||
|
.laundry-board__card[data-state="ready"] .laundry-board__strip { background: $lm-color-delivery; }
|
||||||
|
.laundry-board__card[data-state="delivered"] .laundry-board__strip { background: $lm-color-normal; }
|
||||||
|
.laundry-board__card[data-priority="urgent"] .laundry-board__strip { background: $lm-color-urgent; }
|
||||||
|
.laundry-board__card[data-priority="urgent"] { border-color: rgba(239, 68, 68, 0.35); }
|
||||||
|
|
||||||
|
.laundry-board__head {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.laundry-board__title-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: $lm-space-2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.laundry-board__name {
|
||||||
|
font-size: $lm-font-md;
|
||||||
|
color: $lm-text;
|
||||||
|
}
|
||||||
|
|
||||||
|
.laundry-board__total {
|
||||||
|
font-size: $lm-font-md;
|
||||||
|
font-weight: 700;
|
||||||
|
color: $lm-text;
|
||||||
|
}
|
||||||
|
|
||||||
|
.laundry-board__customer {
|
||||||
|
font-size: $lm-font-sm;
|
||||||
|
color: $lm-text-muted;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.laundry-board__badges {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: $lm-space-1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.laundry-board__meta {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: $lm-space-3;
|
||||||
|
font-size: $lm-font-xs;
|
||||||
|
color: $lm-text-muted;
|
||||||
|
}
|
||||||
|
|
||||||
|
.laundry-board__due {
|
||||||
|
color: $lm-color-urgent;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.laundry-board__actions { margin-top: $lm-space-1; }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ── ProductConfiguratorPopup — laundry enhancement ─────────────────────
|
||||||
|
// Scoped exclusively under `.popup-product-configurator.laundry-enhanced`,
|
||||||
|
// applied to the modal-content via Dialog's `contentClass` prop from
|
||||||
|
// xml/product_configurator_popup.xml. Non-laundry configurator popups are
|
||||||
|
// unaffected.
|
||||||
|
.popup-product-configurator.laundry-enhanced {
|
||||||
|
|
||||||
|
// Attribute group — stronger title, better spacing.
|
||||||
|
.modal-body .attribute {
|
||||||
|
margin-bottom: $lm-space-4 !important;
|
||||||
|
padding-bottom: $lm-space-3;
|
||||||
|
border-bottom: 1px dashed $lm-border;
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
margin-bottom: 0 !important;
|
||||||
|
padding-bottom: 0;
|
||||||
|
border-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.attribute_name {
|
||||||
|
font-size: $lm-font-sm;
|
||||||
|
font-weight: 800 !important;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.09em;
|
||||||
|
color: $lm-text-muted;
|
||||||
|
margin-bottom: $lm-space-3 !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Touch-first tiles — applies to both Radio and Pills renderers,
|
||||||
|
// which share the same `.configurator_radio > .attribute-name-cell`
|
||||||
|
// structure in core.
|
||||||
|
.configurator_radio {
|
||||||
|
> .d-flex {
|
||||||
|
flex-wrap: wrap !important;
|
||||||
|
gap: $lm-space-3 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.attribute-name-cell {
|
||||||
|
flex: 1 1 calc(33% - #{$lm-space-3});
|
||||||
|
min-width: 140px;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
|
||||||
|
// Hide the native radio — the label IS the tile.
|
||||||
|
.form-check-input,
|
||||||
|
.radio-check {
|
||||||
|
position: absolute !important;
|
||||||
|
opacity: 0 !important;
|
||||||
|
pointer-events: none !important;
|
||||||
|
width: 0 !important;
|
||||||
|
height: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
> label {
|
||||||
|
display: flex !important;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: $lm-space-2;
|
||||||
|
width: 100%;
|
||||||
|
min-height: 64px;
|
||||||
|
padding: 12px 16px !important;
|
||||||
|
border: 2px solid $lm-border !important;
|
||||||
|
border-radius: 12px !important;
|
||||||
|
background: #fff !important;
|
||||||
|
color: $lm-text !important;
|
||||||
|
font-size: $lm-font-md;
|
||||||
|
font-weight: 700;
|
||||||
|
line-height: 1.25;
|
||||||
|
cursor: pointer;
|
||||||
|
position: relative;
|
||||||
|
box-shadow: none !important;
|
||||||
|
transition: transform 0.14s ease,
|
||||||
|
border-color 0.14s ease,
|
||||||
|
box-shadow 0.16s ease,
|
||||||
|
background 0.14s ease,
|
||||||
|
color 0.14s ease;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
transform: translateY(-1px);
|
||||||
|
box-shadow: $lm-shadow-sm !important;
|
||||||
|
}
|
||||||
|
&:active { transform: translateY(0); }
|
||||||
|
&:focus-visible {
|
||||||
|
outline: 2px solid rgba(79, 70, 229, 0.45);
|
||||||
|
outline-offset: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wrap text span so conflict styling still readable.
|
||||||
|
> span:first-child { word-break: break-word; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Selected state:
|
||||||
|
// - Pills renderer: core toggles `.active` on the <label>.
|
||||||
|
// - Radio renderer: input is :checked and precedes label.
|
||||||
|
.attribute-name-cell > input:checked + label,
|
||||||
|
.attribute-name-cell > label.active {
|
||||||
|
border-color: $lm-color-type !important;
|
||||||
|
box-shadow: 0 0 0 3px rgba(79, 70, 229, 0.14),
|
||||||
|
$lm-shadow-sm !important;
|
||||||
|
background: linear-gradient(135deg,
|
||||||
|
rgba(79, 70, 229, 0.08) 0%,
|
||||||
|
rgba(79, 70, 229, 0.00) 100%) !important;
|
||||||
|
color: $lm-color-type !important;
|
||||||
|
|
||||||
|
> span:first-child { color: $lm-color-type !important; }
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
content: "\f00c"; // FontAwesome check
|
||||||
|
font-family: "FontAwesome", "Font Awesome 6 Free", sans-serif;
|
||||||
|
font-weight: 900;
|
||||||
|
position: absolute;
|
||||||
|
top: 6px;
|
||||||
|
right: 6px;
|
||||||
|
width: 22px;
|
||||||
|
height: 22px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: $lm-color-type;
|
||||||
|
color: #fff;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 11px;
|
||||||
|
box-shadow: $lm-shadow-sm;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Price extra chip — stronger, more scannable.
|
||||||
|
.price-extra-cell {
|
||||||
|
margin: 0 !important;
|
||||||
|
|
||||||
|
.price_extra {
|
||||||
|
font-size: $lm-font-xs !important;
|
||||||
|
font-weight: 800 !important;
|
||||||
|
padding: 2px 10px !important;
|
||||||
|
border-radius: 9999px !important;
|
||||||
|
background: rgba(245, 158, 11, 0.18) !important;
|
||||||
|
color: #B45309 !important;
|
||||||
|
letter-spacing: 0.02em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Conflict state (core adds `text-muted` on the inner span).
|
||||||
|
.attribute-name-cell > label > span.text-muted {
|
||||||
|
opacity: 0.6;
|
||||||
|
text-decoration: line-through;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Custom-value text input — keep larger, consistent.
|
||||||
|
.custom-value-cell {
|
||||||
|
flex: 1 1 100%;
|
||||||
|
|
||||||
|
.form-control { height: 48px; font-size: $lm-font-md; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CTA footer — bigger, bolder, full width on tablet.
|
||||||
|
.modal-footer {
|
||||||
|
padding: $lm-space-3 $lm-space-4;
|
||||||
|
|
||||||
|
> .d-flex {
|
||||||
|
gap: $lm-space-3 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn.btn-primary,
|
||||||
|
.btn.btn-secondary {
|
||||||
|
min-height: 56px;
|
||||||
|
font-size: $lm-font-lg;
|
||||||
|
font-weight: 800;
|
||||||
|
letter-spacing: 0.02em;
|
||||||
|
border-radius: $lm-radius-md;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn.btn-primary {
|
||||||
|
box-shadow: 0 2px 8px rgba(79, 70, 229, 0.22);
|
||||||
|
|
||||||
|
&:hover:not(.disabled) {
|
||||||
|
box-shadow: 0 4px 14px rgba(79, 70, 229, 0.32);
|
||||||
|
}
|
||||||
|
&.disabled {
|
||||||
|
opacity: 0.55;
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Full-width primary button below tablet breakpoint.
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.modal-footer > .d-flex {
|
||||||
|
flex-direction: column-reverse;
|
||||||
|
|
||||||
|
.btn { width: 100% !important; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Archived-combination banner — keep legible but calmer.
|
||||||
|
.alert.alert-warning {
|
||||||
|
border-radius: $lm-radius-md;
|
||||||
|
border: 0;
|
||||||
|
background: rgba(245, 158, 11, 0.12);
|
||||||
|
color: #92400E;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Laundry Orders POS popup ──────────────────────────────────────────
|
||||||
|
// Scoped to `.laundry-orders-popup` (applied to .modal-content via the
|
||||||
|
// Dialog `contentClass` prop). Touches only modal contents — no leak
|
||||||
|
// to other POS popups.
|
||||||
|
.laundry-orders-popup {
|
||||||
|
|
||||||
|
.laundry-orders-popup__body {
|
||||||
|
padding: $lm-space-3;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: $lm-space-3;
|
||||||
|
max-height: 70vh;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.laundry-orders-popup__header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: $lm-space-3;
|
||||||
|
padding-bottom: $lm-space-2;
|
||||||
|
border-bottom: 1px solid $lm-border;
|
||||||
|
}
|
||||||
|
|
||||||
|
.laundry-orders-popup__partner {
|
||||||
|
font-size: $lm-font-lg;
|
||||||
|
font-weight: 700;
|
||||||
|
color: $lm-text;
|
||||||
|
|
||||||
|
.fa { color: $lm-color-type; }
|
||||||
|
}
|
||||||
|
|
||||||
|
.laundry-orders-popup__phone {
|
||||||
|
font-size: $lm-font-sm;
|
||||||
|
font-weight: 500;
|
||||||
|
color: $lm-text-muted;
|
||||||
|
|
||||||
|
.fa { color: $lm-text-muted; font-size: 0.85em; }
|
||||||
|
}
|
||||||
|
|
||||||
|
.laundry-orders-popup__count { font-size: $lm-font-sm; }
|
||||||
|
|
||||||
|
.laundry-orders-popup__search {
|
||||||
|
margin-bottom: 0;
|
||||||
|
.input-group .btn { font-weight: 600; }
|
||||||
|
}
|
||||||
|
|
||||||
|
.laundry-orders-popup__error {
|
||||||
|
margin-bottom: 0;
|
||||||
|
border-radius: $lm-radius-md;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Loading skeleton
|
||||||
|
.laundry-orders-popup__loading {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: $lm-space-2;
|
||||||
|
}
|
||||||
|
.laundry-orders-popup__skeleton {
|
||||||
|
height: 96px;
|
||||||
|
border-radius: $lm-radius-md;
|
||||||
|
background: linear-gradient(90deg,
|
||||||
|
$lm-bg-soft 0%, #ECF0F4 50%, $lm-bg-soft 100%);
|
||||||
|
background-size: 200% 100%;
|
||||||
|
animation: laundry-skeleton-shimmer 1.4s ease-in-out infinite;
|
||||||
|
}
|
||||||
|
@keyframes laundry-skeleton-shimmer {
|
||||||
|
0% { background-position: 200% 0; }
|
||||||
|
100% { background-position: -200% 0; }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Empty state
|
||||||
|
.laundry-orders-popup__empty {
|
||||||
|
background: $lm-bg-soft;
|
||||||
|
border: 1px dashed $lm-border;
|
||||||
|
border-radius: $lm-radius-md;
|
||||||
|
.fa { color: $lm-text-muted; opacity: 0.5; }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Card list
|
||||||
|
.laundry-orders-popup__list {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: $lm-space-3;
|
||||||
|
}
|
||||||
|
.laundry-orders-popup__card {
|
||||||
|
background: $lm-bg-card;
|
||||||
|
border: 1px solid $lm-border;
|
||||||
|
border-left: 4px solid $lm-color-normal;
|
||||||
|
border-radius: $lm-radius-md;
|
||||||
|
padding: $lm-space-3 $lm-space-4;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: $lm-space-2;
|
||||||
|
box-shadow: $lm-shadow-sm;
|
||||||
|
transition: box-shadow 0.16s ease;
|
||||||
|
|
||||||
|
&:hover { box-shadow: $lm-shadow-md; }
|
||||||
|
|
||||||
|
&[data-state="intake"] { border-left-color: #3B82F6; }
|
||||||
|
&[data-state="processing"] { border-left-color: #F59E0B; }
|
||||||
|
&[data-state="ready"] { border-left-color: $lm-color-delivery; }
|
||||||
|
&[data-state="delivered"] { border-left-color: $lm-color-normal; }
|
||||||
|
&[data-state="cancelled"] {
|
||||||
|
border-left-color: $lm-text-muted;
|
||||||
|
opacity: 0.75;
|
||||||
|
}
|
||||||
|
&[data-payment="due"] { border-left-color: $lm-color-urgent; }
|
||||||
|
|
||||||
|
&[data-busy="1"] { opacity: 0.7; pointer-events: none; }
|
||||||
|
}
|
||||||
|
|
||||||
|
.laundry-orders-popup__card-head {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: baseline;
|
||||||
|
gap: $lm-space-3;
|
||||||
|
}
|
||||||
|
.laundry-orders-popup__order-name {
|
||||||
|
font-size: $lm-font-md;
|
||||||
|
font-weight: 800;
|
||||||
|
color: $lm-text;
|
||||||
|
letter-spacing: 0.01em;
|
||||||
|
}
|
||||||
|
.laundry-orders-popup__pos-ref {
|
||||||
|
font-size: $lm-font-xs;
|
||||||
|
color: $lm-text-muted;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
.laundry-orders-popup__date {
|
||||||
|
font-size: $lm-font-xs;
|
||||||
|
color: $lm-text-muted;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.laundry-orders-popup__card-meta {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: baseline;
|
||||||
|
gap: $lm-space-3;
|
||||||
|
font-size: $lm-font-sm;
|
||||||
|
color: $lm-text;
|
||||||
|
.fa { color: $lm-text-muted; }
|
||||||
|
}
|
||||||
|
.laundry-orders-popup__services {
|
||||||
|
font-size: $lm-font-xs;
|
||||||
|
max-width: 280px;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: bottom;
|
||||||
|
}
|
||||||
|
.laundry-orders-popup__total { font-weight: 700; font-size: $lm-font-md; }
|
||||||
|
.laundry-orders-popup__due { color: $lm-color-urgent; font-weight: 700; }
|
||||||
|
|
||||||
|
// Badges
|
||||||
|
.laundry-orders-popup__badges {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: $lm-space-2;
|
||||||
|
}
|
||||||
|
.laundry-badge {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 3px $lm-space-2;
|
||||||
|
font-size: $lm-font-xs;
|
||||||
|
font-weight: 700;
|
||||||
|
letter-spacing: 0.04em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
border-radius: 9999px;
|
||||||
|
white-space: nowrap;
|
||||||
|
line-height: 1.3;
|
||||||
|
.fa { font-size: 0.85em; }
|
||||||
|
}
|
||||||
|
.badge-state-intake { background: rgba(59,130,246,.12); color: #1E40AF; }
|
||||||
|
.badge-state-processing { background: rgba(245,158,11,.14); color: #92400E; }
|
||||||
|
.badge-state-ready { background: rgba(16,185,129,.14); color: #065F46; }
|
||||||
|
.badge-state-delivered { background: rgba(107,114,128,.16); color: #1F2937; }
|
||||||
|
.badge-state-cancelled { background: rgba(107,114,128,.10); color: $lm-text-muted; }
|
||||||
|
.badge-payment-paid { background: rgba(16,185,129,.16); color: #065F46; }
|
||||||
|
.badge-payment-deferred { background: rgba(245,158,11,.16); color: #92400E; }
|
||||||
|
.badge-payment-settled { background: rgba(99,102,241,.12); color: $lm-color-type; }
|
||||||
|
.badge-payment-due { background: rgba(239,68,68,.16); color: #991B1B; }
|
||||||
|
.laundry-badge--delivery {
|
||||||
|
background: rgba(16,185,129,.14);
|
||||||
|
color: $lm-color-delivery;
|
||||||
|
}
|
||||||
|
.laundry-badge--source {
|
||||||
|
background: $lm-bg-soft;
|
||||||
|
color: $lm-text-muted;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Actions row
|
||||||
|
.laundry-orders-popup__actions {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: $lm-space-2;
|
||||||
|
padding-top: $lm-space-2;
|
||||||
|
border-top: 1px dashed $lm-border;
|
||||||
|
|
||||||
|
.btn {
|
||||||
|
min-height: 44px; // touch target
|
||||||
|
font-weight: 700;
|
||||||
|
letter-spacing: 0.01em;
|
||||||
|
padding-inline: $lm-space-3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.laundry-orders-popup__due-hint { margin-top: 2px; }
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Thermal Work Order receipt ─────────────────────────────────────────
|
||||||
|
// Rendered by LaundryWorkOrderThermal when sent through pos.printer.print.
|
||||||
|
// Sized for an 80 mm POS printer; @media print collapses to full-width
|
||||||
|
// for the browser fallback (`webPrintFallback: true`).
|
||||||
|
.laundry-thermal {
|
||||||
|
font-family: "Courier New", "Courier", monospace;
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 1.4;
|
||||||
|
width: 76mm;
|
||||||
|
padding: 4mm;
|
||||||
|
color: #000;
|
||||||
|
background: #fff;
|
||||||
|
|
||||||
|
&__header { text-align: center; margin-bottom: 4px; }
|
||||||
|
&__company { font-weight: 700; font-size: 13px; }
|
||||||
|
&__title {
|
||||||
|
font-weight: 800; font-size: 16px;
|
||||||
|
margin: 4px 0 2px; letter-spacing: 0.06em;
|
||||||
|
}
|
||||||
|
&__name { font-size: 13px; font-weight: 700; }
|
||||||
|
|
||||||
|
&__divider { border-top: 1px dashed #000; margin: 4px 0; }
|
||||||
|
|
||||||
|
&__row {
|
||||||
|
display: flex; justify-content: space-between; gap: 8px;
|
||||||
|
margin: 1px 0;
|
||||||
|
}
|
||||||
|
&__label { font-weight: 700; }
|
||||||
|
|
||||||
|
&__lines {
|
||||||
|
width: 100%;
|
||||||
|
font-size: 11px;
|
||||||
|
border-collapse: collapse;
|
||||||
|
|
||||||
|
th, td { padding: 2px 4px; vertical-align: top; text-align: left; }
|
||||||
|
th { border-bottom: 1px solid #000; font-weight: 700; }
|
||||||
|
td.qty, th.qty { width: 28px; text-align: center; }
|
||||||
|
td.amount, th.amount { text-align: right; white-space: nowrap; }
|
||||||
|
}
|
||||||
|
|
||||||
|
&__tracking { font-size: 10px; color: #555; margin-top: 1px; }
|
||||||
|
|
||||||
|
&__totals { margin-top: 4px; }
|
||||||
|
&__total { font-weight: 700; }
|
||||||
|
&__due {
|
||||||
|
font-weight: 800;
|
||||||
|
border-top: 1px solid #000;
|
||||||
|
padding-top: 2px;
|
||||||
|
margin-top: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__status {
|
||||||
|
text-align: center;
|
||||||
|
font-weight: 700;
|
||||||
|
font-size: 13px;
|
||||||
|
margin-top: 4px;
|
||||||
|
letter-spacing: 0.05em;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__footer {
|
||||||
|
text-align: center;
|
||||||
|
font-size: 10px;
|
||||||
|
margin-top: 6px;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media print {
|
||||||
|
// Browser-fallback path — let the receipt fill the printable area.
|
||||||
|
.laundry-thermal { width: 100%; max-width: 80mm; margin: 0 auto; }
|
||||||
|
}
|
||||||
|
|
||||||
Reference in New Issue
Block a user