c3e4b99d4b
- Full 4-category consent modal: Necesare (locked) / Funcționale / Analitice / Marketing with toggle switches per category - Banner shows simple view first; "Personalizează" opens detailed panel - Consent stored as JSON in localStorage (cookie_consent_v2) - window.cookieConsent object set on load; dispatches cookie-consent-updated / cookie-analytics-granted / cookie-marketing-granted CustomEvents for deferred script loading - Floating 🍪 FAB button lets users re-open settings at any time - Policy page: "Personalizează" button resets consent, reopens modal - Full RO/RU translation for all categories and UI strings Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
211 lines
10 KiB
PHP
211 lines
10 KiB
PHP
{{-- Full cookie consent — GDPR/Legea 195/2024 compliant --}}
|
|
@php
|
|
$bannerEnabled = data_get($tenant->settings ?? [], 'cookie.banner_enabled', true);
|
|
$tc = $themeColor ?? '#3B82F6';
|
|
@endphp
|
|
@if ($bannerEnabled)
|
|
<style>
|
|
[x-cloak]{display:none!important}
|
|
.ck-overlay{position:fixed;inset:0;background:rgba(0,0,0,.5);z-index:10000;display:flex;align-items:flex-end;justify-content:center;padding:0}
|
|
@media(min-width:600px){.ck-overlay{align-items:center;padding:20px}}
|
|
.ck-modal{background:#fff;width:100%;max-width:560px;border-radius:16px 16px 0 0;padding:24px 20px 20px;max-height:90vh;overflow-y:auto;box-shadow:0 -8px 40px rgba(0,0,0,.18);font-family:system-ui,-apple-system,'Segoe UI',sans-serif}
|
|
@media(min-width:600px){.ck-modal{border-radius:16px;max-height:80vh}}
|
|
.ck-modal h2{font-size:17px;font-weight:700;color:#111827;margin-bottom:6px}
|
|
.ck-modal .ck-intro{font-size:13px;color:#6b7280;margin-bottom:16px;line-height:1.5}
|
|
.ck-cat{border:1px solid #e5e7eb;border-radius:10px;padding:14px 14px 12px;margin-bottom:8px}
|
|
.ck-cat-head{display:flex;align-items:center;justify-content:space-between;gap:10px}
|
|
.ck-cat-head label{font-size:14px;font-weight:600;color:#111827;cursor:pointer;flex:1}
|
|
.ck-cat p{font-size:12px;color:#6b7280;margin-top:8px;line-height:1.5}
|
|
.ck-toggle{position:relative;width:40px;height:22px;flex-shrink:0}
|
|
.ck-toggle input{opacity:0;width:0;height:0;position:absolute}
|
|
.ck-slider{position:absolute;inset:0;border-radius:22px;background:#d1d5db;transition:background .2s;cursor:pointer}
|
|
.ck-slider:before{content:'';position:absolute;width:16px;height:16px;left:3px;top:3px;border-radius:50%;background:#fff;transition:transform .2s;box-shadow:0 1px 3px rgba(0,0,0,.2)}
|
|
input:checked+.ck-slider{background:{{ $tc }}}
|
|
input:checked+.ck-slider:before{transform:translateX(18px)}
|
|
input:disabled+.ck-slider{opacity:.6;cursor:default}
|
|
.ck-badge{font-size:11px;background:#f3f4f6;color:#6b7280;border-radius:4px;padding:1px 7px;font-weight:500}
|
|
.ck-badge.always{background:#dcfce7;color:#166534}
|
|
.ck-btns{display:flex;gap:8px;margin-top:16px;flex-wrap:wrap}
|
|
.ck-btn{flex:1;min-width:120px;padding:10px 12px;border-radius:8px;font-size:13px;font-weight:600;cursor:pointer;border:none;white-space:nowrap;text-align:center}
|
|
.ck-btn-primary{background:{{ $tc }};color:#fff}
|
|
.ck-btn-secondary{background:#f3f4f6;color:#374151;border:1px solid #e5e7eb}
|
|
.ck-btn-ghost{background:transparent;color:#6b7280;border:1px solid #d1d5db}
|
|
.ck-link{color:{{ $tc }};text-decoration:none;font-size:12px}
|
|
.ck-link:hover{opacity:.8}
|
|
.ck-footer{display:flex;justify-content:space-between;align-items:center;margin-top:14px;padding-top:12px;border-top:1px solid #f3f4f6;flex-wrap:wrap;gap:8px}
|
|
/* Floating re-open button */
|
|
.ck-fab{position:fixed;bottom:16px;left:16px;z-index:9000;background:#fff;border:1px solid #e5e7eb;border-radius:50%;width:44px;height:44px;display:flex;align-items:center;justify-content:center;cursor:pointer;box-shadow:0 2px 8px rgba(0,0,0,.12);font-size:20px;transition:box-shadow .15s}
|
|
.ck-fab:hover{box-shadow:0 4px 16px rgba(0,0,0,.18)}
|
|
@media(prefers-color-scheme:dark){
|
|
.ck-modal{background:#1e293b;color:#f1f5f9}
|
|
.ck-modal h2{color:#f1f5f9}
|
|
.ck-modal .ck-intro{color:#94a3b8}
|
|
.ck-cat{border-color:#334155}
|
|
.ck-cat-head label{color:#f1f5f9}
|
|
.ck-cat p{color:#94a3b8}
|
|
.ck-slider{background:#475569}
|
|
.ck-badge{background:#334155;color:#94a3b8}
|
|
.ck-badge.always{background:#14532d;color:#86efac}
|
|
.ck-btn-secondary{background:#334155;color:#f1f5f9;border-color:#475569}
|
|
.ck-btn-ghost{color:#94a3b8;border-color:#475569}
|
|
.ck-footer{border-color:#334155}
|
|
.ck-fab{background:#1e293b;border-color:#334155;color:#f1f5f9}
|
|
}
|
|
</style>
|
|
|
|
<div
|
|
x-data="cookieConsent()"
|
|
x-init="init()"
|
|
>
|
|
{{-- Backdrop + Modal --}}
|
|
<div class="ck-overlay" x-show="open" x-cloak @click.self="open=false">
|
|
<div class="ck-modal" @click.stop>
|
|
<div x-show="!showDetails">
|
|
{{-- Simple banner view --}}
|
|
<h2>🍪 {{ __('portal.cookie.banner_title') }}</h2>
|
|
<p class="ck-intro">
|
|
{{ __('portal.cookie.banner_body') }}
|
|
<a href="{{ route('cookie.policy') }}" class="ck-link" target="_blank" rel="noopener">{{ __('portal.cookie.read_policy') }} →</a>
|
|
</p>
|
|
<div class="ck-btns">
|
|
<button class="ck-btn ck-btn-primary" @click="acceptAll()">{{ __('portal.cookie.accept') }}</button>
|
|
<button class="ck-btn ck-btn-secondary" @click="showDetails=true">{{ __('portal.cookie.customize') }}</button>
|
|
<button class="ck-btn ck-btn-ghost" @click="rejectAll()">{{ __('portal.cookie.reject') }}</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div x-show="showDetails" x-cloak>
|
|
{{-- Detailed categories view --}}
|
|
<h2>{{ __('portal.cookie.settings_title') }}</h2>
|
|
<p class="ck-intro">{{ __('portal.cookie.settings_body') }}</p>
|
|
|
|
{{-- Necessary --}}
|
|
<div class="ck-cat">
|
|
<div class="ck-cat-head">
|
|
<label>{{ __('portal.cookie.cat_necessary') }}</label>
|
|
<span class="ck-badge always">{{ __('portal.cookie.always_on') }}</span>
|
|
<label class="ck-toggle">
|
|
<input type="checkbox" checked disabled>
|
|
<span class="ck-slider"></span>
|
|
</label>
|
|
</div>
|
|
<p>{{ __('portal.cookie.cat_necessary_desc') }}</p>
|
|
</div>
|
|
|
|
{{-- Functional --}}
|
|
<div class="ck-cat">
|
|
<div class="ck-cat-head">
|
|
<label for="ck_func">{{ __('portal.cookie.cat_functional') }}</label>
|
|
<label class="ck-toggle">
|
|
<input id="ck_func" type="checkbox" x-model="consent.functional">
|
|
<span class="ck-slider"></span>
|
|
</label>
|
|
</div>
|
|
<p>{{ __('portal.cookie.cat_functional_desc') }}</p>
|
|
</div>
|
|
|
|
{{-- Analytics --}}
|
|
<div class="ck-cat">
|
|
<div class="ck-cat-head">
|
|
<label for="ck_anal">{{ __('portal.cookie.cat_analytics') }}</label>
|
|
<label class="ck-toggle">
|
|
<input id="ck_anal" type="checkbox" x-model="consent.analytics">
|
|
<span class="ck-slider"></span>
|
|
</label>
|
|
</div>
|
|
<p>{{ __('portal.cookie.cat_analytics_desc') }}</p>
|
|
</div>
|
|
|
|
{{-- Marketing --}}
|
|
<div class="ck-cat">
|
|
<div class="ck-cat-head">
|
|
<label for="ck_mktg">{{ __('portal.cookie.cat_marketing') }}</label>
|
|
<label class="ck-toggle">
|
|
<input id="ck_mktg" type="checkbox" x-model="consent.marketing">
|
|
<span class="ck-slider"></span>
|
|
</label>
|
|
</div>
|
|
<p>{{ __('portal.cookie.cat_marketing_desc') }}</p>
|
|
</div>
|
|
|
|
<div class="ck-btns">
|
|
<button class="ck-btn ck-btn-primary" @click="saveSettings()">{{ __('portal.cookie.save_settings') }}</button>
|
|
<button class="ck-btn ck-btn-secondary" @click="acceptAll()">{{ __('portal.cookie.accept') }}</button>
|
|
</div>
|
|
|
|
<div class="ck-footer">
|
|
<a href="{{ route('cookie.policy') }}" class="ck-link" target="_blank" rel="noopener">{{ __('portal.cookie.read_policy') }}</a>
|
|
<button class="ck-btn-ghost" style="border:none;background:transparent;font-size:12px;color:#6b7280;cursor:pointer;" @click="showDetails=false">← {{ __('portal.cookie.back') }}</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{{-- Floating re-open button (shown after consent given) --}}
|
|
<button class="ck-fab" x-show="!open && resolved" x-cloak @click="open=true;showDetails=true" title="{{ __('portal.cookie.manage_title') }}">🍪</button>
|
|
</div>
|
|
|
|
<script>
|
|
function cookieConsent() {
|
|
return {
|
|
open: false,
|
|
resolved: false,
|
|
showDetails: false,
|
|
consent: { necessary: true, functional: true, analytics: false, marketing: false },
|
|
|
|
init() {
|
|
const saved = localStorage.getItem('cookie_consent_v2');
|
|
if (saved) {
|
|
try {
|
|
const data = JSON.parse(saved);
|
|
this.consent = { necessary: true, ...data };
|
|
this.resolved = true;
|
|
this._apply();
|
|
} catch(e) {
|
|
this.open = true;
|
|
}
|
|
} else {
|
|
// Small delay so page content loads first
|
|
setTimeout(() => { this.open = true; }, 600);
|
|
}
|
|
},
|
|
|
|
acceptAll() {
|
|
this.consent = { necessary: true, functional: true, analytics: true, marketing: true };
|
|
this._save();
|
|
},
|
|
|
|
rejectAll() {
|
|
this.consent = { necessary: true, functional: false, analytics: false, marketing: false };
|
|
this._save();
|
|
},
|
|
|
|
saveSettings() {
|
|
this.consent.necessary = true;
|
|
this._save();
|
|
this.showDetails = false;
|
|
},
|
|
|
|
_save() {
|
|
localStorage.setItem('cookie_consent_v2', JSON.stringify(this.consent));
|
|
this.resolved = true;
|
|
this.open = false;
|
|
this._apply();
|
|
},
|
|
|
|
_apply() {
|
|
window.cookieConsent = { ...this.consent };
|
|
window.dispatchEvent(new CustomEvent('cookie-consent-updated', { detail: { ...this.consent } }));
|
|
// Load deferred analytics/marketing scripts based on consent
|
|
if (this.consent.analytics) {
|
|
document.dispatchEvent(new CustomEvent('cookie-analytics-granted'));
|
|
}
|
|
if (this.consent.marketing) {
|
|
document.dispatchEvent(new CustomEvent('cookie-marketing-granted'));
|
|
}
|
|
}
|
|
};
|
|
}
|
|
</script>
|
|
@endif
|