From 776fa66bc8ee83667ee637bde0eb0be2abab8fb4 Mon Sep 17 00:00:00 2001 From: Do Siki Date: Thu, 10 Sep 2026 12:33:34 +0200 Subject: [PATCH] feat(cms): Partners + Media collection MVP (MITHOME-89) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Payload built-in upload collection (Media) + a Partners collection (name, url, logo -> Media relationship) mirroring home.json's partners.items. Scope decision (see Plane MITHOME-89/118): the old logo editor (crop/rotate/transparent-background — MITHOME-76/84) is entirely client-side canvas logic (scripts/cms-logo-client.js, 345 lines), not server-side processing. Porting that UX into the Payload admin is a real custom React field component, split into its own ticket (MITHOME-118) rather than bundled here. This ticket covers plain upload only. - src/collections/Media.ts, src/collections/Partners.ts, registered in payload.config.ts. - migrate-content-to-payload.ts: upsertPartner() uploads the existing processed logo file (filePath) into Media (idempotent — matched by `alt` == partner name) and upserts the Partner document (matched by `name`). - .gitignore: Payload's default local upload storage lands at proto/media/ (not proto/public/) — runtime data, not source, needs a persistent volume in staging/production (flagged for MITHOME-97). Also ignored the generated src/payload-types.ts. Verified: migration run twice against the real dev MongoDB produced exactly 1 Media doc + 1 Partner doc (no duplicates, confirmed via mongosh) with the correct file size (12289 bytes, matching the source PNG). Real browser: logged into /admin, Partners list shows the migrated entry, and the document editor renders the logo thumbnail (270x80, 12KB) correctly. build/lint/tsc/test (58 passed) all clean. Co-Authored-By: Claude Sonnet 5 --- proto/.gitignore | 6 +++ proto/scripts/migrate-content-to-payload.ts | 60 ++++++++++++++++++++- proto/src/collections/Media.ts | 33 ++++++++++++ proto/src/collections/Partners.ts | 25 +++++++++ proto/src/payload.config.ts | 8 ++- 5 files changed, 129 insertions(+), 3 deletions(-) create mode 100644 proto/src/collections/Media.ts create mode 100644 proto/src/collections/Partners.ts diff --git a/proto/.gitignore b/proto/.gitignore index 5ef6a52..7f376ea 100755 --- a/proto/.gitignore +++ b/proto/.gitignore @@ -39,3 +39,9 @@ yarn-error.log* # typescript *.tsbuildinfo next-env.d.ts + +# Payload CMS — local upload storage (MITHOME-89). Runtime data, not source; +# needs a persistent Docker volume in staging/production (see MITHOME-97). +/media +# Payload CMS — generated TS types (payload.config.ts typescript.outputFile) +src/payload-types.ts diff --git a/proto/scripts/migrate-content-to-payload.ts b/proto/scripts/migrate-content-to-payload.ts index 8c12c3c..384c755 100644 --- a/proto/scripts/migrate-content-to-payload.ts +++ b/proto/scripts/migrate-content-to-payload.ts @@ -12,6 +12,8 @@ * írja a Payload-ban lévő korábbi állapotot. NEM törli/nem érinti a JSON * fájlokat. */ +import path from 'path' +import { fileURLToPath } from 'url' import { getPayload, type Payload } from 'payload' import config from '../src/payload.config' @@ -23,6 +25,9 @@ import commonJson from '../src/content/common.json' import adatvedelemJson from '../src/content/pages/adatvedelem.json' import hasznalatiFeltetelekJson from '../src/content/pages/hasznalati-feltetelek.json' +const scriptDir = path.dirname(fileURLToPath(import.meta.url)) +const publicDir = path.resolve(scriptDir, '../public') + /** string[] -> [{ value: string }] — lásd src/globals/fields/stringArray.ts */ function toStringArray(items: readonly string[]): { value: string }[] { return items.map((value) => ({ value })) @@ -109,6 +114,54 @@ async function upsertLegalPage(payload: Payload, slug: string, json: LegalPageJs } } +type PartnerJson = { name: string; url: string; logo: string } + +/** + * MITHOME-89 (MVP): a home.json partners.items publikus /partners/*.png + * útvonalait tölti fel Media collection dokumentumként, majd Partner + * rekordot hoz létre/frissít rá hivatkozva. Idempotens: a Media dokumentumot + * `alt` (== partner név) alapján, a Partnert `name` alapján keresi. + */ +async function upsertPartner(payload: Payload, partner: PartnerJson) { + const existingMedia = await payload.find({ + collection: 'media', + where: { alt: { equals: partner.name } }, + limit: 1, + }) + + let mediaId: string | number + if (existingMedia.docs.length > 0) { + mediaId = existingMedia.docs[0].id + } else { + const filePath = path.resolve(publicDir, partner.logo.replace(/^\//, '')) + const created = await payload.create({ + collection: 'media', + data: { alt: partner.name }, + filePath, + }) + mediaId = created.id + } + + const existingPartner = await payload.find({ + collection: 'partners', + where: { name: { equals: partner.name } }, + limit: 1, + }) + + if (existingPartner.docs.length > 0) { + await payload.update({ + collection: 'partners', + id: existingPartner.docs[0].id, + data: { name: partner.name, url: partner.url, logo: mediaId }, + }) + } else { + await payload.create({ + collection: 'partners', + data: { name: partner.name, url: partner.url, logo: mediaId }, + }) + } +} + async function run() { const payload = await getPayload({ config }) @@ -133,7 +186,12 @@ async function run() { await upsertLegalPage(payload, 'hasznalati-feltetelek', hasznalatiFeltetelekJson) payload.logger.info('LegalPages/hasznalati-feltetelek migrálva') - payload.logger.info('MITHOME-87/88 migráció kész.') + for (const partner of homeJson.partners.items) { + await upsertPartner(payload, partner) + payload.logger.info(`Partners/${partner.name} migrálva`) + } + + payload.logger.info('MITHOME-87/88/89 migráció kész.') process.exit(0) } diff --git a/proto/src/collections/Media.ts b/proto/src/collections/Media.ts new file mode 100644 index 0000000..55f1d68 --- /dev/null +++ b/proto/src/collections/Media.ts @@ -0,0 +1,33 @@ +import type { CollectionConfig } from 'payload' + +/** + * MITHOME-89 (MVP scope) — Payload beépített upload collection. + * + * WHY nincs saját `staticDir` a `proto/public/`-ba mutatva: a fájlokat a + * Payload a saját REST route-jain (src/app/(payload)/api/[...slug]/route.ts) + * szolgálja ki, nem közvetlen statikus mappából — így nem ütközik a régi + * content-editor.js-féle /public/partners/*.png fájlokkal, amik MITHOME-93 + * leépítéséig még élnek. + * + * A tényleges vágás/forgatás/áttetszővé tétel szerkesztő NEM ide tartozik — + * lásd MITHOME-118 (külön, egyedi admin field component). + */ +export const Media: CollectionConfig = { + slug: 'media', + admin: { + group: 'Média', + }, + upload: { + mimeTypes: ['image/*'], + }, + fields: [ + { + name: 'alt', + type: 'text', + required: true, + admin: { + description: 'Alternatív szöveg (accessibility) — pl. "Angele Pihenőház logó".', + }, + }, + ], +} diff --git a/proto/src/collections/Partners.ts b/proto/src/collections/Partners.ts new file mode 100644 index 0000000..d469a80 --- /dev/null +++ b/proto/src/collections/Partners.ts @@ -0,0 +1,25 @@ +import type { CollectionConfig } from 'payload' + +/** + * Mirrors home.json `partners.items` (proto/src/content/pages/home.json) — + * MITHOME-89 (MVP). Korábban a Home Global szándékosan NEM tartalmazta ezt + * a listát (lásd src/globals/Home.ts megjegyzése) — itt lesz az önálló + * collection, logo = Media upload reference. + */ +export const Partners: CollectionConfig = { + slug: 'partners', + admin: { + useAsTitle: 'name', + group: 'Oldalak', + }, + fields: [ + { name: 'name', type: 'text', required: true }, + { name: 'url', type: 'text', required: true }, + { + name: 'logo', + type: 'upload', + relationTo: 'media', + required: true, + }, + ], +} diff --git a/proto/src/payload.config.ts b/proto/src/payload.config.ts index 9211fe1..628abcc 100644 --- a/proto/src/payload.config.ts +++ b/proto/src/payload.config.ts @@ -8,7 +8,9 @@ * 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. * MITHOME-88: LegalPages collection hozzáadva (adatvedelem, hasznalati- - * feltetelek). A Partners/Media collectionök a MITHOME-89-ben kerülnek be. + * feltetelek). + * MITHOME-89: Partners + Media collection (MVP — feltöltés, a vágás/ + * áttetszővé tétel szerkesztő külön, MITHOME-118). */ import path from 'path' import { fileURLToPath } from 'url' @@ -19,6 +21,8 @@ import sharp from 'sharp' import { Users } from './collections/Users' import { LegalPages } from './collections/LegalPages' +import { Media } from './collections/Media' +import { Partners } from './collections/Partners' import { Home } from './globals/Home' import { About } from './globals/About' import { Services } from './globals/Services' @@ -37,7 +41,7 @@ export default buildConfig({ editor: lexicalEditor(), - collections: [Users, LegalPages], + collections: [Users, LegalPages, Media, Partners], globals: [Home, About, Services, Contact, Common],