import { useMemo, useState } from 'react' import { useRecentContext } from '../../api/context' import { useMappings } from '../../api/mappings' import { CATEGORY_COLORS } from '../../lib/constants' interface ActivityFeedProps { minutes: number taskId?: number } const PAGE_SIZE = 20 function resolveCategory( appName: string, mappings: { pattern: string; matchType: string; category: string }[], ): string { for (const m of mappings) { if (m.matchType === 'Exact' && m.pattern.toLowerCase() === appName.toLowerCase()) { return m.category } if (m.matchType === 'Contains' && appName.toLowerCase().includes(m.pattern.toLowerCase())) { return m.category } if (m.matchType === 'Regex') { try { if (new RegExp(m.pattern, 'i').test(appName)) return m.category } catch { // skip invalid regex } } } return 'Unknown' } function formatTimestamp(ts: string): string { const date = new Date(ts) const now = new Date() const diffMs = now.getTime() - date.getTime() const diffMin = Math.floor(diffMs / 60_000) if (diffMin < 1) return 'just now' if (diffMin < 60) return `${diffMin}m ago` // Show time for older events const h = date.getHours() const m = date.getMinutes() const ampm = h >= 12 ? 'pm' : 'am' const hour12 = h % 12 || 12 const mins = m.toString().padStart(2, '0') return `${hour12}:${mins}${ampm}` } export default function ActivityFeed({ minutes, taskId }: ActivityFeedProps) { const { data: events, isLoading: eventsLoading } = useRecentContext(minutes) const { data: mappings, isLoading: mappingsLoading } = useMappings() const [visibleCount, setVisibleCount] = useState(PAGE_SIZE) const sortedEvents = useMemo(() => { if (!events) return [] let filtered = events if (taskId) { filtered = events.filter((e) => e.workTaskId === taskId) } // Reverse chronological return [...filtered].sort( (a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime(), ) }, [events, taskId]) const visibleEvents = sortedEvents.slice(0, visibleCount) const hasMore = visibleCount < sortedEvents.length if (eventsLoading || mappingsLoading) { return (
Loading activity...
) } if (sortedEvents.length === 0) { return (
No activity events for this time range.
) } return (
{visibleEvents.map((evt, idx) => { const category = mappings ? resolveCategory(evt.appName, mappings) : 'Unknown' const color = CATEGORY_COLORS[category] ?? CATEGORY_COLORS['Unknown'] const detail = evt.url || evt.windowTitle || '' const isLast = idx === visibleEvents.length - 1 return (
{/* Timeline connector + dot */}
{!isLast && (
)}
{/* Content */}
{formatTimestamp(evt.timestamp)} {evt.appName}
{detail && (

{detail}

)}
) })}
{hasMore && ( )}
) }