← back to Stayclaim

src/components/TierBadge.tsx

54 lines

/**
 * TierBadge — source-tier chip required on every A/B/C fact on public pages.
 *
 *   A = government record (assessor, recorder, city planning)
 *   B = archive (Sanborn, HABS, library, newspaper)
 *   C = verified secondary source
 *   D = social / lead — NEVER renders on public pages (compile-time guard)
 *
 * Constraint source: docs/deep-research-report.md:60-65
 */

export type SourceTier = 'A' | 'B' | 'C' | 'D';

type PublicTier = Exclude<SourceTier, 'D'>;

const TIER_LABEL: Record<PublicTier, string> = {
  A: 'Government record',
  B: 'Archive',
  C: 'Verified secondary',
};

const TIER_SHORT: Record<PublicTier, string> = { A: 'A', B: 'B', C: 'C' };

/**
 * Accepts only A/B/C at the type level — rendering a D tier on a public page
 * is a TypeScript error, not a runtime check. If D leaks through runtime data,
 * `isPublicTier` filters it.
 */
export function TierBadge({
  tier,
  sourceLabel,
  className = '',
}: {
  tier: PublicTier;
  sourceLabel?: string;
  className?: string;
}) {
  return (
    <span
      className={`inline-flex items-center gap-1 font-mono text-[10px] tracking-wider uppercase text-ink/70 ${className}`}
      title={sourceLabel ? `${TIER_LABEL[tier]} — ${sourceLabel}` : TIER_LABEL[tier]}
    >
      <span className="inline-flex h-4 w-4 items-center justify-center rounded-full border border-moss/50 text-moss">
        {TIER_SHORT[tier]}
      </span>
      {sourceLabel && <span className="text-ink/50 normal-case tracking-normal">{sourceLabel}</span>}
    </span>
  );
}

export function isPublicTier(t: SourceTier | null | undefined): t is PublicTier {
  return t === 'A' || t === 'B' || t === 'C';
}