Files
vyndr/web/src/components/BookComparison.tsx
T

74 lines
2.6 KiB
TypeScript

'use client';
/**
* BookComparison (Session 28).
*
* Presentational book-by-book grid for a single prop with the best line
* highlighted. Fed by /api/books/:sport/:player/:stat (or the prop grid
* from a parent). Pure render — no fetching here, so it drops cleanly into
* a modal or an expanded prop row.
*/
export interface BookRow {
book: string;
line?: number | null;
over_odds?: number | null;
under_odds?: number | null;
isBest?: boolean;
}
export interface BookComparisonProps {
player: string;
stat: string;
line?: number | null;
side?: 'over' | 'under';
books: BookRow[];
savings?: number;
}
function fmt(o?: number | null) {
if (o == null) return '—';
return o > 0 ? `+${o}` : `${o}`;
}
export default function BookComparison({ player, stat, line, side = 'over', books, savings }: BookComparisonProps) {
if (!books || books.length === 0) return null;
return (
<div className="book-comparison" style={{ display: 'grid', gap: 8 }}>
<div className="mono" style={{ fontSize: 11, color: 'var(--text-tertiary, #6B6B7B)', letterSpacing: '0.06em', textTransform: 'uppercase' }}>
{player} · {stat.replace(/_/g, ' ')}{line != null ? ` ${line}` : ''} · {side}
</div>
<div style={{ display: 'grid', gap: 4 }}>
{books.map((b) => (
<div
key={b.book}
style={{
display: 'grid',
gridTemplateColumns: '1fr auto auto',
gap: 10,
alignItems: 'center',
padding: '6px 10px',
borderRadius: 8,
background: b.isBest ? 'rgba(0,212,160,0.10)' : 'var(--bg-2, #12121A)',
border: `1px solid ${b.isBest ? 'var(--grade-a, #00D4A0)' : 'var(--border, #1A1A24)'}`,
}}
>
<span style={{ fontSize: 13, fontWeight: 700, textTransform: 'capitalize', color: 'var(--text-0, #F0F0F5)' }}>{b.book}</span>
<span className="mono" style={{ fontSize: 12, color: 'var(--text-secondary, #8A8A9A)' }}>
{fmt(b.over_odds)} / {fmt(b.under_odds)}
</span>
{b.isBest ? (
<span className="mono" style={{ fontSize: 9, fontWeight: 800, letterSpacing: '0.08em', color: '#06060B', background: 'var(--grade-a, #00D4A0)', padding: '2px 6px', borderRadius: 4 }}>BEST</span>
) : <span />}
</div>
))}
</div>
{savings != null && savings > 0 && (
<div style={{ fontSize: 12, color: 'var(--grade-a, #00D4A0)' }}>
Betting the best line saves ~${savings.toFixed(2)} per $100.
</div>
)}
</div>
);
}