feat(cms): LegalPages collection + migration (MITHOME-88)
CI Pipeline with Test Management / 🧪 Run Tests & Generate Reports (push) Waiting to run
CI Pipeline with Test Management / 🐳 Docker Integration Tests (push) Blocked by required conditions
CI Pipeline with Test Management / 🏗️ Build Docker Image (push) Blocked by required conditions
CI Pipeline with Test Management / 📊 Generate Test Summary (push) Blocked by required conditions
Test Reporting & Gherkin Analysis / 🧪 Run Tests & Generate Reports (push) Waiting to run
Test Reporting & Gherkin Analysis / 📊 Analyze Test Coverage (push) Blocked by required conditions
Test Reporting & Gherkin Analysis / 🔄 Sync with Linear (push) Blocked by required conditions
Test Reporting & Gherkin Analysis / ⚡ Performance Monitoring (push) Blocked by required conditions

Slug-based collection mirroring LegalPageContent (proto/src/content/
types.ts) for the two legal pages (adatvedelem, hasznalati-feltetelek).

- src/collections/LegalPages.ts: slug (unique), title, lastUpdated,
  sections[] (id/title/content). `content` stays a plain textarea, not
  lexical richText — the current frontend
  (src/app/(frontend)/adatvedelem/page.tsx) renders it through a
  hand-rolled "•"/"**bold**" regex converter, not a real Markdown/
  richText parser, matching the same bootstrap-scope call made for
  Contact.gdpr.label in MITHOME-87.
