diff --git a/next.config.ts b/next.config.ts index de1ccf6..b17aaa3 100644 --- a/next.config.ts +++ b/next.config.ts @@ -1,9 +1,23 @@ import { withPayload } from '@payloadcms/next/withPayload' import type { NextConfig } from 'next' +const ONE_YEAR = 'public, max-age=31536000, immutable' + const nextConfig: NextConfig = { output: 'standalone', reactStrictMode: true, + async headers() { + return [ + { + source: '/images/:path*', + headers: [{ key: 'Cache-Control', value: ONE_YEAR }], + }, + { + source: '/_next/static/:path*', + headers: [{ key: 'Cache-Control', value: ONE_YEAR }], + }, + ] + }, webpack: (config) => { config.watchOptions = { ...config.watchOptions, diff --git a/package.json b/package.json index 276b311..4f78fab 100644 --- a/package.json +++ b/package.json @@ -40,6 +40,7 @@ "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "cyrillic-to-translit-js": "^3.2.1", + "drizzle-kit": "0.31.7", "graphql": "^16.9.0", "next": "^16.2.6", "payload": "^3.33.0", diff --git a/payload.config.ts b/payload.config.ts index c9a3cc8..e1eee7e 100644 --- a/payload.config.ts +++ b/payload.config.ts @@ -45,7 +45,20 @@ export default buildConfig({ editor: lexicalEditor(), sharp, - collections: [Users, Media, Pages, BlogPosts, Categories, Tags, Tariffs, Leads, Orders, Locations, Reviews, BirthdayPackages], + collections: [ + Users, + Media, + Pages, + BlogPosts, + Categories, + Tags, + Tariffs, + Leads, + Orders, + Locations, + Reviews, + BirthdayPackages, + ], globals: [HomePage, CheckoutPage, ThankYouPage, Header, Footer, SiteSettings], diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7de768a..66e79f9 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -29,6 +29,9 @@ importers: cyrillic-to-translit-js: specifier: ^3.2.1 version: 3.2.1 + drizzle-kit: + specifier: 0.31.7 + version: 0.31.7 graphql: specifier: ^16.9.0 version: 16.14.0 diff --git a/public/images/figma/081e52b5-d35a-41d2-b506-a9d751b0b563.webp b/public/images/figma/081e52b5-d35a-41d2-b506-a9d751b0b563.webp new file mode 100644 index 0000000..5cd2ce3 Binary files /dev/null and b/public/images/figma/081e52b5-d35a-41d2-b506-a9d751b0b563.webp differ diff --git a/public/images/figma/109f13b4-25f9-4f94-a06d-77421ff2b4fe.webp b/public/images/figma/109f13b4-25f9-4f94-a06d-77421ff2b4fe.webp new file mode 100644 index 0000000..226d4e5 Binary files /dev/null and b/public/images/figma/109f13b4-25f9-4f94-a06d-77421ff2b4fe.webp differ diff --git a/public/images/figma/17d78753-cec3-40ca-a1fc-4125c2b79eff.webp b/public/images/figma/17d78753-cec3-40ca-a1fc-4125c2b79eff.webp new file mode 100644 index 0000000..d5d548f Binary files /dev/null and b/public/images/figma/17d78753-cec3-40ca-a1fc-4125c2b79eff.webp differ diff --git a/public/images/figma/2936ec5e-4f99-441e-9bf2-34f23c283170.webp b/public/images/figma/2936ec5e-4f99-441e-9bf2-34f23c283170.webp new file mode 100644 index 0000000..dac81fb Binary files /dev/null and b/public/images/figma/2936ec5e-4f99-441e-9bf2-34f23c283170.webp differ diff --git a/public/images/figma/2c6a3e5e-7346-4c3e-b8a0-fae1facb87ad.webp b/public/images/figma/2c6a3e5e-7346-4c3e-b8a0-fae1facb87ad.webp new file mode 100644 index 0000000..33b457a Binary files /dev/null and b/public/images/figma/2c6a3e5e-7346-4c3e-b8a0-fae1facb87ad.webp differ diff --git a/public/images/figma/455670b9-c89a-4bea-81fe-5a4d93b25483.webp b/public/images/figma/455670b9-c89a-4bea-81fe-5a4d93b25483.webp new file mode 100644 index 0000000..dc09f88 Binary files /dev/null and b/public/images/figma/455670b9-c89a-4bea-81fe-5a4d93b25483.webp differ diff --git a/public/images/figma/5088f41f-b898-4958-b7f6-519496b65382.webp b/public/images/figma/5088f41f-b898-4958-b7f6-519496b65382.webp new file mode 100644 index 0000000..7f522e9 Binary files /dev/null and b/public/images/figma/5088f41f-b898-4958-b7f6-519496b65382.webp differ diff --git a/public/images/figma/58218f84-457d-4ff5-8d4e-2ace40b45568.webp b/public/images/figma/58218f84-457d-4ff5-8d4e-2ace40b45568.webp new file mode 100644 index 0000000..7dc46bc Binary files /dev/null and b/public/images/figma/58218f84-457d-4ff5-8d4e-2ace40b45568.webp differ diff --git a/public/images/figma/640999e1-7096-4623-bf96-12bb8ef62ffc.webp b/public/images/figma/640999e1-7096-4623-bf96-12bb8ef62ffc.webp new file mode 100644 index 0000000..35cc975 Binary files /dev/null and b/public/images/figma/640999e1-7096-4623-bf96-12bb8ef62ffc.webp differ diff --git a/public/images/figma/7a2627b2-b6ce-4325-a0b1-fbc3393aca4c.webp b/public/images/figma/7a2627b2-b6ce-4325-a0b1-fbc3393aca4c.webp new file mode 100644 index 0000000..1f0b991 Binary files /dev/null and b/public/images/figma/7a2627b2-b6ce-4325-a0b1-fbc3393aca4c.webp differ diff --git a/public/images/figma/908a8aab-6129-4d7d-b7ac-c43b6fa60044.webp b/public/images/figma/908a8aab-6129-4d7d-b7ac-c43b6fa60044.webp new file mode 100644 index 0000000..12b737d Binary files /dev/null and b/public/images/figma/908a8aab-6129-4d7d-b7ac-c43b6fa60044.webp differ diff --git a/public/images/figma/abacda18-57c7-441d-9313-c70d22c6f0f0.webp b/public/images/figma/abacda18-57c7-441d-9313-c70d22c6f0f0.webp new file mode 100644 index 0000000..6eeaf2e Binary files /dev/null and b/public/images/figma/abacda18-57c7-441d-9313-c70d22c6f0f0.webp differ diff --git a/public/images/figma/ac7f3a5e-0e66-4971-bccc-89901a7c314d.webp b/public/images/figma/ac7f3a5e-0e66-4971-bccc-89901a7c314d.webp new file mode 100644 index 0000000..b109e50 Binary files /dev/null and b/public/images/figma/ac7f3a5e-0e66-4971-bccc-89901a7c314d.webp differ diff --git a/public/images/figma/b5597790-7b54-4ad7-9977-4cb15633286e.webp b/public/images/figma/b5597790-7b54-4ad7-9977-4cb15633286e.webp new file mode 100644 index 0000000..628e18a Binary files /dev/null and b/public/images/figma/b5597790-7b54-4ad7-9977-4cb15633286e.webp differ diff --git a/public/images/figma/c3053789-6cd0-4dda-9774-d4ae4bc400e1.webp b/public/images/figma/c3053789-6cd0-4dda-9774-d4ae4bc400e1.webp new file mode 100644 index 0000000..2a63a1d Binary files /dev/null and b/public/images/figma/c3053789-6cd0-4dda-9774-d4ae4bc400e1.webp differ diff --git a/public/images/figma/check-mark.webp b/public/images/figma/check-mark.webp new file mode 100644 index 0000000..870957d Binary files /dev/null and b/public/images/figma/check-mark.webp differ diff --git a/public/images/figma/de9ad287-5e83-4a0c-ad06-f2420bff4096.webp b/public/images/figma/de9ad287-5e83-4a0c-ad06-f2420bff4096.webp new file mode 100644 index 0000000..628e18a Binary files /dev/null and b/public/images/figma/de9ad287-5e83-4a0c-ad06-f2420bff4096.webp differ diff --git a/public/images/figma/e42d6611-82e6-47b0-9d84-271f9810ab1a.webp b/public/images/figma/e42d6611-82e6-47b0-9d84-271f9810ab1a.webp new file mode 100644 index 0000000..cbab9f9 Binary files /dev/null and b/public/images/figma/e42d6611-82e6-47b0-9d84-271f9810ab1a.webp differ diff --git a/public/images/figma/e9a8cee6-6ee5-4c74-b270-1c133a762c0a.webp b/public/images/figma/e9a8cee6-6ee5-4c74-b270-1c133a762c0a.webp new file mode 100644 index 0000000..019e09b Binary files /dev/null and b/public/images/figma/e9a8cee6-6ee5-4c74-b270-1c133a762c0a.webp differ diff --git a/public/images/figma/f4e2bff2-754c-460c-baba-baa95521bfc9.webp b/public/images/figma/f4e2bff2-754c-460c-baba-baa95521bfc9.webp new file mode 100644 index 0000000..9a075d4 Binary files /dev/null and b/public/images/figma/f4e2bff2-754c-460c-baba-baa95521bfc9.webp differ diff --git a/public/images/figma/f5d32fc0-8ea7-4fd1-b193-819d6aa1a68e.webp b/public/images/figma/f5d32fc0-8ea7-4fd1-b193-819d6aa1a68e.webp new file mode 100644 index 0000000..4e561ad Binary files /dev/null and b/public/images/figma/f5d32fc0-8ea7-4fd1-b193-819d6aa1a68e.webp differ diff --git a/public/images/figma/footer-bg.webp b/public/images/figma/footer-bg.webp new file mode 100644 index 0000000..9b930c0 Binary files /dev/null and b/public/images/figma/footer-bg.webp differ diff --git a/public/images/figma/gallery-1.webp b/public/images/figma/gallery-1.webp new file mode 100644 index 0000000..5e9c825 Binary files /dev/null and b/public/images/figma/gallery-1.webp differ diff --git a/public/images/figma/gallery-2.webp b/public/images/figma/gallery-2.webp new file mode 100644 index 0000000..1580746 Binary files /dev/null and b/public/images/figma/gallery-2.webp differ diff --git a/public/images/figma/gallery-3.webp b/public/images/figma/gallery-3.webp new file mode 100644 index 0000000..5e9c825 Binary files /dev/null and b/public/images/figma/gallery-3.webp differ diff --git a/public/images/figma/gallery-4.webp b/public/images/figma/gallery-4.webp new file mode 100644 index 0000000..53085a9 Binary files /dev/null and b/public/images/figma/gallery-4.webp differ diff --git a/public/images/figma/gallery-6.webp b/public/images/figma/gallery-6.webp new file mode 100644 index 0000000..d4372a9 Binary files /dev/null and b/public/images/figma/gallery-6.webp differ diff --git a/public/images/figma/gallery-7.webp b/public/images/figma/gallery-7.webp new file mode 100644 index 0000000..3d64245 Binary files /dev/null and b/public/images/figma/gallery-7.webp differ diff --git a/public/images/figma/gallery-8.webp b/public/images/figma/gallery-8.webp new file mode 100644 index 0000000..bcab104 Binary files /dev/null and b/public/images/figma/gallery-8.webp differ diff --git a/public/images/figma/hero-bg-family.webp b/public/images/figma/hero-bg-family.webp new file mode 100644 index 0000000..35cc975 Binary files /dev/null and b/public/images/figma/hero-bg-family.webp differ diff --git a/public/images/figma/hero-bg1.webp b/public/images/figma/hero-bg1.webp new file mode 100644 index 0000000..5cd2ce3 Binary files /dev/null and b/public/images/figma/hero-bg1.webp differ diff --git a/public/images/figma/hero-bg2.webp b/public/images/figma/hero-bg2.webp new file mode 100644 index 0000000..932d741 Binary files /dev/null and b/public/images/figma/hero-bg2.webp differ diff --git a/public/images/figma/hero-blur-mask.webp b/public/images/figma/hero-blur-mask.webp new file mode 100644 index 0000000..3af4494 Binary files /dev/null and b/public/images/figma/hero-blur-mask.webp differ diff --git a/public/images/figma/loc-dinopark.webp b/public/images/figma/loc-dinopark.webp new file mode 100644 index 0000000..1c7796c Binary files /dev/null and b/public/images/figma/loc-dinopark.webp differ diff --git a/public/images/figma/loc-divo-lis.webp b/public/images/figma/loc-divo-lis.webp new file mode 100644 index 0000000..408efc0 Binary files /dev/null and b/public/images/figma/loc-divo-lis.webp differ diff --git a/public/images/figma/loc-map.webp b/public/images/figma/loc-map.webp new file mode 100644 index 0000000..226d4e5 Binary files /dev/null and b/public/images/figma/loc-map.webp differ diff --git a/public/images/figma/news-bg1.webp b/public/images/figma/news-bg1.webp new file mode 100644 index 0000000..70cbb8c Binary files /dev/null and b/public/images/figma/news-bg1.webp differ diff --git a/public/images/figma/news-bg2.webp b/public/images/figma/news-bg2.webp new file mode 100644 index 0000000..f2f4c21 Binary files /dev/null and b/public/images/figma/news-bg2.webp differ diff --git a/public/images/figma/news-bg3.webp b/public/images/figma/news-bg3.webp new file mode 100644 index 0000000..a766634 Binary files /dev/null and b/public/images/figma/news-bg3.webp differ diff --git a/public/images/figma/news-bg4.webp b/public/images/figma/news-bg4.webp new file mode 100644 index 0000000..019e09b Binary files /dev/null and b/public/images/figma/news-bg4.webp differ diff --git a/public/images/figma/news-bg5.webp b/public/images/figma/news-bg5.webp new file mode 100644 index 0000000..a2807bd Binary files /dev/null and b/public/images/figma/news-bg5.webp differ diff --git a/public/images/figma/news-bg6.webp b/public/images/figma/news-bg6.webp new file mode 100644 index 0000000..6eeaf2e Binary files /dev/null and b/public/images/figma/news-bg6.webp differ diff --git a/public/images/figma/review-avatar-bg.webp b/public/images/figma/review-avatar-bg.webp new file mode 100644 index 0000000..fdd12c3 Binary files /dev/null and b/public/images/figma/review-avatar-bg.webp differ diff --git a/public/images/figma/video-preview.webp b/public/images/figma/video-preview.webp new file mode 100644 index 0000000..dc09f88 Binary files /dev/null and b/public/images/figma/video-preview.webp differ diff --git a/public/images/figma/why-parents-1.webp b/public/images/figma/why-parents-1.webp new file mode 100644 index 0000000..31a5c7e Binary files /dev/null and b/public/images/figma/why-parents-1.webp differ diff --git a/public/images/figma/why-parents-2.webp b/public/images/figma/why-parents-2.webp new file mode 100644 index 0000000..c2a822e Binary files /dev/null and b/public/images/figma/why-parents-2.webp differ diff --git a/public/images/figma/why-parents-3.webp b/public/images/figma/why-parents-3.webp new file mode 100644 index 0000000..bfc390f Binary files /dev/null and b/public/images/figma/why-parents-3.webp differ diff --git a/public/images/figma/why-parents-4.webp b/public/images/figma/why-parents-4.webp new file mode 100644 index 0000000..49dd1e5 Binary files /dev/null and b/public/images/figma/why-parents-4.webp differ diff --git a/scripts/compress-images.mjs b/scripts/compress-images.mjs new file mode 100644 index 0000000..6e21be0 --- /dev/null +++ b/scripts/compress-images.mjs @@ -0,0 +1,74 @@ +import sharp from 'sharp' +import { readdir, stat } from 'fs/promises' +import path from 'path' +import { fileURLToPath } from 'url' + +const __dirname = path.dirname(fileURLToPath(import.meta.url)) +const imgDir = path.resolve(__dirname, '../public/images/figma') + +const SKIP = /\.(svg|webp|gif)$/i + +const SIZE_LIMITS = { + // hero / footer / video-preview — full bleed, keep wide + 'hero-bg1.png': { width: 1920 }, + 'hero-bg2.png': { width: 1920 }, + 'hero-bg-family.png': { width: 1920 }, + 'footer-bg.png': { width: 1920 }, + 'video-preview.png': { width: 1920 }, + // location cards — displayed at ~560px, 2× retina = 1120 + 'loc-dinopark.jpg': { width: 1200 }, + 'loc-divo-lis.png': { width: 1200 }, + // gallery tiles — displayed at ~380px, 2× = 760 + 'gallery-1.png': { width: 800 }, + 'gallery-2.png': { width: 800 }, + 'gallery-3.png': { width: 800 }, + 'gallery-4.png': { width: 800 }, + 'gallery-6.png': { width: 800 }, + 'gallery-7.png': { width: 800 }, + 'gallery-8.png': { width: 800 }, + // why-parents side tiles + 'why-parents-1.png': { width: 800 }, + 'why-parents-2.png': { width: 800 }, + 'why-parents-3.png': { width: 800 }, + 'why-parents-4.png': { width: 800 }, + // news / blog thumbnails + 'news-bg1.jpg': { width: 800 }, + 'news-bg2.png': { width: 800 }, + 'news-bg3.jpg': { width: 800 }, + // misc + 'review-avatar-bg.jpg': { width: 160 }, + 'check-mark.png': { width: 48 }, +} + +const files = await readdir(imgDir) + +let totalBefore = 0 +let totalAfter = 0 + +for (const file of files) { + if (SKIP.test(file)) continue + if (file.endsWith('.webp')) continue + + const src = path.join(imgDir, file) + const dst = path.join(imgDir, file.replace(/\.(png|jpg|jpeg)$/i, '.webp')) + + const srcStat = await stat(src) + totalBefore += srcStat.size + + const limit = SIZE_LIMITS[file] ?? { width: 1920 } + + try { + const info = await sharp(src) + .resize({ width: limit.width, withoutEnlargement: true }) + .webp({ quality: 82, effort: 4 }) + .toFile(dst) + + totalAfter += info.size + const pct = ((1 - info.size / srcStat.size) * 100).toFixed(1) + console.log(`✓ ${file} → ${file.replace(/\.(png|jpg|jpeg)$/i, '.webp')} ${(srcStat.size / 1e6).toFixed(1)}MB → ${(info.size / 1e6).toFixed(1)}MB (${pct}% smaller)`) + } catch (err) { + console.error(`✗ ${file}: ${err.message}`) + } +} + +console.log(`\nTotal: ${(totalBefore / 1e6).toFixed(1)}MB → ${(totalAfter / 1e6).toFixed(1)}MB (${((1 - totalAfter / totalBefore) * 100).toFixed(1)}% reduction)`) diff --git a/src/app/(frontend)/lokatsii/page.tsx b/src/app/(frontend)/lokatsii/page.tsx index 0f3f1a4..37a5767 100644 --- a/src/app/(frontend)/lokatsii/page.tsx +++ b/src/app/(frontend)/lokatsii/page.tsx @@ -19,7 +19,7 @@ const STATIC_LOCATIONS: LocationCMS[] = [ slug: 'dynopark', tagline: 'портал у світ динозаврів', shortDesc: 'Прогуляйтесь серед реалістичних динозаврів у повний зріст. Понад 20 видів доісторичних тварин у природному середовищі.', - image: '/images/figma/loc-dinopark.jpg', + image: '/images/figma/loc-dinopark.webp', }, { id: 'dyvolis', @@ -27,7 +27,7 @@ const STATIC_LOCATIONS: LocationCMS[] = [ slug: 'dyvolis', tagline: 'зона казкових топіарних фігур', shortDesc: 'Чарівний ліс з інтерактивними атракціонами, мотузковими парками та пригодами для всіх вікових груп.', - image: '/images/figma/loc-divo-lis.png', + image: '/images/figma/loc-divo-lis.webp', }, { id: 'maze', @@ -35,7 +35,7 @@ const STATIC_LOCATIONS: LocationCMS[] = [ slug: 'maze', tagline: 'справжній виклик кмітливості', shortDesc: 'Захоплюючий лабіринт з дзеркалами, оптичними ілюзіями та таємничими кімнатами.', - image: '/images/figma/gallery-1.png', + image: '/images/figma/gallery-1.webp', }, { id: 'tir', @@ -43,7 +43,7 @@ const STATIC_LOCATIONS: LocationCMS[] = [ slug: 'tir', tagline: 'перемога, яку ви розділите разом', shortDesc: 'Влаштуйте дружні змагання, дайте малечі декілька уроків та виграйте класний приз.', - image: '/images/figma/gallery-3.png', + image: '/images/figma/gallery-3.webp', }, { id: 'playground', @@ -51,7 +51,7 @@ const STATIC_LOCATIONS: LocationCMS[] = [ slug: 'playground', tagline: 'територія забав і нових друзів', shortDesc: 'Поки малеча підкорює гірки та знаходить перших друзів, ви можете нарешті зробити паузу.', - image: '/images/figma/gallery-8.png', + image: '/images/figma/gallery-8.webp', }, ] @@ -90,7 +90,7 @@ export default async function LocationsPage() {
{locations.map((loc, i) => { - const imgUrl = getMediaUrl(loc.image) ?? '/images/figma/loc-dinopark.jpg' + const imgUrl = getMediaUrl(loc.image) ?? '/images/figma/loc-dinopark.webp' const color = COLORS[i % COLORS.length] const slug = loc.slug diff --git a/src/collections/Leads.ts b/src/collections/Leads.ts index 0ef2a78..f942c74 100644 --- a/src/collections/Leads.ts +++ b/src/collections/Leads.ts @@ -38,7 +38,12 @@ export const Leads: CollectionConfig = { }, { name: 'message', type: 'textarea', label: 'Повідомлення' }, { name: 'groupSize', type: 'number', label: 'Кількість учасників' }, - { name: 'preferredDate', type: 'date', label: 'Бажана дата', admin: { date: { pickerAppearance: 'dayOnly' } } }, + { + name: 'preferredDate', + type: 'date', + label: 'Бажана дата', + admin: { date: { pickerAppearance: 'dayOnly' } }, + }, { name: 'packageSlug', type: 'text', label: 'Пакет (slug)' }, { name: 'notes', type: 'textarea' }, { diff --git a/src/components/forms/BirthdayBookingForm.tsx b/src/components/forms/BirthdayBookingForm.tsx index 2e44bcc..36d5896 100644 --- a/src/components/forms/BirthdayBookingForm.tsx +++ b/src/components/forms/BirthdayBookingForm.tsx @@ -46,10 +46,9 @@ export function BirthdayBookingForm({ defaultPackage }: BirthdayBookingFormProps packageSlug: packageSlug || undefined, groupSize: guestCount ? Number(guestCount) : undefined, preferredDate: preferredDate || undefined, - message: [ - childAge ? `Вік іменинника: ${childAge}` : '', - wishes, - ].filter(Boolean).join('\n') || undefined, + message: + [childAge ? `Вік іменинника: ${childAge}` : '', wishes].filter(Boolean).join('\n') || + undefined, ...utm, }), }) @@ -61,7 +60,7 @@ export function BirthdayBookingForm({ defaultPackage }: BirthdayBookingFormProps } setSuccess(true) } catch { - setError('Помилка мережі. Перевірте з\'єднання та спробуйте ще раз.') + setError("Помилка мережі. Перевірте з'єднання та спробуйте ще раз.") } }) } @@ -73,7 +72,7 @@ export function BirthdayBookingForm({ defaultPackage }: BirthdayBookingFormProps

Заявку отримано!

-

+

Менеджер зв'яжеться з вами протягом 30 хвилин для уточнення деталей свята.

@@ -220,7 +219,11 @@ function Field({ }) { return (
-
@@ -198,7 +200,11 @@ function Field({ }) { return (
-