init commit for iPAL-kit plugin

This commit is contained in:
2026-07-18 20:30:23 +02:00
parent 10638127a9
commit 5733a9c8bf
119 changed files with 6892 additions and 411 deletions
+66
View File
@@ -0,0 +1,66 @@
import type { I18nConfig } from '../i18n/index.js'
import type { SystemPageRole } from './types.js'
import { buildLocalizedPath, getLocalizedSlugs } from '../i18n/index.js'
/**
* The shape we need from an assigned system-page document: its localized slug
* field, as returned by Payload when the parent is read with `locale: 'all'`.
*
* The plugin doesn't know the client's Pages type, so it depends only on this
* minimal contract.
*/
type AssignedPage = {
slug?: null | Record<string, unknown>
}
type GetSystemPagePathArgs = {
config: I18nConfig
/**
* Slug that represents the site root. Defaults to 'home'. When the assigned
* page's slug in the target locale equals this, the path collapses to the
* locale root (/pl, /en).
*/
homeSlug?: string
/** Target locale to build the path for. */
locale: string
/**
* The resolved system-page assignment from SiteSettings — the related
* document object (not just an ID), read with `locale: 'all'` so its slug
* is a locale→value map. Pass null/undefined if the role is unassigned.
*/
page: AssignedPage | null | undefined
}
/**
* Resolves a system-page assignment to a locale-aware path.
*
* Bridges the Pages module (which document plays a role) and the i18n module
* (how that document's localized slug becomes a URL). This is what powers
* "visit /pl → serve the homepage": read SiteSettings.homepage, pass the
* related document here, get /pl/strona-glowna (or /pl if it's the home slug).
*
* Returns undefined when the role is unassigned or the assigned page has no
* slug in the target locale — the caller decides the fallback (e.g. 404,
* redirect to default locale).
*
* @example
* const settings = await payload.findGlobal({ slug: 'site-settings', locale: 'all', depth: 1 })
* getSystemPagePath({ page: settings.homepage, locale: 'pl', config })
* // → '/pl' (home slug collapses to root)
* getSystemPagePath({ page: settings.privacyPolicy, locale: 'en', config })
* // → '/en/privacy-policy'
*/
export function getSystemPagePath({
config,
homeSlug = 'home',
locale,
page,
}: GetSystemPagePathArgs): string | undefined {
if (!page?.slug) {
return undefined
}
const slugs = getLocalizedSlugs({ config, slugField: page.slug })
return buildLocalizedPath({ config, homeSlug, locale, slugs })
}
+4
View File
@@ -0,0 +1,4 @@
export { getSystemPagePath } from './getSystemPagePath.js'
export { buildSystemPagesFields } from './systemPagesFields.js'
export type { PagesOption, SystemPageRole } from './types.js'
export { ALL_SYSTEM_PAGE_ROLES } from './types.js'
+43
View File
@@ -0,0 +1,43 @@
import type { Field, RelationshipField } from 'payload'
import type { PagesOption, SystemPageRole } from './types.js'
import { ALL_SYSTEM_PAGE_ROLES } from './types.js'
/** Admin-facing labels per role. */
const ROLE_LABELS: Record<SystemPageRole, string> = {
cookiePolicy: 'Cookie Policy',
homepage: 'Homepage',
privacyPolicy: 'Privacy Policy',
}
/** Admin descriptions per role. */
const ROLE_DESCRIPTIONS: Record<SystemPageRole, string> = {
cookiePolicy: 'Page linked from the cookie consent banner.',
homepage: 'Page served at the locale root (e.g. /pl, /en).',
privacyPolicy: 'Page linked as the privacy policy.',
}
/**
* Builds one relationship field per configured system-page role.
*
* Each field points at the client's Pages collection (via the provided slug)
* and holds a single document reference. The referenced document's slug is
* localized, so the same assignment resolves to /pl/strona-glowna and
* /en/home at runtime — role assignment is locale-agnostic, path resolution
* is locale-aware (see getSystemPagePath).
*/
export function buildSystemPagesFields(pages: PagesOption): Field[] {
const roles = pages.roles?.length ? pages.roles : [...ALL_SYSTEM_PAGE_ROLES]
return roles.map((role): RelationshipField => ({
name: role,
type: 'relationship',
admin: {
description: ROLE_DESCRIPTIONS[role],
},
label: ROLE_LABELS[role],
maxDepth: 1,
relationTo: pages.slug,
}))
}
+32
View File
@@ -0,0 +1,32 @@
/**
* Built-in "system page" roles — documents from the client's Pages
* collection that the site references by function rather than by slug.
*/
export type SystemPageRole = 'cookiePolicy' | 'homepage' | 'privacyPolicy'
/**
* All system roles in a stable order (used when no subset is configured).
*/
export const ALL_SYSTEM_PAGE_ROLES: readonly SystemPageRole[] = [
'homepage',
'privacyPolicy',
'cookiePolicy',
]
/**
* Plugin option enabling system-page assignments in SiteSettings.
*
* The plugin doesn't own the Pages collection — the client creates it in
* their own project. This option hands the plugin the collection's slug so
* it can build relationship fields pointing at it. No hardcoding: the slug
* always comes from the client.
*/
export type PagesOption = {
/**
* Which system roles to expose as assignable fields.
* Defaults to all roles when omitted.
*/
roles?: SystemPageRole[]
/** Slug of the client's Pages collection, e.g. 'pages'. */
slug: string
}