🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
403 lines
14 KiB
Plaintext
403 lines
14 KiB
Plaintext
---
|
|
// Cookie consent banner component
|
|
export interface Props {
|
|
position?: 'bottom' | 'top';
|
|
}
|
|
|
|
const { position = 'bottom' } = Astro.props;
|
|
---
|
|
|
|
<div
|
|
id="cookie-consent-banner"
|
|
class={`fixed ${position === 'bottom' ? 'bottom-0' : 'top-0'} left-0 right-0 z-50 bg-gray-900 text-white shadow-lg transform translate-y-full transition-transform duration-300 ease-in-out`}
|
|
style="display: none;"
|
|
>
|
|
<div class="max-w-7xl mx-auto px-4 py-4 sm:px-6 lg:px-8">
|
|
<div class="flex flex-col sm:flex-row items-start sm:items-center justify-between gap-4">
|
|
<!-- Cookie notice content -->
|
|
<div class="flex-1">
|
|
<div class="flex items-start gap-3">
|
|
<div class="flex-shrink-0 mt-1">
|
|
<svg class="w-5 h-5 text-blue-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
|
|
</svg>
|
|
</div>
|
|
<div>
|
|
<h3 class="text-sm font-semibold mb-1">Cookie Preferences</h3>
|
|
<p class="text-sm text-gray-300 leading-relaxed">
|
|
We use essential cookies to make our website work and analytics cookies to understand how you interact with our site.
|
|
<a href="/privacy" target="_blank" class="text-blue-400 hover:text-blue-300 underline">
|
|
Learn more in our Privacy Policy
|
|
</a>
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Action buttons -->
|
|
<div class="flex flex-col sm:flex-row gap-2 min-w-fit">
|
|
<button
|
|
id="cookie-settings-btn"
|
|
class="px-4 py-2 text-sm font-medium text-gray-300 hover:text-white border border-gray-600 hover:border-gray-500 rounded-lg transition-colors"
|
|
>
|
|
Manage Preferences
|
|
</button>
|
|
<button
|
|
id="cookie-accept-btn"
|
|
class="px-4 py-2 text-sm font-medium bg-blue-600 hover:bg-blue-700 text-white rounded-lg transition-colors"
|
|
>
|
|
Accept All
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Cookie preferences modal -->
|
|
<div
|
|
id="cookie-preferences-modal"
|
|
class="fixed inset-0 z-50 bg-black bg-opacity-50 flex items-center justify-center p-4"
|
|
style="display: none;"
|
|
>
|
|
<div class="bg-white rounded-2xl shadow-2xl max-w-2xl w-full max-h-[90vh] overflow-y-auto">
|
|
<!-- Modal header -->
|
|
<div class="flex items-center justify-between p-6 border-b border-gray-200">
|
|
<h2 class="text-xl font-bold text-gray-900">Cookie Preferences</h2>
|
|
<button
|
|
id="cookie-modal-close"
|
|
class="p-2 hover:bg-gray-100 rounded-lg transition-colors"
|
|
>
|
|
<svg class="w-5 h-5 text-gray-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
|
|
</svg>
|
|
</button>
|
|
</div>
|
|
|
|
<!-- Modal content -->
|
|
<div class="p-6 space-y-6">
|
|
<p class="text-gray-600">
|
|
We use cookies to enhance your experience on our website. You can choose which types of cookies to allow below.
|
|
</p>
|
|
|
|
<!-- Essential cookies -->
|
|
<div class="border border-gray-200 rounded-lg p-4">
|
|
<div class="flex items-center justify-between mb-2">
|
|
<h3 class="font-semibold text-gray-900">Essential Cookies</h3>
|
|
<div class="bg-gray-100 text-gray-500 text-xs px-2 py-1 rounded">
|
|
Always Active
|
|
</div>
|
|
</div>
|
|
<p class="text-sm text-gray-600 mb-3">
|
|
These cookies are necessary for the website to function and cannot be disabled. They include authentication, security, and basic functionality.
|
|
</p>
|
|
<details class="text-xs text-gray-500">
|
|
<summary class="cursor-pointer hover:text-gray-700">View details</summary>
|
|
<div class="mt-2 pl-4 border-l-2 border-gray-200">
|
|
<ul class="space-y-1">
|
|
<li>• Authentication tokens (Supabase)</li>
|
|
<li>• CSRF protection tokens</li>
|
|
<li>• Session management</li>
|
|
<li>• Security preferences</li>
|
|
</ul>
|
|
</div>
|
|
</details>
|
|
</div>
|
|
|
|
<!-- Analytics cookies -->
|
|
<div class="border border-gray-200 rounded-lg p-4">
|
|
<div class="flex items-center justify-between mb-2">
|
|
<h3 class="font-semibold text-gray-900">Analytics Cookies</h3>
|
|
<label class="relative inline-flex items-center cursor-pointer">
|
|
<input type="checkbox" id="analytics-toggle" class="sr-only peer">
|
|
<div class="w-11 h-6 bg-gray-200 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-blue-300 rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-blue-600"></div>
|
|
</label>
|
|
</div>
|
|
<p class="text-sm text-gray-600 mb-3">
|
|
Help us understand how visitors interact with our website by collecting and reporting information anonymously.
|
|
</p>
|
|
<details class="text-xs text-gray-500">
|
|
<summary class="cursor-pointer hover:text-gray-700">View details</summary>
|
|
<div class="mt-2 pl-4 border-l-2 border-gray-200">
|
|
<ul class="space-y-1">
|
|
<li>• Page views and user interactions</li>
|
|
<li>• Performance metrics</li>
|
|
<li>• Error tracking (anonymized)</li>
|
|
<li>• Usage patterns (no personal data)</li>
|
|
</ul>
|
|
</div>
|
|
</details>
|
|
</div>
|
|
|
|
<!-- Marketing cookies -->
|
|
<div class="border border-gray-200 rounded-lg p-4">
|
|
<div class="flex items-center justify-between mb-2">
|
|
<h3 class="font-semibold text-gray-900">Marketing Cookies</h3>
|
|
<label class="relative inline-flex items-center cursor-pointer">
|
|
<input type="checkbox" id="marketing-toggle" class="sr-only peer">
|
|
<div class="w-11 h-6 bg-gray-200 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-blue-300 rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-blue-600"></div>
|
|
</label>
|
|
</div>
|
|
<p class="text-sm text-gray-600 mb-3">
|
|
These cookies track your activity to deliver more relevant advertisements and marketing communications.
|
|
</p>
|
|
<details class="text-xs text-gray-500">
|
|
<summary class="cursor-pointer hover:text-gray-700">View details</summary>
|
|
<div class="mt-2 pl-4 border-l-2 border-gray-200">
|
|
<ul class="space-y-1">
|
|
<li>• Advertising preferences</li>
|
|
<li>• Email campaign effectiveness</li>
|
|
<li>• Social media integration</li>
|
|
<li>• Retargeting pixels</li>
|
|
</ul>
|
|
</div>
|
|
</details>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Modal footer -->
|
|
<div class="flex flex-col sm:flex-row gap-3 p-6 border-t border-gray-200">
|
|
<button
|
|
id="cookie-reject-all"
|
|
class="flex-1 px-4 py-2 text-sm font-medium text-gray-700 bg-gray-100 hover:bg-gray-200 border border-gray-300 rounded-lg transition-colors"
|
|
>
|
|
Reject All
|
|
</button>
|
|
<button
|
|
id="cookie-save-preferences"
|
|
class="flex-1 px-4 py-2 text-sm font-medium text-white bg-blue-600 hover:bg-blue-700 rounded-lg transition-colors"
|
|
>
|
|
Save Preferences
|
|
</button>
|
|
<button
|
|
id="cookie-accept-all"
|
|
class="flex-1 px-4 py-2 text-sm font-medium text-white bg-green-600 hover:bg-green-700 rounded-lg transition-colors"
|
|
>
|
|
Accept All
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
// Cookie consent management
|
|
class CookieConsent {
|
|
private consentKey = 'bct_cookie_consent';
|
|
private banner: HTMLElement;
|
|
private modal: HTMLElement;
|
|
|
|
constructor() {
|
|
this.banner = document.getElementById('cookie-consent-banner')!;
|
|
this.modal = document.getElementById('cookie-preferences-modal')!;
|
|
this.init();
|
|
}
|
|
|
|
private init() {
|
|
// Check if consent has already been given
|
|
const consent = this.getConsent();
|
|
if (!consent) {
|
|
this.showBanner();
|
|
} else {
|
|
this.applyConsent(consent);
|
|
}
|
|
|
|
this.bindEvents();
|
|
}
|
|
|
|
private bindEvents() {
|
|
// Banner buttons
|
|
document.getElementById('cookie-accept-btn')?.addEventListener('click', () => {
|
|
this.acceptAll();
|
|
});
|
|
|
|
document.getElementById('cookie-settings-btn')?.addEventListener('click', () => {
|
|
this.showModal();
|
|
});
|
|
|
|
// Modal buttons
|
|
document.getElementById('cookie-modal-close')?.addEventListener('click', () => {
|
|
this.hideModal();
|
|
});
|
|
|
|
document.getElementById('cookie-accept-all')?.addEventListener('click', () => {
|
|
this.acceptAll();
|
|
});
|
|
|
|
document.getElementById('cookie-reject-all')?.addEventListener('click', () => {
|
|
this.rejectAll();
|
|
});
|
|
|
|
document.getElementById('cookie-save-preferences')?.addEventListener('click', () => {
|
|
this.savePreferences();
|
|
});
|
|
|
|
// Modal backdrop click
|
|
this.modal.addEventListener('click', (e) => {
|
|
if (e.target === this.modal) {
|
|
this.hideModal();
|
|
}
|
|
});
|
|
}
|
|
|
|
private showBanner() {
|
|
this.banner.style.display = 'block';
|
|
setTimeout(() => {
|
|
this.banner.classList.remove('translate-y-full');
|
|
}, 100);
|
|
}
|
|
|
|
private hideBanner() {
|
|
this.banner.classList.add('translate-y-full');
|
|
setTimeout(() => {
|
|
this.banner.style.display = 'none';
|
|
}, 300);
|
|
}
|
|
|
|
private showModal() {
|
|
// Load current preferences
|
|
const consent = this.getConsent();
|
|
if (consent) {
|
|
(document.getElementById('analytics-toggle') as HTMLInputElement).checked = consent.analytics;
|
|
(document.getElementById('marketing-toggle') as HTMLInputElement).checked = consent.marketing;
|
|
}
|
|
|
|
this.modal.style.display = 'flex';
|
|
document.body.style.overflow = 'hidden';
|
|
}
|
|
|
|
private hideModal() {
|
|
this.modal.style.display = 'none';
|
|
document.body.style.overflow = '';
|
|
}
|
|
|
|
private acceptAll() {
|
|
const consent = {
|
|
essential: true,
|
|
analytics: true,
|
|
marketing: true,
|
|
timestamp: Date.now()
|
|
};
|
|
|
|
this.saveConsent(consent);
|
|
this.applyConsent(consent);
|
|
this.hideBanner();
|
|
this.hideModal();
|
|
}
|
|
|
|
private rejectAll() {
|
|
const consent = {
|
|
essential: true,
|
|
analytics: false,
|
|
marketing: false,
|
|
timestamp: Date.now()
|
|
};
|
|
|
|
this.saveConsent(consent);
|
|
this.applyConsent(consent);
|
|
this.hideBanner();
|
|
this.hideModal();
|
|
}
|
|
|
|
private savePreferences() {
|
|
const analyticsToggle = document.getElementById('analytics-toggle') as HTMLInputElement;
|
|
const marketingToggle = document.getElementById('marketing-toggle') as HTMLInputElement;
|
|
|
|
const consent = {
|
|
essential: true,
|
|
analytics: analyticsToggle.checked,
|
|
marketing: marketingToggle.checked,
|
|
timestamp: Date.now()
|
|
};
|
|
|
|
this.saveConsent(consent);
|
|
this.applyConsent(consent);
|
|
this.hideBanner();
|
|
this.hideModal();
|
|
}
|
|
|
|
private saveConsent(consent: any) {
|
|
localStorage.setItem(this.consentKey, JSON.stringify(consent));
|
|
|
|
// Also save to cookie for server-side access
|
|
document.cookie = `${this.consentKey}=${JSON.stringify(consent)}; max-age=31536000; path=/; SameSite=Strict; Secure`;
|
|
}
|
|
|
|
private getConsent() {
|
|
try {
|
|
const stored = localStorage.getItem(this.consentKey);
|
|
if (stored) {
|
|
const consent = JSON.parse(stored);
|
|
// Check if consent is older than 12 months
|
|
if (Date.now() - consent.timestamp > 365 * 24 * 60 * 60 * 1000) {
|
|
return null;
|
|
}
|
|
return consent;
|
|
}
|
|
} catch (e) {
|
|
console.error('Error reading cookie consent:', e);
|
|
}
|
|
return null;
|
|
}
|
|
|
|
private applyConsent(consent: any) {
|
|
// Apply analytics consent
|
|
if (consent.analytics) {
|
|
this.enableAnalytics();
|
|
} else {
|
|
this.disableAnalytics();
|
|
}
|
|
|
|
// Apply marketing consent
|
|
if (consent.marketing) {
|
|
this.enableMarketing();
|
|
} else {
|
|
this.disableMarketing();
|
|
}
|
|
|
|
// Dispatch custom event for other scripts
|
|
window.dispatchEvent(new CustomEvent('cookieConsentUpdated', {
|
|
detail: consent
|
|
}));
|
|
}
|
|
|
|
private enableAnalytics() {
|
|
// Enable analytics tracking
|
|
console.log('Analytics enabled');
|
|
// TODO: Initialize analytics services (Google Analytics, etc.)
|
|
}
|
|
|
|
private disableAnalytics() {
|
|
// Disable analytics tracking
|
|
console.log('Analytics disabled');
|
|
// TODO: Disable analytics services
|
|
}
|
|
|
|
private enableMarketing() {
|
|
// Enable marketing cookies
|
|
console.log('Marketing enabled');
|
|
// TODO: Enable marketing pixels, retargeting, etc.
|
|
}
|
|
|
|
private disableMarketing() {
|
|
// Disable marketing cookies
|
|
console.log('Marketing disabled');
|
|
// TODO: Disable marketing pixels, retargeting, etc.
|
|
}
|
|
|
|
// Public method to show preferences modal
|
|
public showPreferences() {
|
|
this.showModal();
|
|
}
|
|
}
|
|
|
|
// Initialize cookie consent when DOM is loaded
|
|
if (document.readyState === 'loading') {
|
|
document.addEventListener('DOMContentLoaded', () => {
|
|
new CookieConsent();
|
|
});
|
|
} else {
|
|
new CookieConsent();
|
|
}
|
|
|
|
// Export for global access
|
|
(window as any).cookieConsent = CookieConsent;
|
|
</script> |