Sessions 5-7a: 955 tests, deployment ready

This commit is contained in:
Kev
2026-06-08 18:35:13 -04:00
parent 06b82624a2
commit 1fa04dc776
371 changed files with 49366 additions and 955 deletions
+148
View File
@@ -0,0 +1,148 @@
'use client';
import { useEffect, useState } from 'react';
interface LiveProp {
player: string;
stat: string;
line: number;
direction: string;
grade: string;
sport: string;
}
const SPORT_COLOR: Record<string, string> = {
NBA: '#E94B3C',
MLB: '#1E90FF',
WNBA: '#FFB347',
};
function gradeColor(grade: string): string {
const g = (grade || '').trim().toUpperCase().charAt(0);
if (g === 'A') return 'var(--grade-a)';
if (g === 'B') return 'var(--grade-b)';
if (g === 'C') return 'var(--grade-c)';
return 'var(--grade-d)';
}
export default function LivePropsStrip() {
const [props, setProps] = useState<LiveProp[] | null>(null);
const [error, setError] = useState(false);
useEffect(() => {
let cancelled = false;
async function load() {
try {
const res = await fetch('/api/props/live');
if (!res.ok) {
if (!cancelled) setError(true);
return;
}
const data = await res.json();
if (cancelled) return;
if (Array.isArray(data) && data.length > 0) {
setProps(data.slice(0, 12));
setError(false);
} else {
setProps([]);
}
} catch {
if (!cancelled) setError(true);
}
}
load();
const id = setInterval(load, 60_000);
return () => {
cancelled = true;
clearInterval(id);
};
}, []);
// Loading / fallback state
if (props === null) return null;
if (error || props.length === 0) {
return (
<section
style={{
padding: '24px',
borderTop: '1px solid var(--border)',
borderBottom: '1px solid var(--border)',
background: 'var(--bg-surface)',
}}
>
<p
className="mono"
style={{
textAlign: 'center',
fontSize: 12,
color: 'var(--text-tertiary)',
letterSpacing: '0.08em',
}}
>
TONIGHT&apos;S GRADES LOAD AT 5 PM ET
</p>
</section>
);
}
// Duplicate for seamless ticker scroll
const ticker = [...props, ...props];
return (
<section
style={{
padding: '20px 0',
borderTop: '1px solid var(--border)',
borderBottom: '1px solid var(--border)',
background: 'var(--bg-surface)',
overflow: 'hidden',
}}
>
<div className="animate-ticker" style={{ display: 'flex', gap: 16, whiteSpace: 'nowrap', width: 'max-content' }}>
{ticker.map((p, i) => (
<div
key={`${p.player}-${i}`}
style={{
display: 'inline-flex',
gap: 12,
alignItems: 'center',
padding: '8px 16px',
background: 'var(--bg-elevated)',
border: '1px solid var(--border)',
borderRadius: 12,
}}
>
<span
className="mono"
style={{
fontSize: 10,
fontWeight: 700,
padding: '2px 6px',
borderRadius: 999,
color: SPORT_COLOR[p.sport] || 'var(--text-secondary)',
background: `${SPORT_COLOR[p.sport] || 'var(--text-secondary)'}1F`,
}}
>
{p.sport}
</span>
<span style={{ fontSize: 13, fontWeight: 600 }}>{p.player}</span>
<span className="mono" style={{ fontSize: 12, color: 'var(--text-secondary)' }}>
{p.direction} {p.line} {p.stat}
</span>
<span
className="mono"
style={{
fontSize: 16,
fontWeight: 800,
color: gradeColor(p.grade),
}}
>
{p.grade}
</span>
</div>
))}
</div>
</section>
);
}