169 lines
5.6 KiB
Markdown
169 lines
5.6 KiB
Markdown
# consent
|
|
|
|
Banner zgody na cookies (GDPR): 4 kategorie (necessary / functional /
|
|
analytics / marketing), zapis w cookie z wersjonowaniem, Google Consent Mode,
|
|
treść z globala CookieSettings. Domyślny wygląd w czystym Tailwind,
|
|
nadpisywalny.
|
|
|
|
## Zależność
|
|
|
|
Banner używa ikony z `lucide-react`:
|
|
|
|
```json
|
|
"dependencies": { "lucide-react": "^0.400.0" }
|
|
```
|
|
|
|
## Config
|
|
|
|
Brak opcji — global **CookieSettings** jest zawsze budowany. Edytor zarządza
|
|
treścią bannera (message, przyciski, kategorie, settingsTitle) w panelu,
|
|
localized. Link do polityki prywatności bierze się z system pages
|
|
(`privacyPolicy`), nie z osobnego pola.
|
|
|
|
## Front — Provider + banner
|
|
|
|
Provider owija aplikację, banner i button renderują się same. Import z
|
|
`@intecion/ipal-kit/client`:
|
|
|
|
```tsx
|
|
// app/(frontend)/[locale]/layout.tsx
|
|
import { ConsentProvider, CookieBanner, CookieButton } from '@intecion/ipal-kit/client'
|
|
import { getConsentTexts } from '@intecion/ipal-kit'
|
|
|
|
export default async function Layout({ children, params }) {
|
|
const { locale } = await params
|
|
const payload = await getPayload({ config })
|
|
|
|
// teksty z CookieSettings + link do polityki z system pages
|
|
const settings = await payload.findGlobal({ slug: 'site-settings', locale: 'all', depth: 1 })
|
|
const texts = await getConsentTexts({
|
|
payload, config: i18nConfig, locale,
|
|
privacyPolicy: { label: 'Polityka prywatności', page: settings.privacyPolicy },
|
|
})
|
|
|
|
return (
|
|
<ConsentProvider texts={texts}>
|
|
{children}
|
|
<CookieBanner />
|
|
<CookieButton />
|
|
</ConsentProvider>
|
|
)
|
|
}
|
|
```
|
|
|
|
## Nadpisywanie wyglądu (Poziom 2)
|
|
|
|
Domyślne klasy Tailwind można nadpisać przez `classNames`:
|
|
|
|
```tsx
|
|
<CookieBanner classNames={{
|
|
root: 'fixed inset-x-0 bottom-0 ...', // Twój layout
|
|
primaryButton: 'btn btn-primary', // np. DaisyUI
|
|
secondaryButton: 'btn btn-ghost',
|
|
}} />
|
|
```
|
|
|
|
## Gating skryptów wg zgody
|
|
|
|
```ts
|
|
import { updateConsent, setDefaultConsent } from '@intecion/ipal-kit'
|
|
// wysyła sygnały do Google Consent Mode (gtag) na podstawie stanu zgody
|
|
```
|
|
|
|
Logika (kategorie, storage, parsowanie) też jest dostępna server-safe z
|
|
`@intecion/ipal-kit`:
|
|
|
|
```ts
|
|
import { parseConsent, CONSENT_COOKIE, CONSENT_CATEGORIES } from '@intecion/ipal-kit'
|
|
// np. gating skryptów server-side na podstawie cookie zgody
|
|
```
|
|
|
|
## Wygląd — nadpisywanie stylów
|
|
|
|
Banner i przycisk mają domyślny, neutralny wygląd (light + dark) i działają bez
|
|
żadnej konfiguracji. Kolory i zaokrąglenia idą przez CSS custom properties z
|
|
fallbackami — żeby przestylować pod klienta, zadeklaruj zmienne w swoim CSS.
|
|
Bez importów, bez propsów, bez walki ze specificity:
|
|
|
|
```css
|
|
/* global.css — wszystko opcjonalne, nadpisz tylko to, co chcesz */
|
|
:root {
|
|
--ipal-primary: #16a34a;
|
|
--ipal-primary-hover: #15803d;
|
|
--ipal-radius: 1rem;
|
|
}
|
|
```
|
|
|
|
Dostępne tokeny (każdy ma odpowiednik `-dark` używany pod `dark:`):
|
|
|
|
| Token | Domyślnie | Co koloruje |
|
|
|---|---|---|
|
|
| `--ipal-surface` | `#fff` | tło bannera i przycisku |
|
|
| `--ipal-border` | `#e5e5e5` | obramowania |
|
|
| `--ipal-text` | `#404040` | tekst treści |
|
|
| `--ipal-text-strong` | `#171717` | nagłówki, nazwy kategorii |
|
|
| `--ipal-text-muted` | `#737373` | opisy kategorii |
|
|
| `--ipal-primary` | `#2563eb` | przycisk główny, ikona, link, checkbox |
|
|
| `--ipal-primary-hover` | `#1d4ed8` | hover przycisku głównego |
|
|
| `--ipal-primary-text` | `#fff` | tekst na przycisku głównym |
|
|
| `--ipal-hover` | `#f5f5f5` | hover przycisków drugorzędnych |
|
|
| `--ipal-radius` | `0.375rem` | zaokrąglenie przycisków |
|
|
|
|
Wymaga `@source` skanującego pakiet (patrz getting-started.md) — inaczej Tailwind
|
|
nie wygeneruje tych klas.
|
|
|
|
### Gdy tokeny nie wystarczą
|
|
|
|
Układ (odstępy, pozycja, breakpointy) nie jest tokenizowany — to nie jest coś,
|
|
co zmienia się per brand, a wystawienie go oznaczałoby wymyślanie CSS od nowa,
|
|
zmienna po zmiennej. Na większe zmiany są `classNames`:
|
|
|
|
```tsx
|
|
<CookieBanner classNames={{
|
|
root: 'fixed inset-0 z-50 grid place-items-center bg-black/50', // modal zamiast paska
|
|
primaryButton: 'btn btn-primary', // np. DaisyUI
|
|
secondaryButton: 'btn btn-ghost',
|
|
}} />
|
|
<CookieButton className="fixed bottom-6 right-6 ..." />
|
|
```
|
|
|
|
Sloty: `root`, `primaryButton`, `secondaryButton`. Podany className zastępuje
|
|
domyślny (nie dokleja się).
|
|
|
|
Elementy mają też `data-ipal="banner"` i `data-ipal="cookie-button"` — stabilne
|
|
uchwyty do CSS albo testów e2e.
|
|
|
|
## Locale jako cookie functional (wbudowane)
|
|
|
|
Plugin sam zarządza jedną cookie functional: **`NEXT_LOCALE`** (wybór języka).
|
|
Nie musisz nic konfigurować — działa out of the box:
|
|
|
|
- **Zapis za zgodą.** Middleware zapisuje `NEXT_LOCALE` tylko, gdy użytkownik
|
|
zaakceptował kategorię **functional**. Bez zgody język działa (negocjacja per
|
|
żądanie), ale nie jest utrwalany w cookie.
|
|
- **Sprzątanie po cofnięciu.** Gdy użytkownik cofnie zgodę na functional, hook
|
|
consent usuwa `NEXT_LOCALE` automatycznie. Odpowiada za to `DEFAULT_COOKIE_MAP`:
|
|
|
|
```ts
|
|
const DEFAULT_COOKIE_MAP = {
|
|
functional: [LOCALE_COOKIE_NAME], // 'NEXT_LOCALE' — plugin zna własną cookie
|
|
}
|
|
```
|
|
|
|
### Twoje własne cookie functional/analytics
|
|
|
|
Jeśli ustawiasz własne cookie podlegające zgodzie, rozszerz mapę — hook wtedy
|
|
sprzątnie też Twoje po cofnięciu zgody:
|
|
|
|
```ts
|
|
useConsent({
|
|
functional: ['NEXT_LOCALE', 'moje-ustawienie'],
|
|
analytics: ['_ga', '_gid'],
|
|
})
|
|
```
|
|
|
|
Przekazana mapa zastępuje domyślną — pamiętaj dołączyć `NEXT_LOCALE`, jeśli
|
|
chcesz zachować sprzątanie locale (albo zaimportuj `LOCALE_COOKIE_NAME` i dodaj).
|
|
|
|
> Mechanizm zgody dla locale jest opisany też od strony i18n:
|
|
> [i18n.md](./i18n.md#cookie-locale-a-zgoda-rodo). |