TypeScript 61.9%
HTML 37.2%
SQL 0.7%
1import { FEE_SCHEDULE_AS_OF, type FeeSchedule } from '@rareindex/valuation';2import { fmtMoney, fmtPct, cn } from '@/lib/format';3import { Badge, Table, th, td, tdNum } from '@/components/ui/primitives';45export function FeeConfidenceBadge({ confidence, className }: { confidence: FeeSchedule['confidence'] | 'unknown'; className?: string }) {6 const map: Record<string, { tone: 'gain' | 'index' | 'neutral' | 'alert'; label: string; title: string }> = {7 published: { tone: 'gain', label: 'Published', title: `Taken from the house's published terms as of ${FEE_SCHEDULE_AS_OF}` },8 approximate: { tone: 'index', label: '≈ Approximate', title: 'Widely reported schedule not re-verified against the current terms — every all-in figure built on it is labelled as an estimate' },9 none: { tone: 'neutral', label: 'No buyer premium', title: 'The house bills the hammer price; the seller pays the commission' },10 default: { tone: 'alert', label: 'Default 22 %', title: 'House not on file — a typical 22 % premium is applied and labelled' },11 unknown: { tone: 'alert', label: 'Unknown', title: 'No schedule on file' },12 };13 const m = map[confidence] ?? map.unknown!;14 return (15 <Badge tone={m.tone} className={className}>16 <span title={m.title}>{m.label}</span>17 </Badge>18 );19}2021/** Buyer's premium schedule of one house (§35): marginal tiers, minimum/maximum, fixed fee, confidence and source. */22export function FeeScheduleTable({ schedule, className }: { schedule: FeeSchedule; className?: string }) {23 const cur = schedule.currency;24 const tiers = schedule.tiers.map((t, i) => ({ from: i === 0 ? 0 : (schedule.tiers[i - 1]!.upTo ?? 0), to: t.upTo, rate: t.rate }));25 return (26 <div className={cn('space-y-2', className)}>27 <Table>28 <thead>29 <tr>30 <th className={th}>Hammer price ({cur})</th>31 <th className={cn(th, 'text-right')}>Buyer's premium</th>32 </tr>33 </thead>34 <tbody>35 {tiers.map((t, i) => (36 <tr key={i}>37 <td className={td}>{t.to === null ? `above ${fmtMoney(t.from, cur)}` : t.from === 0 ? `up to ${fmtMoney(t.to, cur)}` : `${fmtMoney(t.from, cur)} – ${fmtMoney(t.to, cur)}`}</td>38 <td className={tdNum}>{fmtPct(t.rate, t.rate * 100 % 1 ? 1 : 0, false)}</td>39 </tr>40 ))}41 {schedule.minimum !== undefined ? (42 <tr>43 <td className={td}>Minimum premium</td>44 <td className={tdNum}>{fmtMoney(schedule.minimum, cur)}</td>45 </tr>46 ) : null}47 {schedule.maximum !== undefined ? (48 <tr>49 <td className={td}>Maximum premium</td>50 <td className={tdNum}>{fmtMoney(schedule.maximum, cur)}</td>51 </tr>52 ) : null}53 {schedule.fixedFee !== undefined ? (54 <tr>55 <td className={td}>Fixed fee per lot</td>56 <td className={tdNum}>{fmtMoney(schedule.fixedFee, cur)}</td>57 </tr>58 ) : null}59 </tbody>60 </Table>61 <p className="text-[11px] text-muted">62 <FeeConfidenceBadge confidence={schedule.confidence} className="mr-1.5" />63 Schedule as of {FEE_SCHEDULE_AS_OF}.{schedule.note ? ` ${schedule.note}` : ''} VAT or sales tax on the premium, import duties and shipping are not included.64 {schedule.source ? (65 <>66 {' '}67 <a href={schedule.source} target="_blank" rel="noopener nofollow" className="underline-offset-2 hover:underline">68 Source ↗69 </a>70 </>71 ) : null}72 </p>73 </div>74 );75}7677/** One-line summary of a schedule for lists: "27 % → 15 % (3 tiers)" or "20 %". */78export function feeScheduleSummary(s: FeeSchedule): string {79 const rates = s.tiers.map((t) => fmtPct(t.rate, t.rate * 100 % 1 ? 1 : 0, false));80 const base = rates.length === 1 ? rates[0]! : `${rates[0]} → ${rates.at(-1)} (${rates.length} tiers)`;81 const extras = [s.minimum !== undefined ? `min ${fmtMoney(s.minimum, s.currency)}` : null, s.maximum !== undefined ? `cap ${fmtMoney(s.maximum, s.currency)}` : null, s.fixedFee !== undefined ? `+ ${fmtMoney(s.fixedFee, s.currency)}/lot` : null].filter(Boolean);82 return extras.length ? `${base} · ${extras.join(' · ')}` : base;83}84