fix(build): remove .js extensions from payload.config imports + fix TS errors
Some checks are pending
CI / Type Check (push) Waiting to run
CI / Lint (push) Waiting to run
CI / Unit Tests (push) Waiting to run
Deploy / Build & Push Image (push) Waiting to run
Deploy / Deploy to VPS (push) Blocked by required conditions

payload.config.ts used explicit .js extensions on local imports which Turbopack
and webpack cannot resolve to .ts files without extensionAliases config. Revert
to extension-less imports (original pattern from first Payload commit) that both
bundlers handle natively.

Also fix two TypeScript strict-mode errors:
- seed/route.ts: doc.id (number) cast to string via String() instead of `as string`
- getHomeData.ts: Payload HomePage vs HomePageGlobal cast via `as unknown as`

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Vadym Samoilenko 2026-05-15 18:34:38 +01:00
parent 83c4f7973a
commit 66f9a0d645
5 changed files with 158 additions and 54 deletions

View file

@ -8,31 +8,31 @@ import sharp from 'sharp'
import path from 'path'
import { fileURLToPath } from 'url'
import { Users } from './src/collections/Users.js'
import { Media } from './src/collections/Media.js'
import { Pages } from './src/collections/Pages.js'
import { BlogPosts } from './src/collections/BlogPosts.js'
import { Categories } from './src/collections/Categories.js'
import { Tags } from './src/collections/Tags.js'
import { Tariffs } from './src/collections/Tariffs.js'
import { Leads } from './src/collections/Leads.js'
import { Orders } from './src/collections/Orders.js'
import { Locations } from './src/collections/Locations.js'
import { Reviews } from './src/collections/Reviews.js'
import { BirthdayPackages } from './src/collections/BirthdayPackages.js'
import { Users } from './src/collections/Users'
import { Media } from './src/collections/Media'
import { Pages } from './src/collections/Pages'
import { BlogPosts } from './src/collections/BlogPosts'
import { Categories } from './src/collections/Categories'
import { Tags } from './src/collections/Tags'
import { Tariffs } from './src/collections/Tariffs'
import { Leads } from './src/collections/Leads'
import { Orders } from './src/collections/Orders'
import { Locations } from './src/collections/Locations'
import { Reviews } from './src/collections/Reviews'
import { BirthdayPackages } from './src/collections/BirthdayPackages'
import { HomePage } from './src/globals/HomePage.js'
import { CheckoutPage } from './src/globals/CheckoutPage.js'
import { ThankYouPage } from './src/globals/ThankYouPage.js'
import { Header } from './src/globals/Header.js'
import { Footer } from './src/globals/Footer.js'
import { SiteSettings } from './src/globals/SiteSettings.js'
import { DyvoLisPage } from './src/globals/DyvoLisPage.js'
import { GroupVisitsPage } from './src/globals/GroupVisitsPage.js'
import { BirthdayPage } from './src/globals/BirthdayPage.js'
import { TicketsPage } from './src/globals/TicketsPage.js'
import { LocationsPage } from './src/globals/LocationsPage.js'
import { BlogIndexPage } from './src/globals/BlogIndexPage.js'
import { HomePage } from './src/globals/HomePage'
import { CheckoutPage } from './src/globals/CheckoutPage'
import { ThankYouPage } from './src/globals/ThankYouPage'
import { Header } from './src/globals/Header'
import { Footer } from './src/globals/Footer'
import { SiteSettings } from './src/globals/SiteSettings'
import { DyvoLisPage } from './src/globals/DyvoLisPage'
import { GroupVisitsPage } from './src/globals/GroupVisitsPage'
import { BirthdayPage } from './src/globals/BirthdayPage'
import { TicketsPage } from './src/globals/TicketsPage'
import { LocationsPage } from './src/globals/LocationsPage'
import { BlogIndexPage } from './src/globals/BlogIndexPage'
const filename = fileURLToPath(import.meta.url)
const dirname = path.dirname(filename)

View file

@ -27,7 +27,7 @@ async function uploadMedia(
file: { data: buffer, mimetype: getMimeType(filename), name: filename, size: buffer.length },
overrideAccess: true,
})
return doc.id as string
return String(doc.id)
} catch {
return null
}
@ -47,7 +47,7 @@ async function findOrUploadMedia(
limit: 1,
overrideAccess: true,
})
if (existing.docs.length > 0) return existing.docs[0]!.id as string
if (existing.docs.length > 0) return String(existing.docs[0]!.id)
const fullPath = path.resolve(process.cwd(), localPath)
if (!fs.existsSync(fullPath)) return null
const buffer = fs.readFileSync(fullPath)
@ -57,7 +57,7 @@ async function findOrUploadMedia(
file: { data: buffer, mimetype: getMimeType(filename), name: filename, size: buffer.length },
overrideAccess: true,
})
return doc.id as string
return String(doc.id)
} catch {
return null
}

