spb/company-atlas
Public
Python 66.3%
TypeScript 22.7%
JavaScript 8.6%
HTML 1.4%
CSS 0.7%
1'use client';2import { useEffect, useState } from 'react';3import { cn } from '@/lib/cn';4import { DASH, fmtAgo, fmtDateTime } from '@/lib/format';56/** Green live dot with optional pulse ring. */7export function Dot({ pulse = false, className, tone = 'live' }: { pulse?: boolean; className?: string; tone?: 'live' | 'warning' | 'danger' | 'muted' }) {8 return <span className={cn('dot', pulse && 'pulse', tone === 'warning' && 'bg-warning', tone === 'danger' && 'bg-danger', tone === 'muted' && 'bg-ink-3', className)} aria-hidden />;9}1011/**12 * "17 sec ago" that ticks every second for the first minutes, then every 30 s. Server renders the absolute UTC time13 * (identical on both sides); the relative label is applied after mount (`suppressHydrationWarning`).14 */15export function LiveAgo({ at, prefix = '', className, absoluteFallback = true, tick = 1000 }: { at: string | null | undefined; prefix?: string; className?: string; absoluteFallback?: boolean; tick?: number }) {16 const [now, setNow] = useState<number | null>(null);17 useEffect(() => {18 setNow(Date.now());19 const t = setInterval(() => setNow(Date.now()), tick);20 return () => clearInterval(t);21 }, [tick]);22 const label = now === null ? (absoluteFallback ? fmtDateTime(at) : DASH) : fmtAgo(at, now);23 return (24 <time dateTime={at ?? undefined} title={at ? fmtDateTime(at) : undefined} className={cn('tnum', className)} suppressHydrationWarning>25 {prefix}26 {label}27 </time>28 );29}3031/** "Live · updated 12 s ago" status line for panels fed by SSE/polling. */32export function LiveStatus({ updatedAt, connected = true, className }: { updatedAt: number | string | null; connected?: boolean; className?: string }) {33 const [now, setNow] = useState<number | null>(null);34 useEffect(() => {35 setNow(Date.now());36 const t = setInterval(() => setNow(Date.now()), 1000);37 return () => clearInterval(t);38 }, []);39 const ts = typeof updatedAt === 'number' ? updatedAt : updatedAt ? new Date(updatedAt).getTime() : null;40 const s = ts && now ? Math.max(0, Math.round((now - ts) / 1000)) : null;41 return (42 <span className={cn('inline-flex items-center gap-1.5 text-xs text-ink-3', className)} suppressHydrationWarning>43 <Dot pulse={connected} tone={connected ? 'live' : 'muted'} />44 <span className={cn('font-medium', connected ? 'text-positive' : 'text-ink-3')}>{connected ? 'Live' : 'Paused'}</span>45 {s !== null && <span className="tnum">· updated {s < 1 ? 'now' : `${s} s ago`}</span>}46 </span>47 );48}49