← back to Stayclaim

src/components/PermitTable.tsx

137 lines

import { SourceLinkInline } from './SourceLink';
import type { RealPermit, CodeEnforcementCase, NewsAddressMention } from '@/lib/db';

/** Permits attached to a listing — LADBS / BH / Pasadena / SM. Real records, real links. */
export function PermitTable({ permits }: { permits: RealPermit[] }) {
  if (!permits?.length) return null;
  return (
    <section className="max-w-6xl mx-auto px-6 py-12 border-t border-ink/10">
      <div className="flex items-baseline justify-between gap-4 mb-5 flex-wrap">
        <div>
          <p className="text-xs uppercase tracking-[0.15em] text-ink/50">Building permits</p>
          <h2 className="font-display text-3xl text-ink tracking-[-0.01em]">
            {permits.length} {permits.length === 1 ? 'permit' : 'permits'} on file
          </h2>
        </div>
        <p className="text-[10px] uppercase tracking-[0.15em] text-ink/40 font-mono">
          tier A · government primary record
        </p>
      </div>
      <div className="bg-white border border-ink/10 overflow-x-auto">
        <table className="w-full text-xs">
          <thead className="bg-ink/[0.04] text-[10px] uppercase tracking-[0.15em] text-ink/55">
            <tr>
              <th className="text-left p-2.5">issued</th>
              <th className="text-left p-2.5">type</th>
              <th className="text-left p-2.5">use</th>
              <th className="text-left p-2.5">work described</th>
              <th className="text-right p-2.5">value</th>
              <th className="text-left p-2.5">status</th>
              <th className="text-right p-2.5">source</th>
            </tr>
          </thead>
          <tbody>
            {permits.map(p => (
              <tr key={p.id} className="border-t border-ink/5 align-top">
                <td className="p-2.5 font-mono text-ink/80">{p.issue_date ?? '—'}</td>
                <td className="p-2.5">
                  <span className="font-medium text-ink">{p.permit_type ?? '—'}</span>
                  {p.permit_sub_type && <div className="text-ink/55 text-[11px]">{p.permit_sub_type}</div>}
                </td>
                <td className="p-2.5 text-ink/65">{p.use_desc ?? '—'}</td>
                <td className="p-2.5 text-ink/65 max-w-md">{p.work_desc ?? '—'}</td>
                <td className="p-2.5 text-right font-mono tabular-nums text-ink/80">
                  {(() => {
                    const v = p.valuation ? Number(String(p.valuation).replace(/[^0-9.-]/g, '')) : NaN;
                    return Number.isFinite(v) && v > 0 ? `$${v.toLocaleString()}` : '—';
                  })()}
                </td>
                <td className="p-2.5 text-ink/65">{p.status_desc ?? '—'}</td>
                <td className="p-2.5 text-right">
                  <SourceLinkInline label={`#${p.permit_number}`} url={p.source_url} />
                  <div className="text-[10px] text-ink/40 mt-0.5">{p.source_label ?? p.source}</div>
                </td>
              </tr>
            ))}
          </tbody>
        </table>
      </div>
    </section>
  );
}

export function CodeEnforcementTable({ cases }: { cases: CodeEnforcementCase[] }) {
  if (!cases?.length) return null;
  return (
    <section className="max-w-6xl mx-auto px-6 py-12 border-t border-ink/10">
      <div className="mb-5">
        <p className="text-xs uppercase tracking-[0.15em] text-ink/50">Code enforcement</p>
        <h2 className="font-display text-3xl text-ink tracking-[-0.01em]">
          {cases.length} {cases.length === 1 ? 'case' : 'cases'} on file
        </h2>
      </div>
      <div className="bg-white border border-ink/10 overflow-x-auto">
        <table className="w-full text-xs">
          <thead className="bg-ink/[0.04] text-[10px] uppercase tracking-[0.15em] text-ink/55">
            <tr>
              <th className="text-left p-2.5">opened</th>
              <th className="text-left p-2.5">type</th>
              <th className="text-left p-2.5">status</th>
              <th className="text-right p-2.5">source</th>
            </tr>
          </thead>
          <tbody>
            {cases.map(c => (
              <tr key={c.id} className="border-t border-ink/5 align-top">
                <td className="p-2.5 font-mono text-ink/80">{c.opened_at ?? '—'}</td>
                <td className="p-2.5 text-ink/80">{c.case_type ?? '—'}</td>
                <td className="p-2.5 text-ink/65">{c.status ?? '—'}</td>
                <td className="p-2.5 text-right">
                  <SourceLinkInline label={`#${c.case_number}`} url={c.source_url} />
                </td>
              </tr>
            ))}
          </tbody>
        </table>
      </div>
    </section>
  );
}

export function NewsMentionsList({ mentions }: { mentions: NewsAddressMention[] }) {
  if (!mentions?.length) return null;
  return (
    <section className="max-w-6xl mx-auto px-6 py-12 border-t border-ink/10">
      <div className="mb-5">
        <p className="text-xs uppercase tracking-[0.15em] text-ink/50">In the newspaper</p>
        <h2 className="font-display text-3xl text-ink tracking-[-0.01em]">
          {mentions.length} historical {mentions.length === 1 ? 'mention' : 'mentions'}
        </h2>
        <p className="text-xs text-ink/55 mt-1">
          Click any source to view the actual scanned newspaper page on Internet Archive.
        </p>
      </div>
      <ul className="divide-y divide-ink/10">
        {mentions.map(m => (
          <li key={m.id} className="py-4 flex flex-col gap-1">
            <div className="flex items-baseline justify-between gap-3 flex-wrap">
              <span className="font-display text-lg text-ink">{m.raw_address}</span>
              <span className="font-mono text-[11px] text-ink/55">
                {m.paper_name} · {m.issue_date ?? 'undated'}
              </span>
            </div>
            {m.context && (
              <p className="text-sm text-ink/70 leading-relaxed italic">
                &ldquo;{m.context.slice(0, 280)}{m.context.length > 280 ? '…' : ''}&rdquo;
              </p>
            )}
            <div>
              <SourceLinkInline label={`view scanned page on Internet Archive`} url={m.ia_url} />
            </div>
          </li>
        ))}
      </ul>
    </section>
  );
}