From 5f23806a82996fd30fc9dc7f189983b84e29caaf Mon Sep 17 00:00:00 2001 From: Vadym Samoilenko Date: Wed, 3 Jun 2026 17:08:43 +0100 Subject: [PATCH] fix(ui): remove black bg on hero images, improve dino quality, protect seed from overwriting admin data - DyvoLisHero: add mix-blend-mode:screen to hero img so JPEG black bg becomes transparent and the orange ellipse shows through - DinoPageContent: add contrast/saturation filter to hero-dino for richer colour rendering - seed.ts: globals (home-page, header, footer, site-settings) now only updated when FORCE_SEED=true; tariffs seeded only when table is empty (never deleted), preventing accidental data loss on redeploy Co-Authored-By: Claude Sonnet 4.6 --- src/components/sections/DinoPageContent.tsx | 1 + src/components/sections/DyvoLisHero.tsx | 3 +- src/seed.ts | 397 +++++++++++--------- 3 files changed, 212 insertions(+), 189 deletions(-) diff --git a/src/components/sections/DinoPageContent.tsx b/src/components/sections/DinoPageContent.tsx index 21b468b..fcf22e2 100644 --- a/src/components/sections/DinoPageContent.tsx +++ b/src/components/sections/DinoPageContent.tsx @@ -298,6 +298,7 @@ const DYNO_CSS = ` .dino-page .hero-dino{ position:absolute;top:12px;right:-60px;height:1096px;width:auto;max-width:none; z-index:0;pointer-events:none; + filter:contrast(1.08) saturate(1.12); } .dino-page .hero-text{position:relative;z-index:2;max-width:608px;padding:60px 0 40px;} .dino-page .hero-text h1{ diff --git a/src/components/sections/DyvoLisHero.tsx b/src/components/sections/DyvoLisHero.tsx index 52fbfd5..01d905c 100644 --- a/src/components/sections/DyvoLisHero.tsx +++ b/src/components/sections/DyvoLisHero.tsx @@ -142,7 +142,7 @@ export function DyvoLisHero({ }} /> - {/* Cat */} + {/* Cat — screen blend removes black JPEG background, shows ellipse behind */} Топіарна фігура ДивоЛісу diff --git a/src/seed.ts b/src/seed.ts index 8bd7bfa..5c00949 100644 --- a/src/seed.ts +++ b/src/seed.ts @@ -5,6 +5,15 @@ import config from '../payload.config.js' async function seed(): Promise { const payload = await getPayload({ config }) + // FORCE_SEED=true is required to overwrite globals that may have been edited in the admin UI. + // Without it, only empty collections (users, blog posts, tariffs, locations) are seeded. + const force = process.env['FORCE_SEED'] === 'true' + + if (!force) { + console.log('Running in safe mode — globals will NOT be overwritten.') + console.log('Set FORCE_SEED=true to re-seed all globals (will overwrite admin changes).') + } + // Admin user const { totalDocs } = await payload.find({ collection: 'users', @@ -28,115 +37,123 @@ async function seed(): Promise { console.log('Users already exist, skipping user seed.') } + if (!force) { + console.log('Skipping globals seed — run with FORCE_SEED=true to overwrite.') + } + // Home page global with real content - await payload.updateGlobal({ - slug: 'home-page', - data: { - hero: { - title: 'Започаткуйте традицію:', - subtitle: 'щороку фотографуйтесь біля улюбленого динозавра', + if (force) { + await payload.updateGlobal({ + slug: 'home-page', + data: { + hero: { + title: 'Започаткуйте традицію:', + subtitle: 'щороку фотографуйтесь біля улюбленого динозавра', + ctaLabel: 'Купити квиток', + ctaHref: '/kvytky', + }, + sectionTitles: { + locations: 'ЛАСКАВО ПРОСИМО ДО ШУМІЛЕНДУ', + whyParents: 'ЧОМУ БАТЬКИ ОБИРАЮТЬ ШУМІЛЕНД', + birthday: 'ДЕНЬ НАРОДЖЕННЯ В ШУМІЛЕНДІ', + gallery: 'ФОТОГАЛЕРЕЯ', + reviews: 'ВІДГУКИ', + news: 'НОВИНИ', + }, + locations: [ + { + name: 'ДиноПарк', + shortDesc: 'Прогуляйтесь серед реалістичних динозаврів у повний зріст', + href: '/lokatsii', + }, + { + name: 'Диво Ліс', + shortDesc: 'Чарівний ліс з інтерактивними атракціонами та мотузковими парками', + href: '/lokatsii', + }, + { + name: 'Дзеркальний Лабіринт', + shortDesc: 'Захоплюючий лабіринт з дзеркалами та оптичними ілюзіями', + href: '/lokatsii', + }, + ], + features: [ + { + icon: '🦕', + title: 'Безпека', + description: 'Атракціони сертифіковані, майданчик під постійним наглядом', + }, + { icon: '🌲', title: 'Природа', description: 'Парк розташований серед живої природи' }, + { + icon: '🎉', + title: 'Свята', + description: 'Дні народження, корпоративи та шкільні екскурсії', + }, + { + icon: '🎟️', + title: 'Квитки онлайн', + description: 'Купуйте квитки онлайн без черги на касі', + }, + { + icon: '🍕', + title: 'Кафе та їжа', + description: 'Власне кафе з дитячим меню та легкими закусками', + }, + { icon: '🅿️', title: 'Парковка', description: 'Безкоштовна парковка для відвідувачів' }, + ], + news: { title: 'Новини', limit: 3 }, + } as never, + overrideAccess: true, + }) + console.log('Seeded home-page global') + } + + if (force) { + // Header global + await payload.updateGlobal({ + slug: 'header', + data: { + navLinks: [ + { label: 'Головна', href: '/' }, + { label: 'Локації', href: '/lokatsii' }, + { label: 'Блог', href: '/blog' }, + { label: 'Дні народження', href: '/dni-narodzhennia' }, + { label: 'Групові відвідування', href: '/grupovi-vidviduvannia' }, + ], ctaLabel: 'Купити квиток', ctaHref: '/kvytky', - }, - sectionTitles: { - locations: 'ЛАСКАВО ПРОСИМО ДО ШУМІЛЕНДУ', - whyParents: 'ЧОМУ БАТЬКИ ОБИРАЮТЬ ШУМІЛЕНД', - birthday: 'ДЕНЬ НАРОДЖЕННЯ В ШУМІЛЕНДІ', - gallery: 'ФОТОГАЛЕРЕЯ', - reviews: 'ВІДГУКИ', - news: 'НОВИНИ', - }, - locations: [ - { - name: 'ДиноПарк', - shortDesc: 'Прогуляйтесь серед реалістичних динозаврів у повний зріст', - href: '/lokatsii', - }, - { - name: 'Диво Ліс', - shortDesc: 'Чарівний ліс з інтерактивними атракціонами та мотузковими парками', - href: '/lokatsii', - }, - { - name: 'Дзеркальний Лабіринт', - shortDesc: 'Захоплюючий лабіринт з дзеркалами та оптичними ілюзіями', - href: '/lokatsii', - }, - ], - features: [ - { - icon: '🦕', - title: 'Безпека', - description: 'Атракціони сертифіковані, майданчик під постійним наглядом', - }, - { icon: '🌲', title: 'Природа', description: 'Парк розташований серед живої природи' }, - { - icon: '🎉', - title: 'Свята', - description: 'Дні народження, корпоративи та шкільні екскурсії', - }, - { - icon: '🎟️', - title: 'Квитки онлайн', - description: 'Купуйте квитки онлайн без черги на касі', - }, - { - icon: '🍕', - title: 'Кафе та їжа', - description: 'Власне кафе з дитячим меню та легкими закусками', - }, - { icon: '🅿️', title: 'Парковка', description: 'Безкоштовна парковка для відвідувачів' }, - ], - news: { title: 'Новини', limit: 3 }, - } as never, - overrideAccess: true, - }) - console.log('Seeded home-page global') + } as never, + overrideAccess: true, + }) + console.log('Seeded header global') - // Header global - await payload.updateGlobal({ - slug: 'header', - data: { - navLinks: [ - { label: 'Головна', href: '/' }, - { label: 'Локації', href: '/lokatsii' }, - { label: 'Блог', href: '/blog' }, - { label: 'Дні народження', href: '/dni-narodzhennia' }, - { label: 'Групові відвідування', href: '/grupovi-vidviduvannia' }, - ], - ctaLabel: 'Купити квиток', - ctaHref: '/kvytky', - } as never, - overrideAccess: true, - }) - console.log('Seeded header global') + // Footer global + await payload.updateGlobal({ + slug: 'footer', + data: { copyrightText: `© Шуміленд ${new Date().getFullYear()}` } as never, + overrideAccess: true, + }) + console.log('Seeded footer global') - // Footer global - await payload.updateGlobal({ - slug: 'footer', - data: { copyrightText: `© Шуміленд ${new Date().getFullYear()}` } as never, - overrideAccess: true, - }) - console.log('Seeded footer global') + // Site settings + await payload.updateGlobal({ + slug: 'site-settings', + data: { + siteName: 'Шуміленд', + siteURL: process.env['NEXT_PUBLIC_SITE_URL'] ?? 'https://shumiland.ua', + } as never, + overrideAccess: true, + }) + console.log('Seeded site-settings global') - // Site settings - await payload.updateGlobal({ - slug: 'site-settings', - data: { - siteName: 'Шуміленд', - siteURL: process.env['NEXT_PUBLIC_SITE_URL'] ?? 'https://shumiland.ua', - } as never, - overrideAccess: true, - }) - console.log('Seeded site-settings global') - - // Remaining globals — initialize empty - for (const slug of ['checkout-page', 'thank-you-page'] as const) { - try { - await payload.updateGlobal({ slug, data: {} as never, overrideAccess: true }) - console.log(`Initialized global: ${slug}`) - } catch (err) { - console.warn(`Could not initialize global ${slug}:`, err) + // Remaining globals — initialize empty + for (const slug of ['checkout-page', 'thank-you-page'] as const) { + try { + await payload.updateGlobal({ slug, data: {} as never, overrideAccess: true }) + console.log(`Initialized global: ${slug}`) + } catch (err) { + console.warn(`Could not initialize global ${slug}:`, err) + } } } @@ -180,94 +197,98 @@ async function seed(): Promise { console.log('Blog posts already exist, skipping.') } - // Tariffs — update to match Figma designs + // Tariffs — seed only if none exist (never delete admin-managed tariffs) { - // Delete old tariffs and re-seed with correct data - const existing = await payload.find({ collection: 'tariffs', limit: 100, overrideAccess: true }) - for (const t of existing.docs) { - await payload.delete({ collection: 'tariffs', id: t.id, overrideAccess: true }) - } + const { totalDocs: tariffCount } = await payload.find({ + collection: 'tariffs', + limit: 1, + overrideAccess: true, + }) - const tariffs = [ - // Individual dino-park tickets (shown on both Dino and DyvoLis pages) - { - ezy_id: 1001, - last_synced_name: 'Вхід до Динопарку', - display_name: 'Вхід до Динопарку', - last_synced_price: 300, - category_tag: 'dyno', - sort: 1, - visible: true, - }, - { - ezy_id: 1002, - last_synced_name: 'Звичайна екскурсія', - display_name: 'Звичайна екскурсія', - last_synced_price: 150, - category_tag: 'dyno', - sort: 2, - visible: true, - }, - { - ezy_id: 1003, - last_synced_name: 'Палеонтологічна екскурсія', - display_name: 'Палеонтологічна екскурсія', - last_synced_price: 300, - category_tag: 'dyno', - sort: 3, - visible: true, - }, - { - ezy_id: 1004, - last_synced_name: 'ДиноРодо', - display_name: 'ДиноРодо', - last_synced_price: 50, - category_tag: 'dyno', - sort: 4, - visible: true, - }, - // Combo tickets - { - ezy_id: 3001, - last_synced_name: 'Комбо на 1 людину', - display_name: 'Комбо на 1 людину', - last_synced_price: 600, - category_tag: 'combo', - sort: 1, - visible: true, - }, - { - ezy_id: 3002, - last_synced_name: 'Комбо на 3 людини', - display_name: 'Комбо на 3 людини', - last_synced_price: 1500, - category_tag: 'combo', - sort: 2, - visible: true, - }, - { - ezy_id: 3003, - last_synced_name: 'Комбо на 4 людини', - display_name: 'Комбо на 4 людини', - last_synced_price: 1800, - category_tag: 'combo', - sort: 3, - visible: true, - }, - { - ezy_id: 3004, - last_synced_name: 'Комбо на 5 людин', - display_name: 'Комбо на 5 людин', - last_synced_price: 2000, - category_tag: 'combo', - sort: 4, - visible: true, - }, - ] - for (const t of tariffs) { - await payload.create({ collection: 'tariffs', data: t as never, overrideAccess: true }) + if (tariffCount > 0) { + console.log('Tariffs already exist, skipping.') + } else { + const tariffs = [ + // Individual dino-park tickets (shown on both Dino and DyvoLis pages) + { + ezy_id: 1001, + last_synced_name: 'Вхід до Динопарку', + display_name: 'Вхід до Динопарку', + last_synced_price: 300, + category_tag: 'dyno', + sort: 1, + visible: true, + }, + { + ezy_id: 1002, + last_synced_name: 'Звичайна екскурсія', + display_name: 'Звичайна екскурсія', + last_synced_price: 150, + category_tag: 'dyno', + sort: 2, + visible: true, + }, + { + ezy_id: 1003, + last_synced_name: 'Палеонтологічна екскурсія', + display_name: 'Палеонтологічна екскурсія', + last_synced_price: 300, + category_tag: 'dyno', + sort: 3, + visible: true, + }, + { + ezy_id: 1004, + last_synced_name: 'ДиноРодо', + display_name: 'ДиноРодо', + last_synced_price: 50, + category_tag: 'dyno', + sort: 4, + visible: true, + }, + // Combo tickets + { + ezy_id: 3001, + last_synced_name: 'Комбо на 1 людину', + display_name: 'Комбо на 1 людину', + last_synced_price: 600, + category_tag: 'combo', + sort: 1, + visible: true, + }, + { + ezy_id: 3002, + last_synced_name: 'Комбо на 3 людини', + display_name: 'Комбо на 3 людини', + last_synced_price: 1500, + category_tag: 'combo', + sort: 2, + visible: true, + }, + { + ezy_id: 3003, + last_synced_name: 'Комбо на 4 людини', + display_name: 'Комбо на 4 людини', + last_synced_price: 1800, + category_tag: 'combo', + sort: 3, + visible: true, + }, + { + ezy_id: 3004, + last_synced_name: 'Комбо на 5 людин', + display_name: 'Комбо на 5 людин', + last_synced_price: 2000, + category_tag: 'combo', + sort: 4, + visible: true, + }, + ] + for (const t of tariffs) { + await payload.create({ collection: 'tariffs', data: t as never, overrideAccess: true }) + } + console.log('Seeded tariffs') } - console.log('Seeded tariffs (updated to match Figma)') } // Locations