Files
yourhouse/src/components/CallbackModal.tsx

195 lines
9.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

'use client';
import { useState } from 'react';
import { X, CheckCircle, AlertCircle } from 'lucide-react';
interface CallbackModalProps {
isOpen: boolean;
onClose: () => void;
}
const CallbackModal = ({ isOpen, onClose }: CallbackModalProps) => {
const [name, setName] = useState('');
const [phone, setPhone] = useState('');
const [message, setMessage] = useState('');
const [error, setError] = useState('');
const [success, setSuccess] = useState(false);
const [loading, setLoading] = useState(false);
if (!isOpen) return null;
const validatePhone = (value: string) => {
const digits = value.replace(/\D/g, '');
if (digits.length !== 11) return false;
if (!(digits.startsWith('7') || digits.startsWith('8'))) return false;
if (/^(7|8)0{10}$/.test(digits)) return false;
return true;
};
const validateName = (value: string) => {
return /^[А-Яа-яA-Za-zЁё\-]{2,}$/.test(value.trim());
};
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
setError('');
if (!validateName(name)) return setError('Пожалуйста, укажите корректное имя (только буквы, не менее 2 символов)');
if (!validatePhone(phone)) return setError('Пожалуйста, укажите корректный российский номер телефона');
setLoading(true);
try {
const res = await fetch('/api/send-telegram', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name, phone, message, material: 'Звонок' }),
});
if (res.ok) {
setSuccess(true);
setName('');
setPhone('');
setMessage('');
} else {
setError('Ошибка отправки. Попробуйте позже.');
}
} catch {
setError('Ошибка отправки. Попробуйте позже.');
} finally {
setLoading(false);
}
};
const closeModal = () => {
setName('');
setPhone('');
setMessage('');
setError('');
setSuccess(false);
setLoading(false);
onClose();
};
return (
<div className="fixed inset-0 bg-gray-900/80 backdrop-blur-sm z-50 flex items-center justify-center p-4">
<div className="relative max-w-md w-full">
{/* Декоративные фоновые элементы */}
<div className="absolute -top-8 -left-8 w-32 h-32 bg-gradient-to-br from-blue-500/30 to-purple-500/30 rounded-full blur-2xl opacity-70"></div>
<div className="absolute -bottom-8 -right-8 w-24 h-24 bg-gradient-to-br from-purple-500/30 to-blue-500/30 rounded-full blur-2xl opacity-70"></div>
{/* Основной контейнер модального окна */}
<div className="relative bg-gradient-to-br from-gray-900/95 via-gray-800/95 to-gray-900/95 backdrop-blur-md border border-white/20 rounded-2xl p-8 shadow-2xl">
{/* Кнопка закрытия */}
<button
onClick={closeModal}
className="absolute top-4 right-4 text-gray-400 hover:text-white transition-colors duration-300 hover:bg-white/10 rounded-full p-2 group"
>
<X className="w-5 h-5 group-hover:scale-110 transition-transform duration-300" />
</button>
{/* Заголовок */}
<div className="text-center mb-8">
<h2 className="text-3xl font-bold bg-gradient-to-r from-white via-blue-100 to-white bg-clip-text text-transparent mb-2">
Заказать звонок
</h2>
<div className="w-16 h-1 bg-gradient-to-r from-blue-500 to-purple-500 mx-auto rounded-full"></div>
</div>
{!success ? (
<form className="space-y-6" onSubmit={handleSubmit}>
{/* Поле имени */}
<div className="relative group">
<input
type="text"
placeholder="Ваше имя"
value={name}
onChange={e => setName(e.target.value)}
className="w-full px-4 py-3 bg-white/10 backdrop-blur-sm border border-white/20 rounded-xl text-white placeholder-gray-400 focus:outline-none focus:border-blue-500/50 focus:bg-white/20 transition-all duration-300 group-hover:border-white/30"
/>
<div className="absolute inset-0 rounded-xl bg-gradient-to-r from-blue-500/10 to-purple-500/10 opacity-0 group-hover:opacity-100 transition-opacity duration-300 pointer-events-none"></div>
</div>
{/* Поле телефона */}
<div className="relative group">
<input
type="tel"
placeholder="Ваш телефон"
value={phone}
onChange={e => {
const val = e.target.value.replace(/\D/g, '').slice(0, 11);
setPhone(val);
}}
className="w-full px-4 py-3 bg-white/10 backdrop-blur-sm border border-white/20 rounded-xl text-white placeholder-gray-400 focus:outline-none focus:border-blue-500/50 focus:bg-white/20 transition-all duration-300 group-hover:border-white/30"
/>
<div className="absolute inset-0 rounded-xl bg-gradient-to-r from-blue-500/10 to-purple-500/10 opacity-0 group-hover:opacity-100 transition-opacity duration-300 pointer-events-none"></div>
</div>
{/* Поле сообщения */}
<div className="relative group">
<textarea
placeholder="Ваше сообщение (необязательно)"
rows={3}
value={message}
onChange={e => setMessage(e.target.value)}
className="w-full px-4 py-3 bg-white/10 backdrop-blur-sm border border-white/20 rounded-xl text-white placeholder-gray-400 focus:outline-none focus:border-blue-500/50 focus:bg-white/20 transition-all duration-300 group-hover:border-white/30 resize-none"
/>
<div className="absolute inset-0 rounded-xl bg-gradient-to-r from-blue-500/10 to-purple-500/10 opacity-0 group-hover:opacity-100 transition-opacity duration-300 pointer-events-none"></div>
</div>
{/* Сообщение об ошибке */}
{error && (
<div className="relative p-4 rounded-xl bg-gradient-to-r from-red-500/20 to-red-600/20 border border-red-500/30 backdrop-blur-sm">
<div className="flex items-center">
<AlertCircle className="w-5 h-5 text-red-400 mr-3 shrink-0" />
<span className="text-red-200 text-sm leading-relaxed">{error}</span>
</div>
</div>
)}
{/* Кнопка отправки */}
<button
type="submit"
disabled={loading}
className="relative w-full group overflow-hidden rounded-xl bg-gradient-to-r from-blue-500/20 to-purple-500/20 border border-white/20 backdrop-blur-sm hover:scale-105 transition-all duration-300 disabled:opacity-60 disabled:hover:scale-100"
>
<div className="absolute inset-0 bg-gradient-to-r from-blue-500/30 to-purple-500/30 opacity-0 group-hover:opacity-100 transition-opacity duration-300"></div>
<span className="relative block py-3 px-6 text-white font-semibold">
{loading ? 'Отправка...' : 'Отправить'}
</span>
</button>
</form>
) : (
<div className="text-center py-8 space-y-6">
{/* Иконка успеха */}
<div className="relative mx-auto w-20 h-20 rounded-full bg-gradient-to-br from-green-500/20 to-emerald-500/20 border border-green-500/30 backdrop-blur-sm flex items-center justify-center group">
<div className="absolute inset-0 rounded-full bg-gradient-to-br from-green-500/30 to-emerald-500/30 opacity-0 group-hover:opacity-100 transition-opacity duration-500"></div>
<CheckCircle className="w-10 h-10 text-green-400 relative z-10" />
</div>
{/* Сообщение об успехе */}
<div className="space-y-3">
<h3 className="text-xl font-bold text-white">
Спасибо за заявку!
</h3>
<p className="text-gray-300 leading-relaxed">
Ваши данные успешно отправлены. Мы свяжемся с вами в ближайшее время.
</p>
</div>
{/* Кнопка закрытия */}
<button
onClick={closeModal}
className="relative group overflow-hidden rounded-xl bg-gradient-to-r from-blue-500/20 to-purple-500/20 border border-white/20 backdrop-blur-sm hover:scale-105 transition-all duration-300 px-8 py-3"
>
<div className="absolute inset-0 bg-gradient-to-r from-blue-500/30 to-purple-500/30 opacity-0 group-hover:opacity-100 transition-opacity duration-300"></div>
<span className="relative text-white font-medium">Закрыть</span>
</button>
</div>
)}
{/* Декоративный элемент */}
<div className="absolute top-4 left-4 w-2 h-2 bg-gradient-to-r from-blue-500 to-purple-500 rounded-full opacity-50"></div>
</div>
</div>
</div>
);
};
export default CallbackModal;