74 lines
2.6 KiB
TypeScript
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>
|
|
);
|
|
}
|