Files
autocrm/resources/views/partials/cookie-banner.blade.php
T
Vasyka c3e4b99d4b feat: complete cookie consent with categories & technical processing
- 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>
2026-09-17 20:39:13 +00:00

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