SPB Git forge
28commits 1branches 0releases
7.7 MBsize
maindefault branch
10 days agolast push
Python 66.3% TypeScript 22.7% JavaScript 8.6% HTML 1.4% CSS 0.7%
3.3 KB · 75 lines tsx
Raw Blame History
1'use client';2import { ArrowUpRight } from 'lucide-react';3import Link from 'next/link';4import { useEffect, useState } from 'react';5import { CountryChip, EventTypeBadge, ImportanceMeter } from '@/components/ui/badges';6import { LiveAgo } from '@/components/ui/live';7import { Sheet } from '@/components/ui/sheet';8import { clientApi } from '@/lib/client-api';9import { routes } from '@/lib/site';10import type { EventDetail, EventSource } from '@/lib/types';11import { useEventDrawer } from './event-drawer-context';12import { EventEvidence } from './event-evidence';1314/** Right-side (≥ lg) / bottom (< lg) drawer: the event, its evidence and links to the permanent pages. */15export function EventDrawer() {16  const { event, close } = useEventDrawer();17  const [detail, setDetail] = useState<{ id: string; sources: EventSource[]; loaded: boolean } | null>(null);1819  useEffect(() => {20    if (!event) return;21    const ctrl = new AbortController();22    setDetail({ id: event.id, sources: event.sources ?? [], loaded: false });23    clientApi24      .event(event.id, ctrl.signal)25      .then((d: EventDetail) => setDetail({ id: event.id, sources: d.sources ?? [], loaded: true }))26      .catch(() => setDetail((s) => (s ? { ...s, loaded: true } : s)));27    return () => ctrl.abort();28  }, [event]);2930  return (31    <Sheet32      open={event !== null}33      onClose={close}34      eyebrow={event ? <EventTypeBadge type={event.event_type} subtype={event.event_subtype} /> : undefined}35      title={event?.title}36      footer={37        event ? (38          <div className="flex flex-wrap items-center gap-2 text-sm">39            <Link href={routes.event(event.id)} onClick={close} className="btn btn-primary btn-sm">40              Open event page <ArrowUpRight className="size-3.5" aria-hidden />41            </Link>42            <Link href={routes.company(event.company.slug)} onClick={close} className="btn btn-sm">43              {event.company.display_name}44            </Link>45            {event.source_url && (46              <a href={event.source_url} target="_blank" rel="noopener noreferrer" className="btn btn-sm">47                Source ↗48              </a>49            )}50          </div>51        ) : undefined52      }53    >54      {event && (55        <div className="space-y-4" data-event-drawer>56          <div className="flex flex-wrap items-center gap-x-3 gap-y-1 text-sm">57            <Link href={routes.company(event.company.slug)} onClick={close} className="font-medium text-ink hover:text-accent">58              {event.company.display_name}59            </Link>60            <span className="mono text-xs text-ink-3">{event.company.canonical_domain}</span>61            <CountryChip code={event.company.country} />62            <span className="ml-auto inline-flex items-center gap-2 text-xs text-ink-3">63              <ImportanceMeter importance={event.importance} />64              <LiveAgo at={event.detected_at} />65            </span>66          </div>67          {event.summary && <p className="text-[14px] leading-relaxed text-ink-2">{event.summary}</p>}68          <EventEvidence event={event} sources={detail?.id === event.id ? detail.sources : event.sources} compact />69          {detail && detail.id === event.id && !detail.loaded && <p className="text-[11px] text-ink-3">Loading full source list…</p>}70        </div>71      )}72    </Sheet>73  );74}75