From 28d38156e26975b3eac83e822d3fc04c83799e75 Mon Sep 17 00:00:00 2001 From: Jacek Pyziak Date: Sun, 26 Apr 2026 22:50:45 +0200 Subject: [PATCH] feat(03-cookie-consent): integrate CookieNoticePro with Google Consent Mode v2 Phase 3 complete: - Copy CookieNoticePro library to libraries/CookieNoticePro/ - Fix bug: analytics_storage sent 'granted' instead of 'denied' on rejection - Add Consent Mode v2 default-denied init before GTM snippet in layout-logged.php - Fix plugin init: cookieNoticePro.init() per library docs - Fix duplicate button label (CSS display:none for acceptBtnSettingsLabel) - Add Bootstrap 5 compatibility reset for banner buttons - Set regulamin link to https://bilety.brzezovka.pl/tickets/regulamin/ v0.2 Analytics & Privacy milestone complete. Co-Authored-By: Claude Sonnet 4.6 --- .paul/PROJECT.md | 8 +- .paul/ROADMAP.md | 6 +- .paul/STATE.md | 49 +- .paul/changelog/2026-04-26.md | 15 + .paul/phases/03-cookie-consent/03-01-PLAN.md | 227 +++++++ .../phases/03-cookie-consent/03-01-SUMMARY.md | 134 +++++ .../CookieNoticePro/cookienoticepro.script.js | 438 ++++++++++++++ .../CookieNoticePro/cookienoticepro.style.css | 560 ++++++++++++++++++ templates/site/layout-logged.php | 20 + 9 files changed, 1429 insertions(+), 28 deletions(-) create mode 100644 .paul/phases/03-cookie-consent/03-01-PLAN.md create mode 100644 .paul/phases/03-cookie-consent/03-01-SUMMARY.md create mode 100644 libraries/CookieNoticePro/cookienoticepro.script.js create mode 100644 libraries/CookieNoticePro/cookienoticepro.style.css diff --git a/.paul/PROJECT.md b/.paul/PROJECT.md index 6bd2ca7..2d8dcc9 100644 --- a/.paul/PROJECT.md +++ b/.paul/PROJECT.md @@ -13,7 +13,7 @@ Uzytkownicy moga szybko i bezpiecznie kupic bilety online oraz otrzymac natychmi - [x] Poprawne rejestrowanie zamowien i danych transakcyjnych - [x] Data layer purchase po finalizacji zakupu (wdrozone w Phase 1) - [x] Event purchase capturuje 100% zamowien — fires przy skladaniu, nie przy powrocie z P24 (Phase 2) -- [ ] Zgodnosc z RODO — baner zgody na cookies z Google Consent Mode v2 (Phase 3) +- [x] Zgodnosc z RODO — baner zgody na cookies z Google Consent Mode v2 (Phase 3) ### Should Have - [x] Spojny tracking analityczny dla zdarzen ecommerce @@ -34,13 +34,15 @@ Uzytkownicy moga szybko i bezpiecznie kupic bilety online oraz otrzymac natychmi | Event purchase na order-confirm (post-payment) | Phase 1 | Eliminuje falszywe konwersje | | ZMIANA: Event purchase na przelewy24 (pre-payment, post-order) | Phase 2 | Capturuje 100% zamowien, beacon transport | | Payload ecommerce budowany w backendzie | Phase 1 | Bezpieczenstwo, brak XSS | +| cookieNoticePro.init() zamiast $.fn.cookieNoticePro() | Phase 3 | Zgodnie z dokumentacja biblioteki | +| Consent Mode v2 default-denied przed GTM snippetem | Phase 3 | Wymog Google od marca 2024 | ## Success Criteria - Event purchase trafia do data layer po zlozeniu zamowienia (osiagniete — Phase 2) - Payload zawiera wszystkie dostepne dane transakcyjne i produktowe - Integracja nie wplywa negatywnie na istniejacy checkout -- Baner cookies zgodny z RODO + Google Consent Mode v2 (cel Phase 3) +- Baner cookies zgodny z RODO + Google Consent Mode v2 (osiagniete — Phase 3) --- *Created: 2026-04-19 20:20* -*Last updated: 2026-04-26 after Phase 2* +*Last updated: 2026-04-26 after Phase 3 (v0.2 complete)* diff --git a/.paul/ROADMAP.md b/.paul/ROADMAP.md index 8189ca9..0ca8535 100644 --- a/.paul/ROADMAP.md +++ b/.paul/ROADMAP.md @@ -5,8 +5,8 @@ W najblizszej iteracji skupiamy sie na uzupelnieniu warstwy analitycznej po zaku ## Current Milestone **v0.2 Analytics & Privacy** (v0.2.0) -Status: In progress -Phases: 1 of 2 complete +Status: Complete (2026-04-26) +Phases: 3 of 3 complete ## Previous Milestones @@ -20,7 +20,7 @@ Phases: 1 of 1 complete |-------|------|-------|--------|-----------| | 1 | Purchase Data Layer | 1 | Complete | 2026-04-19 | | 2 | Purchase Event Pre-Payment | 1 | Complete | 2026-04-26 | -| 3 | Cookie Consent Banner | 1 | Not started | — | +| 3 | Cookie Consent Banner | 1 | Complete | 2026-04-26 | ## Phase Details diff --git a/.paul/STATE.md b/.paul/STATE.md index 61702a5..fd0a206 100644 --- a/.paul/STATE.md +++ b/.paul/STATE.md @@ -2,29 +2,29 @@ ## Project Reference -See: .paul/PROJECT.md (updated 2026-04-19) +See: .paul/PROJECT.md (updated 2026-04-26) **Core value:** Uzytkownicy moga szybko i bezpiecznie kupic bilety online oraz otrzymac natychmiastowe potwierdzenie zakupu. -**Current focus:** v0.2 Analytics & Privacy — Phase 2 (Purchase Event Pre-Payment) planning +**Current focus:** v0.2 Analytics & Privacy — COMPLETE ## Current Position -Milestone: v0.2 Analytics & Privacy -Phase: 2 of 3 (Purchase Event Pre-Payment) — Planning -Plan: 02-01 executed -Status: APPLY complete, ready for UNIFY -Last activity: 2026-04-26 — Executed 02-01-PLAN.md (2 tasks, 3 files modified) +Milestone: v0.2 Analytics & Privacy — COMPLETE +Phase: 3 of 3 (Cookie Consent Banner) — Complete +Plan: 03-01 complete +Status: Milestone complete — ready for next milestone +Last activity: 2026-04-26 — Phase 3 complete, v0.2 shipped Progress: -- Milestone: [░░░░░░░░░░] 0% -- Phase 2: [░░░░░░░░░░] 0% +- Milestone: [##########] 100% +- Phase 3: [##########] 100% ## Loop Position Current loop state: ``` -PLAN --> APPLY --> UNIFY - ✓ ✓ ○ [APPLY complete, awaiting UNIFY] +PLAN ──▶ APPLY ──▶ UNIFY + ✓ ✓ ✓ [Loop complete — milestone complete] ``` ## Accumulated Context @@ -34,34 +34,39 @@ Date: 2026-04-26 Documents: `.paul/codebase/` (8 files — stack, architecture, structure, conventions, testing, integrations, concerns, db_schema) ### GTM Status -- GTM-TW9WCD9J już wdrożony w templates/site/layout-logged.php (i layout-unlogged.php) +- GTM-TW9WCD9J już wdrożony w templates/site/layout-logged.php - layout-unlogged.php NIE jest renderowany — view/class.Site.php zawsze używa layout-logged.php ### Decisions -- 2026-04-19: Event `purchase` emitowany na `order-confirm`, nie na `przelewy24` (eliminuje falszywe konwersje). +- 2026-04-19: Event purchase emitowany na order-confirm, nie na przelewy24 (eliminuje falszywe konwersje). - 2026-04-19: Payload ecommerce budowany w backendzie i serializowany bezpiecznie do widoku. -- 2026-04-26: ZMIANA DECYZJI — event purchase przenoszony na `przelewy24` (post-order, pre-payment) aby capturować użytkowników którzy nie wracają po płatności. +- 2026-04-26: ZMIANA — event purchase przeniesiony na przelewy24 (post-order, pre-payment). Capturuje 100% zamowien. +- 2026-04-26: cookieNoticePro.init() zamiast $.fn.cookieNoticePro() — zgodnie z dokumentacja biblioteki. +- 2026-04-26: Consent Mode v2 default-denied inicjalizowany przed GTM snippetem (wymog Google od marca 2024). -### Cookie Consent Bug -- cookienoticepro.script.js ~linia 351: gdy analytics NIE zaakceptowane, kod wywołuje gtag('consent','update',{'analytics_storage':'granted'}) zamiast 'denied' +### Cookie Consent (Phase 3 — COMPLETE) +- Biblioteka: libraries/CookieNoticePro/ (skopiowana z pomysloweprezenty.pl) +- Bug naprawiony: linia ~351 — blok else teraz wysyla analytics_storage: 'denied' +- Dodatkowe poprawki: CSS initial hide dla acceptBtnSettingsLabel, Bootstrap 5 compat reset +- Consent Mode v2 init PRZED snippetem GTM w layout-logged.php ### Deferred Issues -- Phase 3: Cookie Consent Banner (zaplanowane po Phase 2) +None. ### Blockers/Concerns -None yet. +None. ### Git State -Last commit: 9de0429 +Last commit: 86e712b Branch: main Feature branches merged: none ## Session Continuity Last session: 2026-04-26 -Stopped at: Plan 02-01 created -Next action: Review and approve plan, then run /paul:apply .paul/phases/02-purchase-event-prepayment/02-01-PLAN.md -Resume file: .paul/phases/02-purchase-event-prepayment/02-01-PLAN.md +Stopped at: v0.2 milestone complete +Next action: /paul:milestone — zdefiniuj v0.3 lub /paul:complete-milestone +Resume file: .paul/ROADMAP.md --- *STATE.md - Updated after every significant action* diff --git a/.paul/changelog/2026-04-26.md b/.paul/changelog/2026-04-26.md index bd8ac1a..43fd932 100644 --- a/.paul/changelog/2026-04-26.md +++ b/.paul/changelog/2026-04-26.md @@ -12,3 +12,18 @@ - `autoload/controls/class.Tickets.php` - `templates/tickets/przelewy24.php` - `templates/tickets/order-confirm.php` + +--- + +- [Phase 3, Plan 01] Wdrożono baner zgody na cookies (CookieNoticePro) z Google Consent Mode v2 +- Skopiowano bibliotekę CookieNoticePro do libraries/CookieNoticePro/ +- Naprawiono błąd biblioteki: analytics_storage wysyłało 'granted' zamiast 'denied' przy odmowie +- Dodano inicjalizację Consent Mode v2 (default: denied) przed snippetem GTM w layout-logged.php +- Naprawiono inicjalizację pluginu: cookieNoticePro.init() +- Naprawiono duplikat tekstu w przycisku (CSS display:none dla acceptBtnSettingsLabel) +- Zmieniono link regulaminu na https://bilety.brzezovka.pl/tickets/regulamin/ +- Milestone v0.2 Analytics & Privacy — COMPLETE + +- `libraries/CookieNoticePro/cookienoticepro.script.js` +- `libraries/CookieNoticePro/cookienoticepro.style.css` +- `templates/site/layout-logged.php` diff --git a/.paul/phases/03-cookie-consent/03-01-PLAN.md b/.paul/phases/03-cookie-consent/03-01-PLAN.md new file mode 100644 index 0000000..dd8012d --- /dev/null +++ b/.paul/phases/03-cookie-consent/03-01-PLAN.md @@ -0,0 +1,227 @@ +--- +phase: 03-cookie-consent +plan: 01 +type: execute +wave: 1 +depends_on: [] +files_modified: + - libraries/CookieNoticePro/cookienoticepro.script.js + - libraries/CookieNoticePro/cookienoticepro.style.css + - templates/site/layout-logged.php +autonomous: false +delegation: off +--- + + +## Goal +Wdrożyć baner zgody na cookies (CookieNoticePro) z Google Consent Mode v2, naprawić błąd `analytics_storage: 'granted'` w bibliotece oraz dodać domyślną inicjalizację consent mode przed snippetem GTM. + +## Purpose +Wymaganie RODO — serwis musi uzyskać zgodę użytkownika przed uruchomieniem trackerów analitycznych i marketingowych. Google Consent Mode v2 zapewnia zgodność z wymaganiami Google (od marca 2024) i zachowanie modelowanych danych konwersji nawet przy braku zgody. + +## Output +- `libraries/CookieNoticePro/cookienoticepro.script.js` — zkopiony z pomysloweprezenty.pl i naprawiony (linia ~351: `'granted'` → `'denied'`) +- `libraries/CookieNoticePro/cookienoticepro.style.css` — zkopiony z pomysloweprezenty.pl +- `templates/site/layout-logged.php` — consent mode v2 default init przed GTM, załadowane CSS/JS banera, wywołanie `$.cookieNoticePro()` + + + +## Project Context +@.paul/PROJECT.md +@.paul/ROADMAP.md +@.paul/STATE.md + +## Source Files +@templates/site/layout-logged.php + +## Biblioteka źródłowa (pomysloweprezenty.pl) +Pliki do skopiowania: +- `c:\visual studio code\projekty\pomysloweprezenty.pl\libraries\CookieNoticePro\cookienoticepro.script.js` +- `c:\visual studio code\projekty\pomysloweprezenty.pl\libraries\CookieNoticePro\cookienoticepro.style.css` + + + + +## AC-1: Consent Mode v2 default init przed GTM +```gherkin +Given layout-logged.php jest renderowany w przeglądarce +When strona się ładuje +Then w , PRZED snippetem GTM, znajduje się blok gtag('consent','default',{...}) ustawiający wszystkie storage na 'denied' +``` + +## AC-2: Baner cookies wyświetla się przy pierwszej wizycie +```gherkin +Given użytkownik odwiedza serwis po raz pierwszy (brak cookie COOKIE_CONSENT) +When strona się załaduje +Then pojawia się baner CookieNoticePro z przyciskami akceptacji/odrzucenia/dostosowania +``` + +## AC-3: Naprawa błędu analytics_storage denied +```gherkin +Given użytkownik odwiedza serwis i wybiera "Odrzuć" lub wyłącza analytics w ustawieniach +When biblioteka wywołuje googleConsentModeHandler() +Then gtag('consent','update') wysyła {'analytics_storage': 'denied'} (nie 'granted') +``` + +## AC-4: Akceptacja cookies uruchamia gtag update z 'granted' +```gherkin +Given użytkownik wybrał akceptację wszystkich cookies +When biblioteka wywołuje googleConsentModeHandler() +Then gtag('consent','update') wysyła {'analytics_storage': 'granted'} i {'ad_storage': 'granted', ...} +``` + +## AC-5: Pliki biblioteki obecne w projekcie +```gherkin +Given wdrożenie jest kompletne +When sprawdzamy strukturę projektu +Then istnieją pliki libraries/CookieNoticePro/cookienoticepro.script.js i cookienoticepro.style.css +``` + + + + + + + Task 1: Skopiuj pliki CookieNoticePro i napraw błąd analytics_storage + libraries/CookieNoticePro/cookienoticepro.script.js, libraries/CookieNoticePro/cookienoticepro.style.css + + 1. Skopiuj oba pliki z `c:\visual studio code\projekty\pomysloweprezenty.pl\libraries\CookieNoticePro\` do `libraries/CookieNoticePro\` w tym projekcie. + + 2. W `cookienoticepro.script.js`, napraw błąd w funkcji `googleConsentModeHandler` (~linia 351): + - Znajdź blok `else` po `if(preferences.indexOf("analytics") > -1)` — ten blok obsługuje sytuację gdy analytics NIE jest zaakceptowane + - W tym bloku `else`, zmień: + ```js + gtag('consent', 'update', { + 'analytics_storage': 'granted' + }); + ``` + na: + ```js + gtag('consent', 'update', { + 'analytics_storage': 'denied' + }); + ``` + - Blok `if` (analytics zaakceptowane, ~linia 341-343) musi pozostać z `'granted'` — nie ruszaj go. + + Unikaj: modyfikowania innych fragmentów pliku; zmiany konfiguracji `config {}` (kolory, tekst) — to ustawi się w layout. + + + Grep w skopiowanym pliku: + - Potwierdź że istnieje dokładnie JEDNO wywołanie `'analytics_storage': 'denied'` w bloku else + - Potwierdź że `'analytics_storage': 'granted'` nadal istnieje w bloku if (analytics accepted) + Komenda: grep -n "analytics_storage" libraries/CookieNoticePro/cookienoticepro.script.js + + AC-3, AC-4, AC-5 satisfied: plik skopiowany, błąd naprawiony, 'denied' w else, 'granted' w if + + + + Task 2: Dodaj Consent Mode v2 default init i integruj baner w layout-logged.php + templates/site/layout-logged.php + + W `templates/site/layout-logged.php`: + + 1. **Consent Mode v2 default — PRZED snippetem GTM** (wstaw przed komentarzem ``): + ```html + + + + ``` + + 2. **CSS banera** — dodaj do sekcji `` po istniejących linkach CSS (przed zamknięciem ``): + ```html + + ``` + + 3. **JS banera + inicjalizacja** — dodaj przed zamknięciem ``, po istniejącym bloku ` + + ``` + + Kolejność w `` po zmianie (od góry): + 1. Meta tagi + 2. Linki CSS (istniejące + nowy CSS banera) + 3. jQuery i inne JS + 4. **Consent Mode v2 default** ← NOWE (musi być przed GTM) + 5. **GTM snippet** ← istniejący (bez zmian) + + Unikaj: modyfikowania snippetu GTM; zmiany kolejności istniejących skryptów; dodawania atrybutu `async` lub `defer` do skryptu banera. + + + Sprawdź w pliku layout-logged.php: + - Blok consent default pojawia się PRZED `` + - `cookienoticepro.style.css` jest linkowany w `` + - `cookienoticepro.script.js` i wywołanie `$.cookieNoticePro()` są przed `` + Komenda: grep -n "Consent Mode\|cookienoticepro\|GTM-TW9WCD9J" templates/site/layout-logged.php + + AC-1, AC-2 satisfied: consent default init przed GTM, CSS i JS banera załadowane + + + + Baner CookieNoticePro z Consent Mode v2 zintegrowany w layout-logged.php. Pliki biblioteki w libraries/CookieNoticePro/. Błąd analytics_storage naprawiony. + + 1. Wyślij pliki na serwer przez FTP (Ctrl+Shift+P → "FTP-kr: Upload") + 2. Otwórz https://bilety.brzezovka.pl w trybie incognito (brak cookies) + 3. Sprawdź czy pojawia się baner zgody na cookies + 4. Otwórz DevTools → Console — sprawdź czy NIE ma błędów JS + 5. Otwórz DevTools → Network → filtruj "gtm" — sprawdź czy GTM się ładuje + 6. W Console wpisz: `document.cookie` — nie powinno być COOKIE_CONSENT (przed akceptacją) + 7. Kliknij "Odrzuć wszystkie" → w DevTools → Application → Cookies sprawdź czy GOOGLE_CONSENT_MODE_ANALYTICS_STORAGE ma wartość false/brak + 8. Odśwież stronę — baner NIE powinien się pojawić ponownie (cookie zapamiętane) + 9. Kliknij ikonę ciasteczka (minimize) → wybierz "Akceptuj wszystkie" → sprawdź że gtag update wysyła 'granted' (DevTools → Console → wpisz: `dataLayer` i poszukaj consent update) + + Wpisz "approved" jeśli baner działa poprawnie, lub opisz problemy do naprawienia + + + + + + +## DO NOT CHANGE +- `templates/tickets/przelewy24.php` — purchase event (Phase 2, nie ruszać) +- `templates/tickets/order-confirm.php` — po czyszczeniu Phase 2 +- Snippet GTM w layout-logged.php — tylko kolejność, nie treść +- `config.php` — konfiguracja poza zakresem + +## SCOPE LIMITS +- Nie konfiguruj cookienoticepro dla innych layoutów (layout-unlogged.php nie jest używany) +- Nie zmieniaj kolorów/tekstu/języka banera — domyślna konfiguracja z biblioteki jest wystarczająca na start +- Nie dodawaj nowych zależności JS poza biblioteką CookieNoticePro +- Nie wdrażaj server-side consent storage — tylko frontend cookies + + + + +Przed ogłoszeniem planu za kompletny: +- [ ] `grep -n "analytics_storage" libraries/CookieNoticePro/cookienoticepro.script.js` → pokazuje 'denied' w else i 'granted' w if +- [ ] `grep -n "consent.*default\|cookienoticepro\|GTM-TW9WCD9J" templates/site/layout-logged.php` → consent default PRZED GTM +- [ ] Baner pojawia się na stronie przy pierwszej wizycie (weryfikacja manualna — checkpoint) +- [ ] Brak błędów JS w konsoli +- [ ] Odrzucenie cookies → analytics_storage pozostaje denied (nie grantowane) + + + +- Pliki CookieNoticePro obecne w libraries/CookieNoticePro/ +- Błąd analytics_storage naprawiony (denied zamiast granted w bloku else) +- Consent Mode v2 default init znajduje się przed GTM snippetem w layout-logged.php +- Baner wyświetla się przy pierwszej wizycie i zapamiętuje wybór +- Serwis spełnia wymaganie RODO z PROJECT.md (Must Have: Zgodność z RODO) + + + +Po zakończeniu utwórz `.paul/phases/03-cookie-consent/03-01-SUMMARY.md` + diff --git a/.paul/phases/03-cookie-consent/03-01-SUMMARY.md b/.paul/phases/03-cookie-consent/03-01-SUMMARY.md new file mode 100644 index 0000000..6b9bc65 --- /dev/null +++ b/.paul/phases/03-cookie-consent/03-01-SUMMARY.md @@ -0,0 +1,134 @@ +--- +phase: 03-cookie-consent +plan: 01 +subsystem: ui +tags: [cookies, gdpr, consent-mode-v2, gtm, cookienoticepro] + +requires: + - phase: 02-purchase-event-prepayment + provides: GTM już wdrożony w layout-logged.php + +provides: + - Baner zgody na cookies (CookieNoticePro) zintegrowany z serwisem + - Google Consent Mode v2 z domyślnymi denied przed GTM + - Naprawiony błąd analytics_storage w bibliotece CookieNoticePro +affects: [] + +tech-stack: + added: [CookieNoticePro jQuery plugin] + patterns: [Consent Mode v2 default-denied init before GTM snippet] + +key-files: + created: + - libraries/CookieNoticePro/cookienoticepro.script.js + - libraries/CookieNoticePro/cookienoticepro.style.css + modified: + - templates/site/layout-logged.php + +key-decisions: + - "Inicjalizacja: cookieNoticePro.init() zamiast $.fn.cookieNoticePro() — zgodnie z dokumentacją biblioteki" + - "acceptBtnSettingsLabel ukryty przez CSS (display:none) — JS nie ustawiał stanu początkowego" + +patterns-established: + - "Consent Mode v2 default init zawsze przed snippetem GTM w " + +duration: ~45min +started: 2026-04-26T00:00:00Z +completed: 2026-04-26T00:00:00Z +--- + +# Phase 3 Plan 01: Cookie Consent Banner Summary + +**CookieNoticePro z Google Consent Mode v2 wdrożony — serwis spełnia wymogi RODO, analytics_storage poprawnie ustawiane na denied przy odmowie zgody.** + +## Performance + +| Metric | Value | +|--------|-------| +| Duration | ~45 min | +| Tasks | 2 auto + 1 checkpoint | +| Files modified | 3 | + +## Acceptance Criteria Results + +| Criterion | Status | Notes | +|-----------|--------|-------| +| AC-1: Consent Mode v2 default przed GTM | Pass | Blok w `` przed `` | +| AC-2: Baner przy pierwszej wizycie | Pass | Zweryfikowane manualnie | +| AC-3: Naprawa analytics_storage denied | Pass | Linia ~351: `'granted'` → `'denied'` w bloku else | +| AC-4: Akceptacja → granted | Pass | Blok if (analytics accepted) bez zmian | +| AC-5: Pliki biblioteki obecne | Pass | `libraries/CookieNoticePro/` skonfigurowany | + +## Accomplishments + +- Skopiowano i zintegrowano bibliotekę CookieNoticePro z istniejącym projektem +- Naprawiono błąd biblioteki: `analytics_storage: 'granted'` → `'denied'` w bloku odrzucenia analytics +- Consent Mode v2 inicjalizowany z defaultami `denied` przed snippetem GTM — wymaganie Google od marca 2024 +- Baner wyświetla się przy pierwszej wizycie, zapamiętuje wybór przez 365 dni + +## Files Created/Modified + +| File | Change | Purpose | +|------|--------|---------| +| `libraries/CookieNoticePro/cookienoticepro.script.js` | Created | Biblioteka banera — zkopiona + naprawiony błąd analytics_storage + link regulaminu | +| `libraries/CookieNoticePro/cookienoticepro.style.css` | Created | Style banera — zkopione + Bootstrap 5 compat reset + initial state fix | +| `templates/site/layout-logged.php` | Modified | Consent Mode v2 default przed GTM, CSS/JS banera, inicjalizacja `cookieNoticePro.init()` | + +## Decisions Made + +| Decision | Rationale | Impact | +|----------|-----------|--------| +| `cookieNoticePro.init()` zamiast `$.fn.cookieNoticePro()` | Demo `index.html` z biblioteki pokazuje właściwy sposób inicjalizacji | Poprawne uruchomienie banera | +| CSS `display:none` na `.acceptBtnSettingsLabel` | JS przełącza etykiety tylko na click — nie ustawia stanu początkowego | Brak duplikatu tekstu w przycisku "Akceptuj/Zatwierdź" | +| Bootstrap 5 compat reset w CSS | Bootstrap nadpisuje `line-height`, `font-size` przycisków | Poprawny wygląd przycisków banera | + +## Deviations from Plan + +### Summary + +| Type | Count | Impact | +|------|-------|--------| +| Auto-fixed | 3 | Niezbędne poprawki, brak scope creep | +| Scope additions | 1 | Drobna zmiana na prośbę użytkownika | + +**Total impact:** Drobne poprawki wynikające z integracji z Bootstrap 5 i specyfiki biblioteki + +### Auto-fixed Issues + +**1. Inicjalizacja pluginu** +- **Found during:** Checkpoint (błąd konsoli) +- **Issue:** `$.cookieNoticePro is not a function` — plugin rejestruje się jako `$.fn`, nie `$` +- **Fix:** `$.cookieNoticePro()` → `$.fn.cookieNoticePro()` → `cookieNoticePro.init()` +- **Files:** `templates/site/layout-logged.php` + +**2. Duplikat tekstu w przycisku akceptacji** +- **Found during:** Checkpoint (feedback użytkownika) +- **Issue:** `.acceptBtnSettingsLabel` ("Zatwierdź") widoczny razem z `.acceptBtnLabel` ("Akceptuj") — brak initial hide w JS +- **Fix:** Dodano `display: none` w CSS dla `.acceptBtnSettingsLabel` +- **Files:** `libraries/CookieNoticePro/cookienoticepro.style.css` + +**3. Bootstrap 5 button override** +- **Found during:** Checkpoint (button layout) +- **Issue:** Bootstrap nadpisuje `line-height` i `font-size` przycisków banera +- **Fix:** CSS reset z `!important`-safe specificity dla `.btn-wrap button` +- **Files:** `libraries/CookieNoticePro/cookienoticepro.style.css` + +### Scope Additions + +- Link regulaminu zmieniony na `https://bilety.brzezovka.pl/tickets/regulamin/` (prośba użytkownika podczas weryfikacji) + +## Next Phase Readiness + +**Ready:** +- v0.2 Analytics & Privacy — wszystkie 3 fazy kompletne +- Tracking ecommerce (Phase 1+2) + RODO (Phase 3) wdrożone + +**Concerns:** +- Brak — integracja stabilna + +**Blockers:** +- None + +--- +*Phase: 03-cookie-consent, Plan: 01* +*Completed: 2026-04-26* diff --git a/libraries/CookieNoticePro/cookienoticepro.script.js b/libraries/CookieNoticePro/cookienoticepro.script.js new file mode 100644 index 0000000..e742686 --- /dev/null +++ b/libraries/CookieNoticePro/cookienoticepro.script.js @@ -0,0 +1,438 @@ +/* + * Copyright (c) 2024 Flerosoft (https://flerosoft.com) + * Software Name: Cookie Notice Pro - jQuery Plugin + * Product Page : https://cookienoticepro.flerosoft.com + * Documentation: https://cookienoticepro.flerosoft.com/docs + * Description: Cookie Notice Pro, a lightweight jQuery plugin, helps you to comply with GDPR. +Make your own cookie information popup in minutes. + * Changelog: https://cookienoticepro.flerosoft.com/docs/getting-started#item-1-4 +*/ + +(function ($) { + + 'use strict' + + const settingsIcon = + ''; + + const cookieIcon = + ' '; + + const closeIcon = ''; + + /* + *-------------------------------------------------------------------------- + * CONFIG OR SETTINGS - Customize the popup banner START + *-------------------------------------------------------------------------- + */ + const config = { + themeSettings: { + primaryColor: "#b7174d", // Primary Color of Popup Banner + darkColor: "#3b3e4a", // Dark Theme Color + lightColor: "#ffffff", // Light Theme Color + themeMode: "light", // Theme Mode (light|dark) + }, + enableGoogleConsentMode: true, // Add support for Google consent mode v2 (https://cookiebannergenerator.com/implementing-google-consent-mode-v2-with-cookie-notice-pro-a-step-by-step-guide/) + enableMinimize: true, // Enable minimized floating cookie icon to adjust preferences + showCookieIcon: true, // Hide or show the cookie icon + showSettingsBtn: true, // Hide or show the preference settings(true|false) + showCloseIcon: false, // Hide or show the popup close icon(true|false) + showDeclineBtn: true, // Hide or show the cookie decline button(true|false) + fullWidth: false, // Full width popup works only when "displayPosition" is set to top/bottom + allCheckboxesChecked: true, // The setting checkboxes should be checked by default initially or not(true|false) + + displayPosition: "bottom", // Where popup should appear(top|right|bottom|left) + settingsBtnLabel: "Dostosuj", // Text of settings button + + delay: 1, // After how much time should popup appear(2000 is equal to 2 seconds) + expires: 365, // Expiry date of cookie(365 is equal to 365 days) + + title: "Cenimy prywatność użytkowników", // Title of popup bannner + description: "Używamy plików cookie, aby poprawić jakość przeglądania, wyświetlać reklamy lub treści dostosowane do indywidualnych potrzeb użytkowników oraz analizować ruch na stronie. Kliknięcie przycisku „Akceptuj wszystkie” oznacza zgodę na wykorzystywanie przez nas plików cookie.", // Message + acceptBtnLabel: "Akceptuj", // Accept cookie button text + declineInfoBtnLabel: "Dostosuj", // Decline cookie button text + acceptBtnSettingsLabel: "Zatwierdź", + moreInfoBtnLink: "https://bilety.brzezovka.pl/tickets/regulamin/", // Learn more link(default: privacy policy page) + moreInfoBtnLabel: "Regulamin", // More info link text + cookieTypesTitle: "Wybierz pliki cookies do akceptacji", // Title of cookie preference options + necessaryCookieTypeLabel: "Wymagane", // Label text of Necessary cookie item + floatingIconTooltip: "Dostosuj", // Tooltip of floating cookie icon (Minimized) + necessaryCookieTypeDesc: "Te ciasteczka są niezbędne do działania strony internetowej i nie mogą zostać wyłączone w naszych systemach.", // Hover text of necessary cookies + onConsentAccept: ()=> { // It will inject scripts in head if cookie preferences menu(showSettingsBtn) is enabled + console.log("Consent accepted!") + }, + onConsentReject: ()=> { // This code will run on cookie reject/decline + console.log("Consent Rejected!"); + }, + cookieTypes: [ + // Cookie types, value and description (Cookie Preferences Selection) + { + type: "Funkcjonalne", + value: "preferences", // WARNING: DO NOT EDIT THIS VALUE + description: "Niezbędne pliki cookie mają kluczowe znaczenie dla podstawowych funkcji witryny i witryna nie będzie działać w zamierzony sposób bez nich.Te pliki cookie nie przechowują żadnych danych umożliwiających identyfikację osoby.", + }, + { + type: "Reklama", + value: "marketing", // WARNING: DO NOT EDIT THIS VALUE + description: "Reklamowe pliki cookie służą do dostarczania użytkownikom spersonalizowanych reklam w oparciu o strony, które odwiedzili wcześniej, oraz do analizowania skuteczności kampanii reklamowej.", + }, + { + type: "Analityka", + value: "analytics", // WARNING: DO NOT EDIT THIS VALUE + description: "Analityczne pliki cookie służą do zrozumienia, w jaki sposób użytkownicy wchodzą w interakcję z witryną. Te pliki cookie pomagają dostarczać informacje o metrykach liczby odwiedzających, współczynniku odrzuceń, źródle ruchu itp.", + }, + ], + }; + /* + *-------------------------------------------------------------------------- + * CONFIG OR SETTINGS - Customize the popup banner END + *-------------------------------------------------------------------------- + */ + + const COOKIE_CONSENT = "cnp_consent"; // WARNING: DO NOT EDIT THIS VALUE + const COOKIE_CONSENT_PREFS = "cnp_prefs"; // WARNING: DO NOT EDIT THIS VALUE + const GOOGLE_CONSENT_MODE_AD_PREFS = "cnp_gconsent_ad_prefs" // WARNING: DO NOT EDIT THIS VALUE + const GOOGLE_CONSENT_MODE_ANALYTICS_STORAGE = "cnp_gconsent_analytics_storage" // WARNING: DO NOT EDIT THIS VALUE + + $.fn.cookieNoticePro = (event) => { + changeRootVariables(); + let cookieConsentExists = cookieExists(COOKIE_CONSENT); + let cookiePrefsValue = accessCookie(COOKIE_CONSENT_PREFS); + + if ( !cookieConsentExists) { + // add layer to the body below cookies to prevent click + $('body').append(''); + } + + // If consent is not accepted + if (!cookieConsentExists || event == "open") { + $("#cookieNoticePro").remove(); + $("#cookieMinimizeIcon").remove(); + + let cookieTypes='
  • "; + + const preferences = JSON.parse(cookiePrefsValue); + $.each(config.cookieTypes, (index, field) => { + if (field.type !== "" && field.value !== "") { + let cookieTypeDescription = ""; + if (field.description !== false) { + cookieTypeDescription = ' title="' + field.description + '"'; + } + cookieTypes+='
  • "; + } + }); + + let cookieNotice='
    '+ closeIcon +'
    '+cookieIcon+"

    "+config.title+'

    '+config.description+' '+config.moreInfoBtnLabel+'

    '+settingsIcon+config.settingsBtnLabel+'
    "; + + + setTimeout(() => { + $("body").append(cookieNotice); + $("#cookieNoticePro").hide().fadeIn("slow", () => { + if(event == "open") { + $("#cookieSettings").trigger("click"); + $.each(preferences, (index, field) => { + $("input#gdprPrefItem" + field).prop("checked", true); + }); + } + }); + if(!config.showSettingsBtn) { + $("#cookieNoticePro #cookieSettings").hide(); + } + // if(!config.showDeclineBtn) { + // $("#cookieNoticePro #cookieReject").hide(); + // } + if(!config.showCookieIcon) { + $("#cookieNoticePro #cookieIcon").hide(); + } + if(!config.showCloseIcon) { + $("#cookieNoticePro #closeIcon").hide(); + } + }, event === "open" ? 0 : config.delay); + $("body").on("click", "#cookieAccept", () => { + hideCookieBanner(true, config.expires); + $('input[name="gdprPrefItem"][data-compulsory="on"]').prop("checked", true); + let prefs = []; + $.each($('input[name="gdprPrefItem"]').serializeArray(), (i, field) => { + prefs.push(field.value); + }); + createCookie(COOKIE_CONSENT_PREFS, encodeURIComponent(JSON.stringify(prefs)), { + expires: daysToUTC(365), + path: "/" + }); + config.onConsentAccept.call(this); + config.showSettingsBtn? injectScripts() : null; + if(config.enableGoogleConsentMode) { + googleConsentModeHandler(); + } + }); + $("body").on("click", "#cookieSettings, #cookieReject", () => { + if(event === "open"){ + $('input[name="gdprPrefItem"]:not(:disabled)').attr("data-compulsory", "off").prop("checked", false); + $("#cookieTypes").slideDown("fast", function () { + $("#cookieSettings").prop("disabled", true); + }); + } else { + $('input[name="gdprPrefItem"]:not(:disabled)').attr("data-compulsory", "off").prop("checked", config.allCheckboxesChecked); + $("#cookieTypes").toggle("fast", function () { + $("#cookieSettings").prop("disabled", false); + + if ($("#cookieTypes").is(":visible")) { + $("#cookieAccept .acceptBtnLabel").hide(); + $(".acceptBtnSettingsLabel").show(); + } else { + $("#cookieAccept .acceptBtnLabel").show(); + $("#cookieAccept .acceptBtnSettingsLabel").hide(); + } + }); + } + }); + $("body").on("click", "#closeIcon", () => { + $("#cookieNoticePro").remove(); + }); + // $("body").on("click", "#cookieReject", () => { + // hideCookieBanner(false, config.expires); + // config.onConsentReject.call(this); + + // // Delete prefs cookie from brower on reject + // createCookie(COOKIE_CONSENT_PREFS, "", { + // expires: daysToUTC(-365), + // path: "/" + // }); + // }); + } + // If already consent is accepted, inject preferences + else { + if(config.showSettingsBtn) { + injectScripts(); + } + if(config.enableMinimize){ + minimizeCookieBanner(); + } + if(config.enableGoogleConsentMode) { + googleConsentModeHandler(); + } + } + + + }; + + /** + * Check if cookie exists + * @param {string} cookieName + */ + const cookieExists = (cookieName) => { + if(document.cookie.indexOf(cookieName) > -1){ + return true; + } + return false; + }; + + /** + * Create the cookie and hide the banner + * @param {string} value + * @param {string} expiryDays + */ + const hideCookieBanner = (value, expiryDays) => { + createCookie(COOKIE_CONSENT, value, { + expires: daysToUTC(expiryDays), + path: "/", + }); + $("#cookieNoticePro").fadeOut("fast", () => { + $('body').off('click', "#cookieSettings"); + // $('body').off('click', "#cookieReject"); + $('body').off('click', "#closeIcon"); + $('body').off('click', "#cookieAccept"); + $(this).remove(); + }); + if(config.enableMinimize) { + minimizeCookieBanner(); + } + }; + + /** + * Minimize the cookie banner and show a minimized icon. + */ + const minimizeCookieBanner = () => { + $( '#cookie-layer' ).remove(); + // Remove any existing minimize icon before appending a new one. + $('#cookieMinimizeIcon').remove(); + + // Minimize the banner and show an icon instead. + let minimizeIcon = $('
    ' + cookieIcon + '
    '+ config.floatingIconTooltip +'
    '); + $('body').append(minimizeIcon); + + // Define the click behavior for the minimize icon. + $('#cookieMinimizeIcon').on('click', function() { + if ($(this).hasClass('zoomIn')) { + $(this).removeClass('zoomIn').addClass('zoomOut'); + setTimeout(() => { + $.fn.cookieNoticePro("open"); // Reopen the cookie notice + }, 200); // Match the timeout to the animation duration + } else { + $(this).removeClass('zoomOut').addClass('zoomIn'); + } + }); + }; + + + /** + * Set Cookie + * @param {string} name - Cookie Name + * @param {string} value - Cookie Value + * @param {string} expiryDays - Expiry Date of cookie + */ + const createCookie = (name, value, options={})=> { + document.cookie = `${name}=${value}${ + Object.keys(options) + .reduce((acc, key) => { + return acc + `;${key.replace(/([A-Z])/g, $1 => '-' + $1.toLowerCase())}=${ + options[key]}`; + }, '') + }`; + }; + + /** + * Converts Days Into UTC String + * @param {number} days - Name of the cookie + * @return {string} UTC date string + */ + const daysToUTC = (days) => { + const newDate = new Date(); + newDate.setTime(newDate.getTime() + days * 24 * 60 * 60 * 1000); + return newDate.toUTCString(); + }; + + /** + * Get Cookie By Name + * @param {string} name - Name of the cookie + * @return {string(number|Array)} Value of the cookie + */ + const accessCookie = (name) => { + const cookies = document.cookie.split(";").reduce((acc, cookieString) => { + const [key, value] = cookieString.split("=").map((s) => s.trim()); + if (key && value) { + acc[key] = decodeURIComponent(value); + } + return acc; + }, {}); + return name ? cookies[name] || false : cookies; + }; + + /** + * Updates Google Consent Mode based on user consent and preferences. + */ + const googleConsentModeHandler = () => { + if(!config.enableGoogleConsentMode) return; + const consent = JSON.parse(accessCookie(COOKIE_CONSENT)); + const preferences = JSON.parse(accessCookie(COOKIE_CONSENT_PREFS)); + const googleConsentAnalyticsStorage = JSON.parse(accessCookie(GOOGLE_CONSENT_MODE_ANALYTICS_STORAGE)); + const googleConsentAdPrefs = JSON.parse(accessCookie(GOOGLE_CONSENT_MODE_AD_PREFS)); + + try { + if(consent === true) { + // if analytics is accepted + if(preferences.indexOf("analytics") > -1) { + if(!googleConsentAnalyticsStorage) { + createCookie(GOOGLE_CONSENT_MODE_ANALYTICS_STORAGE, encodeURIComponent(true), { + expires: daysToUTC(365), + path: "/" + }); + } + gtag('consent', 'update', { + 'analytics_storage': 'granted' + }); + } else { + if(googleConsentAnalyticsStorage === true) { + createCookie(GOOGLE_CONSENT_MODE_ANALYTICS_STORAGE, "", { + expires: daysToUTC(-365), + path: "/" + }); + } + gtag('consent', 'update', { + 'analytics_storage': 'denied' + }); + } + // if marketing or advertising is accepted + if( preferences.indexOf("marketing") > -1) { + if(!googleConsentAdPrefs) { + createCookie(GOOGLE_CONSENT_MODE_AD_PREFS, encodeURIComponent(true), { + expires: daysToUTC(365), + path: "/" + }); + } + gtag('consent', 'update', { + 'ad_storage': 'granted', + 'ad_user_data': 'granted', + 'ad_personalization': 'granted' + }); + } else { + if(googleConsentAdPrefs === true) { + gtag('consent', 'update', { + 'ad_storage': 'denied', + 'ad_user_data': 'denied', + 'ad_personalization': 'denied' + }); + createCookie(GOOGLE_CONSENT_MODE_AD_PREFS, "", { + expires: daysToUTC(-365), + path: "/" + }); + } + + } + } + + } catch (error) { + console.warn("CookieNoticePro: Error initializing Google Consent Mode. Ensure gtag.js is correctly installed:", error); + } + } + + const changeRootVariables = () =>{ + $(':root').css('--cookieNoticeProLight', config.themeSettings.lightColor); + $(':root').css('--cookieNoticeProDark', config.themeSettings.darkColor); + } + + return window.cookieNoticePro = { + init:()=>{ + $.fn.cookieNoticePro(); + }, + /** + * Reopens the cookie notice banner + */ + reinit:()=>{ + $.fn.cookieNoticePro("open"); + }, + + /** + * Returns true if consent is given else false + */ + isAccepted: ()=>{ + let consent = accessCookie(COOKIE_CONSENT); + return JSON.parse(consent); + }, + + /** + * Returns the value of the cookieConsentPrefs cookie + */ + getPreferences: ()=>{ + let preferences = accessCookie(COOKIE_CONSENT_PREFS); + return JSON.parse(preferences); + }, + + /** + * Check if a particular preference is accepted + * @param {string} cookieName + */ + isPreferenceAccepted: (cookieTypeValue)=>{ + let consent = JSON.parse(accessCookie(COOKIE_CONSENT)); + let preferences = accessCookie(COOKIE_CONSENT_PREFS); + preferences = JSON.parse(preferences); + if (consent === false) { + return false; + } + if (preferences === false || preferences.indexOf(cookieTypeValue) === -1) { + return false; + } + return true; + }, + }; +})(jQuery); diff --git a/libraries/CookieNoticePro/cookienoticepro.style.css b/libraries/CookieNoticePro/cookienoticepro.style.css new file mode 100644 index 0000000..5a93247 --- /dev/null +++ b/libraries/CookieNoticePro/cookienoticepro.style.css @@ -0,0 +1,560 @@ +/* + * Copyright (c) 2024 Flerosoft (https://flerosoft.com) + * Software Name: Cookie Notice Pro - jQuery Plugin + * Product Page : https://cookienoticepro.flerosoft.com + * Documentation: https://cookienoticepro.flerosoft.com/docs + * Description: Cookie Notice Pro, a lightweight jQuery plugin, helps you to comply with GDPR. +Make your own cookie information popup in minutes. + */ + +/*********************** Cookie Dialog ***********************/ +@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@400;500&display=swap'); + +:root { + --cookieNoticeProLight: #ffffff; + --cookieNoticeProDark: #393d4d; +} + +@keyframes cookieNoticeProZoomIn { + from { + transform: scale(0); + opacity: 0; + } + + to { + transform: scale(1); + opacity: 1; + } +} + +@keyframes cookieNoticeProZoomOut { + from { + transform: scale(1); + opacity: 1; + } + + to { + transform: scale(0); + opacity: 0; + } +} + +#cookieNoticePro * { + margin: 0; + padding: 0; + text-decoration: none; + list-style: none; + font-family: 'Poppins', sans-serif; + -webkit-box-sizing: border-box; + box-sizing: border-box; +} + +#cookieNoticePro button, +#cookieNoticePro a { + cursor: pointer; + color: inherit; + font-family: inherit; +} + +#cookieNoticePro button { + outline: none; + border: none; + -moz-appearance: none; + appearance: none; + -webkit-appearance: none; + appearance: none; +} + +#cookieNoticePro strong, +#cookieNoticePro em { + font-weight: bold; + font-family: inherit; +} + +#cookieNoticePro a:hover { + text-decoration: none; + cursor: pointer; +} + +#cookieNoticePro a:focus, +#cookieNoticePro input:focus { + outline: none; + list-style: none; +} + + +#cookieNoticePro.light { + background-color: #ffffff; + background-color: var(--cookieNoticeProLight); + color: #393d4d; + color: var(--cookieNoticeProDark); +} + +#cookieNoticePro.light p, +#cookieNoticePro.light ul { + color: #393d4d; + color: var(--cookieNoticeProDark); +} + +#cookieNoticePro.light h5 { + color: var(--cookieNoticeProDark); +} + +#cookieNoticePro.light button#cookieReject { + background: #ffffff; + background: var(--cookieNoticeProLight); + font-size: 13px; +} + +@media (max-width: 600px) { + #cookieNoticePro.light button#cookieReject { + order: 1; + } + + #cookieNoticePro.light button#cookieAccept { + order: 2; + } +} + +#cookieNoticePro.dark { + background-color: #393d4d; + background-color: var(--cookieNoticeProDark); + color: #ffffff; + color: var(--cookieNoticeProLight); +} + +#cookieNoticePro.dark p, +#cookieNoticePro.dark ul { + color: #ffffff; + color: var(--cookieNoticeProLight); +} + +#cookieNoticePro.dark h5 { + color: #ffffff; + color: var(--cookieNoticeProLight); +} + +#cookieNoticePro.dark button#cookieReject { + background: transparent; + color: #ffffff; + color: var(--cookieNoticeProLight); + border: 1px solid #ffffff; + border: 1px solid var(--cookieNoticeProLight); +} + +#cookieNoticePro { + -webkit-box-sizing: border-box; + box-sizing: border-box; + position: fixed; + padding: 20px; + border-radius: 10px; + -webkit-box-shadow: 0 6px 6px rgba(0, 0, 0, 0.25); + box-shadow: 0 6px 6px rgba(0, 0, 0, 0.25); + font-family: inherit; + z-index: 999997; +} + +#cookieNoticePro #closeIcon { + width: 20px; + height: 20px; + cursor: pointer; + color: #bfb9b9; + overflow: hidden; + opacity: 0.85; + z-index: 999999; + position: absolute; + top: 4px; + right: 4px; +} + +#cookieNoticePro svg { + display: block; +} + +#cookieNoticePro.display-left { + left: 30px; + bottom: 30px; + max-width: 395px; +} + +#cookieNoticePro.display-right { + right: 30px; + bottom: 30px; + max-width: 395px; +} + +#cookieNoticePro.display-top { + top: 30px; + width: 800px; + left: 50%; + margin-left: -400px; +} + +#cookieNoticePro.display-bottom { + bottom: 30px; + width: 800px; + left: 50%; + margin-left: -400px; +} + +#cookieNoticePro.display-top .content-wrap, +#cookieNoticePro.display-bottom .content-wrap { + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -webkit-box-pack: justify; + -ms-flex-pack: justify; + justify-content: space-between; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; +} + +#cookieNoticePro.display-top .msg-wrap, +#cookieNoticePro.display-bottom .msg-wrap { + max-width: 65%; + width: 100%; +} + +#cookieNoticePro.display-top .msg-wrap, +#cookieNoticePro.display-bottom .msg-wrap { + margin-bottom: 0; +} + +#cookieNoticePro.display-top #cookieSettings, +#cookieNoticePro.display-bottom #cookieSettings { + margin-bottom: 0; +} + +#cookieNoticePro.display-top #cookieTypes, +#cookieNoticePro.display-bottom #cookieTypes { + margin-top: 20px; +} + +#cookieNoticePro .btn-wrap { + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -webkit-box-orient: horizontal; + -webkit-box-direction: normal; + -ms-flex-direction: row; + flex-direction: row; + font-weight: bold; + -webkit-box-pack: center; + -ms-flex-pack: center; + justify-content: center; + margin: 0 -5px 0 -5px; + -ms-flex-wrap: wrap; + flex-wrap: wrap; +} + +#cookieNoticePro .btn-wrap button { + -webkit-box-flex: 1; + -ms-flex-positive: 1; + flex-grow: 1; + padding: 0 7px; + margin: 0 5px 10px 5px; + border-radius: 20px; + cursor: pointer; + white-space: nowrap; + min-width: 130px; + line-height: 36px; + border: none; + font-family: inherit; + font-size: 16px; + -webkit-transition: -webkit-box-shadow .3s; + transition: -webkit-box-shadow .3s; + -o-transition: box-shadow .3s; + transition: box-shadow .3s; + transition: box-shadow .3s, -webkit-box-shadow .3s; +} + +#cookieNoticePro .btn-wrap button:hover { + -webkit-transition: -webkit-box-shadow .4s cubic-bezier(.25, .8, .25, 1), -webkit-transform .4s cubic-bezier(.25, .8, .25, 1); + transition: -webkit-box-shadow .4s cubic-bezier(.25, .8, .25, 1), -webkit-transform .4s cubic-bezier(.25, .8, .25, 1); + -o-transition: box-shadow .4s cubic-bezier(.25, .8, .25, 1), transform .4s cubic-bezier(.25, .8, .25, 1); + transition: box-shadow .4s cubic-bezier(.25, .8, .25, 1), transform .4s cubic-bezier(.25, .8, .25, 1); + transition: box-shadow .4s cubic-bezier(.25, .8, .25, 1), transform .4s cubic-bezier(.25, .8, .25, 1), -webkit-box-shadow .4s cubic-bezier(.25, .8, .25, 1), -webkit-transform .4s cubic-bezier(.25, .8, .25, 1); + -webkit-box-shadow: 0 2px 5px 0 rgba(0, 0, 0, .3); + box-shadow: 0 2px 5px 0 rgba(0, 0, 0, .3); + -webkit-transform: translate3d(0, -1px, 0); + transform: translate3d(0, -1px, 0); +} + +#cookieNoticePro #cookieSettings { + font-size: 12px; + font-weight: bold; + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + text-transform: uppercase; + cursor: pointer; + font-weight: normal; + margin-bottom: 20px; +} + +#cookieNoticePro #cookieSettings svg { + vertical-align: middle; + margin-right: 8px; +} + +#cookieNoticePro h4 { + font-family: inherit; + font-weight: bold; + font-size: 18px; +} + +#cookieNoticePro .title-wrap { + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + margin-bottom: 10px; +} + +#cookieNoticePro .title-wrap svg { + margin-right: 10px; +} + +#cookieNoticePro h5 { + font-family: inherit; + font-weight: bold; + font-size: 12px; + margin-bottom: 10px; + text-transform: uppercase; +} + +#cookieNoticePro p, +#cookieNoticePro ul { + font-size: 12px; + margin-bottom: 10px; +} + +#cookieNoticePro p:last-child { + margin-bottom: 0; + text-align: left; +} + +#cookieNoticePro li { + width: 49%; + display: inline-block; +} + +#cookieNoticePro a { + text-decoration: none; + font-size: 14px; + padding-bottom: 2px; + border-bottom: 1px dotted rgba(255, 255, 255, 0.75); + -webkit-transition: all 0.3s ease-in; + -o-transition: all 0.3s ease-in; + transition: all 0.3s ease-in; +} + + +#cookieTypes label { + text-transform: uppercase; + font-size: 13px; +} + +#cookieNoticePro button:disabled { + opacity: 0.3; +} + +#cookieNoticePro input[type="checkbox"] { + -webkit-appearance: auto; + -moz-appearance: auto; + appearance: auto; + -webkit-font-smoothing: antialiased; + text-rendering: optimizeSpeed; + width: 14px; + height: 14px; + margin: 4px 10px 0px 0px; + display: block; + float: left; + position: relative; + outline: none; + border: none; +} + +#cookieNoticePro input[type="checkbox"]:checked:after { + background: #d3d3d3; + content: "\2714"; + color: #808080; +} + +#cookieNoticePro input[type="checkbox"]:after { + content: ""; + vertical-align: middle; + text-align: center; + line-height: 13px; + position: absolute; + cursor: pointer; + height: 14px; + width: 14px; + left: 0; + top: 0; + font-size: 10px; + background: #d3d3d3; +} + +#cookieNoticePro.display-top.full-width-true, +#cookieNoticePro.display-bottom.full-width-true { + width: 100%; + max-width: 100%; + left: auto; + right: auto; + bottom: auto; + top: 0; + border-radius: 0; + margin: auto; +} + +#cookieNoticePro.display-bottom.full-width-true { + bottom: 0; + top: auto; + -webkit-box-shadow: 0 -3px 3px rgb(0 0 0 / 25%); + box-shadow: 0 -3px 3px rgb(0 0 0 / 25%); +} + +#cookieNoticePro.display-top.full-width-true .title-wrap, +#cookieNoticePro.display-bottom.full-width-true .title-wrap { + display: none; +} + +#cookieNoticePro.display-top.full-width-true .btn-wrap button, +#cookieNoticePro.display-bottom.full-width-true .btn-wrap button { + margin-bottom: 10px; +} + +#cookieMinimizeIcon { + position: fixed; + right: 20px; + bottom: 20px; + border-radius: 50%; + cursor: pointer; + box-shadow: 0 6px 6px rgba(0, 0, 0, 0.25); + cursor: pointer; + animation: cookieNoticeProZoomIn 0.2s forwards; + transition: box-shadow 0.1s ease-in-out; + z-index: 99999; +} + +#cookieMinimizeIcon svg { + display: block; +} + +#cookieMinimizeIcon.zoomIn { + animation: cookieNoticeProZoomIn 0.2s forwards; +} + +#cookieMinimizeIcon.zoomOut { + animation: cookieNoticeProZoomOut 0.2s forwards; +} + +#cookieMinimizeIcon .floating-text { + position: absolute; + top: 50%; + left: -10%; + transform: translate(-100%, -50%); + background: rgba(51, 51, 51, 0.5); + padding: 2px 4px; + color: #ffffff; + border-radius: 2px; + font-family: 'Poppins', sans-serif; + font-size: 12px; + text-wrap: nowrap; + display: none; +} + +#cookieMinimizeIcon:hover { + box-shadow: 0 12px 12px rgba(0, 0, 0, 0.25); +} + +#cookieMinimizeIcon:hover>.floating-text { + display: block; +} + + + +@media only screen and (max-width: 800px) { + + #cookieNoticePro.display-top, + #cookieNoticePro.display-bottom { + width: 100%; + max-width: 100%; + left: auto; + right: auto; + bottom: auto; + top: 0; + border-radius: 0; + margin: auto; + } + + #cookieNoticePro.display-bottom { + bottom: 0; + top: auto; + -webkit-box-shadow: 0 -3px 3px rgb(0 0 0 / 25%); + box-shadow: 0 -3px 3px rgb(0 0 0 / 25%); + } + + #cookieNoticePro.display-top .btn-wrap button, + #cookieNoticePro.display-bottom .btn-wrap button { + margin-bottom: 10px; + } + + #cookieNoticePro.display-top .msg-wrap, + #cookieNoticePro.display-bottom .msg-wrap { + margin-right: 20px; + } +} + +@media only screen and (max-width: 600px) { + + #cookieNoticePro.display-left, + #cookieNoticePro.display-right { + width: 100%; + max-width: 100%; + left: auto; + right: auto; + bottom: 0; + border-radius: 0; + -webkit-box-shadow: 0 -3px 3px rgb(0 0 0 / 25%); + box-shadow: 0 -3px 3px rgb(0 0 0 / 25%); + } + + #cookieNoticePro.display-top .content-wrap, + #cookieNoticePro.display-bottom .content-wrap { + display: block; + } + + #cookieNoticePro.display-top .msg-wrap, + #cookieNoticePro.display-bottom .msg-wrap { + max-width: 100%; + } + + #cookieNoticePro.display-top #cookieSettings, + #cookieNoticePro.display-bottom #cookieSettings { + margin-bottom: 20px; + } +} + +/* Initial state: settings label hidden until settings panel opens */ +#cookieNoticePro .acceptBtnSettingsLabel { + display: none; +} + +/* Bootstrap 5 compatibility reset */ +#cookieNoticePro .btn-wrap button { + line-height: 36px; + font-size: 16px; + padding: 0 7px; + border-radius: 20px; + border: none; + -webkit-appearance: none; + appearance: none; +} \ No newline at end of file diff --git a/templates/site/layout-logged.php b/templates/site/layout-logged.php index e70fc2e..e87a7e2 100644 --- a/templates/site/layout-logged.php +++ b/templates/site/layout-logged.php @@ -20,6 +20,7 @@ + + + + + +