Refactor: Add full PDPA compliance features
- Cookie consent system (banner + modal) with Thai language - Consent logging database (Astro DB + SQLite) - API endpoints for consent management (POST/GET/DELETE) - Admin dashboard for viewing consent logs (/admin/consent-logs) - Umami Analytics integration (conditional loading with consent) - Updated Privacy Policy (full 14-section PDPA Section 36 compliance) - Updated Terms & Conditions (17 sections, Thailand law) - Dockerfile updated with SQLite runtime - Node.js adapter for SSR support - Admin password: moreminimore2026!Secure (CHANGE IN PRODUCTION) TODO: Configure Umami Analytics with actual Website ID
This commit is contained in:
187
src/components/consent/ConsentModal.astro
Normal file
187
src/components/consent/ConsentModal.astro
Normal file
@@ -0,0 +1,187 @@
|
||||
---
|
||||
// ConsentModal.astro - Standalone consent preferences modal
|
||||
---
|
||||
|
||||
<div
|
||||
id="consent-modal-standalone"
|
||||
class="fixed inset-0 z-50 hidden"
|
||||
aria-labelledby="modal-title"
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
>
|
||||
<!-- Backdrop -->
|
||||
<div class="fixed inset-0 bg-black bg-opacity-50 transition-opacity consent-modal-backdrop"></div>
|
||||
|
||||
<!-- Modal Panel -->
|
||||
<div class="fixed inset-0 z-10 overflow-y-auto">
|
||||
<div class="flex min-h-full items-center justify-center p-4">
|
||||
<div
|
||||
class="relative transform overflow-hidden rounded-xl bg-white dark:bg-gray-800 shadow-2xl transition-all max-w-lg w-full max-h-[90vh] overflow-y-auto consent-modal-panel"
|
||||
>
|
||||
<!-- Header -->
|
||||
<div class="px-6 py-4 border-b border-gray-200 dark:border-gray-700">
|
||||
<h3 id="modal-title" class="text-xl font-bold text-gray-900 dark:text-white">
|
||||
การตั้งค่าคุกกี้
|
||||
</h3>
|
||||
<p class="text-base text-gray-600 dark:text-gray-300 mt-2">
|
||||
จัดการการตั้งค่าคุกกี้ของคุณที่นี่
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Body -->
|
||||
<div class="px-6 py-4 space-y-4">
|
||||
<!-- Essential -->
|
||||
<div class="flex items-start gap-3 p-3 rounded-lg border border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-700">
|
||||
<input
|
||||
type="checkbox"
|
||||
name="consent-essential"
|
||||
checked
|
||||
disabled
|
||||
class="mt-1 w-5 h-5 rounded border-gray-300 text-secondary focus:ring-secondary"
|
||||
/>
|
||||
<div class="flex-1">
|
||||
<span class="block font-semibold text-gray-900 dark:text-white text-base">
|
||||
คุกกี้ที่จำเป็น
|
||||
</span>
|
||||
<span class="block text-sm text-gray-600 dark:text-gray-400 mt-1">
|
||||
จำเป็นสำหรับการทำงานของเว็บไซต์ ไม่สามารถปิดใช้งานได้
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Analytics -->
|
||||
<label class="flex items-start gap-3 p-3 rounded-lg border border-gray-200 dark:border-gray-700 hover:bg-gray-50 dark:hover:bg-gray-700 transition cursor-pointer">
|
||||
<input
|
||||
type="checkbox"
|
||||
name="consent-analytics"
|
||||
id="standalone-analytics"
|
||||
class="mt-1 w-5 h-5 rounded border-gray-300 text-secondary focus:ring-secondary consent-checkbox"
|
||||
/>
|
||||
<div class="flex-1">
|
||||
<span class="block font-semibold text-gray-900 dark:text-white text-base">
|
||||
คุกกี้วิเคราะห์ข้อมูล
|
||||
</span>
|
||||
<span class="block text-sm text-gray-600 dark:text-gray-400 mt-1">
|
||||
ช่วยเราเข้าใจว่าผู้เยี่ยมชมใช้งานเว็บไซต์ (Umami Analytics)
|
||||
</span>
|
||||
</div>
|
||||
</label>
|
||||
|
||||
<!-- Marketing -->
|
||||
<label class="flex items-start gap-3 p-3 rounded-lg border border-gray-200 dark:border-gray-700 hover:bg-gray-50 dark:hover:bg-gray-700 transition cursor-pointer">
|
||||
<input
|
||||
type="checkbox"
|
||||
name="consent-marketing"
|
||||
id="standalone-marketing"
|
||||
class="mt-1 w-5 h-5 rounded border-gray-300 text-secondary focus:ring-secondary consent-checkbox"
|
||||
/>
|
||||
<div class="flex-1">
|
||||
<span class="block font-semibold text-gray-900 dark:text-white text-base">
|
||||
คุกกี้การตลาด
|
||||
</span>
|
||||
<span class="block text-sm text-gray-600 dark:text-gray-400 mt-1">
|
||||
ใช้สำหรับติดตามและแสดงโฆษณาที่ตรงกับความสนใจของคุณ
|
||||
</span>
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<!-- Footer -->
|
||||
<div class="px-6 py-4 border-t border-gray-200 dark:border-gray-700 flex gap-3">
|
||||
<button
|
||||
id="consent-modal-save"
|
||||
class="flex-1 px-6 py-3 bg-secondary hover:bg-secondary-hover text-white rounded-lg font-medium text-base transition focus:outline-none focus:ring-2 focus:ring-secondary focus:ring-offset-2"
|
||||
>
|
||||
บันทึกการตั้งค่า
|
||||
</button>
|
||||
<button
|
||||
id="consent-modal-close"
|
||||
class="flex-1 px-6 py-3 bg-gray-200 dark:bg-gray-700 text-gray-900 dark:text-white rounded-lg font-medium text-base transition hover:bg-gray-300 dark:hover:bg-gray-600 focus:outline-none focus:ring-2 focus:ring-gray-400 focus:ring-offset-2"
|
||||
>
|
||||
ปิด
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const CONSENT_KEY = 'consent-preferences';
|
||||
|
||||
const modal = document.getElementById('consent-modal-standalone');
|
||||
const backdrop = modal?.querySelector('.consent-modal-backdrop');
|
||||
const panel = modal?.querySelector('.consent-modal-panel');
|
||||
|
||||
const analyticsCheckbox = document.getElementById('standalone-analytics') as HTMLInputElement;
|
||||
const marketingCheckbox = document.getElementById('standalone-marketing') as HTMLInputElement;
|
||||
|
||||
const btnSave = document.getElementById('consent-modal-save');
|
||||
const btnClose = document.getElementById('consent-modal-close');
|
||||
|
||||
// Open modal function (expose globally)
|
||||
(window as any).openConsentModal = function() {
|
||||
modal?.classList.remove('hidden');
|
||||
// Sync with existing preferences
|
||||
const existing = localStorage.getItem(CONSENT_KEY);
|
||||
if (existing) {
|
||||
try {
|
||||
const prefs = JSON.parse(existing);
|
||||
if (analyticsCheckbox) analyticsCheckbox.checked = prefs.analytics || false;
|
||||
if (marketingCheckbox) marketingCheckbox.checked = prefs.marketing || false;
|
||||
} catch (e) {
|
||||
// ignore parse errors
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Close modal function
|
||||
function closeModal() {
|
||||
modal?.classList.add('hidden');
|
||||
}
|
||||
|
||||
// Save preferences
|
||||
async function savePreferences() {
|
||||
const consentData = {
|
||||
essential: true,
|
||||
analytics: analyticsCheckbox?.checked || false,
|
||||
marketing: marketingCheckbox?.checked || false,
|
||||
timestamp: new Date().toISOString()
|
||||
};
|
||||
|
||||
localStorage.setItem(CONSENT_KEY, JSON.stringify(consentData));
|
||||
|
||||
try {
|
||||
await fetch('/api/consent', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
sessionId: crypto.randomUUID(),
|
||||
essential: consentData.essential,
|
||||
analytics: consentData.analytics,
|
||||
marketing: consentData.marketing,
|
||||
policyVersion: '1.0',
|
||||
locale: 'th'
|
||||
})
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Failed to save consent:', error);
|
||||
}
|
||||
|
||||
closeModal();
|
||||
|
||||
// Dispatch event for other components to listen
|
||||
window.dispatchEvent(new CustomEvent('consent-updated', { detail: consentData }));
|
||||
}
|
||||
|
||||
// Event listeners
|
||||
btnSave?.addEventListener('click', savePreferences);
|
||||
btnClose?.addEventListener('click', closeModal);
|
||||
backdrop?.addEventListener('click', closeModal);
|
||||
|
||||
document.addEventListener('keydown', (e) => {
|
||||
if (e.key === 'Escape' && !modal?.classList.contains('hidden')) {
|
||||
closeModal();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
347
src/components/consent/CookieBanner.astro
Normal file
347
src/components/consent/CookieBanner.astro
Normal file
@@ -0,0 +1,347 @@
|
||||
---
|
||||
// CookieBanner.astro - Simple cookie consent banner with Tailwind CSS
|
||||
---
|
||||
|
||||
<div
|
||||
id="cookie-banner"
|
||||
class="fixed bottom-0 left-0 right-0 z-50 bg-white dark:bg-gray-800 border-t border-gray-200 dark:border-gray-700 shadow-lg transform translate-y-full transition-transform duration-300"
|
||||
>
|
||||
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-6">
|
||||
<!-- Title -->
|
||||
<h2 class="text-lg font-bold text-gray-900 dark:text-white mb-2">
|
||||
เราใช้คุกกี้
|
||||
</h2>
|
||||
|
||||
<!-- Description -->
|
||||
<p class="text-base text-gray-600 dark:text-gray-300 mb-4">
|
||||
เราใช้คุกกี้เพื่อปรับปรุงประสบการณ์การใช้งานของคุณ และวิเคราะห์การใช้งานเว็บไซต์
|
||||
</p>
|
||||
|
||||
<!-- Checkboxes -->
|
||||
<div class="space-y-3 mb-6">
|
||||
<!-- Essential -->
|
||||
<label class="flex items-start gap-3 p-3 rounded-lg border border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-700">
|
||||
<input
|
||||
type="checkbox"
|
||||
name="consent-essential"
|
||||
checked
|
||||
disabled
|
||||
class="mt-1 w-5 h-5 rounded border-gray-300 text-secondary focus:ring-secondary"
|
||||
/>
|
||||
<div class="flex-1">
|
||||
<span class="block font-semibold text-gray-900 dark:text-white text-base">
|
||||
คุกกี้ที่จำเป็น
|
||||
</span>
|
||||
<span class="block text-sm text-gray-600 dark:text-gray-400 mt-1">
|
||||
จำเป็นสำหรับการทำงานของเว็บไซต์ ไม่สามารถปิดใช้งานได้
|
||||
</span>
|
||||
</div>
|
||||
</label>
|
||||
|
||||
<!-- Analytics -->
|
||||
<label class="flex items-start gap-3 p-3 rounded-lg border border-gray-200 dark:border-gray-700 hover:bg-gray-50 dark:hover:bg-gray-700 transition cursor-pointer">
|
||||
<input
|
||||
type="checkbox"
|
||||
name="consent-analytics"
|
||||
id="banner-analytics"
|
||||
class="mt-1 w-5 h-5 rounded border-gray-300 text-secondary focus:ring-secondary"
|
||||
/>
|
||||
<div class="flex-1">
|
||||
<span class="block font-semibold text-gray-900 dark:text-white text-base">
|
||||
คุกกี้วิเคราะห์ข้อมูล
|
||||
</span>
|
||||
<span class="block text-sm text-gray-600 dark:text-gray-400 mt-1">
|
||||
ช่วยเราเข้าใจว่าผู้เยี่ยมชมใช้งานเว็บไซต์ (Umami Analytics)
|
||||
</span>
|
||||
</div>
|
||||
</label>
|
||||
|
||||
<!-- Marketing -->
|
||||
<label class="flex items-start gap-3 p-3 rounded-lg border border-gray-200 dark:border-gray-700 hover:bg-gray-50 dark:hover:bg-gray-700 transition cursor-pointer">
|
||||
<input
|
||||
type="checkbox"
|
||||
name="consent-marketing"
|
||||
id="banner-marketing"
|
||||
class="mt-1 w-5 h-5 rounded border-gray-300 text-secondary focus:ring-secondary"
|
||||
/>
|
||||
<div class="flex-1">
|
||||
<span class="block font-semibold text-gray-900 dark:text-white text-base">
|
||||
คุกกี้การตลาด
|
||||
</span>
|
||||
<span class="block text-sm text-gray-600 dark:text-gray-400 mt-1">
|
||||
ใช้สำหรับติดตามและแสดงโฆษณาที่ตรงกับความสนใจของคุณ
|
||||
</span>
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<!-- Buttons -->
|
||||
<div class="flex flex-col sm:flex-row gap-3">
|
||||
<button
|
||||
id="btn-accept-all"
|
||||
class="flex-1 px-6 py-3 bg-secondary hover:bg-secondary-hover text-white rounded-lg font-medium text-base transition focus:outline-none focus:ring-2 focus:ring-secondary focus:ring-offset-2"
|
||||
>
|
||||
ยอมรับทั้งหมด
|
||||
</button>
|
||||
<button
|
||||
id="btn-reject-all"
|
||||
class="flex-1 px-6 py-3 bg-gray-200 dark:bg-gray-700 text-gray-900 dark:text-white rounded-lg font-medium text-base transition hover:bg-gray-300 dark:hover:bg-gray-600 focus:outline-none focus:ring-2 focus:ring-gray-400 focus:ring-offset-2"
|
||||
>
|
||||
ปฏิเสธ
|
||||
</button>
|
||||
<button
|
||||
id="btn-customize"
|
||||
class="flex-1 px-6 py-3 bg-primary hover:bg-primary-hover text-white rounded-lg font-medium text-base transition focus:outline-none focus:ring-2 focus:ring-primary focus:ring-offset-2"
|
||||
>
|
||||
ปรับแต่ง
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Consent Modal -->
|
||||
<div
|
||||
id="consent-modal"
|
||||
class="fixed inset-0 z-50 hidden"
|
||||
aria-labelledby="modal-title"
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
>
|
||||
<!-- Backdrop -->
|
||||
<div class="fixed inset-0 bg-black bg-opacity-50 transition-opacity" id="modal-backdrop"></div>
|
||||
|
||||
<!-- Modal Panel -->
|
||||
<div class="fixed inset-0 z-10 overflow-y-auto">
|
||||
<div class="flex min-h-full items-center justify-center p-4">
|
||||
<div
|
||||
id="modal-panel"
|
||||
class="relative transform overflow-hidden rounded-xl bg-white dark:bg-gray-800 shadow-2xl transition-all max-w-lg w-full max-h-[90vh] overflow-y-auto"
|
||||
>
|
||||
<!-- Header -->
|
||||
<div class="px-6 py-4 border-b border-gray-200 dark:border-gray-700">
|
||||
<h3 id="modal-title" class="text-xl font-bold text-gray-900 dark:text-white">
|
||||
การตั้งค่าคุกกี้
|
||||
</h3>
|
||||
<p class="text-base text-gray-600 dark:text-gray-300 mt-2">
|
||||
จัดการการตั้งค่าคุกกี้ของคุณที่นี่
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Body -->
|
||||
<div class="px-6 py-4 space-y-4">
|
||||
<!-- Essential -->
|
||||
<div class="flex items-start gap-3 p-3 rounded-lg border border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-700">
|
||||
<input
|
||||
type="checkbox"
|
||||
name="modal-essential"
|
||||
checked
|
||||
disabled
|
||||
class="mt-1 w-5 h-5 rounded border-gray-300 text-secondary focus:ring-secondary"
|
||||
/>
|
||||
<div class="flex-1">
|
||||
<span class="block font-semibold text-gray-900 dark:text-white text-base">
|
||||
คุกกี้ที่จำเป็น
|
||||
</span>
|
||||
<span class="block text-sm text-gray-600 dark:text-gray-400 mt-1">
|
||||
จำเป็นสำหรับการทำงานของเว็บไซต์ ไม่สามารถปิดใช้งานได้
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Analytics -->
|
||||
<label class="flex items-start gap-3 p-3 rounded-lg border border-gray-200 dark:border-gray-700 hover:bg-gray-50 dark:hover:bg-gray-700 transition cursor-pointer">
|
||||
<input
|
||||
type="checkbox"
|
||||
name="modal-analytics"
|
||||
id="modal-analytics"
|
||||
class="mt-1 w-5 h-5 rounded border-gray-300 text-secondary focus:ring-secondary"
|
||||
/>
|
||||
<div class="flex-1">
|
||||
<span class="block font-semibold text-gray-900 dark:text-white text-base">
|
||||
คุกกี้วิเคราะห์ข้อมูล
|
||||
</span>
|
||||
<span class="block text-sm text-gray-600 dark:text-gray-400 mt-1">
|
||||
ช่วยเราเข้าใจว่าผู้เยี่ยมชมใช้งานเว็บไซต์ (Umami Analytics)
|
||||
</span>
|
||||
</div>
|
||||
</label>
|
||||
|
||||
<!-- Marketing -->
|
||||
<label class="flex items-start gap-3 p-3 rounded-lg border border-gray-200 dark:border-gray-700 hover:bg-gray-50 dark:hover:bg-gray-700 transition cursor-pointer">
|
||||
<input
|
||||
type="checkbox"
|
||||
name="modal-marketing"
|
||||
id="modal-marketing"
|
||||
class="mt-1 w-5 h-5 rounded border-gray-300 text-secondary focus:ring-secondary"
|
||||
/>
|
||||
<div class="flex-1">
|
||||
<span class="block font-semibold text-gray-900 dark:text-white text-base">
|
||||
คุกกี้การตลาด
|
||||
</span>
|
||||
<span class="block text-sm text-gray-600 dark:text-gray-400 mt-1">
|
||||
ใช้สำหรับติดตามและแสดงโฆษณาที่ตรงกับความสนใจของคุณ
|
||||
</span>
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<!-- Footer -->
|
||||
<div class="px-6 py-4 border-t border-gray-200 dark:border-gray-700 flex gap-3">
|
||||
<button
|
||||
id="btn-save-preferences"
|
||||
class="flex-1 px-6 py-3 bg-secondary hover:bg-secondary-hover text-white rounded-lg font-medium text-base transition focus:outline-none focus:ring-2 focus:ring-secondary focus:ring-offset-2"
|
||||
>
|
||||
บันทึกการตั้งค่า
|
||||
</button>
|
||||
<button
|
||||
id="btn-close-modal"
|
||||
class="flex-1 px-6 py-3 bg-gray-200 dark:bg-gray-700 text-gray-900 dark:text-white rounded-lg font-medium text-base transition hover:bg-gray-300 dark:hover:bg-gray-600 focus:outline-none focus:ring-2 focus:ring-gray-400 focus:ring-offset-2"
|
||||
>
|
||||
ปิด
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// Consent Manager
|
||||
const CONSENT_KEY = 'consent-preferences';
|
||||
|
||||
// Get elements
|
||||
const banner = document.getElementById('cookie-banner');
|
||||
const modal = document.getElementById('consent-modal');
|
||||
const modalBackdrop = document.getElementById('modal-backdrop');
|
||||
const modalPanel = document.getElementById('modal-panel');
|
||||
|
||||
// Banner checkboxes
|
||||
const bannerAnalytics = document.getElementById('banner-analytics') as HTMLInputElement;
|
||||
const bannerMarketing = document.getElementById('banner-marketing') as HTMLInputElement;
|
||||
|
||||
// Modal checkboxes
|
||||
const modalAnalytics = document.getElementById('modal-analytics') as HTMLInputElement;
|
||||
const modalMarketing = document.getElementById('modal-marketing') as HTMLInputElement;
|
||||
|
||||
// Buttons
|
||||
const btnAcceptAll = document.getElementById('btn-accept-all');
|
||||
const btnRejectAll = document.getElementById('btn-reject-all');
|
||||
const btnCustomize = document.getElementById('btn-customize');
|
||||
const btnSavePreferences = document.getElementById('btn-save-preferences');
|
||||
const btnCloseModal = document.getElementById('btn-close-modal');
|
||||
|
||||
// Save consent to localStorage and POST to API
|
||||
async function saveConsent(preferences: { essential: boolean; analytics: boolean; marketing: boolean }) {
|
||||
const consentData = {
|
||||
...preferences,
|
||||
timestamp: new Date().toISOString()
|
||||
};
|
||||
|
||||
// Save to localStorage
|
||||
localStorage.setItem(CONSENT_KEY, JSON.stringify(consentData));
|
||||
|
||||
// POST to API
|
||||
try {
|
||||
await fetch('/api/consent', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
sessionId: crypto.randomUUID(),
|
||||
essential: preferences.essential,
|
||||
analytics: preferences.analytics,
|
||||
marketing: preferences.marketing,
|
||||
policyVersion: '1.0',
|
||||
locale: 'th'
|
||||
})
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Failed to save consent:', error);
|
||||
}
|
||||
|
||||
// Hide banner
|
||||
hideBanner();
|
||||
}
|
||||
|
||||
// Show banner
|
||||
function showBanner() {
|
||||
banner?.classList.remove('translate-y-full');
|
||||
}
|
||||
|
||||
// Hide banner
|
||||
function hideBanner() {
|
||||
banner?.classList.add('translate-y-full');
|
||||
}
|
||||
|
||||
// Show modal
|
||||
function showModal() {
|
||||
modal?.classList.remove('hidden');
|
||||
// Sync checkbox states from banner
|
||||
if (modalAnalytics) modalAnalytics.checked = bannerAnalytics?.checked || false;
|
||||
if (modalMarketing) modalMarketing.checked = bannerMarketing?.checked || false;
|
||||
}
|
||||
|
||||
// Hide modal
|
||||
function hideModal() {
|
||||
modal?.classList.add('hidden');
|
||||
}
|
||||
|
||||
// Check if consent already exists
|
||||
function hasConsent(): boolean {
|
||||
return localStorage.getItem(CONSENT_KEY) !== null;
|
||||
}
|
||||
|
||||
// Initialize
|
||||
function init() {
|
||||
if (!hasConsent()) {
|
||||
showBanner();
|
||||
}
|
||||
}
|
||||
|
||||
// Event Listeners
|
||||
btnAcceptAll?.addEventListener('click', () => {
|
||||
saveConsent({
|
||||
essential: true,
|
||||
analytics: true,
|
||||
marketing: true
|
||||
});
|
||||
});
|
||||
|
||||
btnRejectAll?.addEventListener('click', () => {
|
||||
saveConsent({
|
||||
essential: true,
|
||||
analytics: false,
|
||||
marketing: false
|
||||
});
|
||||
});
|
||||
|
||||
btnCustomize?.addEventListener('click', () => {
|
||||
showModal();
|
||||
});
|
||||
|
||||
btnSavePreferences?.addEventListener('click', () => {
|
||||
saveConsent({
|
||||
essential: true,
|
||||
analytics: modalAnalytics?.checked || false,
|
||||
marketing: modalMarketing?.checked || false
|
||||
});
|
||||
hideModal();
|
||||
});
|
||||
|
||||
btnCloseModal?.addEventListener('click', () => {
|
||||
hideModal();
|
||||
});
|
||||
|
||||
modalBackdrop?.addEventListener('click', () => {
|
||||
hideModal();
|
||||
});
|
||||
|
||||
// Close modal on escape key
|
||||
document.addEventListener('keydown', (e) => {
|
||||
if (e.key === 'Escape' && !modal?.classList.contains('hidden')) {
|
||||
hideModal();
|
||||
}
|
||||
});
|
||||
|
||||
// Run init on DOM ready
|
||||
init();
|
||||
</script>
|
||||
Reference in New Issue
Block a user