271 lines
12 KiB
TypeScript
271 lines
12 KiB
TypeScript
import React, { useState, useEffect } from 'react';
|
||
import { CookiePreferences, initializeAnalytics, initializeMarketing } from '@/lib/cookie-utils';
|
||
|
||
interface CookieConsentProps {
|
||
onAccept?: () => void;
|
||
onDecline?: () => void;
|
||
onConfigure?: (preferences: CookiePreferences) => void;
|
||
}
|
||
|
||
const CookieConsent: React.FC<CookieConsentProps> = ({ onAccept, onDecline, onConfigure }) => {
|
||
const [isVisible, setIsVisible] = useState(false);
|
||
const [showDetails, setShowDetails] = useState(false);
|
||
const [preferences, setPreferences] = useState<CookiePreferences>({
|
||
necessary: true, // Всегда включены
|
||
analytics: false,
|
||
marketing: false,
|
||
functional: false,
|
||
});
|
||
|
||
useEffect(() => {
|
||
// Проверяем, есть ли уже согласие в localStorage
|
||
const cookieConsent = localStorage.getItem('cookieConsent');
|
||
if (!cookieConsent) {
|
||
setIsVisible(true);
|
||
}
|
||
}, []);
|
||
|
||
const handleAcceptAll = () => {
|
||
const allAccepted = {
|
||
necessary: true,
|
||
analytics: true,
|
||
marketing: true,
|
||
functional: true,
|
||
};
|
||
localStorage.setItem('cookieConsent', 'accepted');
|
||
localStorage.setItem('cookiePreferences', JSON.stringify(allAccepted));
|
||
|
||
// Инициализируем сервисы после согласия
|
||
initializeAnalytics();
|
||
initializeMarketing();
|
||
|
||
setIsVisible(false);
|
||
onAccept?.();
|
||
};
|
||
|
||
const handleDeclineAll = () => {
|
||
const onlyNecessary = {
|
||
necessary: true,
|
||
analytics: false,
|
||
marketing: false,
|
||
functional: false,
|
||
};
|
||
localStorage.setItem('cookieConsent', 'declined');
|
||
localStorage.setItem('cookiePreferences', JSON.stringify(onlyNecessary));
|
||
setIsVisible(false);
|
||
onDecline?.();
|
||
};
|
||
|
||
const handleSavePreferences = () => {
|
||
localStorage.setItem('cookieConsent', 'configured');
|
||
localStorage.setItem('cookiePreferences', JSON.stringify(preferences));
|
||
|
||
// Инициализируем сервисы согласно настройкам
|
||
if (preferences.analytics) {
|
||
initializeAnalytics();
|
||
}
|
||
if (preferences.marketing) {
|
||
initializeMarketing();
|
||
}
|
||
|
||
setIsVisible(false);
|
||
onConfigure?.(preferences);
|
||
};
|
||
|
||
const togglePreference = (key: keyof CookiePreferences) => {
|
||
if (key === 'necessary') return; // Необходимые cookies нельзя отключить
|
||
setPreferences(prev => ({
|
||
...prev,
|
||
[key]: !prev[key]
|
||
}));
|
||
};
|
||
|
||
if (!isVisible) return null;
|
||
|
||
return (
|
||
<div className="fixed bottom-0 left-0 right-0 z-50 bg-white border-t border-gray-200 shadow-lg cookie-consent-enter">
|
||
<div className="max-w-7xl mx-auto p-6 max-md:p-4">
|
||
{!showDetails ? (
|
||
// Основной вид
|
||
<div className="flex flex-col md:flex-row md:items-center md:justify-between gap-4">
|
||
{/* Текст согласия */}
|
||
<div className="flex-1">
|
||
<div className="flex items-start gap-3">
|
||
{/* Иконка cookie */}
|
||
<div className="flex-shrink-0 mt-1">
|
||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" className="text-gray-600">
|
||
<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-1.5 3.5c.55 0 1-.45 1-1s-.45-1-1-1-1 .45-1 1 .45 1 1 1zm3 2c.55 0 1-.45 1-1s-.45-1-1-1-1 .45-1 1 .45 1 1 1zm-6 1c.55 0 1-.45 1-1s-.45-1-1-1-1 .45-1 1 .45 1 1 1zm2.5 3c.55 0 1-.45 1-1s-.45-1-1-1-1 .45-1 1 .45 1 1 1zm4.5-1c.55 0 1-.45 1-1s-.45-1-1-1-1 .45-1 1 .45 1 1 1zm-2 4c.55 0 1-.45 1-1s-.45-1-1-1-1 .45-1 1 .45 1 1 1zm-3.5-2c.55 0 1-.45 1-1s-.45-1-1-1-1 .45-1 1 .45 1 1 1z" fill="currentColor"/>
|
||
</svg>
|
||
</div>
|
||
|
||
<div>
|
||
<h3 className="text-lg font-semibold text-gray-950 mb-2">
|
||
Мы используем файлы cookie
|
||
</h3>
|
||
<p className="text-sm text-gray-600 leading-relaxed">
|
||
Наш сайт использует файлы cookie для улучшения работы сайта, персонализации контента и анализа трафика.
|
||
Продолжая использовать сайт, вы соглашаетесь с нашей{' '}
|
||
<a
|
||
href="/privacy-policy"
|
||
className="text-red-600 hover:text-red-700 underline"
|
||
target="_blank"
|
||
rel="noopener noreferrer"
|
||
>
|
||
политикой конфиденциальности
|
||
</a>
|
||
{' '}и использованием файлов cookie.
|
||
</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Кнопки */}
|
||
<div className="flex flex-col sm:flex-row gap-3 md:flex-shrink-0">
|
||
<button
|
||
onClick={() => setShowDetails(true)}
|
||
className="px-6 py-3 text-sm font-medium text-gray-700 bg-gray-100 hover:bg-gray-200 rounded-lg transition-colors duration-200 min-w-[120px]"
|
||
>
|
||
Настроить
|
||
</button>
|
||
<button
|
||
onClick={handleDeclineAll}
|
||
className="px-6 py-3 text-sm font-medium text-gray-700 bg-gray-100 hover:bg-gray-200 rounded-lg transition-colors duration-200 min-w-[120px]"
|
||
>
|
||
Отклонить
|
||
</button>
|
||
<button
|
||
onClick={handleAcceptAll}
|
||
className="px-6 py-3 text-sm font-medium text-white bg-red-600 hover:bg-red-700 rounded-lg transition-colors duration-200 min-w-[120px]"
|
||
>
|
||
Принять все
|
||
</button>
|
||
</div>
|
||
</div>
|
||
) : (
|
||
// Детальный вид с настройками
|
||
<div className="space-y-6">
|
||
{/* Заголовок */}
|
||
<div className="flex items-center justify-between">
|
||
<h3 className="text-lg font-semibold text-gray-950">
|
||
Настройки файлов cookie
|
||
</h3>
|
||
<button
|
||
onClick={() => setShowDetails(false)}
|
||
className="text-gray-500 hover:text-gray-700 p-1"
|
||
>
|
||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none">
|
||
<path d="M15 5L5 15M5 5l10 10" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/>
|
||
</svg>
|
||
</button>
|
||
</div>
|
||
|
||
{/* Настройки cookies */}
|
||
<div className="space-y-4">
|
||
{/* Необходимые cookies */}
|
||
<div className="flex items-start justify-between p-4 bg-gray-50 rounded-lg">
|
||
<div className="flex-1">
|
||
<div className="flex items-center gap-3 mb-2">
|
||
<h4 className="font-medium text-gray-950">Необходимые cookies</h4>
|
||
<span className="text-xs px-2 py-1 bg-gray-200 text-gray-600 rounded">Обязательные</span>
|
||
</div>
|
||
<p className="text-sm text-gray-600">
|
||
Эти файлы cookie необходимы для работы сайта и не могут быть отключены.
|
||
</p>
|
||
</div>
|
||
<div className="flex-shrink-0 ml-4">
|
||
<div className="w-12 h-6 bg-red-600 rounded-full flex items-center justify-end px-1">
|
||
<div className="w-4 h-4 bg-white rounded-full"></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Аналитические cookies */}
|
||
<div className="flex items-start justify-between p-4 bg-gray-50 rounded-lg">
|
||
<div className="flex-1">
|
||
<h4 className="font-medium text-gray-950 mb-2">Аналитические cookies</h4>
|
||
<p className="text-sm text-gray-600">
|
||
Помогают нам понять, как посетители взаимодействуют с сайтом.
|
||
</p>
|
||
</div>
|
||
<div className="flex-shrink-0 ml-4">
|
||
<button
|
||
onClick={() => togglePreference('analytics')}
|
||
className={`w-12 h-6 rounded-full flex items-center transition-colors duration-200 ${
|
||
preferences.analytics ? 'bg-red-600 justify-end' : 'bg-gray-300 justify-start'
|
||
} px-1`}
|
||
>
|
||
<div className="w-4 h-4 bg-white rounded-full"></div>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Маркетинговые cookies */}
|
||
<div className="flex items-start justify-between p-4 bg-gray-50 rounded-lg">
|
||
<div className="flex-1">
|
||
<h4 className="font-medium text-gray-950 mb-2">Маркетинговые cookies</h4>
|
||
<p className="text-sm text-gray-600">
|
||
Используются для отслеживания посетителей и показа релевантной рекламы.
|
||
</p>
|
||
</div>
|
||
<div className="flex-shrink-0 ml-4">
|
||
<button
|
||
onClick={() => togglePreference('marketing')}
|
||
className={`w-12 h-6 rounded-full flex items-center transition-colors duration-200 ${
|
||
preferences.marketing ? 'bg-red-600 justify-end' : 'bg-gray-300 justify-start'
|
||
} px-1`}
|
||
>
|
||
<div className="w-4 h-4 bg-white rounded-full"></div>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Функциональные cookies */}
|
||
<div className="flex items-start justify-between p-4 bg-gray-50 rounded-lg">
|
||
<div className="flex-1">
|
||
<h4 className="font-medium text-gray-950 mb-2">Функциональные cookies</h4>
|
||
<p className="text-sm text-gray-600">
|
||
Обеспечивают расширенную функциональность и персонализацию.
|
||
</p>
|
||
</div>
|
||
<div className="flex-shrink-0 ml-4">
|
||
<button
|
||
onClick={() => togglePreference('functional')}
|
||
className={`w-12 h-6 rounded-full flex items-center transition-colors duration-200 ${
|
||
preferences.functional ? 'bg-red-600 justify-end' : 'bg-gray-300 justify-start'
|
||
} px-1`}
|
||
>
|
||
<div className="w-4 h-4 bg-white rounded-full"></div>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Кнопки действий */}
|
||
<div className="flex flex-col sm:flex-row gap-3 pt-4 border-t border-gray-200">
|
||
<button
|
||
onClick={handleDeclineAll}
|
||
className="px-6 py-3 text-sm font-medium text-gray-700 bg-gray-100 hover:bg-gray-200 rounded-lg transition-colors duration-200 flex-1 sm:flex-initial min-w-[120px]"
|
||
>
|
||
Только необходимые
|
||
</button>
|
||
<button
|
||
onClick={handleSavePreferences}
|
||
className="px-6 py-3 text-sm font-medium text-white bg-red-600 hover:bg-red-700 rounded-lg transition-colors duration-200 flex-1 sm:flex-initial min-w-[120px]"
|
||
>
|
||
Сохранить настройки
|
||
</button>
|
||
<button
|
||
onClick={handleAcceptAll}
|
||
className="px-6 py-3 text-sm font-medium text-white bg-red-600 hover:bg-red-700 rounded-lg transition-colors duration-200 flex-1 sm:flex-initial min-w-[120px]"
|
||
>
|
||
Принять все
|
||
</button>
|
||
</div>
|
||
</div>
|
||
)}
|
||
</div>
|
||
</div>
|
||
);
|
||
};
|
||
|
||
export default CookieConsent;
|