- Registered in payload.config.ts.
- migrate-content-to-payload.ts: added an idempotent upsertLegalPage
  helper (find-by-slug, then update or create — Collections don't have
  Globals' fixed-slug updateGlobal) and seeded both legal pages from
  their existing JSON.

Verified: migration run twice against the real dev MongoDB produced
exactly 2 documents (no duplicates) — confirmed via mongosh. Real
browser: logged into /admin, Legal Pages list shows both entries with
correct titles/slugs, opened the adatvedelem document and the slug/
title/body fields all show the migrated content correctly. build/lint/
tsc/test (58 passed) all clean.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Do Siki
2026-09-10 12:16:09 +02:00
co-authored by Claude Sonnet 5
parent 570f4b45e5
commit 17c6d63ae3
3 changed files with 100 additions and 9 deletions
+46 -7
View File
@@ -1,16 +1,18 @@
/** /**
* MITHOME-87: egyszeri migrációs script — a proto/src/content/pages/*.json * MITHOME-87/88: egyszeri migrációs script — a proto/src/content/pages/*.json
* + common.json tartalmát átemeli a Payload Globals-ekbe (Home, About, * + common.json tartalmát átemeli a Payload Globals-ekbe (Home, About,
* Services, Contact, Common) a Local API-n keresztül. * Services, Contact, Common) és a LegalPages collection-be a Local API-n
* keresztül.
* *
* Futtatás (proto/ mappából, futó MongoDB-vel és beállított env-ekkel): * Futtatás (proto/ mappából, futó MongoDB-vel és beállított env-ekkel):
* node --env-file=.env.local --import tsx scripts/migrate-content-to-payload.ts * node --env-file=.env.local --import tsx scripts/migrate-content-to-payload.ts
* *
* Idempotens: updateGlobal-t hív, tetszőlegesen többször futtatható — * Idempotens: updateGlobal-t / slug-alapú upsert-et hív, tetszőlegesen
* mindig a JSON az aktuális "forrás igazság", felülírja a Payload-ban * többször futtatható — mindig a JSON az aktuális "forrás igazság", felül-
* lévő korábbi állapotot. NEM törli/nem érinti a JSON fájlokat. * írja a Payload-ban lévő korábbi állapotot. NEM törli/nem érinti a JSON
* fájlokat.
*/ */
import { getPayload } from 'payload' import { getPayload, type Payload } from 'payload'
import config from '../src/payload.config' import config from '../src/payload.config'
import homeJson from '../src/content/pages/home.json' import homeJson from '../src/content/pages/home.json'
@@ -18,6 +20,8 @@ import aboutJson from '../src/content/pages/about.json'
import servicesJson from '../src/content/pages/services.json' import servicesJson from '../src/content/pages/services.json'
import contactJson from '../src/content/pages/contact.json' import contactJson from '../src/content/pages/contact.json'
import commonJson from '../src/content/common.json' import commonJson from '../src/content/common.json'
import adatvedelemJson from '../src/content/pages/adatvedelem.json'
import hasznalatiFeltetelekJson from '../src/content/pages/hasznalati-feltetelek.json'
/** string[] -> [{ value: string }] — lásd src/globals/fields/stringArray.ts */ /** string[] -> [{ value: string }] — lásd src/globals/fields/stringArray.ts */
function toStringArray(items: readonly string[]): { value: string }[] { function toStringArray(items: readonly string[]): { value: string }[] {
@@ -76,6 +80,35 @@ function buildServicesData(json: typeof servicesJson) {
} }
} }
type LegalPageJson = {
title: string
lastUpdated: string
sections: { id: string; title: string; content: string }[]
}
/** Collections have no per-document "known slug" API like Globals do —
* find-by-slug, then update or create. Idempotent across re-runs. */
async function upsertLegalPage(payload: Payload, slug: string, json: LegalPageJson) {
const existing = await payload.find({
collection: 'legal-pages',
where: { slug: { equals: slug } },
limit: 1,
})
if (existing.docs.length > 0) {
await payload.update({
collection: 'legal-pages',
id: existing.docs[0].id,
data: { slug, ...json },
})
} else {
await payload.create({
collection: 'legal-pages',
data: { slug, ...json },
})
}
}
async function run() { async function run() {
const payload = await getPayload({ config }) const payload = await getPayload({ config })
@@ -94,7 +127,13 @@ async function run() {
await payload.updateGlobal({ slug: 'common', data: commonJson }) await payload.updateGlobal({ slug: 'common', data: commonJson })
payload.logger.info('Common global migrálva') payload.logger.info('Common global migrálva')
payload.logger.info('MITHOME-87 migráció kész.') await upsertLegalPage(payload, 'adatvedelem', adatvedelemJson)
payload.logger.info('LegalPages/adatvedelem migrálva')
await upsertLegalPage(payload, 'hasznalati-feltetelek', hasznalatiFeltetelekJson)
payload.logger.info('LegalPages/hasznalati-feltetelek migrálva')
payload.logger.info('MITHOME-87/88 migráció kész.')
process.exit(0) process.exit(0)
} }
+50
View File
@@ -0,0 +1,50 @@
import type { CollectionConfig } from 'payload'
/**
* Mirrors LegalPageContent (proto/src/content/types.ts) — MITHOME-88.
* Slug-alapú collection a jogi oldalaknak (adatvedelem, hasznalati-feltetelek).
*
* WHY `content` textarea és nem lexical richText: a jelenlegi frontend
* (proto/src/app/(frontend)/adatvedelem/page.tsx) a szekció-tartalmat egy
* kézzel írt regex-alapú "•"/"**bold**" -> HTML konverzióval rendereli, nem
* valódi Markdown- vagy richText-parserrel. Ugyanaz a döntés, mint a
* Contact global gdpr.label mezőjénél (MITHOME-87) — a bootstrap szinten
* nem vezetünk be külön szerializációt egy még nem létező render-rétegért.
*/
export const LegalPages: CollectionConfig = {
slug: 'legal-pages',
admin: {
useAsTitle: 'title',
group: 'Oldalak',
},
fields: [
{
name: 'slug',
type: 'text',
required: true,
unique: true,
admin: {
description: 'URL-azonosító, pl. "adatvedelem" vagy "hasznalati-feltetelek".',
},
},
{ name: 'title', type: 'text', required: true },
{
name: 'lastUpdated',
type: 'text',
required: true,
admin: {
description: 'Szabad szöveg (pl. "2026. augusztus 22."), nem dátum mező — így marad a JSON forrással kompatibilis.',
},
},
{
name: 'sections',
type: 'array',
required: true,
fields: [
{ name: 'id', type: 'text', required: true },
{ name: 'title', type: 'text', required: true },
{ name: 'content', type: 'textarea', required: true },
],
},
],
}
+4 -2
View File
@@ -7,7 +7,8 @@
* *
* MITHOME-87: Home/About/Services/Contact/Common Globals hozzáadva — ezek * MITHOME-87: Home/About/Services/Contact/Common Globals hozzáadva — ezek
* tükrözik a proto/src/content/pages/*.json + common.json struktúráját. * tükrözik a proto/src/content/pages/*.json + common.json struktúráját.
* A LegalPages/Partners/Media collectionök a MITHOME-88..89-ben kerülnek be. * MITHOME-88: LegalPages collection hozzáadva (adatvedelem, hasznalati-
* feltetelek). A Partners/Media collectionök a MITHOME-89-ben kerülnek be.
*/ */
import path from 'path' import path from 'path'
import { fileURLToPath } from 'url' import { fileURLToPath } from 'url'
@@ -17,6 +18,7 @@ import { lexicalEditor } from '@payloadcms/richtext-lexical'
import sharp from 'sharp' import sharp from 'sharp'
import { Users } from './collections/Users' import { Users } from './collections/Users'
import { LegalPages } from './collections/LegalPages'
import { Home } from './globals/Home' import { Home } from './globals/Home'
import { About } from './globals/About' import { About } from './globals/About'
import { Services } from './globals/Services' import { Services } from './globals/Services'
@@ -35,7 +37,7 @@ export default buildConfig({
editor: lexicalEditor(), editor: lexicalEditor(),
collections: [Users], collections: [Users, LegalPages],
globals: [Home, About, Services, Contact, Common], globals: [Home, About, Services, Contact, Common],