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 (