OpenClawDashboard/dashboard/src/components/workspace/activity-feed.tsx

79 lines
2.1 KiB
TypeScript

"use client"
/**
* activity-feed.tsx — chronological list of recent file modifications
* across all agents.
*/
import * as React from "react"
import { Clock, Loader2 } from "lucide-react"
import { cn } from "@/lib/utils"
export interface ActivityEvent {
agentId: string
agentName: string
agentEmoji: string
path: string
action: string
ts: number
}
interface Props {
events: ActivityEvent[]
loading?: boolean
}
function relativeTime(ts: number): string {
const diff = Date.now() - ts
const s = Math.floor(diff / 1000)
if (s < 60) return `${s}s ago`
const m = Math.floor(s / 60)
if (m < 60) return `${m}m ago`
const h = Math.floor(m / 60)
if (h < 24) return `${h}h ago`
return `${Math.floor(h / 24)}d ago`
}
export function ActivityFeed({ events, loading }: Props) {
if (loading) {
return (
<div className="flex items-center justify-center py-12">
<Loader2 className="h-5 w-5 animate-spin text-muted-foreground" />
</div>
)
}
if (events.length === 0) {
return (
<div className="flex flex-col items-center justify-center py-12 gap-2 text-muted-foreground">
<Clock className="h-6 w-6" />
<span className="text-sm">No recent activity</span>
</div>
)
}
return (
<div className="space-y-1">
{events.map((ev, i) => (
<div
key={i}
className="flex items-start gap-3 px-3 py-2.5 rounded-md hover:bg-muted/40 transition-colors"
>
{/* Agent avatar */}
<span className="text-base leading-none mt-0.5 shrink-0">{ev.agentEmoji}</span>
<div className="flex-1 min-w-0">
<div className="flex items-center gap-1.5 flex-wrap">
<span className="text-sm font-medium">{ev.agentName}</span>
<span className="text-xs text-muted-foreground">{ev.action}</span>
</div>
<p className="text-xs text-muted-foreground font-mono truncate">{ev.path}</p>
</div>
<span className="text-xs text-muted-foreground shrink-0 mt-0.5">
{relativeTime(ev.ts)}
</span>
</div>
))}
</div>
)
}