+
{error}
)}
@@ -107,14 +107,14 @@ function CheckoutForm() {
Оплата відбувається через Monobank. Ваші дані захищені.
diff --git a/src/app/(frontend)/page.tsx b/src/app/(frontend)/page.tsx
index ef3b2ec..1cb7a60 100644
--- a/src/app/(frontend)/page.tsx
+++ b/src/app/(frontend)/page.tsx
@@ -28,10 +28,7 @@ export default async function HomePage() {
return (
-
+
-
+
-
+
)
diff --git a/src/app/api/admin/seed/route.ts b/src/app/api/admin/seed/route.ts
index c4544ba..53d836b 100644
--- a/src/app/api/admin/seed/route.ts
+++ b/src/app/api/admin/seed/route.ts
@@ -464,6 +464,26 @@ export async function POST(req: NextRequest) {
results.push('Seeded site-settings global')
// === BLOG POSTS ===
+ function makeLexical(paragraphs: string[]) {
+ return {
+ root: {
+ type: 'root',
+ format: '',
+ indent: 0,
+ version: 1,
+ direction: 'ltr',
+ children: paragraphs.map((text) => ({
+ type: 'paragraph',
+ format: '',
+ indent: 0,
+ version: 1,
+ direction: 'ltr',
+ children: [{ type: 'text', detail: 0, format: 0, mode: 'normal', style: '', text, version: 1 }],
+ })),
+ },
+ }
+ }
+
const { totalDocs: postCount } = await payload.find({
collection: 'blog-posts',
limit: 1,
@@ -472,33 +492,67 @@ export async function POST(req: NextRequest) {
if (postCount === 0) {
const postDefs = [
{
- title: 'Сезон динозаврів відкрито!',
- slug: 'sezon-dynozavriv-vidkryto',
- excerpt: 'Шуміленд вітає нових мешканців ДиноПарку — зустрічайте 12 нових динозаврів!',
+ title: 'Сезон пригод відкрито: Shumiland на ВДНГ запрошує у світ, де оживають динозаври та казки!',
+ slug: 'sezon-pryhod-vidkryto',
+ excerpt: 'Сезон стартував із події національного масштабу — фіксації рекорду України: найбільші динозаври країни. 19 квітня у Shumiland представники Книги рекордів України офіційно зафіксували рекорд.',
status: 'published',
- publishedAt: '2025-04-01T10:00:00.000Z',
- imgFile: 'news-bg1.jpg',
+ publishedAt: '2026-04-19T10:00:00.000Z',
+ imgFile: 'sezon-pryhod.webp',
+ imgPath: 'public/images/blog/sezon-pryhod.webp',
+ body: makeLexical([
+ 'Сезон стартував із події національного масштабу — фіксації рекорду України: найбільші динозаври країни. 19 квітня у Shumiland представники Книги рекордів України офіційно зафіксували рекорд. Тепер Shumiland — це не просто парк розваг, а простір, де можна на власні очі побачити рекордсменів і водночас зануритися у світ пригод і казки.',
+ 'Більше ніж парк: простір, де фантазія стає реальністю',
+ 'Після яскравого старту в стилі «Аліси в Дивокраї», парк працює у повноцінному режимі, пропонуючи гостям унікальний формат відпочинку просто неба. На території ВДНГ розгорнувся масштабний всесвіт пригод, що поєднує освіту, розваги та естетику.',
+ 'Динопарк — рекорд, який вражає: Локація в парку, де серед густої зелені «ожили» реалістичні динозаври натуральної величини, серед яких — найбільші динозаври України, офіційно зафіксовані як рекорд. Завдяки сучасним технологіям гіганти рухаються та гарчать, створюючи ефект повної присутності.',
+ 'Диво Ліс та Будиночок Лісовика: Казкова локація з унікальними топіарними фігурами та арт-об\'єктами. Тут розташована резиденція Лісовика, де діти можуть зануритися у світ легенд та інтерактивних історій.',
+ 'Дзеркальний лабіринт: Простір світла та ілюзій, що відкриває нові ракурси реальності та створює десятки ефектних фотолокацій.',
+ 'Атракціони: У сезоні 2026 парк значно розширив зону розваг — на гостей чекає багато нових атракціонів для дітей різного віку.',
+ 'Shumiland — обов\'язкова точка для візиту на мапі Києва. Велика територія для активного відпочинку, продумана комфортна інфраструктура та оновлені фудкорти для всієї родини.',
+ '«Ми створили місце, де кожен візит перетворюється на родинне свято. Shumiland — це простір, де діти стають дослідниками, а дорослі дозволяють собі мріяти», — зазначає команда парку.',
+ ]),
},
{
- title: 'Весняні канікули в Шуміленді',
- slug: 'vesniani-kanikuly',
- excerpt:
- 'Проведіть весняні канікули незабутньо! Спеціальні активності щодня з 28 березня по 6 квітня.',
+ title: 'У Шуміленді заклали капсулу часу',
+ slug: 'kapsula-chasu',
+ excerpt: 'У Шуміленді відбулася особлива подія — ми заклали капсулу часу як символ початку великого шляху, сповненого дитячого сміху, щирих емоцій, сімейних моментів і незабутніх вражень.',
status: 'published',
- publishedAt: '2025-03-20T10:00:00.000Z',
- imgFile: 'news-bg2.png',
+ publishedAt: '2026-04-20T10:00:00.000Z',
+ imgFile: 'kapsula-chasu.webp',
+ imgPath: 'public/images/blog/kapsula-chasu.webp',
+ body: makeLexical([
+ 'У Шуміленді відбулася особлива подія — ми заклали капсулу часу як символ початку великого шляху, сповненого дитячого сміху, щирих емоцій, сімейних моментів і незабутніх вражень.',
+ 'Ця капсула стала знаком нашої віри у майбутнє, у розвиток простору, де діти можуть радіти, мріяти та створювати найтепліші спогади разом із родиною.',
+ 'Відкриття капсули заплановане на 19 квітня 2031 року. Саме тоді ми зможемо повернутися у цей день і побачити, яким яскравим, теплим та наповненим щасливими моментами став шлях Шуміленду.',
+ 'Попереду — багато щасливих історій, свят і дитячих усмішок. І ми раді створювати їх разом з вами.',
+ ]),
},
{
- title: 'Нова локація: Тир з призами',
- slug: 'nova-lokatsiya-tyr-z-pryzamy',
- excerpt: 'Відтепер у Шуміленді є Тир з призами — точний постріл приносить реальний виграш!',
+ title: 'Травень у Шуміленді — місяць пригод для всієї родини',
+ slug: 'traven-u-shumilendt',
+ excerpt: 'Травень у Шуміленді обіцяє бути яскравим, веселим і сповненим незабутніх емоцій. Щовихідних на гостей чекають тематичні програми, квести, улюблені герої та атмосфера справжнього сімейного відпочинку.',
status: 'published',
- publishedAt: '2025-03-10T10:00:00.000Z',
- imgFile: 'news-bg3.jpg',
+ publishedAt: '2026-05-01T10:00:00.000Z',
+ imgFile: 'traven-shymiland.webp',
+ imgPath: 'public/images/blog/traven-shymiland.webp',
+ body: makeLexical([
+ 'Травень у Шуміленді обіцяє бути яскравим, веселим і сповненим незабутніх емоцій. Щовихідних на гостей чекають тематичні програми, квести, улюблені герої, чарівні пригоди та атмосфера справжнього сімейного відпочинку.',
+ 'Плануйте свої вихідні разом із Шумілендом та створюйте теплі спогади всією родиною.',
+ 'Календар подій:',
+ '09.05 — Розважальна програма «Леді Баг»: Захопливий світ пригод разом із відважною супергероїнею.',
+ '10.05 — День Матері: Майстер-клас «Подарунок для мами» та тематичний квест «Русалонька».',
+ '16.05 — Розважальна програма «Футбольний переполох»: Світ драйву та командного духу для маленьких любителів спорту.',
+ '17.05 — Квест «Орел і Решка»: Весела подорож навколо світу у форматі популярної тревел-пригоди.',
+ '23.05 — Розважальна програма «Стіч»: Яскрава гавайська пригода разом із веселим та бешкетним героєм.',
+ '24.05 — Квест «Аніме»: Пройдіть усі випробування та дізнайтеся, де загубився священний меч «Хігуроші».',
+ '30.05 — Випуск «Школа магії»: Діти поринуть у світ чаклунства, таємниць та неймовірних пригод.',
+ '31.05 — Тематичний день Wednesday: Загадковий і стильний світ темної естетики, натхненний атмосферою Wednesday та Академією Невермор.',
+ 'Щодня у Шуміленді на гостей чекають: вражаючий динопарк з динозаврами що рухаються і гарчать, магічний Диво Ліс, атракціони для всієї родини, смачний фудкорт та затишна атмосфера.',
+ 'Київ, пр-т Академіка Глушкова, 1 ВДНГ. Щодня: 11:00–20:00.',
+ ]),
},
]
for (const post of postDefs) {
- const heroId = await uploadMedia(payload, post.imgFile, post.title)
+ const heroId = await findOrUploadMedia(payload, post.imgFile, post.imgPath, post.title)
await payload.create({
collection: 'blog-posts',
data: {
@@ -507,7 +561,8 @@ export async function POST(req: NextRequest) {
excerpt: post.excerpt,
status: post.status,
publishedAt: post.publishedAt,
- hero: heroId ? { image: heroId } : undefined,
+ hero: heroId ?? undefined,
+ body: post.body,
} as never,
overrideAccess: true,
})
diff --git a/src/components/blocks/CTABlockComponent.tsx b/src/components/blocks/CTABlockComponent.tsx
index 073af41..c870da9 100644
--- a/src/components/blocks/CTABlockComponent.tsx
+++ b/src/components/blocks/CTABlockComponent.tsx
@@ -12,31 +12,34 @@ export function CTABlockComponent({ title, subtitle, ctaLabel, ctaHref, variant
if (!ctaHref) return null
const isDark = variant === 'dark'
const bg = isDark ? 'bg-[#272727]' : 'bg-[#396817]'
- const btnCls = variant === 'secondary'
- ? 'border-2 border-white text-white hover:bg-white hover:text-[#396817]'
- : 'bg-[#f28b4a] text-white hover:shadow-[0_0_20px_0_#f28b4a]'
+ const btnCls =
+ variant === 'secondary'
+ ? 'border-2 border-white text-white hover:bg-white hover:text-[#396817]'
+ : 'bg-[#f28b4a] text-white hover:shadow-[0_0_20px_0_#f28b4a]'
return (
-
+
{title && (
{title}
)}
{subtitle && (
-
+
{subtitle}
)}
{ctaLabel && (
{ctaLabel}
diff --git a/src/components/blocks/HeroBlockComponent.tsx b/src/components/blocks/HeroBlockComponent.tsx
index e506928..d26b40a 100644
--- a/src/components/blocks/HeroBlockComponent.tsx
+++ b/src/components/blocks/HeroBlockComponent.tsx
@@ -9,32 +9,45 @@ interface HeroBlockProps {
backgroundImage?: { url?: string | null } | string | null
}
-export function HeroBlockComponent({ title, subtitle, ctaLabel, ctaHref, backgroundImage }: HeroBlockProps) {
+export function HeroBlockComponent({
+ title,
+ subtitle,
+ ctaLabel,
+ ctaHref,
+ backgroundImage,
+}: HeroBlockProps) {
const bgUrl = typeof backgroundImage === 'object' ? backgroundImage?.url : null
return (
-
+
{bgUrl && (
-
+
)}
-
+
{title}
{subtitle && (
-
+
{subtitle}
)}
{ctaLabel && ctaHref && (
{ctaLabel}
diff --git a/src/components/blocks/LeadFormBlockComponent.tsx b/src/components/blocks/LeadFormBlockComponent.tsx
index 9189759..718ec7f 100644
--- a/src/components/blocks/LeadFormBlockComponent.tsx
+++ b/src/components/blocks/LeadFormBlockComponent.tsx
@@ -34,30 +34,35 @@ export function LeadFormBlockComponent({
await fetch('/api/leads', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
- body: JSON.stringify({ name, phone: showPhone ? phone : undefined, email: showEmail ? email : undefined, source: formSource ?? 'block-form' }),
+ body: JSON.stringify({
+ name,
+ phone: showPhone ? phone : undefined,
+ email: showEmail ? email : undefined,
+ source: formSource ?? 'block-form',
+ }),
})
- } catch { /* noop */ }
+ } catch {
+ /* noop */
+ }
setSubmitted(true)
setLoading(false)
}
return (
-
+
{title && (
{title}
)}
- {subtitle && (
-
{subtitle}
- )}
+ {subtitle &&
{subtitle}
}
{submitted ? (
-
-
+
+
{successMessage ?? "Дякуємо! Ми зв'яжемося з вами найближчим часом."}
@@ -69,7 +74,7 @@ export function LeadFormBlockComponent({
value={name}
onChange={(e) => setName(e.target.value)}
placeholder="Ваше ім'я"
- className="w-full px-5 py-3 rounded-[10px] border border-[#272727]/20 text-[#272727] text-[16px] outline-none focus:ring-2 focus:ring-[#f28b4a]"
+ className="w-full rounded-[10px] border border-[#272727]/20 px-5 py-3 text-[16px] text-[#272727] outline-none focus:ring-2 focus:ring-[#f28b4a]"
/>
{showPhone && (
setPhone(e.target.value)}
placeholder="Телефон"
- className="w-full px-5 py-3 rounded-[10px] border border-[#272727]/20 text-[#272727] text-[16px] outline-none focus:ring-2 focus:ring-[#f28b4a]"
+ className="w-full rounded-[10px] border border-[#272727]/20 px-5 py-3 text-[16px] text-[#272727] outline-none focus:ring-2 focus:ring-[#f28b4a]"
/>
)}
{showEmail && (
@@ -87,13 +92,13 @@ export function LeadFormBlockComponent({
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder="Email"
- className="w-full px-5 py-3 rounded-[10px] border border-[#272727]/20 text-[#272727] text-[16px] outline-none focus:ring-2 focus:ring-[#f28b4a]"
+ className="w-full rounded-[10px] border border-[#272727]/20 px-5 py-3 text-[16px] text-[#272727] outline-none focus:ring-2 focus:ring-[#f28b4a]"
/>
)}