'use client' import { useEffect, useRef, useState, useCallback } from 'react' import { motion, AnimatePresence } from 'framer-motion' import { useEngineStore } from '@/store/engineStore' interface LogEntry { id: number time: string msg: string level: 'info' | 'success' | 'warn' | 'error' } let _counter = 1 function makeEntry(msg: string, level: LogEntry['level'] = 'info'): LogEntry { return { id: _counter++, time: new Date().toLocaleTimeString(), msg, level } } const LEVEL_STYLES: Record = { info: 'text-g-muted', success: 'text-g-green', warn: 'text-g-amber', error: 'text-g-red', } const LEVEL_DOT: Record = { info: 'bg-g-faint/40', success: 'bg-g-green', warn: 'bg-g-amber', error: 'bg-g-red', } export default function ActivityLog() { const [entries, setEntries] = useState(() => [ makeEntry('Ghost Node dashboard initialised.', 'success'), ]) const [filter, setFilter] = useState('') const bottomRef = useRef(null) const { status, last_cycle, total_alerts } = useEngineStore() const prevStatus = useRef(null) const prevCycle = useRef(null) const prevAlerts = useRef(-1) const push = useCallback((msg: string, level: LogEntry['level'] = 'info') => { setEntries(prev => [...prev.slice(-199), makeEntry(msg, level)]) }, []) // Track engine status transitions useEffect(() => { if (prevStatus.current === null) { prevStatus.current = status; return } if (prevStatus.current === status) return if (status === 'Running') push('Engine started.', 'success') else if (status === 'Paused') push('Engine paused.', 'warn') else if (status === 'Idle') push('Engine stopped.', 'warn') prevStatus.current = status }, [status, push]) // Track scan cycles useEffect(() => { if (prevCycle.current === null) { prevCycle.current = last_cycle; return } if (prevCycle.current === last_cycle) return if (last_cycle && last_cycle !== 'Never') { push('Scan cycle completed.', 'info') } prevCycle.current = last_cycle }, [last_cycle, push]) // Track new alerts useEffect(() => { if (prevAlerts.current === -1) { prevAlerts.current = total_alerts; return } if (total_alerts > prevAlerts.current) { const delta = total_alerts - prevAlerts.current push(`${delta} new alert${delta > 1 ? 's' : ''} fired!`, 'success') } prevAlerts.current = total_alerts }, [total_alerts, push]) // Auto-scroll to bottom useEffect(() => { bottomRef.current?.scrollIntoView({ behavior: 'smooth' }) }, [entries]) const filtered = filter ? entries.filter(e => e.msg.toLowerCase().includes(filter.toLowerCase())) : entries return ( {/* ── Header ── */}
Activity Log
{entries.length} events
setFilter(e.target.value)} placeholder="Filter events…" className="g-input w-36 h-7 text-[11px] py-0 px-2.5" />
{/* ── Body ── */}
{filtered.length === 0 ? (

No events

) : ( {filtered.map(e => ( {e.time} {e.msg} ))} )}
) }