reponsiveness on the dispatch section

This commit is contained in:
2026-05-22 19:42:24 +05:30
parent a794229728
commit f4b545322b
2 changed files with 554 additions and 63 deletions

View File

@@ -2394,28 +2394,6 @@
opacity: 1; opacity: 1;
} }
.testing-container .zone-order-card.going-on::after {
content: 'IN PROGRESS';
position: absolute;
top: 10px;
right: 12px;
padding: 3px 9px;
border-radius: 999px;
background: #16a34a;
color: #fff;
font-size: 9px;
font-weight: 800;
letter-spacing: 0.08em;
box-shadow: 0 2px 6px rgba(34, 197, 94, 0.4);
animation: zone-order-going-on-pulse 1.6s ease-in-out infinite;
z-index: 2;
}
@keyframes zone-order-going-on-pulse {
0%, 100% { box-shadow: 0 2px 6px rgba(34, 197, 94, 0.4); }
50% { box-shadow: 0 2px 14px rgba(34, 197, 94, 0.75); }
}
/* When the in-progress card is the focused stop, keep the green priority /* When the in-progress card is the focused stop, keep the green priority
signal but tint a hair stronger so the click state is still felt. */ signal but tint a hair stronger so the click state is still felt. */
.testing-container .zone-order-card.going-on.active { .testing-container .zone-order-card.going-on.active {
@@ -4079,6 +4057,12 @@
width: 200px; width: 200px;
} }
/* Hide floating chips overlay when split-map Compare Mode is active,
since the operator is focused on one single rider and list is redundant. */
.testing-container #body.compare-mode #ov-tr {
display: none !important;
}
.testing-container #ov-br { .testing-container #ov-br {
position: absolute; position: absolute;
bottom: 20px; bottom: 20px;
@@ -5433,4 +5417,488 @@
.testing-container .ri-snap-grid { .testing-container .ri-snap-grid {
grid-template-columns: 1fr; grid-template-columns: 1fr;
} }
}
/* ── Laptop Responsive Tuning (max-width: 1366px) ── */
@media (max-width: 1366px) {
/* Header adjustments */
.testing-container #hdr {
height: 48px;
padding: 0 16px;
}
.testing-container .logo-name {
font-size: 15px;
}
.testing-container .logo-badge {
width: 28px;
height: 28px;
font-size: 13px;
border-radius: 6px;
}
.testing-container .logo {
gap: 8px;
}
.testing-container #clock {
font-size: 11px;
padding: 4px 10px;
}
.testing-container .hdr-stats {
gap: 8px;
margin-right: 8px;
}
.testing-container .strat-stat {
padding: 4px 8px;
font-size: 11px;
gap: 4px;
}
.testing-container .strat-stat-label {
display: none; /* Hide profit/loss labels early to fit numbers */
}
.testing-container .live-status {
font-size: 11px;
padding: 4px 8px;
}
.testing-container .live-status-sub {
display: none; /* Hide total orders suffix to save space */
}
.testing-container .live-date-label {
font-size: 11px;
gap: 6px;
}
.testing-container .live-date-label span {
display: none; /* Hide the word 'Date' */
}
.testing-container .live-date-label input[type="date"] {
font-size: 12px;
padding: 4px 8px;
}
/* Strategy Tab Row adjustments */
.testing-container #strat-row {
height: 38px;
padding: 0 16px;
gap: 6px;
}
.testing-container .sbt {
padding: 6px 10px;
font-size: 11px;
gap: 5px;
}
.testing-container .sbt .sbt-icon {
width: 15px;
height: 15px;
font-size: 15px;
}
/* Batch Slots Row adjustments */
.testing-container #batch-row {
padding: 6px 16px;
gap: 6px;
}
.testing-container .batch-label {
font-size: 11px;
}
.testing-container .batch-btn {
padding: 4px 8px;
font-size: 11px;
}
/* Sidebar Layout adjustments */
.testing-container #sidebar {
width: 320px;
flex-basis: 320px;
}
.testing-container .sidebar-toggle-tab {
left: 320px;
}
/* Dynamic reduction in Compare Mode to keep dual maps wide enough */
.testing-container #body.compare-mode #sidebar {
width: 250px;
flex-basis: 250px;
}
.testing-container #body.compare-mode .sidebar-toggle-tab {
left: 250px;
}
.testing-container #body.compare-mode .sidebar-toggle-tab.is-collapsed {
left: 0;
}
/* Trim sidebar item paddings to increase visual density */
.testing-container .sb-header {
padding: 10px 12px;
}
.testing-container .sb-tile {
padding: 6px 8px;
gap: 6px;
}
.testing-container .sb-tile-value {
font-size: 16px;
}
.testing-container .rcard {
padding: 10px;
}
.testing-container .rcard-name {
font-size: 12px;
}
.testing-container .rcard-zone {
font-size: 10px;
}
.testing-container .step-wrap {
padding: 10px;
}
.testing-container #route-detail {
padding: 12px;
}
.testing-container .rd-rider-name {
font-size: 18px;
}
.testing-container .rd-stats-grid {
gap: 4px;
}
.testing-container .rd-stat {
padding: 8px 4px 6px;
}
.testing-container .rd-stat-value {
font-size: 14px;
}
.testing-container .rd-stat-label {
font-size: 9px;
}
/* Dual-map Compare Mode Header compression for 14" laptops */
.testing-container .compare-header-v2 {
padding: 8px 12px 6px;
gap: 6px;
}
.testing-container .compare-header-row .compare-title {
font-size: 13px;
gap: 8px;
}
.testing-container .compare-title-dot {
width: 8px;
height: 8px;
}
.testing-container .compare-title-badge {
padding: 2px 6px;
font-size: 9px;
}
.testing-container .compare-overall-btn,
.testing-container .compare-sync-toggle {
padding: 4px 8px;
font-size: 10px;
gap: 4px;
}
.testing-container .compare-overall-btn svg,
.testing-container .compare-sync-toggle svg {
font-size: 12px;
}
/* Compare Mode Step Timeline Compression for 14" laptops */
.testing-container .compare-timeline-wrap {
gap: 4px;
}
.testing-container .compare-timeline {
padding: 4px 12px 6px;
flex-wrap: nowrap !important;
overflow-x: auto;
scrollbar-width: none; /* Hide standard Firefox scrollbar */
-ms-overflow-style: none; /* Hide IE scrollbar */
}
.testing-container .compare-timeline::-webkit-scrollbar {
height: 4px;
}
.testing-container .compare-timeline::-webkit-scrollbar-track {
background: transparent;
}
.testing-container .compare-timeline::-webkit-scrollbar-thumb {
background: rgba(99, 102, 241, 0.25);
border-radius: 999px;
}
.testing-container .compare-timeline:hover::-webkit-scrollbar-thumb {
background: rgba(99, 102, 241, 0.5);
}
.testing-container .compare-step {
gap: 4px; /* Tighten spacer-circle layout vertical spacing */
}
.testing-container .compare-step-circle {
width: 24px;
height: 24px;
font-size: 10px;
box-shadow: 0 1px 4px rgba(15, 23, 42, 0.1), 0 0 0 1px rgba(255, 255, 255, 0.6);
}
.testing-container .compare-step.is-focused .compare-step-circle {
transform: scale(1.1);
box-shadow:
0 2px 6px rgba(15, 23, 42, 0.15),
0 0 0 2px #fff,
0 0 0 3px var(--step-color, #6366f1);
}
.testing-container .compare-step-spacer {
width: 10px;
margin-bottom: 14px; /* Shift connecting lines up for 24px circles */
}
.testing-container .compare-step-tick {
font-size: 9px;
}
.testing-container .compare-step-flag {
top: -3px;
right: -3px;
width: 8px;
height: 8px;
border-width: 1px;
}
/* Progress Strip inside Timeline */
.testing-container .compare-progress-strip {
margin-top: 2px;
}
.testing-container .compare-progress-text {
font-size: 9px;
}
/* Legend compacting for laptops */
.testing-container .compare-legend {
padding-top: 2px;
margin-top: 0;
gap: 8px;
}
.testing-container .compare-legend-item {
font-size: 9px;
}
.testing-container .compare-legend-swatch {
width: 10px;
height: 10px;
}
.testing-container .compare-legend-note {
display: none; /* Hide wordy GPS smoothing notes */
}
/* Bottom Delta Card panel compression for 14" laptops */
.testing-container .compare-delta {
padding: 8px 12px;
gap: 6px;
}
.testing-container .compare-delta-title {
margin-bottom: 4px;
gap: 8px;
}
.testing-container .compare-delta-step-badge {
width: 20px;
height: 20px;
font-size: 11px;
}
.testing-container .compare-delta-title-main {
font-size: 12px;
}
.testing-container .compare-delta-title-sub {
font-size: 9px;
}
.testing-container .compare-delta-status {
padding: 2px 6px;
font-size: 9px;
}
.testing-container .compare-delta-grid {
gap: 6px;
}
.testing-container .compare-delta-cell {
padding: 5px 8px 4px;
border-radius: 8px;
}
.testing-container .compare-delta-cell-label {
font-size: 9px;
margin-bottom: 1px;
}
.testing-container .compare-delta-cell-val {
font-size: 13px;
}
.testing-container .compare-delta-cell-unit {
font-size: 8px;
}
.testing-container .compare-delta-cell-sub {
font-size: 9px;
margin-top: 1px;
}
}
/* ── Laptop Height Tuning (max-height: 750px) ── */
@media (max-height: 750px) {
/* Let's shrink header rows even further on short screen heights */
.testing-container #hdr {
height: 42px;
}
.testing-container #strat-row {
height: 34px;
}
.testing-container #batch-row {
padding: 4px 16px;
}
/* Dual-map Compare Mode Header compression */
.testing-container .compare-header-v2 {
padding: 8px 12px 6px;
gap: 6px;
}
.testing-container .compare-header-row .compare-title {
font-size: 13px;
gap: 8px;
}
.testing-container .compare-title-dot {
width: 8px;
height: 8px;
}
.testing-container .compare-title-badge {
padding: 2px 6px;
font-size: 9px;
}
.testing-container .compare-overall-btn,
.testing-container .compare-sync-toggle {
padding: 4px 8px;
font-size: 10px;
gap: 4px;
}
.testing-container .compare-overall-btn svg,
.testing-container .compare-sync-toggle svg {
font-size: 12px;
}
/* Compare Mode Step Timeline Compression */
.testing-container .compare-timeline-wrap {
gap: 4px;
}
.testing-container .compare-timeline {
padding-bottom: 2px;
}
.testing-container .compare-step {
gap: 4px; /* Reduced from 11px to bring timeline elements tighter */
}
.testing-container .compare-step-circle {
width: 24px;
height: 24px;
font-size: 10px;
box-shadow: 0 1px 4px rgba(15, 23, 42, 0.1), 0 0 0 1px rgba(255, 255, 255, 0.6);
}
.testing-container .compare-step.is-focused .compare-step-circle {
transform: scale(1.1);
box-shadow:
0 2px 6px rgba(15, 23, 42, 0.15),
0 0 0 2px #fff,
0 0 0 3px var(--step-color, #6366f1);
}
.testing-container .compare-step-spacer {
width: 10px;
margin-bottom: 14px; /* Shift spacer dynamically up to align with 24px circles */
}
.testing-container .compare-step-tick {
font-size: 9px;
}
/* Progress Strip inside Timeline */
.testing-container .compare-progress-strip {
margin-top: 2px;
}
.testing-container .compare-progress-text {
font-size: 9px;
}
/* Legend compacting */
.testing-container .compare-legend {
padding-top: 2px;
margin-top: 0;
gap: 8px;
}
.testing-container .compare-legend-item {
font-size: 9px;
}
.testing-container .compare-legend-swatch {
width: 10px;
height: 10px;
}
.testing-container .compare-legend-note {
display: none; /* Hide verbose Kalman note on short screens */
}
/* Bottom Delta Card panel compression */
.testing-container .compare-delta {
padding: 8px 12px;
gap: 6px;
}
.testing-container .compare-delta-title {
margin-bottom: 4px;
gap: 8px;
}
.testing-container .compare-delta-step-badge {
width: 20px;
height: 20px;
font-size: 11px;
}
.testing-container .compare-delta-title-main {
font-size: 12px;
}
.testing-container .compare-delta-title-sub {
font-size: 9px;
}
.testing-container .compare-delta-status {
padding: 2px 6px;
font-size: 9px;
}
/* Delta Grid - cells and labels */
.testing-container .compare-delta-grid {
gap: 6px;
}
.testing-container .compare-delta-cell {
padding: 5px 8px 4px;
border-radius: 8px;
}
.testing-container .compare-delta-cell-label {
font-size: 9px;
margin-bottom: 1px;
}
.testing-container .compare-delta-cell-val {
font-size: 13px;
}
.testing-container .compare-delta-cell-unit {
font-size: 8px;
}
.testing-container .compare-delta-cell-sub {
font-size: 9px;
margin-top: 1px;
}
}
/* Day summary toggle styles */
.testing-container .compare-delta.is-collapsible.is-collapsed {
padding-bottom: 12px;
}
.testing-container .compare-delta-toggle-icon {
display: inline-flex;
align-items: center;
justify-content: center;
color: var(--text-muted, #64748b);
font-size: 18px;
transition: transform 0.22s cubic-bezier(0.4, 0, 0.2, 1), color 0.15s ease;
}
.testing-container .compare-delta-title:hover .compare-delta-toggle-icon {
color: var(--accent, #6366f1);
}
@media (max-width: 1366px) {
.testing-container .compare-delta.is-collapsible.is-collapsed {
padding-bottom: 8px;
}
.testing-container .compare-delta-toggle-icon {
font-size: 15px;
}
}
@media (max-height: 750px) {
.testing-container .compare-delta.is-collapsible.is-collapsed {
padding-bottom: 8px;
}
.testing-container .compare-delta-toggle-icon {
font-size: 15px;
}
} }

View File

@@ -973,6 +973,7 @@ const Dispatch = ({
// per-rider). Kept as a single state flag so we don't entangle it with // per-rider). Kept as a single state flag so we don't entangle it with
// viewMode/focused* logic. // viewMode/focused* logic.
const [compareOpen, setCompareOpen] = useState(false); const [compareOpen, setCompareOpen] = useState(false);
const [daySummaryOpen, setDaySummaryOpen] = useState(false);
// In controlled mode the parent owns selectedRiderId, so handleRiderFocus only // In controlled mode the parent owns selectedRiderId, so handleRiderFocus only
// fires onRiderSelect — focusedRider won't update until the parent re-renders. // fires onRiderSelect — focusedRider won't update until the parent re-renders.
// This ref lets us defer setCompareOpen(true) until focusedRider is confirmed. // This ref lets us defer setCompareOpen(true) until focusedRider is confirmed.
@@ -1586,6 +1587,7 @@ const Dispatch = ({
// stuck on a step that may not exist in their day. // stuck on a step that may not exist in their day.
useEffect(() => { useEffect(() => {
setFocusedCompareStep(null); setFocusedCompareStep(null);
setDaySummaryOpen(false);
}, [compareOpen, focusedRider?.id]); }, [compareOpen, focusedRider?.id]);
// Mirror pan/zoom from whichever map the user is driving. Called by // Mirror pan/zoom from whichever map the user is driving. Called by
@@ -2989,7 +2991,7 @@ const Dispatch = ({
</div> </div>
</div> </div>
) : ( ) : (
<div id="body" className={sidebarCollapsed ? 'sidebar-collapsed' : ''}> <div id="body" className={`${sidebarCollapsed ? 'sidebar-collapsed' : ''} ${compareOpen ? 'compare-mode' : ''}`.trim()}>
<button <button
type="button" type="button"
className={`sidebar-toggle-tab${sidebarCollapsed ? ' is-collapsed' : ''}`} className={`sidebar-toggle-tab${sidebarCollapsed ? ' is-collapsed' : ''}`}
@@ -4369,8 +4371,13 @@ const Dispatch = ({
: ''; : '';
const total = sum.onTime + sum.late; const total = sum.onTime + sum.late;
return ( return (
<div className="compare-delta"> <div className={`compare-delta is-collapsible${daySummaryOpen ? ' is-expanded' : ' is-collapsed'}`}>
<div className="compare-delta-title"> <div
className="compare-delta-title"
onClick={() => setDaySummaryOpen((v) => !v)}
style={{ cursor: 'pointer', userSelect: 'none' }}
title={daySummaryOpen ? 'Collapse Day Summary' : 'Expand Day Summary'}
>
<span <span
className="compare-delta-step-badge" className="compare-delta-step-badge"
style={{ background: focusedRider.color }} style={{ background: focusedRider.color }}
@@ -4378,49 +4385,65 @@ const Dispatch = ({
<MdPublic /> <MdPublic />
</span> </span>
<div className="compare-delta-title-text"> <div className="compare-delta-title-text">
<div className="compare-delta-title-main">Day summary</div> <div className="compare-delta-title-main" style={{ display: 'flex', alignItems: 'center', gap: '6px' }}>
Day summary
<span
className="compare-delta-toggle-icon"
style={{
display: 'inline-flex',
transform: daySummaryOpen ? 'rotate(180deg)' : 'rotate(0deg)',
transition: 'transform 0.22s cubic-bezier(0.4, 0, 0.2, 1)'
}}
>
<MdExpandMore />
</span>
</div>
<div className="compare-delta-title-sub"> <div className="compare-delta-title-sub">
Click any step above to scrutinize that delivery {daySummaryOpen
? 'Click to collapse summary'
: 'Click to expand summary · Click any step above to scrutinize'}
</div> </div>
</div> </div>
</div> </div>
<div className="compare-delta-grid"> {daySummaryOpen && (
<div className="compare-delta-cell"> <div className="compare-delta-grid">
<span className="compare-delta-cell-label">Total distance</span> <div className="compare-delta-cell">
<span className="compare-delta-cell-val"> <span className="compare-delta-cell-label">Total distance</span>
{sum.actualKm.toFixed(1)}{' '} <span className="compare-delta-cell-val">
<span className="compare-delta-cell-unit">km</span> {sum.actualKm.toFixed(1)}{' '}
</span> <span className="compare-delta-cell-unit">km</span>
<span className="compare-delta-cell-sub"> </span>
planned {sum.plannedKm.toFixed(1)} km <span className="compare-delta-cell-sub">
</span> planned {sum.plannedKm.toFixed(1)} km
</span>
</div>
<div className={`compare-delta-cell${sum.anomalies > 0 ? ' is-anomaly' : ''}`}>
<span className="compare-delta-cell-label">Route deviation</span>
<span className={`compare-delta-cell-val ${deltaCls}`}>
{sum.kmDeltaPct != null
? `${sum.kmDeltaPct > 0 ? '+' : ''}${sum.kmDeltaPct.toFixed(0)}%`
: '—'}
</span>
<span className="compare-delta-cell-sub">
{sum.anomalies > 0
? `${sum.anomalies} step${sum.anomalies > 1 ? 's' : ''} flagged`
: 'within plan'}
</span>
</div>
<div className="compare-delta-cell">
<span className="compare-delta-cell-label">On-time</span>
<span className="compare-delta-cell-val">
{sum.onTime}
{total > 0 && (
<span className="compare-delta-cell-unit">/{total}</span>
)}
</span>
<span className="compare-delta-cell-sub">
{sum.late > 0 ? `${sum.late} late` : 'all on schedule'}
</span>
</div>
</div> </div>
<div className={`compare-delta-cell${sum.anomalies > 0 ? ' is-anomaly' : ''}`}> )}
<span className="compare-delta-cell-label">Route deviation</span>
<span className={`compare-delta-cell-val ${deltaCls}`}>
{sum.kmDeltaPct != null
? `${sum.kmDeltaPct > 0 ? '+' : ''}${sum.kmDeltaPct.toFixed(0)}%`
: '—'}
</span>
<span className="compare-delta-cell-sub">
{sum.anomalies > 0
? `${sum.anomalies} step${sum.anomalies > 1 ? 's' : ''} flagged`
: 'within plan'}
</span>
</div>
<div className="compare-delta-cell">
<span className="compare-delta-cell-label">On-time</span>
<span className="compare-delta-cell-val">
{sum.onTime}
{total > 0 && (
<span className="compare-delta-cell-unit">/{total}</span>
)}
</span>
<span className="compare-delta-cell-sub">
{sum.late > 0 ? `${sum.late} late` : 'all on schedule'}
</span>
</div>
</div>
</div> </div>
); );
})()} })()}