From 3845bd582f5a6f2aee87f2321e98258751c2dc2e Mon Sep 17 00:00:00 2001 From: Ashish Huddar Date: Tue, 24 Mar 2026 18:46:48 +0530 Subject: [PATCH] =?UTF-8?q?feat(web):=20mobile=20action=20strip=20?= =?UTF-8?q?=E2=80=94=20tappable=20urgency=20pills=20replace=20stat=20cards?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On mobile (<=767px), replace the 4 stat cards with a single-row MobileActionStrip showing non-zero urgency pills (respond/merge/review) with colored dots. Tapping a pill calls setExpandedLevel and scrolls to the accordion board via id="mobile-board". Hides stat cards, dashboard subtitle, and board-section-head on mobile to target ~92px hero height. Co-Authored-By: Claude Sonnet 4.6 --- packages/web/src/app/globals.css | 100 ++++++++++++++++++++-- packages/web/src/components/Dashboard.tsx | 76 +++++++++++++++- 2 files changed, 166 insertions(+), 10 deletions(-) diff --git a/packages/web/src/app/globals.css b/packages/web/src/app/globals.css index 222b74c70..a920a8602 100644 --- a/packages/web/src/app/globals.css +++ b/packages/web/src/app/globals.css @@ -1957,27 +1957,111 @@ html.light .xterm .xterm-viewport:hover::-webkit-scrollbar-thumb:active { /* -- Hero section stacks vertically -- */ .dashboard-hero__content { - flex-direction: column; - padding: 12px; - gap: 12px; + flex-direction: row; + flex-wrap: nowrap; + align-items: center; + padding: 0 12px; + gap: 8px; + min-height: 44px; } .dashboard-hero__primary { - flex-direction: column; + flex-direction: row; flex-basis: auto; - gap: 12px; + flex: 1; + align-items: center; + gap: 8px; + min-height: 44px; } + .dashboard-hero__heading { + flex: 1; + min-width: 0; + } + + .dashboard-subtitle { + display: none; + } + + /* Hide stat cards on mobile — replaced by the action strip */ .dashboard-stat-cards { - width: 100%; - margin-left: 0; + display: none; } .dashboard-hero__meta { - width: 100%; + flex-shrink: 0; margin-left: 0; } + /* ── Mobile action strip ─────────────────────────────────────────────── */ + + .mobile-action-strip { + display: flex; + align-items: center; + gap: 6px; + flex-wrap: nowrap; + overflow-x: auto; + -webkit-overflow-scrolling: touch; + scrollbar-width: none; + flex-shrink: 0; + } + + .mobile-action-strip::-webkit-scrollbar { + display: none; + } + + .mobile-action-strip--all-good { + align-items: center; + } + + .mobile-action-strip__all-good { + font-size: 11px; + color: var(--color-text-tertiary); + white-space: nowrap; + } + + .mobile-action-pill { + display: inline-flex; + align-items: center; + gap: 5px; + padding: 5px 10px; + border: 1px solid var(--color-border-default); + background: var(--color-bg-surface); + cursor: pointer; + white-space: nowrap; + min-height: 32px; + transition: + background 0.12s ease, + border-color 0.12s ease; + } + + .mobile-action-pill:hover, + .mobile-action-pill:active { + background: var(--color-bg-hover); + border-color: var(--color-border-strong); + } + + .mobile-action-pill__dot { + width: 7px; + height: 7px; + border-radius: 999px; + flex-shrink: 0; + } + + .mobile-action-pill__count { + font-size: 13px; + font-weight: 600; + font-variant-numeric: tabular-nums; + line-height: 1; + } + + .mobile-action-pill__label { + font-size: 11px; + font-weight: 500; + color: var(--color-text-secondary); + text-transform: lowercase; + } + /* -- Kanban board: stack columns vertically -- */ .kanban-board { flex-direction: column; diff --git a/packages/web/src/components/Dashboard.tsx b/packages/web/src/components/Dashboard.tsx index 4ac48fb33..87647b429 100644 --- a/packages/web/src/components/Dashboard.tsx +++ b/packages/web/src/components/Dashboard.tsx @@ -327,7 +327,17 @@ export function Dashboard({

- + {isMobile ? ( + { + setExpandedLevel(level); + document.getElementById("mobile-board")?.scrollIntoView({ behavior: "smooth", block: "start" }); + }} + /> + ) : ( + + )}
@@ -438,7 +448,7 @@ export function Dashboard({
{isMobile ? ( -
+
{MOBILE_KANBAN_ORDER.map((level) => ( ; + +function MobileActionStrip({ + grouped, + onPillTap, +}: { + grouped: Record; + onPillTap: (level: AttentionLevel) => void; +}) { + const activePills = MOBILE_ACTION_STRIP_LEVELS.filter( + ({ level }) => grouped[level].length > 0, + ); + + if (activePills.length === 0) { + return ( +
+ All clear — agents are working +
+ ); + } + + return ( +
+ {activePills.map(({ level, label, color }) => ( + + ))} +
+ ); +} + function StatusCards({ stats }: { stats: DashboardStats }) { if (stats.totalSessions === 0) { return (