Добавлены новые элементы в меню профиля, включая настройки cookies. Внедрен компонент CookieConsent в приложение для управления согласиями на использование cookies. Добавлена анимация для отображения уведомления о cookies.
This commit is contained in:
271
src/components/CookieConsent.tsx
Normal file
271
src/components/CookieConsent.tsx
Normal file
@ -0,0 +1,271 @@
|
||||
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;
|
@ -13,6 +13,7 @@ const menuItems = [
|
||||
{ label: 'Адреса доставки', href: '/profile-addresses', icon: 'https://cdn.builder.io/api/v1/image/assets/TEMP/1faca7190a7dd71a66fd3cf0127a8c6e45eac5e6?placeholderIfAbsent=true&apiKey=f5bc5a2dc9b841d0aba1cc6c74a35920' },
|
||||
{ label: 'Гараж', href: '/profile-gar', icon: 'https://cdn.builder.io/api/v1/image/assets/TEMP/783501855b4cb8be4ac47a0733e298c3f3ccfc5e?placeholderIfAbsent=true&apiKey=f5bc5a2dc9b841d0aba1cc6c74a35920' },
|
||||
{ label: 'Настройки аккаунта', href: '/profile-set', icon: 'https://cdn.builder.io/api/v1/image/assets/TEMP/b39907028aa6baf08adc313aed84d1294f2be013?placeholderIfAbsent=true&apiKey=f5bc5a2dc9b841d0aba1cc6c74a35920' },
|
||||
{ label: 'Настройки cookies', href: '/profile-cookie-settings', icon: 'https://cdn.builder.io/api/v1/image/assets/TEMP/b39907028aa6baf08adc313aed84d1294f2be013?placeholderIfAbsent=true&apiKey=f5bc5a2dc9b841d0aba1cc6c74a35920' },
|
||||
];
|
||||
|
||||
const financeItems = [
|
||||
|
243
src/components/profile/CookieSettings.tsx
Normal file
243
src/components/profile/CookieSettings.tsx
Normal file
@ -0,0 +1,243 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import {
|
||||
getCookiePreferences,
|
||||
CookiePreferences,
|
||||
initializeAnalytics,
|
||||
initializeMarketing,
|
||||
resetCookieConsent
|
||||
} from '@/lib/cookie-utils';
|
||||
|
||||
const CookieSettings: React.FC = () => {
|
||||
const [preferences, setPreferences] = useState<CookiePreferences>({
|
||||
necessary: true,
|
||||
analytics: false,
|
||||
marketing: false,
|
||||
functional: false,
|
||||
});
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [isSaving, setIsSaving] = useState(false);
|
||||
const [saveMessage, setSaveMessage] = useState<string | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
// Загружаем текущие настройки
|
||||
const currentPreferences = getCookiePreferences();
|
||||
if (currentPreferences) {
|
||||
setPreferences(currentPreferences);
|
||||
}
|
||||
setIsLoading(false);
|
||||
}, []);
|
||||
|
||||
const togglePreference = (key: keyof CookiePreferences) => {
|
||||
if (key === 'necessary') return; // Необходимые cookies нельзя отключить
|
||||
setPreferences(prev => ({
|
||||
...prev,
|
||||
[key]: !prev[key]
|
||||
}));
|
||||
};
|
||||
|
||||
const handleSave = async () => {
|
||||
setIsSaving(true);
|
||||
setSaveMessage(null);
|
||||
|
||||
try {
|
||||
// Сохраняем настройки
|
||||
localStorage.setItem('cookieConsent', 'configured');
|
||||
localStorage.setItem('cookiePreferences', JSON.stringify(preferences));
|
||||
|
||||
// Инициализируем сервисы согласно настройкам
|
||||
if (preferences.analytics) {
|
||||
initializeAnalytics();
|
||||
}
|
||||
if (preferences.marketing) {
|
||||
initializeMarketing();
|
||||
}
|
||||
|
||||
setSaveMessage('Настройки успешно сохранены');
|
||||
|
||||
// Убираем сообщение через 3 секунды
|
||||
setTimeout(() => {
|
||||
setSaveMessage(null);
|
||||
}, 3000);
|
||||
} catch (error) {
|
||||
setSaveMessage('Ошибка при сохранении настроек');
|
||||
} finally {
|
||||
setIsSaving(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleReset = () => {
|
||||
resetCookieConsent();
|
||||
setPreferences({
|
||||
necessary: true,
|
||||
analytics: false,
|
||||
marketing: false,
|
||||
functional: false,
|
||||
});
|
||||
setSaveMessage('Настройки сброшены. Перезагрузите страницу для повторного отображения уведомления о cookies.');
|
||||
};
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<div className="flex justify-center items-center p-8">
|
||||
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-red-600"></div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="bg-white rounded-2xl p-8 max-md:px-5">
|
||||
<div className="mb-8">
|
||||
<h2 className="text-2xl font-bold text-gray-950 mb-2">
|
||||
Настройки файлов cookie
|
||||
</h2>
|
||||
<p className="text-gray-600">
|
||||
Управляйте тем, как мы используем файлы cookie на нашем сайте.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{saveMessage && (
|
||||
<div className={`mb-6 p-4 rounded-lg ${
|
||||
saveMessage.includes('Ошибка')
|
||||
? 'bg-red-50 border border-red-200 text-red-800'
|
||||
: 'bg-green-50 border border-green-200 text-green-800'
|
||||
}`}>
|
||||
{saveMessage}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="space-y-6">
|
||||
{/* Необходимые cookies */}
|
||||
<div className="flex items-start justify-between p-6 bg-gray-50 rounded-lg">
|
||||
<div className="flex-1">
|
||||
<div className="flex items-center gap-3 mb-2">
|
||||
<h3 className="font-semibold text-gray-950">Необходимые cookies</h3>
|
||||
<span className="text-xs px-2 py-1 bg-gray-200 text-gray-600 rounded">
|
||||
Обязательные
|
||||
</span>
|
||||
</div>
|
||||
<p className="text-sm text-gray-600 mb-3">
|
||||
Эти файлы cookie необходимы для работы сайта и не могут быть отключены.
|
||||
Они обеспечивают базовую функциональность, включая корзину покупок, авторизацию и безопасность.
|
||||
</p>
|
||||
<div className="text-xs text-gray-500">
|
||||
Включает: сессии, корзина, авторизация, безопасность
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex-shrink-0 ml-6">
|
||||
<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-6 bg-gray-50 rounded-lg">
|
||||
<div className="flex-1">
|
||||
<h3 className="font-semibold text-gray-950 mb-2">Аналитические cookies</h3>
|
||||
<p className="text-sm text-gray-600 mb-3">
|
||||
Помогают нам понять, как посетители взаимодействуют с сайтом, чтобы улучшить его работу и пользовательский опыт.
|
||||
</p>
|
||||
<div className="text-xs text-gray-500">
|
||||
Включает: Google Analytics, статистика посещений, анализ поведения
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex-shrink-0 ml-6">
|
||||
<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-6 bg-gray-50 rounded-lg">
|
||||
<div className="flex-1">
|
||||
<h3 className="font-semibold text-gray-950 mb-2">Маркетинговые cookies</h3>
|
||||
<p className="text-sm text-gray-600 mb-3">
|
||||
Используются для отслеживания посетителей и показа релевантной рекламы.
|
||||
Помогают измерить эффективность рекламных кампаний.
|
||||
</p>
|
||||
<div className="text-xs text-gray-500">
|
||||
Включает: рекламные пиксели, ретаргетинг, социальные сети
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex-shrink-0 ml-6">
|
||||
<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-6 bg-gray-50 rounded-lg">
|
||||
<div className="flex-1">
|
||||
<h3 className="font-semibold text-gray-950 mb-2">Функциональные cookies</h3>
|
||||
<p className="text-sm text-gray-600 mb-3">
|
||||
Обеспечивают расширенную функциональность и персонализацию сайта,
|
||||
включая предпочтения и настройки пользователя.
|
||||
</p>
|
||||
<div className="text-xs text-gray-500">
|
||||
Включает: языковые настройки, персонализация, чат-боты
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex-shrink-0 ml-6">
|
||||
<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-4 mt-8 pt-6 border-t border-gray-200">
|
||||
<button
|
||||
onClick={handleSave}
|
||||
disabled={isSaving}
|
||||
className="px-6 py-3 text-sm font-medium text-white bg-red-600 hover:bg-red-700 disabled:opacity-50 disabled:cursor-not-allowed rounded-lg transition-colors duration-200 flex-1 sm:flex-initial min-w-[140px]"
|
||||
>
|
||||
{isSaving ? 'Сохранение...' : 'Сохранить настройки'}
|
||||
</button>
|
||||
|
||||
<button
|
||||
onClick={handleReset}
|
||||
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-[140px]"
|
||||
>
|
||||
Сбросить настройки
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Дополнительная информация */}
|
||||
<div className="mt-8 p-4 bg-blue-50 border border-blue-200 rounded-lg">
|
||||
<div className="flex items-start gap-3">
|
||||
<div className="flex-shrink-0 mt-1">
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" className="text-blue-600">
|
||||
<path d="M10 0C4.48 0 0 4.48 0 10s4.48 10 10 10 10-4.48 10-10S15.52 0 10 0zm1 15h-2v-2h2v2zm0-4h-2V5h2v6z" fill="currentColor"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div>
|
||||
<h4 className="font-medium text-blue-900 mb-1">Информация о cookies</h4>
|
||||
<p className="text-sm text-blue-800">
|
||||
Изменения настроек cookies вступают в силу немедленно. Некоторые функции сайта могут работать некорректно при отключении определенных типов cookies.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default CookieSettings;
|
61
src/lib/cookie-utils.ts
Normal file
61
src/lib/cookie-utils.ts
Normal file
@ -0,0 +1,61 @@
|
||||
interface CookiePreferences {
|
||||
necessary: boolean;
|
||||
analytics: boolean;
|
||||
marketing: boolean;
|
||||
functional: boolean;
|
||||
}
|
||||
|
||||
export const getCookieConsent = (): string | null => {
|
||||
if (typeof window === 'undefined') return null;
|
||||
return localStorage.getItem('cookieConsent');
|
||||
};
|
||||
|
||||
export const getCookiePreferences = (): CookiePreferences | null => {
|
||||
if (typeof window === 'undefined') return null;
|
||||
const preferences = localStorage.getItem('cookiePreferences');
|
||||
return preferences ? JSON.parse(preferences) : null;
|
||||
};
|
||||
|
||||
export const hasConsentForAnalytics = (): boolean => {
|
||||
const preferences = getCookiePreferences();
|
||||
return preferences?.analytics || false;
|
||||
};
|
||||
|
||||
export const hasConsentForMarketing = (): boolean => {
|
||||
const preferences = getCookiePreferences();
|
||||
return preferences?.marketing || false;
|
||||
};
|
||||
|
||||
export const hasConsentForFunctional = (): boolean => {
|
||||
const preferences = getCookiePreferences();
|
||||
return preferences?.functional || false;
|
||||
};
|
||||
|
||||
export const isConsentGiven = (): boolean => {
|
||||
const consent = getCookieConsent();
|
||||
return consent !== null && consent !== 'declined';
|
||||
};
|
||||
|
||||
export const resetCookieConsent = (): void => {
|
||||
if (typeof window === 'undefined') return;
|
||||
localStorage.removeItem('cookieConsent');
|
||||
localStorage.removeItem('cookiePreferences');
|
||||
};
|
||||
|
||||
// Функция для интеграции с аналитикой (например, Google Analytics)
|
||||
export const initializeAnalytics = (): void => {
|
||||
if (!hasConsentForAnalytics()) return;
|
||||
|
||||
// Здесь можно добавить инициализацию Google Analytics или других сервисов
|
||||
console.log('Analytics initialized with user consent');
|
||||
};
|
||||
|
||||
// Функция для интеграции с маркетинговыми инструментами
|
||||
export const initializeMarketing = (): void => {
|
||||
if (!hasConsentForMarketing()) return;
|
||||
|
||||
// Здесь можно добавить инициализацию маркетинговых пикселей
|
||||
console.log('Marketing tools initialized with user consent');
|
||||
};
|
||||
|
||||
export type { CookiePreferences };
|
@ -14,6 +14,7 @@ import { CartProvider } from '@/contexts/CartContext';
|
||||
import { FavoritesProvider } from '@/contexts/FavoritesContext';
|
||||
import Layout from "@/components/Layout";
|
||||
import { Toaster } from 'react-hot-toast';
|
||||
import CookieConsent from '@/components/CookieConsent';
|
||||
|
||||
export default function App({ Component, pageProps }: AppProps) {
|
||||
const [isMaintenanceMode, setIsMaintenanceMode] = useState(false);
|
||||
@ -83,6 +84,7 @@ export default function App({ Component, pageProps }: AppProps) {
|
||||
src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js"
|
||||
strategy="afterInteractive"
|
||||
/>
|
||||
<CookieConsent />
|
||||
</CartProvider>
|
||||
</FavoritesProvider>
|
||||
</ApolloProvider>
|
||||
|
177
src/pages/privacy-policy.tsx
Normal file
177
src/pages/privacy-policy.tsx
Normal file
@ -0,0 +1,177 @@
|
||||
import React from 'react';
|
||||
import Head from 'next/head';
|
||||
|
||||
const PrivacyPolicy: React.FC = () => {
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>Политика конфиденциальности | ПротекАвто</title>
|
||||
<meta name="description" content="Политика конфиденциальности интернет-магазина автозапчастей ПротекАвто" />
|
||||
<meta name="robots" content="index, follow" />
|
||||
</Head>
|
||||
|
||||
<div className="min-h-screen bg-gray-50 py-12">
|
||||
<div className="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div className="bg-white rounded-lg shadow-sm p-8">
|
||||
<h1 className="text-3xl font-bold text-gray-950 mb-8">
|
||||
Политика конфиденциальности
|
||||
</h1>
|
||||
|
||||
<div className="prose prose-gray max-w-none space-y-6">
|
||||
<section>
|
||||
<h2 className="text-2xl font-semibold text-gray-950 mb-4">
|
||||
1. Общие положения
|
||||
</h2>
|
||||
<p className="text-gray-600 leading-relaxed">
|
||||
Настоящая Политика конфиденциальности определяет порядок обработки и защиты персональных данных
|
||||
пользователей интернет-магазина ПротекАвто (далее — «Сайт»). Мы уважаем вашу конфиденциальность
|
||||
и стремимся защитить ваши персональные данные.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2 className="text-2xl font-semibold text-gray-950 mb-4">
|
||||
2. Сбор и использование персональных данных
|
||||
</h2>
|
||||
<p className="text-gray-600 leading-relaxed mb-4">
|
||||
Мы собираем следующие категории персональных данных:
|
||||
</p>
|
||||
<ul className="list-disc pl-6 text-gray-600 space-y-2">
|
||||
<li>Контактная информация (имя, телефон, email)</li>
|
||||
<li>Данные для доставки (адрес, индекс)</li>
|
||||
<li>Информация о заказах и покупках</li>
|
||||
<li>Техническая информация (IP-адрес, браузер, устройство)</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2 className="text-2xl font-semibold text-gray-950 mb-4">
|
||||
3. Файлы cookie
|
||||
</h2>
|
||||
<p className="text-gray-600 leading-relaxed mb-4">
|
||||
Наш сайт использует файлы cookie для улучшения пользовательского опыта. Мы используем следующие типы cookie:
|
||||
</p>
|
||||
|
||||
<div className="space-y-4">
|
||||
<div className="bg-gray-50 p-4 rounded-lg">
|
||||
<h3 className="font-semibold text-gray-950 mb-2">Необходимые cookie</h3>
|
||||
<p className="text-sm text-gray-600">
|
||||
Обеспечивают базовую функциональность сайта, включая корзину покупок, авторизацию и безопасность.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="bg-gray-50 p-4 rounded-lg">
|
||||
<h3 className="font-semibold text-gray-950 mb-2">Аналитические cookie</h3>
|
||||
<p className="text-sm text-gray-600">
|
||||
Помогают нам понять, как посетители используют сайт, чтобы улучшить его работу.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="bg-gray-50 p-4 rounded-lg">
|
||||
<h3 className="font-semibold text-gray-950 mb-2">Маркетинговые cookie</h3>
|
||||
<p className="text-sm text-gray-600">
|
||||
Используются для показа релевантной рекламы и отслеживания эффективности рекламных кампаний.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="bg-gray-50 p-4 rounded-lg">
|
||||
<h3 className="font-semibold text-gray-950 mb-2">Функциональные cookie</h3>
|
||||
<p className="text-sm text-gray-600">
|
||||
Обеспечивают расширенную функциональность и персонализацию сайта.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2 className="text-2xl font-semibold text-gray-950 mb-4">
|
||||
4. Цели обработки данных
|
||||
</h2>
|
||||
<p className="text-gray-600 leading-relaxed mb-4">
|
||||
Мы обрабатываем ваши персональные данные для следующих целей:
|
||||
</p>
|
||||
<ul className="list-disc pl-6 text-gray-600 space-y-2">
|
||||
<li>Обработка и выполнение заказов</li>
|
||||
<li>Связь с клиентами по вопросам заказов</li>
|
||||
<li>Улучшение качества обслуживания</li>
|
||||
<li>Анализ использования сайта</li>
|
||||
<li>Маркетинговые коммуникации (с вашего согласия)</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2 className="text-2xl font-semibold text-gray-950 mb-4">
|
||||
5. Передача данных третьим лицам
|
||||
</h2>
|
||||
<p className="text-gray-600 leading-relaxed">
|
||||
Мы не продаем и не передаем ваши персональные данные третьим лицам, за исключением случаев,
|
||||
необходимых для выполнения наших обязательств перед вами (доставка, оплата) или требований законодательства.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2 className="text-2xl font-semibold text-gray-950 mb-4">
|
||||
6. Защита данных
|
||||
</h2>
|
||||
<p className="text-gray-600 leading-relaxed">
|
||||
Мы применяем современные технические и организационные меры для защиты ваших персональных данных
|
||||
от несанкционированного доступа, изменения, раскрытия или уничтожения.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2 className="text-2xl font-semibold text-gray-950 mb-4">
|
||||
7. Ваши права
|
||||
</h2>
|
||||
<p className="text-gray-600 leading-relaxed mb-4">
|
||||
Вы имеете право:
|
||||
</p>
|
||||
<ul className="list-disc pl-6 text-gray-600 space-y-2">
|
||||
<li>Получить информацию о обработке ваших данных</li>
|
||||
<li>Внести изменения в ваши данные</li>
|
||||
<li>Удалить ваши данные</li>
|
||||
<li>Ограничить обработку данных</li>
|
||||
<li>Отозвать согласие на обработку данных</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2 className="text-2xl font-semibold text-gray-950 mb-4">
|
||||
8. Контактная информация
|
||||
</h2>
|
||||
<p className="text-gray-600 leading-relaxed">
|
||||
По вопросам обработки персональных данных вы можете обратиться к нам:
|
||||
</p>
|
||||
<div className="bg-gray-50 p-4 rounded-lg mt-4">
|
||||
<p className="text-gray-600">
|
||||
<strong>Email:</strong> privacy@protekauto.ru<br />
|
||||
<strong>Телефон:</strong> +7 (495) 123-45-67<br />
|
||||
<strong>Адрес:</strong> г. Москва, ул. Примерная, д. 1
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2 className="text-2xl font-semibold text-gray-950 mb-4">
|
||||
9. Изменения в политике
|
||||
</h2>
|
||||
<p className="text-gray-600 leading-relaxed">
|
||||
Мы оставляем за собой право вносить изменения в настоящую Политику конфиденциальности.
|
||||
Актуальная версия всегда доступна на данной странице.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<div className="mt-8 pt-8 border-t border-gray-200">
|
||||
<p className="text-sm text-gray-500">
|
||||
Последнее обновление: {new Date().toLocaleDateString('ru-RU')}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default PrivacyPolicy;
|
34
src/pages/profile-cookie-settings.tsx
Normal file
34
src/pages/profile-cookie-settings.tsx
Normal file
@ -0,0 +1,34 @@
|
||||
import React from 'react';
|
||||
import Head from 'next/head';
|
||||
import LKMenu from '@/components/LKMenu';
|
||||
import CookieSettings from '@/components/profile/CookieSettings';
|
||||
|
||||
const ProfileCookieSettingsPage: React.FC = () => {
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>Настройки cookies | Личный кабинет | ПротекАвто</title>
|
||||
<meta name="description" content="Управление настройками файлов cookie в личном кабинете" />
|
||||
<meta name="robots" content="noindex, nofollow" />
|
||||
</Head>
|
||||
|
||||
<div className="min-h-screen bg-gray-50">
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
|
||||
<div className="flex gap-8 max-md:flex-col">
|
||||
{/* Боковое меню */}
|
||||
<div className="w-80 max-md:w-full">
|
||||
<LKMenu />
|
||||
</div>
|
||||
|
||||
{/* Основной контент */}
|
||||
<div className="flex-1">
|
||||
<CookieSettings />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default ProfileCookieSettingsPage;
|
@ -84,4 +84,20 @@ input[type=number]::-webkit-outer-spin-button {
|
||||
}
|
||||
input[type=number] {
|
||||
-moz-appearance: textfield;
|
||||
}
|
||||
|
||||
/* Анимация для cookie consent */
|
||||
@keyframes slideInFromBottom {
|
||||
from {
|
||||
transform: translateY(100%);
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
transform: translateY(0);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.cookie-consent-enter {
|
||||
animation: slideInFromBottom 0.3s ease-out;
|
||||
}
|
Reference in New Issue
Block a user