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
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:
co-authored by
Claude Sonnet 5
parent
928439bc7b
commit
776fa66bc8
@@ -39,3 +39,9 @@ yarn-error.log*
|
|||||||
# typescript
|
# typescript
|
||||||
*.tsbuildinfo
|
*.tsbuildinfo
|
||||||
next-env.d.ts
|
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
|
||||||
|
|||||||
@@ -12,6 +12,8 @@
|
|||||||
* írja a Payload-ban lévő korábbi állapotot. NEM törli/nem érinti a JSON
|
* írja a Payload-ban lévő korábbi állapotot. NEM törli/nem érinti a JSON
|
||||||
* fájlokat.
|
* fájlokat.
|
||||||
*/
|
*/
|
||||||
|
import path from 'path'
|
||||||
|
import { fileURLToPath } from 'url'
|
||||||
import { getPayload, type Payload } from 'payload'
|
import { getPayload, type Payload } from 'payload'
|
||||||
import config from '../src/payload.config'
|
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 adatvedelemJson from '../src/content/pages/adatvedelem.json'
|
||||||
import hasznalatiFeltetelekJson from '../src/content/pages/hasznalati-feltetelek.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 */
|
/** string[] -> [{ value: string }] — lásd src/globals/fields/stringArray.ts */
|
||||||
function toStringArray(items: readonly string[]): { value: string }[] {
|
function toStringArray(items: readonly string[]): { value: string }[] {
|
||||||
return items.map((value) => ({ value }))
|
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() {
|
async function run() {
|
||||||
const payload = await getPayload({ config })
|
const payload = await getPayload({ config })
|
||||||
|
|
||||||
@@ -133,7 +186,12 @@ async function run() {
|
|||||||
await upsertLegalPage(payload, 'hasznalati-feltetelek', hasznalatiFeltetelekJson)
|
await upsertLegalPage(payload, 'hasznalati-feltetelek', hasznalatiFeltetelekJson)
|
||||||
payload.logger.info('LegalPages/hasznalati-feltetelek migrálva')
|
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)
|
process.exit(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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ó".',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
@@ -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,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
@@ -8,7 +8,9 @@
|
|||||||
* 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.
|
||||||
* MITHOME-88: LegalPages collection hozzáadva (adatvedelem, hasznalati-
|
* 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 path from 'path'
|
||||||
import { fileURLToPath } from 'url'
|
import { fileURLToPath } from 'url'
|
||||||
@@ -19,6 +21,8 @@ import sharp from 'sharp'
|
|||||||
|
|
||||||
import { Users } from './collections/Users'
|
import { Users } from './collections/Users'
|
||||||
import { LegalPages } from './collections/LegalPages'
|
import { LegalPages } from './collections/LegalPages'
|
||||||
|
import { Media } from './collections/Media'
|
||||||
|
import { Partners } from './collections/Partners'
|
||||||
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'
|
||||||
@@ -37,7 +41,7 @@ export default buildConfig({
|
|||||||
|
|
||||||
editor: lexicalEditor(),
|
editor: lexicalEditor(),
|
||||||
|
|
||||||
collections: [Users, LegalPages],
|
collections: [Users, LegalPages, Media, Partners],
|
||||||
|
|
||||||
globals: [Home, About, Services, Contact, Common],
|
globals: [Home, About, Services, Contact, Common],
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user