Session 14: Africa checkout, Tank01 NBA/MLB wiring, WNBA+MLB odds proxies, OAuth icons, loading skeletons (1330 tests)

This commit is contained in:
Kev
2026-06-11 10:06:49 -04:00
parent 10159209fa
commit f5d79cf70d
22 changed files with 979 additions and 27 deletions
+44 -5
View File
@@ -26,6 +26,20 @@ import { useAuth } from '@/contexts/AuthContext';
* key, one error-by-key map. The Slate component is the only writer.
*/
// Session 14 — shimmer skeleton style. Width is a percentage string
// so cards remain responsive at small viewports. The keyframe rule
// lives in globals.css.
function skeletonStyle({ widthPct, height }: { widthPct: number; height: number }): React.CSSProperties {
return {
width: `${widthPct}%`,
height,
borderRadius: 4,
background: 'linear-gradient(90deg, #12121A 0%, #1A1A24 50%, #12121A 100%)',
backgroundSize: '200% 100%',
animation: 'vyndr-shimmer 1.5s ease-in-out infinite',
};
}
type SlateTab = 'all' | 'nba' | 'wnba' | 'mlb' | 'soccer';
const TABS: Array<{ id: SlateTab; label: string }> = [
@@ -38,11 +52,11 @@ const TABS: Array<{ id: SlateTab; label: string }> = [
// Per-tab → list of fetch URLs. `null` indicates "no endpoint yet";
// the Slate renders a soft "coming soon" badge for that sport rather
// than 404-spamming the backend.
// than 404-spamming the backend. Session 14 brought WNBA + MLB online.
const FETCH_URLS: Record<Exclude<SlateTab, 'all'>, string[] | null> = {
nba: ['/api/odds/nba'],
wnba: null, // No /api/odds/wnba proxy yet.
mlb: null, // No /api/odds/mlb proxy yet.
wnba: ['/api/odds/wnba'],
mlb: ['/api/odds/mlb'],
soccer: ['/api/odds/soccer/wc'],
};
@@ -339,8 +353,33 @@ export default function Slate({ initialTab = 'all', tier = 'free' }: SlateProps)
{/* Body */}
{loading && (
<div style={{ padding: 40, textAlign: 'center', color: 'var(--text-tertiary, #6B6B7B)' }}>
Loading the slate
// Session 14 — shimmer skeletons replace the bare "Loading…" text.
// Three placeholder cards approximating GameCard dimensions; the
// shimmer animation lives in globals.css (`@keyframes
// vyndr-shimmer`) so multiple loading surfaces stay in sync.
<div style={{ display: 'grid', gap: 16 }} role="status" aria-label="Loading the slate">
{[0, 1, 2].map((i) => (
<div
key={i}
style={{
background: 'var(--bg-2, #12121A)',
border: '1px solid var(--border, #1A1A24)',
borderRadius: 8,
padding: 16,
display: 'grid',
gap: 10,
}}
aria-hidden="true"
>
<div style={skeletonStyle({ widthPct: 60, height: 18 })} />
<div style={skeletonStyle({ widthPct: 40, height: 10 })} />
<div style={{ display: 'grid', gap: 8, marginTop: 8 }}>
<div style={skeletonStyle({ widthPct: 88, height: 14 })} />
<div style={skeletonStyle({ widthPct: 70, height: 14 })} />
<div style={skeletonStyle({ widthPct: 80, height: 14 })} />
</div>
</div>
))}
</div>
)}