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
+116
View File
@@ -0,0 +1,116 @@
'use client';
import { Suspense, useEffect, useState } from 'react';
import { useRouter, useSearchParams } from 'next/navigation';
import { useAuth } from '@/contexts/AuthContext';
import { getBrowserSupabase } from '@/lib/supabase';
import Wordmark from '@/components/Wordmark';
const RESEND_COOLDOWN_SECONDS = 60;
function VerifyInner() {
const router = useRouter();
const search = useSearchParams();
const emailFromQuery = search.get('email') || '';
const { user } = useAuth();
const [email, setEmail] = useState(emailFromQuery || user?.email || '');
const [cooldown, setCooldown] = useState(0);
const [sending, setSending] = useState(false);
const [error, setError] = useState('');
const [resentAt, setResentAt] = useState<number | null>(null);
useEffect(() => {
if (user?.email_confirmed_at) {
router.replace('/welcome');
}
}, [user, router]);
useEffect(() => {
if (cooldown <= 0) return;
const id = setInterval(() => setCooldown((c) => Math.max(0, c - 1)), 1000);
return () => clearInterval(id);
}, [cooldown]);
const resend = async () => {
setError('');
if (!email) return setError('Enter the email you signed up with.');
if (cooldown > 0) return;
setSending(true);
const supabase = getBrowserSupabase();
if (!supabase) {
setSending(false);
return setError('Auth is not configured.');
}
const { error: err } = await supabase.auth.resend({ type: 'signup', email });
setSending(false);
if (err) return setError(err.message);
setResentAt(Date.now());
setCooldown(RESEND_COOLDOWN_SECONDS);
};
return (
<section style={{ minHeight: '80vh', display: 'flex', alignItems: 'center', justifyContent: 'center', padding: '32px 16px' }}>
<div className="surface diagonal-cut animate-fade-up" style={{ width: '100%', maxWidth: 420, padding: 32 }}>
<a
href="/"
style={{ display: 'flex', justifyContent: 'center', color: 'var(--text-0)', textDecoration: 'none', marginBottom: 24 }}
aria-label="VYNDR — home"
>
<Wordmark size={26} />
</a>
<div style={{ textAlign: 'center', display: 'grid', gap: 8, justifyItems: 'center' }}>
<svg width="56" height="56" viewBox="0 0 24 24" fill="none" aria-hidden>
<rect x="3" y="5" width="18" height="14" rx="2" stroke="var(--grade-a)" strokeWidth="1.5" />
<path d="M3 7l9 6 9-6" stroke="var(--grade-a)" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
</svg>
<h1 style={{ fontSize: 22, fontWeight: 700 }}>Check your inbox</h1>
<p style={{ color: 'var(--text-1)', fontSize: 14, maxWidth: 320 }}>
We sent a verification link to{' '}
<span className="mono" style={{ color: 'var(--text-0)' }}>{email || 'your email'}</span>.
</p>
{!emailFromQuery && !user && (
<input
type="email"
placeholder="you@vyndr.app"
className="input-field"
style={{ marginTop: 8, maxWidth: 280 }}
value={email}
onChange={(e) => setEmail(e.target.value)}
/>
)}
{error && <p style={{ color: 'var(--grade-d)', fontSize: 13 }}>{error}</p>}
<button
type="button"
onClick={resend}
disabled={sending || cooldown > 0}
className="btn-primary"
style={{ marginTop: 12 }}
>
{sending ? 'Sending…' : cooldown > 0 ? `Resend in ${cooldown}s` : "Didn't get it? Resend"}
</button>
{resentAt ? (
<p className="lbl" style={{ color: 'var(--grade-a)', marginTop: 4 }}>SENT</p>
) : null}
<p style={{ fontSize: 13, color: 'var(--text-1)', marginTop: 16 }}>
Wrong email?{' '}
<a href="/signup" style={{ color: 'var(--grade-a)' }}>Sign up again</a>
</p>
</div>
</div>
</section>
);
}
export default function VerifyPage() {
return (
<Suspense fallback={null}>
<VerifyInner />
</Suspense>
);
}