- /kvytky: combo grid (cart add), tabbed catalog with Figma chips/photos, CMS-editable (TicketsPage global) - Tariffs collection: infoChips, badgeLabel, category zone/attraction/program, manual tickets - Birthday & Group pages: Figma design + real photos, pricing sections removed (form-only), unified form style - Thank-you page (pidtverdzhennya) from Figma; combo cards redesigned (4-up grid) - Reviews coverflow slider; blog photos fixed (relative media URLs for next/image) - JSON-LD (WebPage/LocalBusiness/CollectionPage/Service) on home/lokatsii/birthday/group - Footer: photo bg + AImpress credit link; green wave pattern on green sections; consistent footer - Imported 3 real blog posts from staging; fixed text artifacts - Optimized 118 images 563MB -> 72MB (resize 1920px + re-encode) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
128 lines
3.3 KiB
TypeScript
128 lines
3.3 KiB
TypeScript
/* eslint-disable no-console */
|
|
import 'dotenv/config'
|
|
import { writeFile } from 'fs/promises'
|
|
import { getPayload } from 'payload'
|
|
import config from '../payload.config.js'
|
|
|
|
const STAGING = 'https://shumi.ai-impress.com'
|
|
|
|
// Seed placeholders to remove once real articles are imported.
|
|
const SEED_TITLES = [
|
|
'Сезон динозаврів відкрито!',
|
|
'Весняні канікули в Шуміленді',
|
|
'Нова локація: Тир з призами',
|
|
]
|
|
|
|
interface StagingPost {
|
|
title: string
|
|
slug: string
|
|
publishedAt?: string | null
|
|
excerpt?: string | null
|
|
body?: unknown
|
|
status?: string
|
|
hero?: { url?: string | null; alt?: string | null } | null
|
|
}
|
|
|
|
async function downloadHero(url: string, slug: string): Promise<string | null> {
|
|
try {
|
|
const res = await fetch(url)
|
|
if (!res.ok) return null
|
|
const buf = Buffer.from(await res.arrayBuffer())
|
|
const ct = res.headers.get('content-type') ?? ''
|
|
const ext = ct.includes('png')
|
|
? 'png'
|
|
: ct.includes('jpeg')
|
|
? 'jpg'
|
|
: ct.includes('webp')
|
|
? 'webp'
|
|
: 'bin'
|
|
const path = `/tmp/blog-${slug}.${ext}`
|
|
await writeFile(path, buf)
|
|
return path
|
|
} catch {
|
|
return null
|
|
}
|
|
}
|
|
|
|
async function run(): Promise<void> {
|
|
const payload = await getPayload({ config })
|
|
|
|
const res = await fetch(
|
|
`${STAGING}/api/blog-posts?limit=50&depth=1&where[status][equals]=published`
|
|
)
|
|
const data = (await res.json()) as { docs: StagingPost[] }
|
|
const posts = data.docs ?? []
|
|
console.log(`Staging published posts: ${posts.length}`)
|
|
|
|
let created = 0
|
|
let skipped = 0
|
|
|
|
for (const p of posts) {
|
|
const existing = await payload.find({
|
|
collection: 'blog-posts',
|
|
where: { title: { equals: p.title } },
|
|
limit: 1,
|
|
overrideAccess: true,
|
|
})
|
|
if (existing.totalDocs > 0) {
|
|
console.log(`= exists: ${p.title}`)
|
|
skipped++
|
|
continue
|
|
}
|
|
|
|
let heroId: number | undefined
|
|
const heroUrl = p.hero?.url
|
|
if (heroUrl) {
|
|
const full = heroUrl.startsWith('http') ? heroUrl : `${STAGING}${heroUrl}`
|
|
const filePath = await downloadHero(full, p.slug)
|
|
if (filePath) {
|
|
const media = await payload.create({
|
|
collection: 'media',
|
|
filePath,
|
|
data: { alt: p.hero?.alt ?? p.title },
|
|
overrideAccess: true,
|
|
})
|
|
heroId = media.id as number
|
|
}
|
|
}
|
|
|
|
await payload.create({
|
|
collection: 'blog-posts',
|
|
data: {
|
|
title: p.title,
|
|
publishedAt: p.publishedAt ?? undefined,
|
|
excerpt: p.excerpt ?? undefined,
|
|
|
|
body: (p.body ?? undefined) as any,
|
|
status: 'published',
|
|
...(heroId ? { hero: heroId } : {}),
|
|
},
|
|
overrideAccess: true,
|
|
})
|
|
console.log(`+ imported: ${p.title}${heroId ? ' (with hero)' : ''}`)
|
|
created++
|
|
}
|
|
|
|
// Remove seed placeholders
|
|
let deleted = 0
|
|
for (const title of SEED_TITLES) {
|
|
const r = await payload.delete({
|
|
collection: 'blog-posts',
|
|
where: { title: { equals: title } },
|
|
overrideAccess: true,
|
|
})
|
|
const count = Array.isArray(r.docs) ? r.docs.length : 0
|
|
if (count > 0) {
|
|
console.log(`- removed seed: ${title}`)
|
|
deleted += count
|
|
}
|
|
}
|
|
|
|
console.log(`\nDone. Imported: ${created}, skipped: ${skipped}, seed removed: ${deleted}`)
|
|
process.exit(0)
|
|
}
|
|
|
|
run().catch((err) => {
|
|
console.error(err)
|
|
process.exit(1)
|
|
})
|