← back to Stayclaim

src/components/Timeline.tsx

53 lines

import { TierBadge, type SourceTier, isPublicTier } from './TierBadge';
import { safeHttpUrl } from '@/lib/url-guards';

/**
 * Timeline — vertical ownership/transaction/permit history.
 * Every event carries TierBadge. D-tier events are filtered out at render.
 */
export type TimelineEvent = {
  date: string;        // ISO or "1954-06-12" / "1978" / "1940s"
  type: string;        // permit / sale / remodel / occupancy / demolition / historic-designation
  summary: string;
  tier: SourceTier;
  sourceLabel?: string;
  sourceUrl?: string;
};

export function Timeline({ events }: { events: TimelineEvent[] }) {
  const visible = events.filter(e => isPublicTier(e.tier));
  if (visible.length === 0) {
    return (
      <p className="text-sm text-ink/50 italic py-6">
        No publicly-verifiable events recorded for this address yet.
      </p>
    );
  }
  return (
    <ol className="relative border-l border-ink/15 ml-2">
      {visible.map((e, i) => (
        <li key={i} className="pl-6 py-4 relative">
          <span className="absolute -left-[5px] top-6 h-2 w-2 rounded-full bg-moss" aria-hidden />
          <div className="flex items-baseline justify-between gap-3 flex-wrap">
            <time className="font-mono text-xs uppercase tracking-wider text-ink/60">{e.date}</time>
            <span className="text-[10px] uppercase tracking-[0.15em] text-ink/40">{e.type}</span>
          </div>
          <p className="mt-1 text-base text-ink leading-relaxed">{e.summary}</p>
          <div className="mt-2">
            {(() => {
              const safeUrl = safeHttpUrl(e.sourceUrl);
              return safeUrl ? (
                <a href={safeUrl} target="_blank" rel="noopener noreferrer" className="underline-offset-2 hover:underline">
                  <TierBadge tier={e.tier as 'A' | 'B' | 'C'} sourceLabel={e.sourceLabel} />
                </a>
              ) : (
                <TierBadge tier={e.tier as 'A' | 'B' | 'C'} sourceLabel={e.sourceLabel} />
              );
            })()}
          </div>
        </li>
      ))}
    </ol>
  );
}