TypeScript 61.9%
HTML 37.2%
SQL 0.7%
1import Link from 'next/link';2import { ExternalLink } from 'lucide-react';3import { cn, fmtMoney, fmtPct } from '@/lib/format';4import { feeBasisLabel } from '@rareindex/valuation';5import { catName, gradeLabel } from '@/lib/taxonomy';6import { Badge } from '@/components/ui/primitives';78/** Small shared building blocks for market tables and cards. */910import { Thumb } from '@/components/ui/tile-image';11export { Thumb };1213export function CategoryTag({ slug, className }: { slug: string | null | undefined; className?: string }) {14 if (!slug) return null;15 return (16 <Link href={`/markets/${slug}`} className={cn('text-[11px] text-muted hover:text-fg', className)}>17 {catName(slug)}18 </Link>19 );20}2122export function GradeBadge({ grader, grade, className }: { grader: string | null | undefined; grade: string | null | undefined; className?: string }) {23 const label = gradeLabel(grader, grade);24 if (!label) return <span className={cn('text-subtle', className)}>—</span>;25 return (26 <Badge tone={grader && grader !== 'raw' ? 'index' : 'neutral'} className={className}>27 {label}28 </Badge>29 );30}3132/** §178: source attribution with outbound link. */33export function SourceLink({ name, url, className }: { name: string; url: string | null | undefined; className?: string }) {34 if (!url) return <span className={cn('text-muted', className)}>{name}</span>;35 return (36 <a href={url} target="_blank" rel="noopener nofollow" className={cn('inline-flex items-center gap-1 text-muted hover:text-fg', className)} title={`View on ${name}`}>37 {name}38 <ExternalLink className="h-3 w-3" aria-hidden />39 </a>40 );41}4243export function PriceCell({ usd, native, currency, className }: { usd: number | null | undefined; native?: number | null; currency?: string | null; className?: string }) {44 const showNative = native !== null && native !== undefined && currency && currency !== 'USD';45 return (46 <span className={cn('num inline-flex flex-col items-end leading-tight', className)}>47 <span className="font-medium text-fg">{fmtMoney(usd)}</span>48 {showNative ? <span className="text-[10px] text-subtle">{fmtMoney(native, currency!)}</span> : null}49 </span>50 );51}5253export function AssetTitleCell({ slug, title, categorySlug, image, sub, size = 36 }: { slug: string; title: string; categorySlug?: string | null; image?: string | null; sub?: string | null; size?: number }) {54 return (55 <span className="flex min-w-0 items-center gap-2.5">56 <Thumb src={image} alt="" size={size} />57 <span className="min-w-0">58 <Link href={`/asset/${slug}`} className="block truncate font-medium text-fg hover:underline" title={title}>59 {title}60 </Link>61 <span className="block truncate text-[11px] text-muted">62 {categorySlug ? catName(categorySlug) : null}63 {sub ? (categorySlug ? ` · ${sub}` : sub) : null}64 </span>65 </span>66 </span>67 );68}6970/** Heuristic sale verification label (§39, §205): verified · likely · unverified · excluded. Never a claim of independent confirmation. */71export function VerificationBadge({ label, reason, className }: { label: 'verified' | 'likely' | 'unverified' | 'excluded'; reason?: string; className?: string }) {72 const tone = label === 'verified' ? 'gain' : label === 'likely' ? 'neutral' : 'alert';73 const text = label === 'verified' ? 'Verified' : label === 'likely' ? 'Likely' : label === 'unverified' ? 'Unverified' : 'Excluded';74 return (75 <Badge tone={tone} className={className}>76 <span title={`${text} sale — heuristic on source type, sale type, match confidence and source trust${reason ? `: ${reason}` : ''}`}>{text}</span>77 </Badge>78 );79}8081/** Fee basis of an all-in figure (§35): premium included / added (published, ≈ estimated, default) / none / unknown. */82export function FeeBasisPill({ basis, rate, className }: { basis: string | null | undefined; rate?: number | null; className?: string }) {83 if (!basis) return null;84 const approx = basis === 'added_approximate' || basis === 'added_default';85 const text =86 basis === 'included' ? 'premium included' : basis === 'none' ? 'no buyer premium' : basis === 'unknown' ? 'fees unknown' : `+ premium ${approx ? '≈ ' : ''}${rate !== null && rate !== undefined ? fmtPct(rate, rate < 0.1 ? 1 : 0, false) : ''}`.trim();87 const tone = basis === 'unknown' ? 'alert' : basis === 'included' || basis === 'none' ? 'neutral' : 'index';88 return (89 <Badge tone={tone} className={className}>90 <span title={`${feeBasisLabel(basis)}. VAT/sales tax on the premium, duties and shipping are not included.`}>{text}</span>91 </Badge>92 );93}9495/** Buyer-pays amount in USD with the fee basis under it; falls back to the recorded price when no all-in figure exists. */96export function AllInCell({ allInUsd, priceUsd, basis, rate, className }: { allInUsd: number | null | undefined; priceUsd: number | null | undefined; basis?: string | null; rate?: number | null; className?: string }) {97 const v = allInUsd ?? priceUsd;98 const differs = allInUsd !== null && allInUsd !== undefined && priceUsd !== null && priceUsd !== undefined && Math.abs(allInUsd - priceUsd) >= 0.5;99 return (100 <span className={cn('num inline-flex flex-col items-end leading-tight', className)}>101 <span className="font-medium text-fg">{basis === 'added_approximate' || basis === 'added_default' ? '≈ ' : ''}{fmtMoney(v)}</span>102 {differs || (basis && basis !== 'none') ? <FeeBasisPill basis={basis} rate={rate} className="mt-0.5 text-[9px]" /> : null}103 </span>104 );105}106