diff --git a/src/pages/nearle/dispatch/Dispatch.css b/src/pages/nearle/dispatch/Dispatch.css index 7f1f35d..00c8c6f 100644 --- a/src/pages/nearle/dispatch/Dispatch.css +++ b/src/pages/nearle/dispatch/Dispatch.css @@ -3,11 +3,11 @@ --bg-sub: #f8fafc; --bg-card: #ffffff; --border: #e2e8f0; - --border-active: #3b82f6; + --border-active: #9255AB; --text: #1e293b; --text-muted: #64748b; - --accent: #3b82f6; - --accent-soft: rgba(59, 130, 246, 0.08); + --accent: #9255AB; + --accent-soft: rgba(146, 85, 171, 0.08); --kitchen: #f59e0b; --kitchen-soft: rgba(245, 158, 11, 0.1); --success: #22c55e; @@ -68,7 +68,7 @@ width: 32px; height: 32px; border-radius: 8px; - background: linear-gradient(135deg, #3b82f6, #2563eb); + background: linear-gradient(135deg, #9255AB, #662582); display: flex; align-items: center; justify-content: center; @@ -1321,7 +1321,7 @@ /* Unified active style for hourly slot chips — single accent gradient instead of per-wave colors since 12 different colors would be visually noisy. */ .dispatch-container .batch-btn.batch-slot.active { - background: linear-gradient(135deg, #3b82f6, #6366f1); + background: linear-gradient(135deg, #9255AB, #662582); } .dispatch-container .batch-btn-icon { @@ -1684,14 +1684,14 @@ } .dispatch-container .sidebar-toggle-tab:hover { - background: linear-gradient(135deg, #6366f1, #3b82f6); + background: linear-gradient(135deg, #7b2fad, #9255AB); color: #fff; transform: translate(-50%, -50%) scale(1.06); - box-shadow: 0 6px 16px rgba(99, 102, 241, 0.35); + box-shadow: 0 6px 16px rgba(146, 85, 171, 0.35); } .dispatch-container .sidebar-toggle-tab:focus-visible { - outline: 2px solid var(--accent, #3b82f6); + outline: 2px solid var(--accent, #9255AB); outline-offset: 2px; } @@ -1755,7 +1755,7 @@ width: 3px; height: 14px; border-radius: 2px; - background: linear-gradient(180deg, var(--accent), #6366f1); + background: linear-gradient(180deg, var(--accent), #662582); } .dispatch-container .sb-title-text { @@ -1802,7 +1802,7 @@ padding: 4px 10px 4px 8px; border-radius: 999px; background: var(--accent-soft); - border: 1px solid rgba(59, 130, 246, 0.22); + border: 1px solid rgba(146, 85, 171, 0.22); color: var(--accent); font-size: 11px; font-weight: 700; @@ -2121,82 +2121,78 @@ display: flex; flex-direction: column; gap: 12px; - padding: 4px 2px 12px; + padding: 4px 0 16px; +} + +@keyframes adcard-in { + from { opacity: 0; transform: translateY(6px); } + to { opacity: 1; transform: translateY(0); } } .dispatch-container .adcard { - position: relative; - background: #fff; + background: var(--bg-card); border: 1px solid var(--border); border-radius: 14px; - padding: 13px 14px 13px 16px; + padding: 16px; cursor: pointer; + /* Explicit properties so the CSS animation (adcard-in) isn't interrupted by transition: all */ + transition: transform 0.2s cubic-bezier(0.4, 0, 0.2, 1), + box-shadow 0.2s cubic-bezier(0.4, 0, 0.2, 1), + border-color 0.2s cubic-bezier(0.4, 0, 0.2, 1), + background 0.2s cubic-bezier(0.4, 0, 0.2, 1); + animation: adcard-in 0.28s cubic-bezier(0.4, 0, 0.2, 1) both; + box-shadow: var(--shadow); + display: flex; + flex-direction: column; + gap: 10px; + position: relative; overflow: hidden; - box-shadow: 0 1px 2px rgba(15, 23, 42, 0.05); - transition: transform 0.18s ease, box-shadow 0.18s ease, border-color 0.18s ease; -} - -.dispatch-container .adcard::before { - content: ''; - position: absolute; - top: 0; - left: 0; - width: 3px; - height: 100%; - background: var(--ad-accent, var(--accent)); - opacity: 0.9; - transition: width 0.18s ease; + border-left: 4px solid var(--ad-accent, var(--border)); } .dispatch-container .adcard:hover { transform: translateY(-2px); - box-shadow: 0 10px 24px rgba(15, 23, 42, 0.1); + box-shadow: var(--shadow-lg); border-color: var(--ad-accent, var(--accent)); } -.dispatch-container .adcard:hover::before { - width: 5px; -} - .dispatch-container .adcard.is-active { border-color: var(--ad-accent, var(--accent)); - box-shadow: 0 10px 24px rgba(15, 23, 42, 0.12); -} - -.dispatch-container .adcard.is-active::before { - width: 5px; + box-shadow: 0 0 0 2px rgba(146, 85, 171, 0.15), var(--shadow-lg); + background: var(--accent-soft); } .dispatch-container .adcard-top { display: flex; align-items: center; - gap: 11px; + gap: 12px; } .dispatch-container .adcard-avatar { - width: 38px; - height: 38px; - border-radius: 11px; - flex-shrink: 0; + width: 36px; + height: 36px; + border-radius: 8px; + color: #fff; display: flex; align-items: center; justify-content: center; - font-size: 13px; font-weight: 800; - color: #fff; - letter-spacing: 0.02em; + font-size: 13px; + flex-shrink: 0; } .dispatch-container .adcard-titles { flex: 1; min-width: 0; + display: flex; + flex-direction: column; + gap: 2px; } .dispatch-container .adcard-customer { - font-size: 11.5px; - font-weight: 600; - color: var(--text-muted); - letter-spacing: -0.01em; + font-size: 12px; + font-weight: 500; + color: #94a3b8; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; @@ -2205,58 +2201,73 @@ .dispatch-container .adcard-rider { display: flex; align-items: center; - gap: 5px; + gap: 4px; + color: var(--text); font-size: 14.5px; font-weight: 700; - color: var(--text); - margin-top: 2px; - min-width: 0; } .dispatch-container .adcard-status { - flex-shrink: 0; - align-self: flex-start; - font-size: 9.5px; - font-weight: 800; - padding: 4px 9px; - border-radius: 999px; - text-transform: uppercase; - letter-spacing: 0.05em; + font-size: 11px; + font-weight: 700; + padding: 4px 8px; + border-radius: 6px; white-space: nowrap; } .dispatch-container .adcard-addr { display: flex; - align-items: center; - gap: 7px; - margin-top: 11px; + align-items: flex-start; + gap: 6px; + background: var(--bg-sub); + padding: 8px 10px; + border-radius: 8px; font-size: 12px; color: var(--text-muted); +} + +.dispatch-container .adcard-ic { + display: flex; + align-items: center; + font-size: 14px; + color: var(--text-muted); + flex-shrink: 0; + margin-top: 1px; +} + +.dispatch-container .adcard-tx { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; min-width: 0; } -.dispatch-container .adcard-addr .adcard-ic { - color: #94a3b8; +.dispatch-container .adcard-addr-tx { + white-space: normal; + display: -webkit-box; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; + line-height: 1.4; } .dispatch-container .adcard-foot { display: flex; align-items: center; justify-content: space-between; - gap: 10px; - margin-top: 11px; - padding-top: 10px; - border-top: 1px solid var(--border); + gap: 12px; + font-size: 11.5px; + color: var(--text-muted); + font-weight: 500; + margin-top: 2px; } .dispatch-container .adcard-pickup { - display: inline-flex; + display: flex; align-items: center; - gap: 6px; - min-width: 0; - font-size: 12px; + gap: 4px; + color: var(--kitchen); font-weight: 600; - color: var(--text); + min-width: 0; } .dispatch-container .adcard-pickup .adcard-ic { @@ -2264,22 +2275,17 @@ } .dispatch-container .adcard-metrics { - display: inline-flex; + display: flex; align-items: center; - gap: 12px; + gap: 10px; + margin-left: auto; flex-shrink: 0; - font-size: 12px; - font-weight: 700; - font-variant-numeric: tabular-nums; } .dispatch-container .adcard-m { - display: inline-flex; + display: flex; align-items: center; - gap: 4px; -} - -.dispatch-container .adcard-m-km { + gap: 3px; color: var(--text-muted); } @@ -2291,39 +2297,6 @@ color: #10b981; } -.dispatch-container .adcard-ic { - flex-shrink: 0; - display: inline-flex; - align-items: center; - font-size: 14px; -} - -.dispatch-container .adcard-tx { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - min-width: 0; -} - -/* Drop address: allow up to two lines instead of one truncated line so long - addresses stay readable; the location icon hugs the first line. */ -.dispatch-container .adcard-addr { - align-items: flex-start; -} - -.dispatch-container .adcard-addr .adcard-ic { - margin-top: 1px; -} - -.dispatch-container .adcard-addr-tx { - white-space: normal; - display: -webkit-box; - -webkit-line-clamp: 2; - -webkit-box-orient: vertical; - overflow: hidden; - line-height: 1.4; -} - .dispatch-container .zone-order-change-rider { flex-shrink: 0; @@ -2463,6 +2436,15 @@ color: #d97706; } +.dispatch-container .rd-stat-riderkms { + background: linear-gradient(135deg, rgba(8, 145, 178, 0.10), rgba(14, 165, 233, 0.04)); + border-color: rgba(8, 145, 178, 0.25); +} + +.dispatch-container .rd-stat-riderkms .rd-stat-value { + color: #0891b2; +} + .dispatch-container .rd-stat-profit.is-gain { background: linear-gradient(135deg, rgba(34, 197, 94, 0.12), rgba(20, 184, 166, 0.04)); border-color: rgba(34, 197, 94, 0.35); diff --git a/src/pages/nearle/dispatch/Dispatch.js b/src/pages/nearle/dispatch/Dispatch.js index b39b9a1..3303593 100644 --- a/src/pages/nearle/dispatch/Dispatch.js +++ b/src/pages/nearle/dispatch/Dispatch.js @@ -3870,20 +3870,18 @@ const Dispatch = ({ rider is focused (the focused-rider view shows that rider's stats), and also hidden in the Active view when there are no in-progress deliveries, so the empty state isn't topped by a "0 orders / 0 riders" header. */} - {!focusedRider && !(isAllActiveView && activeDeliveryCount === 0) && ( + {!focusedRider && (
- {!isAllActiveView && ( -
-
-
- - - {activeStats.label} - +
+
+
- )} + + + {activeStats.label} + +
@@ -3912,7 +3910,7 @@ const Dispatch = ({
*/} - {(focusedRider || focusedKitchen) ? ( + {((focusedRider && !isAllActiveView) || focusedKitchen) ? (
{focusedRider ? ( @@ -3924,6 +3922,7 @@ const Dispatch = ({ const activeOnly = isAllActiveView ? getActiveOrder(focusedRider.orders) : null; const panelOrders = isAllActiveView ? (activeOnly ? [activeOnly] : []) : focusedRider.orders; const totalKm = panelOrders.reduce((s, o) => s + parseFloat(o.actualkms || o.kms || 0), 0); + const totalRiderKm = panelOrders.reduce((s, o) => s + parseFloat(o.riderkms || 0), 0); return (
@@ -3936,6 +3935,11 @@ const Dispatch = ({
{totalKm.toFixed(1)}km
Distance
+
+
+
{totalRiderKm.toFixed(1)}km
+
Rider KMs
+
); })()} diff --git a/src/pages/nearle/dispatch/dispatchShared.js b/src/pages/nearle/dispatch/dispatchShared.js index ee5a471..3b3f9da 100644 --- a/src/pages/nearle/dispatch/dispatchShared.js +++ b/src/pages/nearle/dispatch/dispatchShared.js @@ -29,12 +29,12 @@ export const getStatusStyle = (status) => export const FINAL_STATUSES = new Set(['delivered']); export const SKIPPED_STATUSES = new Set(['cancelled', 'skipped']); -// An order is "active" (currently in progress) when it's neither completed -// (delivered) nor skipped/cancelled. The Active view uses this to collapse a -// rider down to the single delivery they're working on right now. +// An order is "active" (currently in progress) when its status is exactly +// 'active'. The Active view uses this to collapse a rider down to the single +// delivery they're working on right now. export const isActiveDelivery = (o) => { const s = String(o?.orderstatus || '').toLowerCase(); - return !FINAL_STATUSES.has(s) && !SKIPPED_STATUSES.has(s); + return s === 'active'; }; // A rider's single in-progress delivery: the first non-final, non-skipped