Session 36: Design system Phase E — remaining screens + dashboard Bloomberg lines (1853 tests)
VYNDR 2.0 conversion, Phase E. Frontend-only; zero backend changes. - lib/slateAdapter.js: parseAmericanOdds, detectBestLines, mapScheduleToGameCards (best/worst line detection — the Bloomberg pattern). - Reskinned the LEGACY GameCard's game-lines grid with best/worst highlighting + SportBadge, keeping inline grading intact (a wholesale swap to the display-only vyndr/GameCard would have deleted the slate's grading interaction). - compare/invite/help/about: RouteStubs -> real design-system pages. - login reskinned (scanlines, system voice, new Wordmark); pricing + ClaimMeter. Honest scope: the full GameCard swap needs inline grading ported into the new component first; profile/settings/blog/game-detail reskins are light/deferred. 18 new tests. Backend 1839 -> 1853, 144 suites, zero regressions. Web build clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,13 +1,63 @@
|
||||
import RouteStub from '@/components/vyndr/RouteStub';
|
||||
'use client';
|
||||
|
||||
export const metadata = { title: 'Invite' };
|
||||
import { useState } from 'react';
|
||||
import SectionHead from '@/components/vyndr/SectionHead';
|
||||
import VBtn from '@/components/vyndr/VBtn';
|
||||
import { useAuth } from '@/contexts/AuthContext';
|
||||
|
||||
// Referral system (§12) — bring 3 friends → free Analyst. Progress is read
|
||||
// from the session when available; the live invite ledger wires in later.
|
||||
export default function InvitePage() {
|
||||
const { user } = useAuth();
|
||||
const [copied, setCopied] = useState(false);
|
||||
const code = (user?.email?.split('@')[0] || 'signal').replace(/[^a-z0-9]/gi, '').toUpperCase().slice(0, 10) || 'SIGNAL';
|
||||
const link = `https://vyndr.app/?ref=${code}`;
|
||||
const invited = 0;
|
||||
const goal = 3;
|
||||
|
||||
const copy = () => {
|
||||
if (typeof navigator !== 'undefined' && navigator.clipboard) {
|
||||
void navigator.clipboard.writeText(link);
|
||||
setCopied(true);
|
||||
setTimeout(() => setCopied(false), 1800);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<RouteStub
|
||||
title="Invite"
|
||||
arriving="SESSION 36"
|
||||
blurb="Bring three friends onto the signal and your Analyst plan is on the house."
|
||||
/>
|
||||
<section style={{ maxWidth: 640, margin: '0 auto', padding: '28px 16px 96px' }}>
|
||||
<SectionHead accent="var(--amber)">▚ REFERRAL</SectionHead>
|
||||
<h1 className="mono" style={{ fontSize: 28, fontWeight: 800, letterSpacing: '-0.02em', margin: '8px 0 8px' }}>INVITE</h1>
|
||||
<p className="mono" style={{ fontSize: 14, color: 'var(--text-1)', lineHeight: 1.6, marginBottom: 24 }}>
|
||||
Bring <span style={{ color: 'var(--g-a)', fontWeight: 700 }}>3 friends</span> onto the signal and your Analyst plan is on the house — locked for as long as they stay.
|
||||
</p>
|
||||
|
||||
{/* Progress */}
|
||||
<div style={{ marginBottom: 24 }}>
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 8 }}>
|
||||
<span className="label">FRIENDS JOINED</span>
|
||||
<span className="mono" style={{ fontSize: 13, fontWeight: 700, color: 'var(--g-a)' }}>{invited} / {goal}</span>
|
||||
</div>
|
||||
<div style={{ display: 'flex', gap: 6 }}>
|
||||
{Array.from({ length: goal }).map((_, i) => (
|
||||
<div key={i} style={{ flex: 1, height: 6, borderRadius: 4, background: i < invited ? 'var(--g-a)' : 'var(--bg-2)', border: '1px solid var(--border)', boxShadow: i < invited ? '0 0 10px rgba(0,212,160,.5)' : 'none' }} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Referral link */}
|
||||
<div style={{ marginBottom: 16 }}>
|
||||
<span className="label">YOUR LINK</span>
|
||||
<div style={{ display: 'flex', gap: 8, marginTop: 8 }}>
|
||||
<div className="mono" style={{ flex: 1, padding: '11px 14px', background: 'var(--bg-2)', border: '1px solid var(--border)', borderRadius: 6, fontSize: 13, color: 'var(--g-a)', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
|
||||
{link}
|
||||
</div>
|
||||
<VBtn variant={copied ? 'primary' : 'outline'} onClick={copy}>{copied ? '✓ Copied' : 'Copy'}</VBtn>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style={{ display: 'flex', gap: 10 }}>
|
||||
<VBtn variant="primary" style={{ flex: 1 }} onClick={copy}>↗ Share your link</VBtn>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user