feat(web): mobile action strip — tappable urgency pills replace stat cards
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 <noreply@anthropic.com>
This commit is contained in:
parent
0d2804fcf9
commit
3845bd582f
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -327,7 +327,17 @@ export function Dashboard({
|
|||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<StatusCards stats={liveStats} />
|
||||
{isMobile ? (
|
||||
<MobileActionStrip
|
||||
grouped={grouped}
|
||||
onPillTap={(level) => {
|
||||
setExpandedLevel(level);
|
||||
document.getElementById("mobile-board")?.scrollIntoView({ behavior: "smooth", block: "start" });
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<StatusCards stats={liveStats} />
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="dashboard-hero__meta">
|
||||
|
|
@ -438,7 +448,7 @@ export function Dashboard({
|
|||
</div>
|
||||
|
||||
{isMobile ? (
|
||||
<div className="accordion-board">
|
||||
<div id="mobile-board" className="accordion-board">
|
||||
{MOBILE_KANBAN_ORDER.map((level) => (
|
||||
<AttentionZone
|
||||
key={level}
|
||||
|
|
@ -696,6 +706,68 @@ function ProjectMetric({ label, value, tone }: { label: string; value: number; t
|
|||
);
|
||||
}
|
||||
|
||||
const MOBILE_ACTION_STRIP_LEVELS = [
|
||||
{
|
||||
level: "respond" as const,
|
||||
label: "respond",
|
||||
color: "var(--color-status-error)",
|
||||
},
|
||||
{
|
||||
level: "merge" as const,
|
||||
label: "merge",
|
||||
color: "var(--color-status-ready)",
|
||||
},
|
||||
{
|
||||
level: "review" as const,
|
||||
label: "review",
|
||||
color: "var(--color-accent-orange)",
|
||||
},
|
||||
] satisfies Array<{ level: AttentionLevel; label: string; color: string }>;
|
||||
|
||||
function MobileActionStrip({
|
||||
grouped,
|
||||
onPillTap,
|
||||
}: {
|
||||
grouped: Record<AttentionLevel, DashboardSession[]>;
|
||||
onPillTap: (level: AttentionLevel) => void;
|
||||
}) {
|
||||
const activePills = MOBILE_ACTION_STRIP_LEVELS.filter(
|
||||
({ level }) => grouped[level].length > 0,
|
||||
);
|
||||
|
||||
if (activePills.length === 0) {
|
||||
return (
|
||||
<div className="mobile-action-strip mobile-action-strip--all-good">
|
||||
<span className="mobile-action-strip__all-good">All clear — agents are working</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="mobile-action-strip" role="navigation" aria-label="Urgency quick-nav">
|
||||
{activePills.map(({ level, label, color }) => (
|
||||
<button
|
||||
key={level}
|
||||
type="button"
|
||||
className="mobile-action-pill"
|
||||
onClick={() => onPillTap(level)}
|
||||
aria-label={`${grouped[level].length} ${label} — scroll to section`}
|
||||
>
|
||||
<span
|
||||
className="mobile-action-pill__dot"
|
||||
style={{ background: color }}
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<span className="mobile-action-pill__count" style={{ color }}>
|
||||
{grouped[level].length}
|
||||
</span>
|
||||
<span className="mobile-action-pill__label">{label}</span>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function StatusCards({ stats }: { stats: DashboardStats }) {
|
||||
if (stats.totalSessions === 0) {
|
||||
return (
|
||||
|
|
|
|||
Loading…
Reference in New Issue