- Created complete Next.js 15 teaser website with CHORUS brand styling - Implemented mobile-responsive 3D logo (128px mobile, 512px desktop) - Added proper Exo font loading via Next.js Google Fonts for iOS/Chrome compatibility - Built comprehensive early access form with GDPR compliance and rate limiting - Integrated PostgreSQL database with complete schema for lead capture - Added scroll indicators that auto-hide when scrolling begins - Optimized mobile modal forms with proper scrolling and submit button access - Deployed via Docker Swarm with Traefik SSL termination at chorus.services - Includes database migrations, consent tracking, and email notifications 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
276 lines
9.8 KiB
TypeScript
276 lines
9.8 KiB
TypeScript
'use client'
|
||
|
||
import { useState, FormEvent } from 'react'
|
||
import { EarlyAccessLead, useEarlyAccessCapture } from '../hooks/useEarlyAccessCapture'
|
||
|
||
import { LeadSourceType } from '../hooks/useEarlyAccessCapture'
|
||
|
||
interface EarlyAccessFormProps {
|
||
isOpen: boolean
|
||
onClose: () => void
|
||
leadSource: LeadSourceType
|
||
}
|
||
|
||
export default function EarlyAccessForm({ isOpen, onClose, leadSource }: EarlyAccessFormProps) {
|
||
const { submitEarlyAccess, isSubmitting, submitStatus, errorMessage } = useEarlyAccessCapture()
|
||
|
||
const [formData, setFormData] = useState<EarlyAccessLead>({
|
||
firstName: '',
|
||
lastName: '',
|
||
email: '',
|
||
companyName: '',
|
||
companyRole: '',
|
||
interestLevel: 'general_interest',
|
||
leadSource: leadSource,
|
||
gdprConsent: false,
|
||
marketingConsent: false,
|
||
})
|
||
|
||
const handleSubmit = async (e: FormEvent) => {
|
||
e.preventDefault()
|
||
|
||
if (!formData.gdprConsent) {
|
||
alert('Please accept the privacy policy to continue.')
|
||
return
|
||
}
|
||
|
||
const result = await submitEarlyAccess(formData)
|
||
|
||
if (result.success) {
|
||
// Reset form on success
|
||
setFormData({
|
||
firstName: '',
|
||
lastName: '',
|
||
email: '',
|
||
companyName: '',
|
||
companyRole: '',
|
||
interestLevel: 'general_interest',
|
||
leadSource: leadSource,
|
||
gdprConsent: false,
|
||
marketingConsent: false,
|
||
})
|
||
}
|
||
}
|
||
|
||
const handleInputChange = (field: keyof EarlyAccessLead, value: string | boolean) => {
|
||
setFormData(prev => ({
|
||
...prev,
|
||
[field]: value
|
||
}))
|
||
}
|
||
|
||
if (!isOpen) return null
|
||
|
||
return (
|
||
<div
|
||
className="fixed inset-0 z-50 flex items-center justify-center p-4 bg-black/80 backdrop-blur-sm overflow-y-auto"
|
||
onClick={onClose}
|
||
>
|
||
<div
|
||
className="relative w-full max-w-md bg-gradient-to-b from-mulberry-900 to-carbon-900 text-white p-4 sm:p-chorus-xl rounded-lg border border-mulberry-700/50 shadow-2xl my-8 max-h-[90vh] overflow-y-auto"
|
||
onClick={(e) => e.stopPropagation()}
|
||
>
|
||
{/* Close Button */}
|
||
<button
|
||
onClick={onClose}
|
||
className="absolute top-4 right-4 text-mulberry-300 hover:text-white text-2xl font-light transition-colors duration-200"
|
||
>
|
||
×
|
||
</button>
|
||
|
||
{/* Form Header */}
|
||
<h3 className="text-h3 font-semibold text-white mb-chorus-sm">
|
||
{leadSource === 'request_early_access'
|
||
? 'Request Early Access to CHORUS'
|
||
: 'Join the CHORUS Waitlist'}
|
||
</h3>
|
||
<p className="text-sm text-mulberry-200 font-light mb-4 sm:mb-chorus-xl">
|
||
{leadSource === 'request_early_access'
|
||
? 'Get priority access to contextual AI orchestration'
|
||
: 'Be notified when CHORUS becomes available'}
|
||
</p>
|
||
|
||
{/* Success State */}
|
||
{submitStatus === 'success' ? (
|
||
<div className="text-center py-chorus-xl">
|
||
<div className="text-5xl text-eucalyptus-400 mb-chorus-lg animate-bounce">
|
||
✓
|
||
</div>
|
||
<h4 className="text-lg font-semibold text-eucalyptus-300 mb-chorus-sm">
|
||
{leadSource === 'request_early_access'
|
||
? 'Request submitted successfully!'
|
||
: 'Welcome to the waitlist!'}
|
||
</h4>
|
||
<p className="text-sm text-mulberry-200 font-light">
|
||
{leadSource === 'request_early_access'
|
||
? 'We\'ll prioritize your request and contact you soon.'
|
||
: 'We\'ll notify you when CHORUS becomes available.'}
|
||
</p>
|
||
</div>
|
||
) : (
|
||
/* Form */
|
||
<form onSubmit={handleSubmit}>
|
||
{/* Name Fields */}
|
||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-chorus-sm sm:gap-chorus-md mb-chorus-md">
|
||
<div>
|
||
<label className="block text-sm font-medium text-mulberry-200 mb-chorus-xs">
|
||
First Name *
|
||
</label>
|
||
<input
|
||
type="text"
|
||
required
|
||
value={formData.firstName}
|
||
onChange={(e) => handleInputChange('firstName', e.target.value)}
|
||
className="form-input"
|
||
placeholder="John"
|
||
/>
|
||
</div>
|
||
<div>
|
||
<label className="block text-sm font-medium text-mulberry-200 mb-chorus-xs">
|
||
Last Name *
|
||
</label>
|
||
<input
|
||
type="text"
|
||
required
|
||
value={formData.lastName}
|
||
onChange={(e) => handleInputChange('lastName', e.target.value)}
|
||
className="form-input"
|
||
placeholder="Doe"
|
||
/>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Email */}
|
||
<div className="mb-chorus-md">
|
||
<label className="block text-sm font-medium text-mulberry-200 mb-chorus-xs">
|
||
Email Address *
|
||
</label>
|
||
<input
|
||
type="email"
|
||
required
|
||
value={formData.email}
|
||
onChange={(e) => handleInputChange('email', e.target.value)}
|
||
className="form-input"
|
||
placeholder="john@company.com"
|
||
/>
|
||
</div>
|
||
|
||
{/* Company Information */}
|
||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-chorus-sm sm:gap-chorus-md mb-chorus-md">
|
||
<div>
|
||
<label className="block text-sm font-medium text-mulberry-200 mb-chorus-xs">
|
||
Company
|
||
</label>
|
||
<input
|
||
type="text"
|
||
value={formData.companyName || ''}
|
||
onChange={(e) => handleInputChange('companyName', e.target.value)}
|
||
className="form-input"
|
||
placeholder="Company Name"
|
||
/>
|
||
</div>
|
||
<div>
|
||
<label className="block text-sm font-medium text-mulberry-200 mb-chorus-xs">
|
||
Role
|
||
</label>
|
||
<input
|
||
type="text"
|
||
value={formData.companyRole || ''}
|
||
onChange={(e) => handleInputChange('companyRole', e.target.value)}
|
||
className="form-input"
|
||
placeholder="CTO, Director, etc."
|
||
/>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Interest Level */}
|
||
<div className="mb-4 sm:mb-chorus-lg">
|
||
<label className="block text-sm font-medium text-mulberry-200 mb-chorus-xs">
|
||
Primary Interest
|
||
</label>
|
||
<select
|
||
value={formData.interestLevel}
|
||
onChange={(e) => handleInputChange('interestLevel', e.target.value as any)}
|
||
className="form-input"
|
||
>
|
||
<option value="general_interest">General Interest</option>
|
||
<option value="technical_evaluation">Technical Evaluation</option>
|
||
<option value="strategic_demo">Strategic Demo</option>
|
||
</select>
|
||
</div>
|
||
|
||
{/* GDPR Consent */}
|
||
<div style={{ marginBottom: '1.5rem', fontSize: '0.85rem' }}>
|
||
<label style={{ display: 'flex', alignItems: 'flex-start', gap: '0.5rem', marginBottom: '0.75rem' }}>
|
||
<input
|
||
type="checkbox"
|
||
checked={formData.gdprConsent}
|
||
onChange={(e) => handleInputChange('gdprConsent', e.target.checked)}
|
||
required
|
||
/>
|
||
<span>
|
||
I agree to the privacy policy and consent to processing my personal data for early access communications. *
|
||
</span>
|
||
</label>
|
||
|
||
<label style={{ display: 'flex', alignItems: 'flex-start', gap: '0.5rem' }}>
|
||
<input
|
||
type="checkbox"
|
||
checked={formData.marketingConsent}
|
||
onChange={(e) => handleInputChange('marketingConsent', e.target.checked)}
|
||
/>
|
||
<span>
|
||
I would like to receive updates about CHORUS Services and related products.
|
||
</span>
|
||
</label>
|
||
</div>
|
||
|
||
{/* Error Message */}
|
||
{submitStatus === 'error' && errorMessage && (
|
||
<div style={{
|
||
padding: '0.75rem',
|
||
backgroundColor: 'rgba(239, 68, 68, 0.1)',
|
||
border: '1px solid #dc2626',
|
||
color: '#fca5a5',
|
||
fontSize: '0.85rem',
|
||
marginBottom: '1rem'
|
||
}}>
|
||
⚠ {errorMessage}
|
||
</div>
|
||
)}
|
||
|
||
{/* Submit Button */}
|
||
<button
|
||
type="submit"
|
||
disabled={isSubmitting || !formData.gdprConsent}
|
||
className="btn-primary"
|
||
style={{
|
||
width: '100%',
|
||
opacity: (isSubmitting || !formData.gdprConsent) ? 0.5 : 1,
|
||
cursor: (isSubmitting || !formData.gdprConsent) ? 'not-allowed' : 'pointer'
|
||
}}
|
||
>
|
||
{isSubmitting
|
||
? (leadSource === 'request_early_access' ? 'Submitting Request...' : 'Joining Waitlist...')
|
||
: (leadSource === 'request_early_access' ? 'Submit Request' : 'Join Waitlist')
|
||
}
|
||
</button>
|
||
</form>
|
||
)}
|
||
|
||
{/* Footer */}
|
||
<p style={{
|
||
fontSize: '0.75rem',
|
||
opacity: 0.6,
|
||
textAlign: 'center',
|
||
marginTop: '1.5rem',
|
||
paddingTop: '1.5rem',
|
||
borderTop: '1px solid #444'
|
||
}}>
|
||
By joining our waitlist, you'll receive exclusive early access and product updates.
|
||
We respect your privacy and won't spam you.
|
||
</p>
|
||
</div>
|
||
</div>
|
||
)
|
||
} |