Files
ipal-kit/docs/pages.md
T

3.9 KiB

pages

System pages: centralne przypisanie ról (homepage / privacyPolicy / cookiePolicy) do dokumentów z klienckiej kolekcji Pages, w SiteSettings. Front rozwiązuje rolę na ścieżkę locale-aware.

Config (payload.config.ts)

ipalKit({
  pages: {
    slug: 'pages',   // slug Twojej kolekcji Pages
    // roles: ['homepage', 'privacyPolicy', 'cookiePolicy'],  // domyślnie wszystkie
  },
})

Dodaje tab System Pages w SiteSettings z polami relationship do Twojej kolekcji Pages. Edytor wybiera, który dokument pełni którą rolę. Plugin nie zna Twojej kolekcji — slug podajesz w opcji.

Front — rozwiązanie ścieżki roli

import { getSystemPagePath } from '@intecion/ipal-kit'

// SiteSettings z locale:'all' + depth:1 (żeby relationship był obiektem, nie ID)
const settings = await payload.findGlobal({
  slug: 'site-settings', locale: 'all', depth: 1,
})

// homepage w locale 'pl'
getSystemPagePath({ page: settings.homepage, locale: 'pl', config })
// → '/pl' (home slug zwija się do roota) lub '/pl/strona-glowna'

// polityka prywatności w 'en'
getSystemPagePath({ page: settings.privacyPolicy, locale: 'en', config })
// → '/en/privacy-policy'

Zwraca undefined, gdy rola nieprzypisana lub dokument nie ma slug w danym locale — caller decyduje o fallbacku (404, redirect).

Typowy przypadek: /{locale} → strona główna

W trasie [locale]/page.tsx czytasz settings.homepage, bierzesz jego slug w bieżącym locale i renderujesz ten dokument. Link do polityki prywatności w stopce / bannerze cookies bierzesz z getSystemPagePath({ role: privacyPolicy }).

Role

import { ALL_SYSTEM_PAGE_ROLES } from '@intecion/ipal-kit'
// ['homepage', 'privacyPolicy', 'cookiePolicy']

KRYTYCZNE: generateStaticParams dla ...slug (SEO + head)

Trasa [[...slug]] (opcjonalny catch-all) BEZ generateStaticParams jest przez Next traktowana jako dynamiczna (ƒ Dynamic). W trybie dynamicznym z React 19 serwer wysyła pusty <head>, a metadata streamuje na końcu <body> — crawlery (Lighthouse, Screaming Frog) nie widzą <meta description> w head → SEO spada.

Z generateStaticParams trasa kompiluje się jako SSG (●) → synchroniczny, kompletny <head> → SEO 100/100. To najsilniejsze rozwiązanie problemu metadata-w-head (mocniejsze niż samo ISR/htmlLimitedBots).

Plugin dostarcza gotowy generateStaticParams przez createContentHelpers:

// lib/content.ts — dodaj do destrukturyzacji
export const {
  getCachedPayload, getSettings, resolveRoute,
  generateStaticParams,   // ← z pluginu
  sitemap, robots,
} = createContentHelpers({ config, content: contentConfig, i18n: i18nConfig, baseUrl })

// app/(frontend)/[[...slug]]/page.tsx (albo [locale]/[[...slug]])
export { generateStaticParams } from '@/lib/content'

Helper automatycznie: pobiera pages + kolekcje treści, wyklucza homepage (→ root), drafty, 404/500, noindex; zwraca {slug}[] (jednojęzyczny) albo {locale, slug}[] (wielojęzyczny). Obsługuje slugi wielopoziomowe (a/b → ['a','b']).

PUŁAPKA: await searchParams deoptymalizuje ISR

W Next 15/16 searchParams to Promise. Odczyt const { page } = await searchParams w komponencie strony deoptymalizuje ISR — wymusza dynamiczne renderowanie dla tego requestu (traci cały zysk SSG/ISR + wraca problem metadata w body).

  • Strona BEZ paginacji → NIE przekazuj/nie czytaj searchParams wcale:
    export default async function Page({ params }) {   // bez searchParams
      const { locale, slug } = await params
      const route = await resolveRoute(locale, slug ?? [])   // bez page
    }
    
  • Strona Z paginacją (archiwum) → czytaj searchParams, ale świadomie (ta trasa będzie dynamiczna). Rozważ osobną trasę dla archiwum z paginacją, żeby zwykłe strony zostały SSG.

Reguła: searchParams tylko tam, gdzie NAPRAWDĘ potrzebujesz (paginacja). Wszędzie indziej pomiń — inaczej tracisz SSG i SEO.