feat(cms): Partners + Media collection MVP (MITHOME-89)
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

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 <noreply@anthropic.com>
This commit is contained in:
Do Siki
2026-09-10 12:33:34 +02:00
co-authored by Claude Sonnet 5
parent 928439bc7b
commit 776fa66bc8
5 changed files with 129 additions and 3 deletions
+59 -1
View File
@@ -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)
}