View file

@ -15,10 +15,27 @@ export const getHomeData = cache(async (): Promise<HomeData> => {
const payload = await getPayload({ config: configPromise })
const [home, locResult, revResult, pkgResult] = await Promise.all([
payload.findGlobal({ slug: 'home-page' }) as Promise<HomePageGlobal>,
payload.find({ collection: 'locations', where: { showOnHome: { equals: true } }, sort: 'sort', limit: 20, overrideAccess: true }),
payload.find({ collection: 'reviews', where: { showOnHome: { equals: true } }, sort: 'sort', limit: 20, overrideAccess: true }),
payload.find({ collection: 'birthday-packages', sort: 'sort', limit: 10, overrideAccess: true }),
payload.findGlobal({ slug: 'home-page' }) as unknown as Promise<HomePageGlobal>,
payload.find({
collection: 'locations',
where: { showOnHome: { equals: true } },
sort: 'sort',
limit: 20,
overrideAccess: true,
}),
payload.find({
collection: 'reviews',
where: { showOnHome: { equals: true } },
sort: 'sort',
limit: 20,
overrideAccess: true,
}),
payload.find({
collection: 'birthday-packages',
sort: 'sort',
limit: 10,
overrideAccess: true,
}),
])
return {

View file

@ -1,6 +1,6 @@
import 'dotenv/config'
import { getPayload } from 'payload'
import config from '../payload.config'
import config from '../payload.config.js'
async function seed(): Promise<void> {
const payload = await getPayload({ config })
@ -34,7 +34,8 @@ async function seed(): Promise<void> {
data: {
hero: {
title: 'Шуміленд — світ, де казка оживає',
subtitle: 'Сімейний тематичний парк розваг. ДиноПарк, Диво Ліс, Дзеркальний Лабіринт — незабутні емоції для всієї родини.',
subtitle:
'Сімейний тематичний парк розваг. ДиноПарк, Диво Ліс, Дзеркальний Лабіринт — незабутні емоції для всієї родини.',
ctaLabel: 'Купити квиток',
ctaHref: '/kvytky',
},
@ -56,11 +57,27 @@ async function seed(): Promise<void> {
},
],
features: [
{ icon: '🦕', title: 'Безпека', description: 'Атракціони сертифіковані, майданчик під постійним наглядом' },
{
icon: '🦕',
title: 'Безпека',
description: 'Атракціони сертифіковані, майданчик під постійним наглядом',
},
{ icon: '🌲', title: 'Природа', description: 'Парк розташований серед живої природи' },
{ icon: '🎉', title: 'Свята', description: 'Дні народження, корпоративи та шкільні екскурсії' },
{ icon: '🎟️', title: 'Квитки онлайн', description: 'Купуйте квитки онлайн без черги на касі' },
{ icon: '🍕', title: 'Кафе та їжа', description: 'Власне кафе з дитячим меню та легкими закусками' },
{
icon: '🎉',
title: 'Свята',
description: 'Дні народження, корпоративи та шкільні екскурсії',
},
{
icon: '🎟️',
title: 'Квитки онлайн',
description: 'Купуйте квитки онлайн без черги на касі',
},
{
icon: '🍕',
title: 'Кафе та їжа',
description: 'Власне кафе з дитячим меню та легкими закусками',
},
{ icon: '🅿️', title: 'Парковка', description: 'Безкоштовна парковка для відвідувачів' },
],
news: { title: 'Новини', limit: 3 },
@ -117,7 +134,11 @@ async function seed(): Promise<void> {
}
// Blog posts
const { totalDocs: postCount } = await payload.find({ collection: 'blog-posts', limit: 1, overrideAccess: true })
const { totalDocs: postCount } = await payload.find({
collection: 'blog-posts',
limit: 1,
overrideAccess: true,
})
if (postCount === 0) {
const posts = [
{
@ -130,14 +151,16 @@ async function seed(): Promise<void> {
{
title: 'Весняні канікули в Шуміленді',
slug: 'vesniani-kanikuly-v-shumilenди',
excerpt: 'Проведіть весняні канікули незабутньо! Спеціальні активності щодня з 28 березня по 6 квітня.',
excerpt:
'Проведіть весняні канікули незабутньо! Спеціальні активності щодня з 28 березня по 6 квітня.',
status: 'published' as const,
publishedAt: new Date('2025-03-20').toISOString(),
},
{
title: 'Нова локація: Тир з призами',
slug: 'nova-lokatsiya-tyr-z-pryzamy',
excerpt: 'Відтепер у Шуміленді є новий Тир з призами — точний постріл приносить реальний виграш!',
excerpt:
'Відтепер у Шуміленді є новий Тир з призами — точний постріл приносить реальний виграш!',
status: 'published' as const,
publishedAt: new Date('2025-03-10').toISOString(),
},
@ -151,17 +174,85 @@ async function seed(): Promise<void> {
}
// Tariffs (sample — normally synced from ezy API)
const { totalDocs: tariffCount } = await payload.find({ collection: 'tariffs', limit: 1, overrideAccess: true })
const { totalDocs: tariffCount } = await payload.find({
collection: 'tariffs',
limit: 1,
overrideAccess: true,
})
if (tariffCount === 0) {
const tariffs = [
{ ezy_id: 1001, last_synced_name: 'Дорослий — ДиноПарк', display_name: 'Дорослий', last_synced_price: 350, category_tag: 'dyno', sort: 1, visible: true },
{ ezy_id: 1002, last_synced_name: 'Дитячий — ДиноПарк', display_name: 'Дитячий (312 років)', last_synced_price: 250, category_tag: 'dyno', sort: 2, visible: true },
{ ezy_id: 1003, last_synced_name: 'Дитина до 3 років — ДиноПарк', display_name: 'До 3 років (безкоштовно)', last_synced_price: 0, category_tag: 'dyno', sort: 3, visible: true },
{ ezy_id: 2001, last_synced_name: 'Дорослий — Диво Ліс', display_name: 'Дорослий', last_synced_price: 300, category_tag: 'dyvolis', sort: 1, visible: true },
{ ezy_id: 2002, last_synced_name: 'Дитячий — Диво Ліс', display_name: 'Дитячий (312 років)', last_synced_price: 200, category_tag: 'dyvolis', sort: 2, visible: true },
{ ezy_id: 3001, last_synced_name: 'Комбо — ДиноПарк + Диво Ліс', display_name: 'Комбо дорослий', last_synced_price: 550, category_tag: 'combo', sort: 1, visible: true },
{ ezy_id: 3002, last_synced_name: 'Комбо дитячий — ДиноПарк + Диво Ліс', display_name: 'Комбо дитячий', last_synced_price: 400, category_tag: 'combo', sort: 2, visible: true },
{ ezy_id: 4001, last_synced_name: 'Сімейний (2 дор + 2 діт)', display_name: 'Сімейний квиток', last_synced_price: 1200, category_tag: 'family', sort: 1, visible: true },
{
ezy_id: 1001,
last_synced_name: 'Дорослий — ДиноПарк',
display_name: 'Дорослий',
last_synced_price: 350,
category_tag: 'dyno',
sort: 1,
visible: true,
},
{
ezy_id: 1002,
last_synced_name: 'Дитячий — ДиноПарк',
display_name: 'Дитячий (312 років)',
last_synced_price: 250,
category_tag: 'dyno',
sort: 2,
visible: true,
},
{
ezy_id: 1003,
last_synced_name: 'Дитина до 3 років — ДиноПарк',
display_name: 'До 3 років (безкоштовно)',
last_synced_price: 0,
category_tag: 'dyno',
sort: 3,
visible: true,
},
{
ezy_id: 2001,
last_synced_name: 'Дорослий — Диво Ліс',
display_name: 'Дорослий',
last_synced_price: 300,
category_tag: 'dyvolis',
sort: 1,
visible: true,
},
{
ezy_id: 2002,
last_synced_name: 'Дитячий — Диво Ліс',
display_name: 'Дитячий (312 років)',
last_synced_price: 200,
category_tag: 'dyvolis',
sort: 2,
visible: true,
},
{
ezy_id: 3001,
last_synced_name: 'Комбо — ДиноПарк + Диво Ліс',
display_name: 'Комбо дорослий',
last_synced_price: 550,
category_tag: 'combo',
sort: 1,
visible: true,
},
{
ezy_id: 3002,
last_synced_name: 'Комбо дитячий — ДиноПарк + Диво Ліс',
display_name: 'Комбо дитячий',
last_synced_price: 400,
category_tag: 'combo',
sort: 2,
visible: true,
},
{
ezy_id: 4001,
last_synced_name: 'Сімейний (2 дор + 2 діт)',
display_name: 'Сімейний квиток',
last_synced_price: 1200,
category_tag: 'family',
sort: 1,
visible: true,
},
]
for (const t of tariffs) {
await payload.create({ collection: 'tariffs', data: t as never, overrideAccess: true })

View file

@ -1,7 +1,3 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"moduleResolution": "node16",
"module": "node16"
}
"extends": "./tsconfig.json"
}