feat(apess): surface real board activity on the instructor dashboard

Extend the collective feed to consume node:status / node:activity: the
reducer now tracks per-team board online state and a bounded,
most-recent-first activity feed (preserved across snapshots). Admin gains
a "Boards live" stat and a "Board activity" panel (new BoardActivity
component) showing real generate/compile/flash events across teams.

Tests: front-end 177, typecheck clean, prod build passes.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
This commit is contained in:
Omar Sobh
2026-07-03 05:56:39 -07:00
co-authored by Claude Opus 4.8
parent f21bbdf31e
commit 28a0965108
5 changed files with 150 additions and 7 deletions
+47
View File
@@ -0,0 +1,47 @@
import type { NodeActivityEntry } from '@/lib/useCollective'
import type { NodeActivityKind } from '@/types'
import { cn } from '@/lib/utils'
const KIND_DOT: Record<NodeActivityKind, string> = {
thinking: 'bg-muted-foreground',
tool: 'bg-amber',
flash: 'bg-primary',
error: 'bg-destructive',
response: 'bg-teal',
}
export interface BoardActivityProps {
activity: NodeActivityEntry[]
/** Resolve a teamId to a display name (falls back to the id). */
nameFor?: (teamId: string) => string
}
/** Instructor-facing rolling feed of real on-device agent activity across boards. */
export function BoardActivity({ activity, nameFor }: BoardActivityProps) {
if (activity.length === 0) {
return (
<p data-testid="board-activity-empty" className="font-mono text-[11px] text-muted-foreground">
No board activity yet boards report generate / compile / flash here as teams work.
</p>
)
}
return (
<ul data-testid="board-activity" className="space-y-1.5">
{activity.map((e, i) => (
<li key={i} className="flex items-center gap-2 font-mono text-[11px]">
<span className={cn('w-1.5 h-1.5 rounded-full shrink-0', KIND_DOT[e.kind])} />
<span className="text-muted-foreground shrink-0">{nameFor ? nameFor(e.teamId) : e.teamId}</span>
<span
className={cn(
'truncate',
e.kind === 'error' && 'text-destructive',
e.kind === 'flash' && 'text-foreground font-medium',
)}
>
{e.label}
</span>
</li>
))}
</ul>
)
}