Custom i18n system with typed translation dictionaries (~570 keys), LanguageProvider context, and useTranslation hook. All 31 components and pages wired with t() calls. Chatbot backend passes language hint to Claude for Ukrainian responses. Language preference persists via localStorage. SEO meta tags and html lang attribute update dynamically. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
109 lines
3.6 KiB
TypeScript
109 lines
3.6 KiB
TypeScript
import React, { useState } from 'react';
|
|
import { useTranslation } from '../i18n';
|
|
import './ChatLeadForm.css';
|
|
|
|
export interface LeadInfo {
|
|
name: string;
|
|
email: string;
|
|
company: string;
|
|
}
|
|
|
|
interface ChatLeadFormProps {
|
|
onSubmit: (lead: LeadInfo) => void;
|
|
}
|
|
|
|
const ChatLeadForm: React.FC<ChatLeadFormProps> = ({ onSubmit }) => {
|
|
const { t } = useTranslation();
|
|
const [name, setName] = useState('');
|
|
const [email, setEmail] = useState('');
|
|
const [company, setCompany] = useState('');
|
|
const [consent, setConsent] = useState(false);
|
|
const [errors, setErrors] = useState<Record<string, string>>({});
|
|
|
|
const validate = () => {
|
|
const errs: Record<string, string> = {};
|
|
if (!name.trim()) errs.name = t('chat.lead.nameError');
|
|
if (!email.trim()) errs.email = t('chat.lead.emailError');
|
|
else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) errs.email = t('chat.lead.emailInvalid');
|
|
if (!consent) errs.consent = t('chat.lead.consentError');
|
|
setErrors(errs);
|
|
return Object.keys(errs).length === 0;
|
|
};
|
|
|
|
const handleSubmit = (e: React.FormEvent) => {
|
|
e.preventDefault();
|
|
if (!validate()) return;
|
|
onSubmit({ name: name.trim(), email: email.trim(), company: company.trim() });
|
|
};
|
|
|
|
return (
|
|
<form className="chat-lead-form" onSubmit={handleSubmit}>
|
|
<div className="chat-lead-form__header">
|
|
<div className="chat-lead-form__avatar">AI</div>
|
|
<div>
|
|
<div className="chat-lead-form__title">{t('chat.lead.title')}</div>
|
|
<div className="chat-lead-form__subtitle">{t('chat.lead.subtitle')}</div>
|
|
</div>
|
|
</div>
|
|
|
|
<p className="chat-lead-form__intro">
|
|
{t('chat.lead.intro')}
|
|
</p>
|
|
|
|
<div className="chat-lead-form__fields">
|
|
<div className="chat-lead-form__group">
|
|
<input
|
|
type="text"
|
|
placeholder={t('chat.lead.namePlaceholder')}
|
|
value={name}
|
|
onChange={(e) => setName(e.target.value)}
|
|
className={errors.name ? 'chat-lead-form__input--error' : ''}
|
|
maxLength={100}
|
|
/>
|
|
{errors.name && <span className="chat-lead-form__error">{errors.name}</span>}
|
|
</div>
|
|
|
|
<div className="chat-lead-form__group">
|
|
<input
|
|
type="email"
|
|
placeholder={t('chat.lead.emailPlaceholder')}
|
|
value={email}
|
|
onChange={(e) => setEmail(e.target.value)}
|
|
className={errors.email ? 'chat-lead-form__input--error' : ''}
|
|
maxLength={200}
|
|
/>
|
|
{errors.email && <span className="chat-lead-form__error">{errors.email}</span>}
|
|
</div>
|
|
|
|
<div className="chat-lead-form__group">
|
|
<input
|
|
type="text"
|
|
placeholder={t('chat.lead.companyPlaceholder')}
|
|
value={company}
|
|
onChange={(e) => setCompany(e.target.value)}
|
|
maxLength={200}
|
|
/>
|
|
</div>
|
|
|
|
<label className="chat-lead-form__consent">
|
|
<input
|
|
type="checkbox"
|
|
checked={consent}
|
|
onChange={(e) => setConsent(e.target.checked)}
|
|
/>
|
|
<span>
|
|
{t('chat.lead.consent')}{' '}
|
|
<a href="/privacy" target="_blank" rel="noopener noreferrer">{t('chat.lead.privacyLink')}</a>
|
|
</span>
|
|
</label>
|
|
{errors.consent && <span className="chat-lead-form__error">{errors.consent}</span>}
|
|
</div>
|
|
|
|
<button type="submit" className="chat-lead-form__submit">
|
|
{t('chat.lead.submit')}
|
|
</button>
|
|
</form>
|
|
);
|
|
};
|
|
|
|
export default ChatLeadForm;
|