chore: Add PDPA dependencies
- astro-consent: Cookie consent management - @libsql/client: SQLite database driver - drizzle-orm: Database ORM for consent logging Required for full PDPA compliance features.
This commit is contained in:
247
dealplustech-astro/src/components/CookieConsentBanner.astro
Normal file
247
dealplustech-astro/src/components/CookieConsentBanner.astro
Normal file
@@ -0,0 +1,247 @@
|
||||
---
|
||||
// Cookie Consent Banner Component - PDPA Compliant
|
||||
// Displays on first visit, allows users to accept/reject cookie categories
|
||||
---
|
||||
|
||||
<div
|
||||
id="cookie-consent-banner"
|
||||
class="fixed bottom-0 left-0 right-0 z-50 bg-white border-t-2 border-primary-600 shadow-2xl p-6 md:p-8 transform translate-y-full transition-transform duration-300 ease-in-out"
|
||||
role="region"
|
||||
aria-labelledby="consent-title"
|
||||
aria-describedby="consent-description"
|
||||
style="display: none;"
|
||||
>
|
||||
<div class="container mx-auto px-4 max-w-7xl">
|
||||
<div class="flex flex-col lg:flex-row gap-6 items-start lg:items-center justify-between">
|
||||
<!-- Consent Content -->
|
||||
<div class="flex-1">
|
||||
<h2 id="consent-title" class="text-xl md:text-2xl font-bold text-secondary-900 mb-3">
|
||||
เรายึดถือความเป็นส่วนตัวของคุณ
|
||||
</h2>
|
||||
<p id="consent-description" class="text-base md:text-lg text-secondary-700 leading-relaxed mb-4">
|
||||
เราใช้คุกกี้เพื่อปรับปรุงประสบการณ์การใช้งาน วิเคราะห์การเข้าใช้งาน และแสดงเนื้อหาที่ตรงใจคุณ
|
||||
คุณสามารถเลือกยอมรับหรือปฏิเสธคุกกี้ที่ไม่จำเป็นได้
|
||||
</p>
|
||||
|
||||
<!-- Cookie Categories -->
|
||||
<div class="space-y-3 mt-4">
|
||||
<!-- Essential Cookies (Always On) -->
|
||||
<div class="flex items-center gap-3 bg-secondary-50 p-3 rounded-lg">
|
||||
<input
|
||||
type="checkbox"
|
||||
id="consent-essential"
|
||||
checked
|
||||
disabled
|
||||
class="w-5 h-5 accent-primary-600 rounded"
|
||||
/>
|
||||
<label for="consent-essential" class="flex-1 cursor-pointer">
|
||||
<span class="font-semibold text-secondary-900">คุกกี้จำเป็น</span>
|
||||
<span class="text-sm text-secondary-600 block">ใช้สำหรับการทำงานของเว็บไซต์ ไม่สามารถปิดได้</span>
|
||||
</label>
|
||||
<span class="text-xs px-2 py-1 bg-primary-600 text-white rounded">จำเป็น</span>
|
||||
</div>
|
||||
|
||||
<!-- Analytics Cookies -->
|
||||
<div class="flex items-center gap-3 bg-secondary-50 p-3 rounded-lg">
|
||||
<input
|
||||
type="checkbox"
|
||||
id="consent-analytics"
|
||||
class="w-5 h-5 accent-primary-600 rounded consent-checkbox"
|
||||
/>
|
||||
<label for="consent-analytics" class="flex-1 cursor-pointer">
|
||||
<span class="font-semibold text-secondary-900">คุกกี้วิเคราะห์ข้อมูล</span>
|
||||
<span class="text-sm text-secondary-600 block">ช่วยให้เราเข้าใจพฤติกรรมการใช้งาน</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<!-- Marketing Cookies -->
|
||||
<div class="flex items-center gap-3 bg-secondary-50 p-3 rounded-lg">
|
||||
<input
|
||||
type="checkbox"
|
||||
id="consent-marketing"
|
||||
class="w-5 h-5 accent-primary-600 rounded consent-checkbox"
|
||||
/>
|
||||
<label for="consent-marketing" class="flex-1 cursor-pointer">
|
||||
<span class="font-semibold text-secondary-900">คุกกี้การตลาด</span>
|
||||
<span class="text-sm text-secondary-600 block">ใช้สำหรับแสดงโฆษณาที่เกี่ยวข้อง</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Action Buttons -->
|
||||
<div class="flex flex-col sm:flex-row gap-3 flex-shrink-0">
|
||||
<button
|
||||
id="consent-reject"
|
||||
type="button"
|
||||
class="bg-secondary-800 hover:bg-secondary-900 text-white px-6 py-3 rounded-lg font-semibold transition-colors"
|
||||
>
|
||||
ปฏิเสธทั้งหมด
|
||||
</button>
|
||||
<button
|
||||
id="consent-accept"
|
||||
type="button"
|
||||
class="bg-primary-600 hover:bg-primary-700 text-white px-6 py-3 rounded-lg font-semibold transition-colors"
|
||||
>
|
||||
ยอมรับทั้งหมด
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Privacy Policy Link -->
|
||||
<div class="mt-6 pt-6 border-t border-secondary-200 text-center">
|
||||
<p class="text-sm text-secondary-600">
|
||||
การใช้งานคุกกี้ของเราเป็นไปตาม
|
||||
<a href="/privacy-policy/" class="text-primary-600 hover:underline font-medium">นโยบายความเป็นส่วนตัว</a>
|
||||
และ
|
||||
<a href="/terms-and-conditions/" class="text-primary-600 hover:underline font-medium">ข้อกำหนดการใช้งาน</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
interface ConsentPreferences {
|
||||
essential: boolean;
|
||||
analytics: boolean;
|
||||
marketing: boolean;
|
||||
timestamp: string;
|
||||
policyVersion: string;
|
||||
}
|
||||
|
||||
const POLICY_VERSION = '1.0.0';
|
||||
const CONSENT_STORAGE_KEY = 'consent-preferences';
|
||||
const CONSENT_LOG_API = '/api/consent';
|
||||
|
||||
function generateSessionId(): string {
|
||||
return 'ses_' + Math.random().toString(36).substring(2, 15) +
|
||||
Math.random().toString(36).substring(2, 15);
|
||||
}
|
||||
|
||||
function getStoredConsent(): ConsentPreferences | null {
|
||||
const stored = localStorage.getItem(CONSENT_STORAGE_KEY);
|
||||
return stored ? JSON.parse(stored) : null;
|
||||
}
|
||||
|
||||
async function saveConsent(consent: ConsentPreferences) {
|
||||
localStorage.setItem(CONSENT_STORAGE_KEY, JSON.stringify(consent));
|
||||
|
||||
// Log consent to database (PDPA requirement)
|
||||
try {
|
||||
let sessionId = sessionStorage.getItem('consent_session_id');
|
||||
if (!sessionId) {
|
||||
sessionId = generateSessionId();
|
||||
sessionStorage.setItem('consent_session_id', sessionId);
|
||||
}
|
||||
|
||||
await fetch(CONSENT_LOG_API, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
sessionId,
|
||||
consent,
|
||||
policyVersion: POLICY_VERSION,
|
||||
}),
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Failed to log consent:', error);
|
||||
}
|
||||
}
|
||||
|
||||
function showBanner() {
|
||||
const banner = document.getElementById('cookie-consent-banner') as HTMLElement;
|
||||
if (banner) {
|
||||
banner.style.display = 'block';
|
||||
setTimeout(() => {
|
||||
banner.classList.remove('translate-y-full');
|
||||
}, 10);
|
||||
}
|
||||
}
|
||||
|
||||
function hideBanner() {
|
||||
const banner = document.getElementById('cookie-consent-banner') as HTMLElement;
|
||||
if (banner) {
|
||||
banner.classList.add('translate-y-full');
|
||||
setTimeout(() => {
|
||||
banner.style.display = 'none';
|
||||
}, 300);
|
||||
}
|
||||
}
|
||||
|
||||
function initConsent() {
|
||||
const stored = getStoredConsent();
|
||||
|
||||
if (stored) {
|
||||
// Consent already given - restore checkboxes
|
||||
const analyticsCheckbox = document.getElementById('consent-analytics') as HTMLInputElement;
|
||||
const marketingCheckbox = document.getElementById('consent-marketing') as HTMLInputElement;
|
||||
|
||||
if (analyticsCheckbox) analyticsCheckbox.checked = stored.analytics;
|
||||
if (marketingCheckbox) marketingCheckbox.checked = stored.marketing;
|
||||
|
||||
// Load Umami if consented
|
||||
if (stored.analytics && import.meta.env.PUBLIC_UMAMI_WEBSITE_ID) {
|
||||
const script = document.createElement('script');
|
||||
script.defer = true;
|
||||
script.src = 'https://analytics.moreminimore.com/script.js';
|
||||
script.setAttribute('data-website-id', import.meta.env.PUBLIC_UMAMI_WEBSITE_ID);
|
||||
document.head.appendChild(script);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// First visit - show banner after delay
|
||||
setTimeout(showBanner, 500);
|
||||
}
|
||||
|
||||
function handleAccept() {
|
||||
const analytics = (document.getElementById('consent-analytics') as HTMLInputElement)?.checked ?? false;
|
||||
const marketing = (document.getElementById('consent-marketing') as HTMLInputElement)?.checked ?? false;
|
||||
|
||||
const consent: ConsentPreferences = {
|
||||
essential: true,
|
||||
analytics: analytics || true,
|
||||
marketing: marketing || true,
|
||||
timestamp: new Date().toISOString(),
|
||||
policyVersion: POLICY_VERSION,
|
||||
};
|
||||
|
||||
saveConsent(consent);
|
||||
hideBanner();
|
||||
}
|
||||
|
||||
function handleReject() {
|
||||
const consent: ConsentPreferences = {
|
||||
essential: true,
|
||||
analytics: false,
|
||||
marketing: false,
|
||||
timestamp: new Date().toISOString(),
|
||||
policyVersion: POLICY_VERSION,
|
||||
};
|
||||
|
||||
saveConsent(consent);
|
||||
hideBanner();
|
||||
}
|
||||
|
||||
// Event listeners
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
initConsent();
|
||||
|
||||
document.getElementById('consent-accept')?.addEventListener('click', handleAccept);
|
||||
document.getElementById('consent-reject')?.addEventListener('click', handleReject);
|
||||
});
|
||||
|
||||
// Expose function for footer link
|
||||
(window as any).openConsentPreferences = () => {
|
||||
showBanner();
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
/* Cookie consent banner styles */
|
||||
#cookie-consent-banner {
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user