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
+160
View File
@@ -0,0 +1,160 @@
'use client';
import { useEffect, useState } from 'react';
import { useRouter } from 'next/navigation';
import { useAuth } from '@/contexts/AuthContext';
type Signal = {
id: string;
ts: string;
sport: 'NBA' | 'MLB' | 'WNBA';
severity: 'critical' | 'notable' | 'info';
type: 'evolution' | 'coaching' | 'cascade' | 'abs' | 'line_movement';
title: string;
detail: string;
players?: string[];
};
const SEVERITY: Record<Signal['severity'], { dot: string; label: string }> = {
critical: { dot: '#FF4757', label: 'CRITICAL' },
notable: { dot: '#FFB347', label: 'NOTABLE' },
info: { dot: '#4A9EFF', label: 'INFO' },
};
const SPORT_COLOR: Record<Signal['sport'], string> = {
NBA: '#E94B3C',
MLB: '#1E90FF',
WNBA: '#FFB347',
};
export default function IntelligencePage() {
const router = useRouter();
const { user, tier, loading: authLoading } = useAuth();
const [signals, setSignals] = useState<Signal[] | null>(null);
useEffect(() => {
if (!authLoading && !user) router.replace('/login?next=/intelligence');
}, [authLoading, user, router]);
useEffect(() => {
fetch('/api/intelligence/feed')
.then((r) => r.json())
.then((data) => setSignals(Array.isArray(data?.signals) ? data.signals : []))
.catch(() => setSignals([]));
}, []);
const locked = tier !== 'desk';
if (authLoading || !user) {
return (
<section style={{ minHeight: '60vh', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
<p className="mono" style={{ color: 'var(--text-tertiary)' }}>Loading intelligence feed</p>
</section>
);
}
return (
<section style={{ maxWidth: 760, margin: '0 auto', padding: '24px 16px 120px' }}>
<header style={{ marginBottom: 24 }}>
<h1 style={{ fontSize: 28, fontWeight: 700, letterSpacing: '-0.02em', marginBottom: 6 }}>
Intelligence feed
</h1>
<p style={{ color: 'var(--text-secondary)', fontSize: 14 }}>
Real-time signals the books wish you didn&apos;t see. Evolution. Coaching shifts. Cascade effects. Line movement.
</p>
</header>
{signals === null ? (
<p className="mono" style={{ color: 'var(--text-tertiary)' }}>Loading signals</p>
) : signals.length === 0 ? (
<div className="surface" style={{ padding: 32, textAlign: 'center', color: 'var(--text-secondary)' }}>
<p className="mono" style={{ fontSize: 11, color: 'var(--text-tertiary)', letterSpacing: '0.08em', marginBottom: 8 }}>
QUIET NIGHT
</p>
<p>No signals yet tonight. The engine is watching. When something moves, you&apos;ll see it here first.</p>
</div>
) : (
<div style={{ position: 'relative' }}>
<ol style={{ position: 'relative', display: 'grid', gap: 12, listStyle: 'none', padding: 0 }} className={locked ? 'tier-locked' : ''}>
{signals.map((s, idx) => (
<li
key={s.id}
className={`surface diagonal-cut animate-fade-up stagger-${(idx % 6) + 1}`}
style={{ padding: 18, display: 'grid', gridTemplateColumns: 'auto 1fr', gap: 16 }}
>
<div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', paddingTop: 4 }}>
<span
style={{
width: 10,
height: 10,
borderRadius: 999,
background: SEVERITY[s.severity].dot,
boxShadow: `0 0 0 4px ${SEVERITY[s.severity].dot}33`,
}}
aria-label={SEVERITY[s.severity].label}
/>
</div>
<div>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline', gap: 8, marginBottom: 6 }}>
<div style={{ display: 'flex', gap: 8, alignItems: 'center' }}>
<span
className="mono"
style={{
fontSize: 10,
padding: '2px 8px',
borderRadius: 999,
background: `${SPORT_COLOR[s.sport]}1F`,
color: SPORT_COLOR[s.sport],
fontWeight: 700,
}}
>
{s.sport}
</span>
<span className="mono" style={{ fontSize: 10, color: SEVERITY[s.severity].dot, letterSpacing: '0.08em' }}>
{s.type.toUpperCase().replace('_', ' ')}
</span>
</div>
<span className="mono" style={{ fontSize: 11, color: 'var(--text-tertiary)' }}>
{formatRelative(s.ts)}
</span>
</div>
<h3 style={{ fontSize: 15, fontWeight: 600, marginBottom: 4 }}>{s.title}</h3>
<p style={{ fontSize: 13, color: 'var(--text-secondary)', lineHeight: 1.5 }}>{s.detail}</p>
</div>
</li>
))}
</ol>
{locked && (
<div
className="tier-locked-overlay"
style={{ minHeight: 240 }}
>
<p style={{ fontSize: 15, fontWeight: 600 }}>
Real-time intelligence is a Desk feature.
</p>
<p style={{ fontSize: 13, color: 'var(--text-secondary)', maxWidth: 360 }}>
Evolution alerts. Coaching shifts. Cascade effects. ABS strike zone intel. Line movement signals across the slate.
</p>
<a href="/api/checkout?tier=desk" className="btn-primary">
Go Desk $44.99/mo
</a>
</div>
)}
</div>
)}
</section>
);
}
function formatRelative(ts: string): string {
const diff = Date.now() - new Date(ts).getTime();
if (!Number.isFinite(diff)) return '—';
const m = Math.floor(diff / 60000);
if (m < 1) return 'just now';
if (m < 60) return `${m}m ago`;
const h = Math.floor(m / 60);
if (h < 24) return `${h}h ago`;
const d = Math.floor(h / 24);
return `${d}d ago`;
}