Files
ShieldAI/packages/web/src/components/WaitlistForm.tsx

138 lines
4.1 KiB
TypeScript

import { Component, createSignal, onMount } from 'solid-js';
import { trackWaitlistSignup } from '../hooks/useAnalytics';
interface WaitlistFormProps {
variant?: 'hero' | 'inline';
placeholder?: string;
buttonText?: string;
}
function getUtmParams() {
if (typeof window === 'undefined') return {};
const params = new URLSearchParams(window.location.search);
return {
utmSource: params.get('utm_source') || undefined,
utmMedium: params.get('utm_medium') || undefined,
utmCampaign: params.get('utm_campaign') || undefined,
};
}
const WaitlistForm: Component<WaitlistFormProps> = (props) => {
const [email, setEmail] = createSignal('');
const [name, setName] = createSignal('');
const [tier, setTier] = createSignal('basic');
const [utm, setUtm] = createSignal<Record<string, string | undefined>>({});
const [submitted, setSubmitted] = createSignal(false);
const [loading, setLoading] = createSignal(false);
const [error, setError] = createSignal('');
onMount(() => {
setUtm(getUtmParams());
});
const variant = props.variant || 'hero';
const handleSubmit = async (e: Event) => {
e.preventDefault();
setError('');
if (!email() || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email())) {
setError('Please enter a valid email');
return;
}
setLoading(true);
try {
const res = await fetch('/api/waitlist/signup', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
email: email(),
name: name() || undefined,
tier: tier() !== 'basic' ? tier() : undefined,
...utm(),
}),
});
if (!res.ok) {
const data = await res.json();
throw new Error(data.error || 'Signup failed');
}
trackWaitlistSignup(email(), 'landing_page', tier());
setSubmitted(true);
} catch (err) {
setError(err instanceof Error ? err.message : 'Something went wrong');
} finally {
setLoading(false);
}
};
if (submitted()) {
return (
<div class="waitlist-success">
<div class="success-icon"></div>
<h3>You're on the list!</h3>
<p>We'll keep you updated on our launch and send early access invites.</p>
</div>
);
}
if (variant === 'hero') {
return (
<form class="waitlist-form hero-form" onSubmit={handleSubmit}>
<div class="form-row">
<input
type="email"
value={email()}
onInput={(e) => setEmail(e.currentTarget.value)}
placeholder={props.placeholder || 'Enter your email'}
required
aria-label="Email address"
/>
<button type="submit" disabled={loading()}>
{loading() ? 'Joining...' : props.buttonText || 'Join Waitlist'}
</button>
</div>
<div class="form-row secondary">
<input
type="text"
value={name()}
onInput={(e) => setName(e.currentTarget.value)}
placeholder="Your name (optional)"
aria-label="Your name"
/>
<select value={tier()} onChange={(e) => setTier(e.currentTarget.value)} aria-label="Interest level">
<option value="basic">Free Basic Protection</option>
<option value="plus">Plus $9.99/mo</option>
<option value="premium">Premium $24.99/mo</option>
</select>
</div>
{error() && <p class="form-error">{error()}</p>}
</form>
);
}
return (
<form class="waitlist-form inline-form" onSubmit={handleSubmit}>
<div class="form-row">
<input
type="email"
value={email()}
onInput={(e) => setEmail(e.currentTarget.value)}
placeholder={props.placeholder || 'Your email'}
required
aria-label="Email address"
/>
<button type="submit" disabled={loading()}>
{loading() ? '...' : props.buttonText || 'Sign Up'}
</button>
</div>
{error() && <p class="form-error">{error()}</p>}
</form>
);
};
export default WaitlistForm;