# Hooki pluginu — automatyzacja tworzenia stron Plugin dostarcza hooki, które zdejmują z projektów powtarzalną robotę. Wpinasz je w kolekcje; działają automatycznie. Wszystkie gotowe do użycia (import z pluginu). Powiązane: [pages.md](./pages.md), [seo.md](./seo.md), [wymagania-prawne.md](./wymagania-prawne.md). --- ## buildRevalidateHook — ISR odświeżany po zapisie (NAJWAŻNIEJSZY) Bez tego ISR ma haczyk: redaktor zapisuje stronę i CZEKA na revalidate (do godziny). Z tym — zapisuje i OD RAZU widzi zmianę. To warunek, żeby ISR był używalny dla CMS. ```ts // kolekcja Pages — z pliku projektu, który MOŻE importować next/cache import { revalidatePath } from 'next/cache' import { buildRevalidateHook } from '@intecion/ipal-kit' import { i18nConfig } from '@/i18n.config' const { afterChange, afterDelete } = buildRevalidateHook({ revalidatePath, // wstrzykiwany — plugin NIE importuje next/cache config: i18nConfig, }) export const Pages: CollectionConfig = { slug: 'pages', hooks: { afterChange: [afterChange], afterDelete: [afterDelete] }, // ... } ``` **Dlaczego revalidatePath wstrzykiwany:** plugin nie importuje `next/cache` (to by wywaliło Payload przy generate:importmap / czystym Node). Projekt podaje. Obsługuje: wszystkie języki, root (home), zmianę slug (rewaliduje stary I nowy path — stary URL nie serwuje starej treści), delete. --- ## setPublishedAtHook — auto-data publikacji Ustawia `publishedAt` na teraz przy pierwszej publikacji (jeśli puste). Redaktor nie wpisuje daty ręcznie; data jest dokładna dla Article JSON-LD i sitemap. ```ts import { setPublishedAtHook } from '@intecion/ipal-kit' // kolekcja z draftami (blog, artykuły): hooks: { beforeChange: [setPublishedAtHook] } ``` Ustawia tylko przy przejściu na published; nie nadpisuje istniejącej daty (redaktor może backdatować ręcznie). --- ## buildPreventDeleteSystemPage — ochrona stron systemowych Blokuje usunięcie strony przypisanej do roli (homepage, privacyPolicy, cookiePolicy, termsOfService). Redaktor nie usunie przypadkiem polityki prywatności albo strony głównej → nie rozbije routingu i linków compliance. ```ts import { buildPreventDeleteSystemPage } from '@intecion/ipal-kit' hooks: { beforeDelete: [buildPreventDeleteSystemPage({ settingsSlug: 'site-settings' })] } ``` Żeby usunąć — najpierw odłącz rolę w Site Settings (świadoma decyzja). --- ## buildValidateUniqueRole — jedna strona = jedna rola Zapobiega przypisaniu tej samej strony do dwóch ról systemowych (np. homepage I privacyPolicy naraz → niejednoznaczny routing). ```ts import { buildValidateUniqueRole } from '@intecion/ipal-kit' // na polu roli w SiteSettings: { name: 'privacyPolicy', type: 'relationship', relationTo: 'pages', hooks: { beforeValidate: [buildValidateUniqueRole({ siblingFields: ['homepage', 'cookiePolicy', 'termsOfService'], })] }, } ``` --- ## trackSlugHistoryHook — auto-redirect 301 przy zmianie slug Gdy slug się zmienia, zapisuje STARY slug do pola `slugHistory`. Projekt czyta to i robi 301 ze starego URL na nowy → zmiana adresu nie daje 404 (realna strata SEO z audytu). ```ts import { trackSlugHistoryHook } from '@intecion/ipal-kit' export const Pages: CollectionConfig = { fields: [ // ... { name: 'slugHistory', type: 'array', admin: { readOnly: true }, fields: [{ name: 'slug', type: 'text' }] }, ], hooks: { beforeChange: [trackSlugHistoryHook] }, } ``` Projekt w resolveRoute / sprawdzeniu redirectów: jeśli żądany slug jest w slugHistory jakiejś strony → 301 na jej aktualny slug. Przykład: ```ts // w page.tsx, gdy resolveRoute nie znajdzie strony po slug: const byHistory = await payload.find({ collection: 'pages', where: { 'slugHistory.slug': { equals: requestedSlug } }, limit: 1, }) if (byHistory.docs[0]) { redirect(`/${locale}/${byHistory.docs[0].slug}`) // 301 na aktualny } ``` --- ## KOLEJNOŚĆ hooków (ważne) W jednej kolekcji hooki tej samej fazy uruchamiają się po kolei. Typowa Media: ```ts hooks: { beforeOperation: [normalizeFilenameHook], // czyste nazwy afterChange: [afterChange], // revalidate afterDelete: [afterDelete], } ``` Typowa Pages: ```ts hooks: { beforeChange: [setPublishedAtHook, trackSlugHistoryHook], beforeDelete: [buildPreventDeleteSystemPage(...)], afterChange: [afterChange], // revalidate afterDelete: [afterDelete], } ``` Które hooki wpiąć zależy od kolekcji — nie każda potrzebuje wszystkich (blog: setPublishedAt; wszystkie z URL: revalidate + slugHistory; Pages: + preventDelete).