updated docs
This commit is contained in:
@@ -33,14 +33,29 @@ theirs, and don't put it in any file that ends up in a repository.
|
||||
### Store the token in your environment
|
||||
|
||||
Don't paste the token straight into project files. Keep it in an environment
|
||||
variable, so your install config carries no secret.
|
||||
variable, so your install config carries no secret. How you set it depends on
|
||||
your system:
|
||||
|
||||
**macOS** (default shell is zsh):
|
||||
```bash
|
||||
# add to ~/.zshrc (or ~/.bashrc)
|
||||
export GITEA_TOKEN=paste_your_token_here
|
||||
echo 'export GITEA_TOKEN=paste_your_token_here' >> ~/.zshrc
|
||||
source ~/.zshrc
|
||||
```
|
||||
|
||||
Reload your shell (`source ~/.zshrc`) or open a new terminal window.
|
||||
**Linux / Ubuntu** (default shell is bash):
|
||||
```bash
|
||||
echo 'export GITEA_TOKEN=paste_your_token_here' >> ~/.bashrc
|
||||
source ~/.bashrc
|
||||
```
|
||||
|
||||
**Windows (PowerShell)** — set it permanently for your user, then open a new
|
||||
terminal so it takes effect:
|
||||
```powershell
|
||||
setx GITEA_TOKEN "paste_your_token_here"
|
||||
```
|
||||
|
||||
> On Windows, `setx` writes the variable but does **not** affect the current
|
||||
> window — close it and open a new PowerShell for the token to be visible.
|
||||
|
||||
---
|
||||
|
||||
@@ -55,16 +70,40 @@ specific, unreleased commit straight from the repository.
|
||||
The plugin is published to the package registry in Gitea. You pull a ready-built
|
||||
package and build nothing locally.
|
||||
|
||||
**1. Configure the registry.** Create an `.npmrc` file in your project directory
|
||||
(or add these two lines to `~/.npmrc` to make it work globally):
|
||||
**1. Configure the registry.** Add these two lines to an `.npmrc` file:
|
||||
|
||||
```
|
||||
@intecion:registry=https://git.intecion.net/api/packages/IntecionSoftware/npm/
|
||||
//git.intecion.net/api/packages/IntecionSoftware/npm/:_authToken=${GITEA_TOKEN}
|
||||
```
|
||||
|
||||
Because the token lives in the `${GITEA_TOKEN}` variable, this file holds no
|
||||
secret — you can safely commit it.
|
||||
You can put this file **in the project directory** (applies to that project) or
|
||||
**globally** (applies everywhere). The global location differs by system:
|
||||
|
||||
| System | Global `.npmrc` path |
|
||||
|---|---|
|
||||
| macOS | `~/.npmrc` (i.e. `/Users/you/.npmrc`) |
|
||||
| Linux / Ubuntu | `~/.npmrc` (i.e. `/home/you/.npmrc`) |
|
||||
| Windows | `%USERPROFILE%\.npmrc` (i.e. `C:\Users\you\.npmrc`) |
|
||||
|
||||
Fastest way to create the global file:
|
||||
|
||||
```bash
|
||||
# macOS / Linux
|
||||
npm config set @intecion:registry https://git.intecion.net/api/packages/IntecionSoftware/npm/
|
||||
```
|
||||
```powershell
|
||||
# Windows (PowerShell) — same command, npm handles the path
|
||||
npm config set @intecion:registry https://git.intecion.net/api/packages/IntecionSoftware/npm/
|
||||
```
|
||||
|
||||
Then add the auth line manually (npm config doesn't set tokens with variables).
|
||||
Because the token lives in the `${GITEA_TOKEN}` variable, the file holds no
|
||||
secret — you can safely commit a project-level `.npmrc`.
|
||||
|
||||
> **Windows note:** the `${GITEA_TOKEN}` syntax in `.npmrc` is expanded by npm/pnpm
|
||||
> itself, not by the shell — so it works the same on Windows as on macOS/Linux,
|
||||
> as long as you set the variable with `setx` (see above).
|
||||
|
||||
**2. Install:**
|
||||
|
||||
@@ -141,7 +180,7 @@ nobody gets stuck on a stale one from the cache.
|
||||
Everything lives in the **[docs/](./docs)** directory. To get started:
|
||||
|
||||
- **[getting-started.md](./docs/getting-started.md)** — project setup, step by step
|
||||
- **[install.md](./docs/install.md)** — more on installation and troubleshooting
|
||||
- **[publishing.md](./docs/publishing.md)** — releasing new plugin versions
|
||||
- **[README.md](./docs/README.md)** — index of the plugin's modules
|
||||
|
||||
---
|
||||
@@ -155,5 +194,6 @@ Everything lives in the **[docs/](./docs)** directory. To get started:
|
||||
| `ERR_PNPM_GIT_DEP_PREPARE_NOT_ALLOWED` | an attempt to build on install from git — report it to whoever publishes the plugin |
|
||||
| old code after reinstalling | cache: run `pnpm store prune`, then remove `.next` and `node_modules/@intecion` |
|
||||
|
||||
If you get stuck, check **[docs/install.md](./docs/install.md)** or message the
|
||||
team that maintains the plugin.
|
||||
If you get stuck, check the troubleshooting table above, see
|
||||
**[docs/publishing.md](./docs/publishing.md)** for distribution issues, or message
|
||||
the team that maintains the plugin.
|
||||
+14
-10
@@ -1,8 +1,11 @@
|
||||
# IPAL — Dokumentacja modułów
|
||||
|
||||
**Stawiasz nowy projekt?** → [install.md](./install.md) — instalacja (github/rejestr/tarball) i diagnostyka
|
||||
**Instalacja pakietu** (token Gitea, rejestr vs repozytorium) → główny
|
||||
[README](../README.md).
|
||||
**Nowy projekt krok po kroku** → [getting-started.md](./getting-started.md).
|
||||
|
||||
Konfiguracja: [getting-started.md](./getting-started.md)
|
||||
Ta dokumentacja opisuje **konfigurację i moduły** pluginu — zakłada, że
|
||||
`@intecion/ipal-kit` jest już zainstalowany.
|
||||
|
||||
IPAL (Intecion Payload Advanced Library) to plugin do Payload CMS 3, który
|
||||
dostarcza logikę i konfigurację; projekt klienta zawiera tylko komponenty
|
||||
@@ -14,7 +17,7 @@ wizualne i podłączenia do Next.js.
|
||||
- **Klient (projekt)** = komponenty (wygląd), pliki-podłączenia Next.js
|
||||
(jednolinijkowe re-eksporty), konfiguracja front.
|
||||
|
||||
## Instalacja i wpięcie
|
||||
## Konfiguracja i wpięcie
|
||||
|
||||
### Wymagane zależności
|
||||
|
||||
@@ -62,7 +65,7 @@ importMap`).
|
||||
|
||||
```ts
|
||||
// payload.config.ts
|
||||
import { ipalKit } from 'ipal-kit'
|
||||
import { ipalKit } from '@intecion/ipal-kit'
|
||||
|
||||
export default buildConfig({
|
||||
// ...
|
||||
@@ -88,11 +91,11 @@ export default buildConfig({
|
||||
|
||||
| Import | Zawiera | Kontekst |
|
||||
|---|---|---|
|
||||
| `ipal-kit` | logika server-safe, plugin, helpery | server / config |
|
||||
| `ipal-kit/server` | runtime server-only (sendEmail, verifyTurnstile, submitForm) | Server Actions / route handlers |
|
||||
| `ipal-kit/client` | komponenty client (consent, Turnstile, Analytics) | `'use client'` |
|
||||
| `ipal-kit/rsc` | RenderBlocks (RSC) | server component |
|
||||
| `ipal-kit/next/middleware` | locale middleware | middleware.ts |
|
||||
| `@intecion/ipal-kit` | logika server-safe, plugin, helpery | server / config |
|
||||
| `@intecion/ipal-kit/server` | runtime server-only (sendEmail, verifyTurnstile, submitForm) | Server Actions / route handlers |
|
||||
| `@intecion/ipal-kit/client` | komponenty client (consent, Turnstile, Analytics) | `'use client'` |
|
||||
| `@intecion/ipal-kit/rsc` | RenderBlocks (RSC) | server component |
|
||||
| `@intecion/ipal-kit/next/middleware` | locale middleware | middleware.ts / proxy.ts |
|
||||
|
||||
## Moduły
|
||||
|
||||
@@ -114,6 +117,7 @@ export default buildConfig({
|
||||
|
||||
Nowy projekt krok po kroku: [getting-started.md](./getting-started.md)
|
||||
Referencja wdrożenia frontu: [frontend-setup.md](./frontend-setup.md)
|
||||
Wydawanie nowych wersji wtyczki: [publishing.md](./publishing.md)
|
||||
|
||||
## Zasady dla wszystkich modułów
|
||||
|
||||
@@ -122,6 +126,6 @@ Referencja wdrożenia frontu: [frontend-setup.md](./frontend-setup.md)
|
||||
2. **Sekrety w panelu** — SMTP, Turnstile secret, R2 w SiteIntegrations
|
||||
(admin-only). Odczyt server-side przez Local API.
|
||||
3. **Client/server split** — kod z sekretami ma `server-only`; komponenty
|
||||
client w `ipal-kit/client`.
|
||||
client w `@intecion/ipal-kit/client`.
|
||||
4. **Generyki na typy klienta** — helpery przyjmują `<T>` (np. wygenerowany
|
||||
`SiteSetting`), bo plugin nie zna typów projektu.
|
||||
+4
-4
@@ -25,7 +25,7 @@ zmieniać role) do wskazanej kolekcji. Kolekcja należy do Ciebie
|
||||
## Predykaty (front / hooki / access)
|
||||
|
||||
```ts
|
||||
import { isAdmin, isEditor, hasMinimumRole } from 'ipal-kit'
|
||||
import { isAdmin, isEditor, hasMinimumRole } from '@intecion/ipal-kit'
|
||||
|
||||
isAdmin(user) // admin?
|
||||
isEditor(user) // editor lub admin (hierarchia)
|
||||
@@ -39,7 +39,7 @@ Przyjmują luźno typowanego usera (jak `req.user`), czytają `roles` defensywni
|
||||
### Collection-level (zwracają boolean | Where)
|
||||
|
||||
```ts
|
||||
import { adminOnly, adminOrEditor, adminOrSelf, requireRole, authenticated } from 'ipal-kit'
|
||||
import { adminOnly, adminOrEditor, adminOrSelf, requireRole, authenticated } from '@intecion/ipal-kit'
|
||||
|
||||
export const Articles = {
|
||||
slug: 'articles',
|
||||
@@ -62,7 +62,7 @@ access: { update: requireRole('editor') }
|
||||
### Field-level (zwracają boolean)
|
||||
|
||||
```ts
|
||||
import { adminOnlyField, adminOrEditorField, requireRoleField } from 'ipal-kit'
|
||||
import { adminOnlyField, adminOrEditorField, requireRoleField } from '@intecion/ipal-kit'
|
||||
|
||||
{
|
||||
name: 'internalNote',
|
||||
@@ -74,6 +74,6 @@ import { adminOnlyField, adminOrEditorField, requireRoleField } from 'ipal-kit'
|
||||
## Hierarchia
|
||||
|
||||
```ts
|
||||
import { ROLE_HIERARCHY } from 'ipal-kit'
|
||||
import { ROLE_HIERARCHY } from '@intecion/ipal-kit'
|
||||
// ['user', 'editor', 'admin'] — wyższa rola spełnia wymóg niższej
|
||||
```
|
||||
@@ -0,0 +1,75 @@
|
||||
# analytics
|
||||
|
||||
Ładuje GA4 albo Google Tag Manager, spięte z modułem consent: Consent Mode
|
||||
dostaje decyzję odwiedzającego, zanim tag się załaduje, i aktualizację w
|
||||
momencie kliknięcia w banerze.
|
||||
|
||||
## Config
|
||||
|
||||
Brak opcji w `payload.config` — plugin czyta ID z globala **Site Integrations**
|
||||
(Settings → Site Integrations):
|
||||
|
||||
- **GA4 Measurement ID** — `G-XXXXXXXXXX`
|
||||
- **GTM Container ID** — `GTM-XXXXXXX`
|
||||
|
||||
Gdy ustawione są oba, wygrywa GTM. Gdy żadne — komponent nie robi nic.
|
||||
|
||||
To jedyne pola z Site Integrations, które trafiają na klienta — są publiczne
|
||||
(widać je w źródle każdej strony z GA). `getAnalyticsConfig` czyta wyłącznie je,
|
||||
więc sekrety (Turnstile, SMTP) nie mają jak wyciec.
|
||||
|
||||
## Front
|
||||
|
||||
W layoucie locale, **wewnątrz `ConsentProvider`**:
|
||||
|
||||
```tsx
|
||||
import { getAnalyticsConfig } from '@intecion/ipal-kit'
|
||||
import { ConsentProvider, CookieBanner, Analytics } from '@intecion/ipal-kit/client'
|
||||
|
||||
export default async function LocaleLayout({ children, params }) {
|
||||
const payload = await getPayload({ config })
|
||||
const analytics = await getAnalyticsConfig(payload) // server
|
||||
|
||||
return (
|
||||
<ConsentProvider texts={texts}>
|
||||
{children}
|
||||
<CookieBanner />
|
||||
<Analytics {...analytics} />
|
||||
</ConsentProvider>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
`Analytics` nie renderuje nic — wstrzykuje skrypty. Zamontuj raz, wysoko w
|
||||
drzewie.
|
||||
|
||||
## Jak działa Consent Mode
|
||||
|
||||
1. Przed załadowaniem tagu: `gtag('consent', 'default', …)` z zapisaną decyzją
|
||||
odwiedzającego (albo wszystko `denied`, gdy jeszcze nie zdecydował).
|
||||
2. Tag się ładuje i respektuje ten stan od pierwszego trafienia.
|
||||
3. Klik w banerze → `gtag('consent', 'update', …)` → tagi reagują natychmiast.
|
||||
|
||||
Mapowanie kategorii na sygnały Google:
|
||||
|
||||
| Kategoria | Sygnały |
|
||||
|---|---|
|
||||
| analytics | `analytics_storage` |
|
||||
| marketing | `ad_storage`, `ad_user_data`, `ad_personalization` |
|
||||
| functional | `functionality_storage`, `personalization_storage` |
|
||||
| necessary | `security_storage` (zawsze `granted`) |
|
||||
|
||||
## Testowanie
|
||||
|
||||
W konsoli: `window.dataLayer` — powinien zawierać `Arguments(3)` z `consent` /
|
||||
`default`, a po decyzji `consent` / `update`. Jeśli widzisz `Array` zamiast
|
||||
`Arguments`, komenda nie zostanie rozpoznana przez Google.
|
||||
|
||||
Ciasteczko `_ga` pojawia się dopiero po zgodzie na analytics — to jest sedno
|
||||
Consent Mode.
|
||||
|
||||
**GTM sam nie ustawia ciasteczek** — to pojemnik. Bez opublikowanego (Submit →
|
||||
Publish, nie sam zapis) tagu GA4 w kontenerze wszystko wygląda dobrze:
|
||||
`gtm.load` w dataLayer, Tag Assistant widzi kontener — a `_ga` nie ma, bo nic go
|
||||
nie tworzy. Przy debugowaniu warto tymczasowo wyczyścić GTM Container ID i
|
||||
zostawić samo GA4, żeby wyeliminować kontener jako zmienną.
|
||||
+2
-2
@@ -12,10 +12,10 @@ Brak opcji w payload.config — bloki definiujesz w swoich kolekcjach
|
||||
|
||||
## Front — RenderBlocks
|
||||
|
||||
Import z `ipal-kit/rsc` (to komponent serwerowy):
|
||||
Import z `@intecion/ipal-kit/rsc` (to komponent serwerowy):
|
||||
|
||||
```tsx
|
||||
import { RenderBlocks } from 'ipal-kit/rsc'
|
||||
import { RenderBlocks } from '@intecion/ipal-kit/rsc'
|
||||
|
||||
// Twój registry: blockType → komponent (komponenty są Twoje)
|
||||
import { Hero } from '@/blocks/Hero'
|
||||
|
||||
+6
-6
@@ -23,12 +23,12 @@ localized. Link do polityki prywatności bierze się z system pages
|
||||
## Front — Provider + banner
|
||||
|
||||
Provider owija aplikację, banner i button renderują się same. Import z
|
||||
`ipal-kit/client`:
|
||||
`@intecion/ipal-kit/client`:
|
||||
|
||||
```tsx
|
||||
// app/(frontend)/[locale]/layout.tsx
|
||||
import { ConsentProvider, CookieBanner, CookieButton } from 'ipal-kit/client'
|
||||
import { getConsentTexts } from 'ipal-kit'
|
||||
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
|
||||
@@ -66,15 +66,15 @@ Domyślne klasy Tailwind można nadpisać przez `classNames`:
|
||||
## Gating skryptów wg zgody
|
||||
|
||||
```ts
|
||||
import { updateConsent, setDefaultConsent } from 'ipal-kit'
|
||||
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
|
||||
`ipal-kit`:
|
||||
`@intecion/ipal-kit`:
|
||||
|
||||
```ts
|
||||
import { parseConsent, CONSENT_COOKIE, CONSENT_CATEGORIES } from 'ipal-kit'
|
||||
import { parseConsent, CONSENT_COOKIE, CONSENT_CATEGORIES } from '@intecion/ipal-kit'
|
||||
// np. gating skryptów server-side na podstawie cookie zgody
|
||||
```
|
||||
|
||||
|
||||
+6
-6
@@ -26,7 +26,7 @@ zarządza tylko przypisaniem archiwum i jego adresem.
|
||||
|
||||
```ts
|
||||
// content.config.ts — współdzielony przez payload.config i front
|
||||
import type { ContentOption } from 'ipal-kit'
|
||||
import type { ContentOption } from '@intecion/ipal-kit'
|
||||
|
||||
export const contentConfig: ContentOption = {
|
||||
collections: [
|
||||
@@ -59,7 +59,7 @@ Serce modułu. Catch-all `[[...slug]]` łapie wszystko, a `resolveRoute` mówi,
|
||||
dana ścieżka jest:
|
||||
|
||||
```ts
|
||||
import { resolveRoute } from 'ipal-kit'
|
||||
import { resolveRoute } from '@intecion/ipal-kit'
|
||||
|
||||
const route = await resolveRoute({
|
||||
payload,
|
||||
@@ -91,7 +91,7 @@ Zamiast pisać cache'owane wrappery w każdym projekcie:
|
||||
|
||||
```ts
|
||||
// src/lib/content.ts
|
||||
import { createContentHelpers } from 'ipal-kit'
|
||||
import { createContentHelpers } from '@intecion/ipal-kit'
|
||||
import config from '@/payload.config'
|
||||
import { contentConfig } from '@/content.config'
|
||||
|
||||
@@ -151,7 +151,7 @@ kolekcji ta strona jest archiwum. Ten sam blok obsługuje `/pl/artykuly` i
|
||||
## Ścieżki i paginacja
|
||||
|
||||
```ts
|
||||
import { buildEntryPath, buildArchivePath, parsePageParam } from 'ipal-kit'
|
||||
import { buildEntryPath, buildArchivePath, parsePageParam } from '@intecion/ipal-kit'
|
||||
|
||||
buildEntryPath({ locale: 'pl', archiveSlug: 'artykuly', entrySlug: 'moj-post' })
|
||||
// '/pl/artykuly/moj-post'
|
||||
@@ -201,6 +201,6 @@ import {
|
||||
parsePageParam,
|
||||
createContentHelpers,
|
||||
archiveFieldName,
|
||||
} from 'ipal-kit'
|
||||
import type { ContentOption, ResolvedRoute, ArchiveEntries } from 'ipal-kit'
|
||||
} from '@intecion/ipal-kit'
|
||||
import type { ContentOption, ResolvedRoute, ArchiveEntries } from '@intecion/ipal-kit'
|
||||
```
|
||||
+27
-2
@@ -19,7 +19,7 @@ Brak opcji — SMTP (host, port, user, password, from) jest w SiteIntegrations
|
||||
## Front — sendEmail (server)
|
||||
|
||||
```ts
|
||||
import { sendEmail } from 'ipal-kit/server'
|
||||
import { sendEmail } from '@intecion/ipal-kit/server'
|
||||
|
||||
const result = await sendEmail({
|
||||
payload,
|
||||
@@ -48,4 +48,29 @@ if (result.sent) {
|
||||
Reset hasła / weryfikacja email idą przez wbudowany mechanizm Payload
|
||||
(`config.email`), którego ten moduł **nie** konfiguruje (celowo — wymagałby
|
||||
SMTP w env). Jeśli ich potrzebujesz, to osobna konfiguracja adaptera przy
|
||||
`buildConfig`.
|
||||
`buildConfig`.
|
||||
|
||||
## Adapter — SMTP z panelu dla całego Payloada
|
||||
|
||||
`sendEmail` wysyła własnym transportem. Osobno plugin daje adapter, który
|
||||
podłącza tę samą skrzynkę pod `payload.sendEmail`:
|
||||
|
||||
```ts
|
||||
// payload.config.ts
|
||||
import { panelSmtpAdapter } from '@intecion/ipal-kit'
|
||||
|
||||
export default buildConfig({
|
||||
email: panelSmtpAdapter(),
|
||||
// panelSmtpAdapter({ fallbackFromAddress, fallbackFromName }) — używane tylko
|
||||
// zanim panel zostanie wypełniony (Payload wymaga adresu synchronicznie przy
|
||||
// starcie, zanim można odczytać globala)
|
||||
})
|
||||
```
|
||||
|
||||
Po wpięciu wszystko, co w Payloadzie wysyła maile, idzie przez SMTP z Site
|
||||
Integrations — w tym wbudowane maile form-buildera (Forms → Emails), maile
|
||||
resetu hasła i weryfikacji konta. Adapter czyta konfigurację przy każdym
|
||||
wysłaniu, więc zmiana skrzynki w panelu działa bez restartu.
|
||||
|
||||
Bez adaptera Payload używa mocka, który tylko loguje do konsoli — maile
|
||||
form-buildera nie wyjdą.
|
||||
+2
-2
@@ -31,7 +31,7 @@ Zwykle w Server Action wywoływanej przez formularz. HTML obu maili składasz
|
||||
na froncie (Twój wygląd):
|
||||
|
||||
```ts
|
||||
import { submitForm } from 'ipal-kit/server'
|
||||
import { submitForm } from '@intecion/ipal-kit/server'
|
||||
|
||||
const result = await submitForm({
|
||||
payload,
|
||||
@@ -86,7 +86,7 @@ wiadomości skonfigurowane przez edytora (Forms → formularz → Emails), przez
|
||||
`payload.sendEmail`. Żeby wyszły, config musi mieć adapter:
|
||||
|
||||
```ts
|
||||
email: panelSmtpAdapter(), // z 'ipal-kit'
|
||||
email: panelSmtpAdapter(), // z '@intecion/ipal-kit'
|
||||
```
|
||||
|
||||
Wtedy idą przez SMTP z Site Integrations. Edytor ustawia odbiorców, temat i
|
||||
|
||||
@@ -22,7 +22,7 @@ export default { plugins: { '@tailwindcss/postcss': {} } }
|
||||
W globalnym CSS (np. app/(frontend)/styles.css):
|
||||
```css
|
||||
@import "tailwindcss";
|
||||
@source "../../../node_modules/ipal-kit/dist/**/*.js";
|
||||
@source "../../../node_modules/@intecion/ipal-kit/dist/**/*.js";
|
||||
```
|
||||
|
||||
**@source jest kluczowy** — Tailwind domyślnie NIE skanuje node_modules, więc
|
||||
@@ -107,7 +107,7 @@ też jedno źródło.
|
||||
// src/middleware.ts
|
||||
import { NextResponse } from 'next/server'
|
||||
import type { NextRequest } from 'next/server'
|
||||
import { createLocaleMiddleware } from 'ipal-kit/next/middleware'
|
||||
import { createLocaleMiddleware } from '@intecion/ipal-kit/next/middleware'
|
||||
import { i18nConfig } from '@/i18n.config'
|
||||
|
||||
const localeMiddleware = createLocaleMiddleware({ config: i18nConfig })
|
||||
@@ -140,8 +140,8 @@ Layout (server) czyta getConsentTexts, przekazuje jako prop do ConsentProvider
|
||||
(client). Banner + button renderują się same:
|
||||
|
||||
```ts
|
||||
import { getConsentTexts } from 'ipal-kit'
|
||||
import { ConsentProvider, CookieBanner, CookieButton } from 'ipal-kit/client'
|
||||
import { getConsentTexts } from '@intecion/ipal-kit'
|
||||
import { ConsentProvider, CookieBanner, CookieButton } from '@intecion/ipal-kit/client'
|
||||
|
||||
const texts = await getConsentTexts({ config, locale, payload, privacyPolicy })
|
||||
// <ConsentProvider texts={texts}>{children}<CookieBanner/><CookieButton/></ConsentProvider>
|
||||
@@ -173,9 +173,9 @@ const enhanceProps = ({ block }) => {
|
||||
|
||||
- FormRenderer (client) — renderuje pola form-buildera (text/email/select/
|
||||
country/checkbox/textarea/number/state/message)
|
||||
- Turnstile widget (ipal-kit/client) — siteKey jako prop, wstrzykiwany przez
|
||||
- Turnstile widget (@intecion/ipal-kit/client) — siteKey jako prop, wstrzykiwany przez
|
||||
enhanceProps (z SiteIntegrations, publiczny — bezpieczny na kliencie)
|
||||
- server action → submitForm (ipal-kit/server) — weryfikuje Turnstile i zapisuje
|
||||
- server action → submitForm (@intecion/ipal-kit/server) — weryfikuje Turnstile i zapisuje
|
||||
|
||||
Maili NIE składa się w kodzie. Po zapisie submission form-builder sam wysyła
|
||||
wiadomości skonfigurowane przez edytora (Forms → dany formularz → Emails:
|
||||
@@ -185,7 +185,7 @@ Email To / CC / BCC / Subject / Message z placeholderami {{pole}}, {{*}},
|
||||
Wymaga w payload.config:
|
||||
|
||||
```ts
|
||||
import { ipalKit, panelSmtpAdapter } from 'ipal-kit'
|
||||
import { ipalKit, panelSmtpAdapter } from '@intecion/ipal-kit'
|
||||
|
||||
export default buildConfig({
|
||||
email: panelSmtpAdapter(),
|
||||
|
||||
+19
-16
@@ -1,8 +1,8 @@
|
||||
# Nowy projekt — krok po kroku
|
||||
|
||||
> **Instalacja:** najszybciej `pnpm add github:rasm-its/ipal-kit`. Pełne drogi
|
||||
> (github / rejestr / tarball) i diagnostyka błędów — install.md.
|
||||
|
||||
> **Instalacja pluginu** (token Gitea, rejestr vs git) jest opisana w głównym
|
||||
> [README](../README.md). Ten przewodnik zakłada, że `@intecion/ipal-kit` jest
|
||||
> już zainstalowany, i przeprowadza przez **konfigurację** projektu.
|
||||
|
||||
Od pustego katalogu do działającej, wielojęzycznej strony z blokami, consentem i
|
||||
formularzem. Kolejność jest istotna: kilka kroków zależy od poprzednich (schemat
|
||||
@@ -20,10 +20,13 @@ npx create-payload-app@latest moj-projekt
|
||||
cd moj-projekt
|
||||
```
|
||||
|
||||
## 2. Instalacja IPAL
|
||||
## 2. Plugin i zależności
|
||||
|
||||
Zainstaluj `@intecion/ipal-kit` zgodnie z [README](../README.md) (rejestr Gitea
|
||||
albo bezpośrednio z repozytorium — wymaga tokenu). Następnie dodaj zależności
|
||||
współdzielone z Payloadem, których plugin nie zaciąga sam:
|
||||
|
||||
```bash
|
||||
pnpm add ipal-kit
|
||||
pnpm add @payloadcms/[email protected] @payloadcms/[email protected] \
|
||||
nodemailer lucide-react slugify server-only
|
||||
```
|
||||
@@ -72,7 +75,7 @@ export const i18nConfig = {
|
||||
## 4. payload.config.ts
|
||||
|
||||
```ts
|
||||
import { ipalKit, panelSmtpAdapter } from 'ipal-kit'
|
||||
import { ipalKit, panelSmtpAdapter } from '@intecion/ipal-kit'
|
||||
import { i18nConfig } from '@/i18n.config'
|
||||
import { Pages } from '@/collections/Pages'
|
||||
|
||||
@@ -101,7 +104,7 @@ export default buildConfig({
|
||||
```ts
|
||||
// src/collections/Pages.ts
|
||||
import type { CollectionConfig } from 'payload'
|
||||
import { buildSlugField } from 'ipal-kit'
|
||||
import { buildSlugField } from '@intecion/ipal-kit'
|
||||
import { ContentBlock } from '@/blocks/Content/config'
|
||||
|
||||
export const Pages: CollectionConfig = {
|
||||
@@ -151,7 +154,7 @@ export function ContentBlockComponent({ heading, body }: { heading?: string; bod
|
||||
|
||||
```ts
|
||||
// src/blocks/registry.ts
|
||||
import type { BlockComponentMap } from 'ipal-kit/rsc'
|
||||
import type { BlockComponentMap } from '@intecion/ipal-kit/rsc'
|
||||
import { ContentBlockComponent } from '@/blocks/Content/Component'
|
||||
|
||||
export const blockRegistry: BlockComponentMap = {
|
||||
@@ -177,7 +180,7 @@ export default { plugins: { '@tailwindcss/postcss': {} } }
|
||||
```css
|
||||
/* src/app/(frontend)/styles.css — na górze */
|
||||
@import "tailwindcss";
|
||||
@source "../../../node_modules/ipal-kit/dist/**/*.js";
|
||||
@source "../../../node_modules/@intecion/ipal-kit/dist/**/*.js";
|
||||
```
|
||||
|
||||
`@source` jest **konieczny** — Tailwind nie skanuje `node_modules`, więc bez
|
||||
@@ -190,7 +193,7 @@ niego klasy komponentów pluginu nie powstaną i banner wyrenderuje się goły.
|
||||
// src/middleware.ts
|
||||
import { NextResponse } from 'next/server'
|
||||
import type { NextRequest } from 'next/server'
|
||||
import { createLocaleMiddleware } from 'ipal-kit/next/middleware'
|
||||
import { createLocaleMiddleware } from '@intecion/ipal-kit/next/middleware'
|
||||
import { i18nConfig } from '@/i18n.config'
|
||||
|
||||
const localeMiddleware = createLocaleMiddleware({ config: i18nConfig })
|
||||
@@ -294,8 +297,8 @@ tablicy (`slug.join is not a function`) i nie łapią samego `/pl`.
|
||||
```tsx
|
||||
// src/app/(frontend)/[locale]/layout.tsx
|
||||
import { notFound } from 'next/navigation'
|
||||
import { getConsentTexts, getAnalyticsConfig } from 'ipal-kit'
|
||||
import { ConsentProvider, CookieBanner, CookieButton, Analytics } from 'ipal-kit/client'
|
||||
import { getConsentTexts, getAnalyticsConfig } from '@intecion/ipal-kit'
|
||||
import { ConsentProvider, CookieBanner, CookieButton, Analytics } from '@intecion/ipal-kit/client'
|
||||
import { i18nConfig } from '@/i18n.config'
|
||||
import { getCachedPayload, getSettings } from '@/lib/payload'
|
||||
import { getConfiguredLocales } from '@/lib/locales'
|
||||
@@ -347,8 +350,8 @@ export async function generateStaticParams() {
|
||||
// src/app/(frontend)/[locale]/[[...slug]]/page.tsx
|
||||
import { notFound } from 'next/navigation'
|
||||
import type { Metadata } from 'next'
|
||||
import { RenderBlocks } from 'ipal-kit/rsc'
|
||||
import { createPageMetadata } from 'ipal-kit'
|
||||
import { RenderBlocks } from '@intecion/ipal-kit/rsc'
|
||||
import { createPageMetadata } from '@intecion/ipal-kit'
|
||||
import { i18nConfig } from '@/i18n.config'
|
||||
import { blockRegistry } from '@/blocks/registry'
|
||||
import { getCachedPayload } from '@/lib/payload'
|
||||
@@ -435,7 +438,7 @@ Pełny opis: content.md. W skrócie:
|
||||
|
||||
2. **content.config.ts** obok i18n.config.ts:
|
||||
```ts
|
||||
import type { ContentOption } from 'ipal-kit'
|
||||
import type { ContentOption } from '@intecion/ipal-kit'
|
||||
export const contentConfig: ContentOption = {
|
||||
collections: [{ slug: 'posts', label: 'Artykuły', perPage: 10 }],
|
||||
}
|
||||
@@ -497,7 +500,7 @@ Pełna lista tokenów: consent.md.
|
||||
|---|---|
|
||||
| Pusty tab SEO / brak kolekcji Forms | rozjazd wersji `@payloadcms/*` — sprawdź `pnpm.overrides` |
|
||||
| `PayloadComponent not found in importMap` | `pnpm payload generate:importmap` |
|
||||
| Banner bez stylów | brak `@source` na `node_modules/ipal-kit` albo brak Tailwinda |
|
||||
| Banner bez stylów | brak `@source` na `node_modules/@intecion/ipal-kit` albo brak Tailwinda |
|
||||
| `/admin` i `/_next` zwracają 500 | matcher w middleware nie jest inline |
|
||||
| `slug.join is not a function` | katalog `[slug]` zamiast `[[...slug]]` |
|
||||
| `/pl` → 404 | Homepage nieustawiony w System Pages |
|
||||
|
||||
+4
-4
@@ -34,7 +34,7 @@ import {
|
||||
getLocaleCodes, getDefaultLocale, isValidLocale, getLocaleDefinition,
|
||||
negotiateLocale, buildLocalizedPath, switchLocalePath,
|
||||
getLocalizedSlugs, LOCALE_COOKIE_NAME,
|
||||
} from 'ipal-kit'
|
||||
} from '@intecion/ipal-kit'
|
||||
|
||||
const config = { defaultLocale: 'pl', locales: [{code:'pl',label:'Polski'},{code:'en',label:'English'}] }
|
||||
|
||||
@@ -69,15 +69,15 @@ fallback na `/{locale}` (root), zamiast dead-endu na 404.
|
||||
## Middleware — patrz osobno
|
||||
|
||||
Negocjacja locale + redirect na wejściu (`domena.com` → `/pl`) jest w
|
||||
`ipal-kit/next/middleware`. Zobacz [middleware w tej sekcji](#middleware)
|
||||
`@intecion/ipal-kit/next/middleware`. Zobacz [middleware w tej sekcji](#middleware)
|
||||
niżej.
|
||||
|
||||
## Middleware
|
||||
|
||||
```ts
|
||||
// next-middleware.ts (projekt klienta) — jedyna logika to podłączenie
|
||||
// middleware.ts (projekt klienta) — jedyna logika to podłączenie
|
||||
import { NextResponse } from 'next/server'
|
||||
import { createLocaleMiddleware, DEFAULT_MIDDLEWARE_MATCHER } from 'ipal-kit/next/middleware'
|
||||
import { createLocaleMiddleware, DEFAULT_MIDDLEWARE_MATCHER } from '@intecion/ipal-kit/next/middleware'
|
||||
|
||||
const i18nConfig = {
|
||||
defaultLocale: 'pl',
|
||||
|
||||
-118
@@ -1,118 +0,0 @@
|
||||
# Instalacja ipal-kit
|
||||
|
||||
Trzy drogi. Wybierz jedną i trzymaj się jej — mieszanie (raz git, raz rejestr,
|
||||
raz tarball) to najczęstsze źródło błędów instalacji.
|
||||
|
||||
## Droga A — z GitHuba (zalecana dla zespołu)
|
||||
|
||||
Wymaga, żeby `dist/` był zacommitowany w repo (nie budowany u instalującego).
|
||||
|
||||
```bash
|
||||
pnpm add github:rasm-its/ipal-kit
|
||||
# konkretny tag (stabilniej):
|
||||
pnpm add github:rasm-its/ipal-kit#v1.0.0
|
||||
```
|
||||
|
||||
Wymagania po stronie projektu:
|
||||
|
||||
1. **`onlyBuiltDependencies`** — pnpm blokuje skrypty build z paczek git.
|
||||
Jeśli pakiet ma jakiekolwiek skrypty postinstall, dodaj w package.json
|
||||
projektu:
|
||||
```json
|
||||
"pnpm": { "onlyBuiltDependencies": ["@intecion/ipal-kit"] }
|
||||
```
|
||||
(Jeśli plugin nie ma `prepare`/postinstall — patrz niżej — to niepotrzebne.)
|
||||
|
||||
2. **peer-zależności** — plugin ich nie zaciąga, projekt musi mieć:
|
||||
```bash
|
||||
pnpm add @payloadcms/[email protected] @payloadcms/[email protected] \
|
||||
nodemailer lucide-react slugify server-only
|
||||
```
|
||||
|
||||
Wymagania po stronie pluginu (raz, przy wydawaniu):
|
||||
|
||||
- **`dist/` w repo** — bo instalacja z git nie buduje. Zbuduj i zacommituj
|
||||
`dist` przed każdym wydaniem.
|
||||
- **BRAK `prepare: pnpm build`** w package.json — inaczej pnpm próbuje budować
|
||||
przy instalacji i żąda `onlyBuiltDependencies`. Skoro `dist` jest w repo, build
|
||||
jest zbędny.
|
||||
- **główny `exports` wskazuje `dist`, nie `src`** — instalacja z git czyta
|
||||
główny `exports` (publishConfig działa TYLKO przy `npm publish`, nie przy git).
|
||||
Wszystkie ścieżki `./dist/*.js` i `./dist/*.d.ts`.
|
||||
- **jeden blok `exports`** — nie zostawiaj `publishConfig.exports` obok głównego;
|
||||
dwa bloki potrafią rozjechać rozwiązywanie modułów.
|
||||
- **żadnej self-reference** — pakiet nie może mieć siebie w `dependencies`
|
||||
(`"@intecion/ipal-kit": "git+..."`). Wchodzi, gdy odpalisz `pnpm add` w
|
||||
katalogu pluginu — NIGDY tego nie rób.
|
||||
|
||||
## Droga B — GitHub Packages (rejestr)
|
||||
|
||||
Publikujesz zbudowany pakiet; instalujący pobiera gotowy `dist`, nie buduje.
|
||||
|
||||
Plugin — `publishConfig.registry` + token z `write:packages`:
|
||||
```bash
|
||||
echo "//npm.pkg.github.com/:_authToken=TOKEN" >> ~/.npmrc
|
||||
npm version patch && npm publish
|
||||
```
|
||||
|
||||
Projekt — `.npmrc` ze scope + token z `read:packages`:
|
||||
```
|
||||
@intecion:registry=https://npm.pkg.github.com
|
||||
//npm.pkg.github.com/:_authToken=TOKEN
|
||||
```
|
||||
```bash
|
||||
pnpm add @intecion/ipal-kit
|
||||
```
|
||||
|
||||
Zaleta nad Drogą A: semver (koniec z ręcznym commitowaniem `dist`), `publishConfig`
|
||||
działa (nie musisz ruszać głównego `exports`). Wada: token u każdego instalującego.
|
||||
|
||||
## Droga C — lokalny tarball (development, przekazanie pliku)
|
||||
|
||||
```bash
|
||||
# w pluginie
|
||||
pnpm build && pnpm pack --out ipal-kit.tgz
|
||||
|
||||
# w PROJEKCIE (nie w pluginie!)
|
||||
pnpm add ~/sciezka/ipal-kit/ipal-kit.tgz
|
||||
```
|
||||
|
||||
`--out ipal-kit.tgz` daje stałą nazwę — bez tego scope zamienia `/` na `-`
|
||||
(`intecion-ipal-kit-1.0.0.tgz`).
|
||||
|
||||
## Twarde zasady (wyparzone w boju)
|
||||
|
||||
- **NIGDY `pnpm add ...ipal-kit...` w katalogu pluginu.** Tworzy self-reference,
|
||||
która zatruwa każdą kolejną instalację. Zawsze w katalogu projektu. Sprawdzaj
|
||||
`pwd` przed każdym `pnpm add`.
|
||||
- **Sprawdzaj REPO, nie plik lokalny.** pnpm z git bierze stan repo. Po zmianie:
|
||||
`git show origin/main:package.json | grep '"import"'` — musi pokazać `./dist/`.
|
||||
Commit z nazwą "fix" nie znaczy, że fix jest w commicie.
|
||||
- **Nie edytuj package.json `sed`em.** Rozjeżdża strukturę (dwa bloki exports).
|
||||
Nadpisuj cały plik.
|
||||
- **Zostajesz na 1.0.0? Czyść cache przy każdym reinstall:**
|
||||
```bash
|
||||
rm -rf node_modules/@intecion node_modules/.pnpm/*ipal-kit* .next
|
||||
pnpm store prune && pnpm install
|
||||
```
|
||||
Publikacja z bumpem wersji (Droga B) to znosi.
|
||||
- **Weryfikuj rozwiązanie modułu, nie tylko instalację:**
|
||||
```bash
|
||||
node -e "console.log(require.resolve('@intecion/ipal-kit/next/middleware'))"
|
||||
```
|
||||
Ma wypisać ścieżkę do `dist`, nie błąd. Jeśli pokazuje `src/...ts` → główny
|
||||
`exports` wskazuje src (patrz Droga A).
|
||||
|
||||
## Diagnostyka — objaw → przyczyna
|
||||
|
||||
| Objaw | Przyczyna |
|
||||
|---|---|
|
||||
| `ERR_PNPM_FETCH_404` na `@scope/...` | pakiet nieopublikowany; użyto nazwy rejestrowej zamiast `github:` |
|
||||
| `ERR_PNPM_GIT_DEP_PREPARE_NOT_ALLOWED` | `prepare` w package.json + brak `onlyBuiltDependencies` |
|
||||
| `Cannot find module .../src/exports/X.ts` | główny `exports` wskazuje src; instalacja z git nie widzi publishConfig |
|
||||
| `ENOENT ...ipal-kit-1.0.0.tgz` przy instalacji | self-reference w dependencies pluginu |
|
||||
| Turbopack „Module not found" mimo pliku na dysku | dwa bloki exports w package.json; Node bierze zły |
|
||||
| `pnpm add` przeszło, pakietu brak w node_modules | instalacja do złego katalogu, albo przerwana — sprawdź `pwd` |
|
||||
| stary kod mimo reinstall (1.0.0) | cache; `pnpm store prune` + `rm -rf .next` |
|
||||
|
||||
Po instalacji — konfiguracja pluginu: getting-started.md.
|
||||
+2
-2
@@ -22,7 +22,7 @@ zna Twojej kolekcji — slug podajesz w opcji.
|
||||
## Front — rozwiązanie ścieżki roli
|
||||
|
||||
```ts
|
||||
import { getSystemPagePath } from 'ipal-kit'
|
||||
import { getSystemPagePath } from '@intecion/ipal-kit'
|
||||
|
||||
// SiteSettings z locale:'all' + depth:1 (żeby relationship był obiektem, nie ID)
|
||||
const settings = await payload.findGlobal({
|
||||
@@ -50,6 +50,6 @@ stopce / bannerze cookies bierzesz z `getSystemPagePath({ role: privacyPolicy })
|
||||
## Role
|
||||
|
||||
```ts
|
||||
import { ALL_SYSTEM_PAGE_ROLES } from 'ipal-kit'
|
||||
import { ALL_SYSTEM_PAGE_ROLES } from '@intecion/ipal-kit'
|
||||
// ['homepage', 'privacyPolicy', 'cookiePolicy']
|
||||
```
|
||||
@@ -11,7 +11,7 @@ Brak — te globale są zawsze budowane przez plugin. Nie ma osobnej opcji.
|
||||
## Front — odczyt globali
|
||||
|
||||
```ts
|
||||
import { getSiteSettings, getSiteIntegrations } from 'ipal-kit'
|
||||
import { getSiteSettings, getSiteIntegrations } from '@intecion/ipal-kit'
|
||||
import type { SiteSetting, SiteIntegration } from '@/payload-types'
|
||||
|
||||
const payload = await getPayload({ config })
|
||||
@@ -49,6 +49,6 @@ return <Form siteKey={turnstileSiteKey} />
|
||||
## Niższy poziom: getGlobal
|
||||
|
||||
```ts
|
||||
import { getGlobal } from 'ipal-kit'
|
||||
import { getGlobal } from '@intecion/ipal-kit'
|
||||
const data = await getGlobal<MyType>(payload, 'moj-global', { locale: 'pl', depth: 1 })
|
||||
```
|
||||
@@ -1,70 +0,0 @@
|
||||
# Publikacja ipal-kit — checklist
|
||||
|
||||
## package.json — wymagane pola
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "@intecion/ipal-kit", // scoped pod organizację
|
||||
"version": "1.0.0", // BUMP przy każdej publikacji (semver)
|
||||
"files": ["dist"], // tylko dist trafia do pakietu (NIE src)
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
"exports": { ... }, // wszystkie entry points na ./dist/*
|
||||
"publishConfig": {
|
||||
"registry": "https://npm.pkg.github.com",
|
||||
"exports": { ... } // mirror z ./dist (masz to już zrobione)
|
||||
},
|
||||
"scripts": {
|
||||
"build": "...",
|
||||
"prepublishOnly": "pnpm clean && pnpm build" // build ZAWSZE przed publish
|
||||
},
|
||||
"peerDependencies": { // NIE dependencies — klient już je ma
|
||||
"payload": "3.84.1",
|
||||
"next": ">=15",
|
||||
"react": ">=19"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Krytyczne przed pierwszą publikacją
|
||||
|
||||
- [ ] **peerDependencies zamiast dependencies** dla payload/next/react —
|
||||
inaczej pakiet zaciąga drugą kopię Payloada i wszystko się sypie.
|
||||
@payloadcms/plugin-seo i plugin-form-builder też jako peer (klient pinuje).
|
||||
- [ ] **`"files": ["dist"]`** — bez tego do pakietu trafia src/ (widzieliśmy to
|
||||
w stack trace). Sam dist.
|
||||
- [ ] **usuń self-reference** — sprawdź, że w dependencies NIE ma
|
||||
"ipal-kit": "file:..." (ta zaraza z pnpm add w złym katalogu).
|
||||
- [ ] **prepublishOnly** buduje przed publikacją — nigdy nie publikuj ręcznie
|
||||
zbudowanego dist (łatwo o nieaktualny).
|
||||
- [ ] **bump wersji** — koniec z 1.0.0 na zawsze. Każda publikacja = nowy numer.
|
||||
To rozwiązuje cały cykl cache/store prune, który gryzł podczas developmentu.
|
||||
|
||||
## Publikacja (GitHub Packages)
|
||||
|
||||
```bash
|
||||
# jednorazowo: token GitHuba z prawami write:packages w ~/.npmrc
|
||||
echo "//npm.pkg.github.com/:_authToken=TWÓJ_TOKEN" >> ~/.npmrc
|
||||
|
||||
# przy każdym wydaniu
|
||||
npm version patch # 1.0.0 → 1.0.1 (albo minor/major)
|
||||
npm publish
|
||||
```
|
||||
|
||||
## Instalacja u pracownika
|
||||
|
||||
```bash
|
||||
# ~/.npmrc w projekcie albo globalnie
|
||||
@intecion:registry=https://npm.pkg.github.com
|
||||
//npm.pkg.github.com/:_authToken=ICH_TOKEN
|
||||
|
||||
# potem normalnie
|
||||
pnpm add @intecion/ipal-kit
|
||||
pnpm add @payloadcms/[email protected] @payloadcms/[email protected] \
|
||||
nodemailer lucide-react slugify server-only
|
||||
```
|
||||
|
||||
## README pakietu
|
||||
|
||||
Wskaż na docs/getting-started.md jako pierwszy krok. Pracownik z dostępem do
|
||||
rejestru + getting-started postawi projekt bez pytania Ciebie o nic.
|
||||
+6
-6
@@ -60,7 +60,7 @@ Pages, grupa meta), więc nie trzeba mu tego opisywać:
|
||||
|
||||
```ts
|
||||
// app/(frontend)/[locale]/[[...slug]]/page.tsx
|
||||
import { createPageMetadata } from 'ipal-kit'
|
||||
import { createPageMetadata } from '@intecion/ipal-kit'
|
||||
import { i18nConfig } from '@/i18n.config'
|
||||
|
||||
const pageMetadata = createPageMetadata({
|
||||
@@ -93,7 +93,7 @@ Dla tras spoza konwencji: inna kolekcja (blog), własna logika obrazka, inny
|
||||
global. Klient dostarcza resolvery.
|
||||
|
||||
```ts
|
||||
import { createMetadataGenerator } from 'ipal-kit'
|
||||
import { createMetadataGenerator } from '@intecion/ipal-kit'
|
||||
|
||||
const generate = createMetadataGenerator({
|
||||
config: i18nConfig,
|
||||
@@ -148,7 +148,7 @@ Next uruchamia `generateMetadata` i komponent strony niezależnie — bez React
|
||||
Gdy chcesz pełną kontrolę:
|
||||
|
||||
```ts
|
||||
import { buildMetadata, getLocalizedSlugs } from 'ipal-kit'
|
||||
import { buildMetadata, getLocalizedSlugs } from '@intecion/ipal-kit'
|
||||
|
||||
return buildMetadata({
|
||||
meta: doc.meta, // z plugin-seo
|
||||
@@ -168,7 +168,7 @@ return buildMetadata({
|
||||
## Pomocnicze
|
||||
|
||||
```ts
|
||||
import { composeTitle, buildHreflangAlternates } from 'ipal-kit'
|
||||
import { composeTitle, buildHreflangAlternates } from '@intecion/ipal-kit'
|
||||
|
||||
composeTitle({ pageTitle: 'O nas', siteName: 'Acme' })
|
||||
// 'O nas | Acme'
|
||||
@@ -184,7 +184,7 @@ buildHreflangAlternates({ slugs, config, baseUrl }) // { pl: '...', en: '...' }
|
||||
eksportowanych, gdybyś budował własny generator metadanych:
|
||||
|
||||
```ts
|
||||
import { readSiteMetaConfig, slugsAcrossLocales } from 'ipal-kit'
|
||||
import { readSiteMetaConfig, slugsAcrossLocales } from '@intecion/ipal-kit'
|
||||
|
||||
// nazwa witryny, separator (dopełniony), kolejność, homeSlug — z SiteSettings
|
||||
const site = await readSiteMetaConfig({ payload, locale })
|
||||
@@ -239,7 +239,7 @@ Pomija: drafty (`_status !== 'published'`) i dokumenty z `meta.noindex`.
|
||||
Niskopoziomowo (własna trasa zamiast handlera z fabryki):
|
||||
|
||||
```ts
|
||||
import { buildSitemapEntries, buildRobots } from 'ipal-kit'
|
||||
import { buildSitemapEntries, buildRobots } from '@intecion/ipal-kit'
|
||||
|
||||
const entries = await buildSitemapEntries({
|
||||
payload, config: i18nConfig, baseUrl, content: contentConfig,
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
# slug
|
||||
|
||||
Pole slug generowane automatycznie z tytułu, per locale.
|
||||
|
||||
## Użycie (kolekcja klienta)
|
||||
|
||||
```ts
|
||||
import { buildSlugField } from '@intecion/ipal-kit'
|
||||
|
||||
export const Pages: CollectionConfig = {
|
||||
slug: 'pages',
|
||||
fields: [
|
||||
{ name: 'title', type: 'text', required: true, localized: true },
|
||||
buildSlugField({ from: 'title' }),
|
||||
// ...
|
||||
],
|
||||
}
|
||||
```
|
||||
|
||||
Zwraca gotowe pole: `slug`, text, localized, required, z hookiem
|
||||
`beforeValidate`.
|
||||
|
||||
## Zachowanie
|
||||
|
||||
Slug generuje się z pola źródłowego **tylko gdy jest pusty**. Cokolwiek edytor
|
||||
wpisze ręcznie, zostaje — także po zmianie tytułu. Zmiana adresu opublikowanej
|
||||
strony to zerwane linki, więc plugin nigdy nie robi tego sam.
|
||||
|
||||
Diakrytyki idą przez `slugify`: „Strona główna" → `strona-glowna`.
|
||||
|
||||
## Per locale
|
||||
|
||||
Slug jest zlokalizowany — każdy język ma własny. Edytując w PL ustawiasz
|
||||
`slug.pl`, w EN `slug.en`.
|
||||
|
||||
**Konsekwencja:** slug generuje się tylko dla locale, w którym wypełniono tytuł.
|
||||
Utworzysz stronę po polsku, przełączysz front na `/en/…` — 404, bo `slug.en`
|
||||
jest puste. Trzeba przełączyć locale w adminie i wpisać tytuł po angielsku.
|
||||
|
||||
To ta sama mapa slugów, z której powstają hreflang alternates (patrz seo.md) i
|
||||
przełącznik języka (patrz i18n.md).
|
||||
|
||||
## Opcje
|
||||
|
||||
```ts
|
||||
buildSlugField({
|
||||
from: 'title', // pole źródłowe
|
||||
name: 'slug', // nazwa pola (domyślnie 'slug')
|
||||
required: true, // domyślnie true
|
||||
})
|
||||
```
|
||||
|
||||
## Pomocnicze
|
||||
|
||||
```ts
|
||||
import { toSlug } from '@intecion/ipal-kit'
|
||||
|
||||
toSlug('Strona główna') // 'strona-glowna'
|
||||
```
|
||||
+4
-4
@@ -11,11 +11,11 @@ SiteIntegrations (tab Turnstile). Edytor wpisuje klucze w panelu.
|
||||
|
||||
## Front — widget (client)
|
||||
|
||||
Import z `ipal-kit/client`. `siteKey` pobierz server-side i przekaż jako prop:
|
||||
Import z `@intecion/ipal-kit/client`. `siteKey` pobierz server-side i przekaż jako prop:
|
||||
|
||||
```tsx
|
||||
// server component — pobiera publiczny siteKey z panelu
|
||||
import { getSiteIntegrations } from 'ipal-kit'
|
||||
import { getSiteIntegrations } from '@intecion/ipal-kit'
|
||||
|
||||
const { turnstileSiteKey } = await getSiteIntegrations(payload)
|
||||
// przekaż do swojego client-formularza → widget
|
||||
@@ -23,7 +23,7 @@ const { turnstileSiteKey } = await getSiteIntegrations(payload)
|
||||
|
||||
```tsx
|
||||
'use client'
|
||||
import { Turnstile } from 'ipal-kit/client'
|
||||
import { Turnstile } from '@intecion/ipal-kit/client'
|
||||
import { useState } from 'react'
|
||||
|
||||
function ContactForm({ siteKey }) {
|
||||
@@ -44,7 +44,7 @@ Widget ładuje skrypt Turnstile sam (bez `next/script`), zwraca token przez
|
||||
## Front — verify (server)
|
||||
|
||||
```ts
|
||||
import { verifyTurnstile } from 'ipal-kit/server'
|
||||
import { verifyTurnstile } from '@intecion/ipal-kit/server'
|
||||
|
||||
const ok = await verifyTurnstile({ token, payload, ip })
|
||||
if (!ok) {
|
||||
|
||||
Reference in New Issue
Block a user