← 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';
}