96 lines
4.9 KiB
React
96 lines
4.9 KiB
React
import React, { useState } from 'react'
|
|
import { FileDown } from 'lucide-react'
|
|
import { useFormSubmit } from '../hooks/useFormSubmit'
|
|
|
|
function validate(data) {
|
|
const errors = {}
|
|
if (!data.name || data.name.trim().length === 0) errors.name = 'Name ist erforderlich.'
|
|
if (data.name && data.name.length > 100) errors.name = 'Max. 100 Zeichen.'
|
|
if (!data.email || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(data.email)) errors.email = 'Gültige E-Mail erforderlich.'
|
|
if (!data.message || data.message.trim().length === 0) errors.message = 'Bitte sag kurz, wer du bist.'
|
|
if (data.message && data.message.length > 2000) errors.message = 'Max. 2000 Zeichen.'
|
|
return { valid: Object.keys(errors).length === 0, errors }
|
|
}
|
|
|
|
const inputStyle = (hasError) => ({
|
|
width: '100%', padding: '12px', background: 'var(--bg-elevated)',
|
|
border: hasError ? '1px solid #ff4444' : '1px solid var(--glass-border)',
|
|
borderRadius: 'var(--radius-sm)', color: 'var(--text-primary)', fontSize: '1rem',
|
|
})
|
|
|
|
export default function SpeakerCvRequest() {
|
|
const [open, setOpen] = useState(false)
|
|
const [form, setForm] = useState({ name: '', email: '', message: '', company_website: '' })
|
|
const { submit, loading, success, errors, serverError } = useFormSubmit('/api/speaker-cv', validate)
|
|
|
|
function handleChange(e) {
|
|
setForm(prev => ({ ...prev, [e.target.name]: e.target.value }))
|
|
}
|
|
|
|
async function handleSubmit(e) {
|
|
e.preventDefault()
|
|
await submit(form)
|
|
}
|
|
|
|
if (!open) {
|
|
return (
|
|
<button type="button" className="primary-button" onClick={() => setOpen(true)} style={{ display: 'inline-flex', alignItems: 'center', gap: '8px' }}>
|
|
<FileDown size={18} /> Speaker CV anfragen
|
|
</button>
|
|
)
|
|
}
|
|
|
|
return (
|
|
<div className="glass-panel" style={{ padding: '32px', marginTop: '8px', width: '100%' }}>
|
|
{success ? (
|
|
<p role="status" aria-live="polite" style={{ margin: 0, color: 'var(--text-primary)' }}>
|
|
Danke! Der Speaker CV ist auf dem Weg in dein Postfach.
|
|
</p>
|
|
) : (
|
|
<form onSubmit={handleSubmit}>
|
|
<p style={{ marginTop: 0, marginBottom: '20px', color: 'var(--text-secondary)' }}>
|
|
Sag mir kurz wer Du bist und warum Du mich kennenlernen möchtest:
|
|
</p>
|
|
|
|
{/* Honeypot */}
|
|
<div hidden>
|
|
<label>
|
|
Firmen-Website (bitte leer lassen)
|
|
<input name="company_website" tabIndex={-1} autoComplete="off" value={form.company_website} onChange={handleChange} />
|
|
</label>
|
|
</div>
|
|
|
|
<div style={{ marginBottom: '16px' }}>
|
|
<label htmlFor="speaker-name" style={{ display: 'block', marginBottom: '6px', fontSize: '0.9rem' }}>Name</label>
|
|
<input id="speaker-name" name="name" value={form.name} onChange={handleChange} aria-invalid={Boolean(errors.name)} aria-describedby={errors.name ? 'speaker-name-error' : undefined} style={inputStyle(errors.name)} />
|
|
{errors.name && <p id="speaker-name-error" role="alert" style={{ color: '#ff4444', fontSize: '0.85rem', margin: '4px 0 0' }}>{errors.name}</p>}
|
|
</div>
|
|
|
|
<div style={{ marginBottom: '16px' }}>
|
|
<label htmlFor="speaker-email" style={{ display: 'block', marginBottom: '6px', fontSize: '0.9rem' }}>E-Mail</label>
|
|
<input id="speaker-email" name="email" type="email" value={form.email} onChange={handleChange} aria-invalid={Boolean(errors.email)} aria-describedby={errors.email ? 'speaker-email-error' : undefined} style={inputStyle(errors.email)} />
|
|
{errors.email && <p id="speaker-email-error" role="alert" style={{ color: '#ff4444', fontSize: '0.85rem', margin: '4px 0 0' }}>{errors.email}</p>}
|
|
</div>
|
|
|
|
<div style={{ marginBottom: '20px' }}>
|
|
<label htmlFor="speaker-message" style={{ display: 'block', marginBottom: '6px', fontSize: '0.9rem' }}>Wer bist Du & warum?</label>
|
|
<textarea id="speaker-message" name="message" value={form.message} onChange={handleChange} rows={4} maxLength={2000} aria-invalid={Boolean(errors.message)} aria-describedby={errors.message ? 'speaker-message-error' : undefined} style={{ ...inputStyle(errors.message), resize: 'vertical' }} />
|
|
{errors.message && <p id="speaker-message-error" role="alert" style={{ color: '#ff4444', fontSize: '0.85rem', margin: '4px 0 0' }}>{errors.message}</p>}
|
|
</div>
|
|
|
|
{serverError && <p role="alert" aria-live="assertive" style={{ color: '#ff4444', marginBottom: '16px' }}>{serverError}</p>}
|
|
|
|
<div style={{ display: 'flex', gap: '12px' }}>
|
|
<button type="submit" className="primary-button" disabled={loading}>
|
|
{loading ? 'Wird gesendet...' : 'Speaker CV anfordern'}
|
|
</button>
|
|
<button type="button" className="secondary-button" onClick={() => setOpen(false)}>
|
|
Abbrechen
|
|
</button>
|
|
</div>
|
|
</form>
|
|
)}
|
|
</div>
|
|
)
|
|
}
|