fix: set #f1fbeb background everywhere except hero and footer
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

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Vadym Samoilenko 2026-05-11 16:33:04 +01:00
parent b93317e5ff
commit b628a2bfb9
9 changed files with 143 additions and 40 deletions

2
next-env.d.ts vendored
View file

@ -1,6 +1,6 @@
/// <reference types="next" />
/// <reference types="next/image-types/global" />
import './.next/types/routes.d.ts'
import "./.next/dev/types/routes.d.ts";
// NOTE: This file should not be edited
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.

View file

@ -1,4 +1,3 @@
/* eslint-disable @next/next/no-img-element */
import type { Metadata } from 'next'
import Link from 'next/link'
import { notFound } from 'next/navigation'
@ -55,15 +54,16 @@ export default async function BlogPostPage({ params }: Props) {
if (!post) notFound()
const publishedDate = post.publishedAt
? new Date(post.publishedAt).toLocaleDateString('uk-UA', { day: 'numeric', month: 'long', year: 'numeric' })
? new Date(post.publishedAt).toLocaleDateString('uk-UA', {
day: 'numeric',
month: 'long',
year: 'numeric',
})
: null
return (
<div className="min-h-screen bg-[#f1fbeb]">
<PageHero
title={post.title}
bgSrc={post.hero?.url ?? undefined}
/>
<PageHero title={post.title} bgSrc={post.hero?.url ?? undefined} />
<div className="mx-auto max-w-[800px] px-8 py-12">
{/* Back + date row */}
@ -74,7 +74,13 @@ export default async function BlogPostPage({ params }: Props) {
style={{ fontFamily: 'var(--font-montserrat, Montserrat), sans-serif' }}
>
<svg width="16" height="16" viewBox="0 0 16 16" fill="none">
<path d="M13 8H3M7 4L3 8l4 4" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round"/>
<path
d="M13 8H3M7 4L3 8l4 4"
stroke="currentColor"
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
Усі статті
</Link>
@ -88,7 +94,7 @@ export default async function BlogPostPage({ params }: Props) {
{/* Excerpt */}
{post.excerpt && (
<p
className="mb-8 text-[18px] leading-relaxed font-medium text-[#272727]/70 border-l-4 border-[#f28b4a] pl-5"
className="mb-8 border-l-4 border-[#f28b4a] pl-5 text-[18px] leading-relaxed font-medium text-[#272727]/70"
style={{ fontFamily: 'var(--font-poppins, Poppins), sans-serif' }}
>
{post.excerpt}
@ -98,13 +104,16 @@ export default async function BlogPostPage({ params }: Props) {
{/* Body */}
{post.body ? (
<div
className="[&_p]:mb-5 [&_p]:text-[17px] [&_p]:leading-[1.85] [&_p]:text-[#272727] [&_h1]:mb-6 [&_h1]:mt-10 [&_h1]:text-[32px] [&_h1]:font-bold [&_h1]:leading-tight [&_h1]:text-[#223e0d] [&_h2]:mb-4 [&_h2]:mt-10 [&_h2]:border-b [&_h2]:border-[#396817]/20 [&_h2]:pb-2 [&_h2]:text-[26px] [&_h2]:font-bold [&_h2]:leading-tight [&_h2]:text-[#223e0d] [&_h3]:mb-3 [&_h3]:mt-8 [&_h3]:text-[20px] [&_h3]:font-semibold [&_h3]:text-[#272727] [&_ul]:mb-5 [&_ul]:list-disc [&_ul]:pl-7 [&_ol]:mb-5 [&_ol]:list-decimal [&_ol]:pl-7 [&_li]:mb-2 [&_li]:text-[17px] [&_li]:leading-[1.75] [&_strong]:font-semibold [&_strong]:text-[#223e0d] [&_em]:italic [&_a]:text-[#396817] [&_a]:underline [&_a]:underline-offset-2 [&_blockquote]:my-6 [&_blockquote]:border-l-4 [&_blockquote]:border-[#f28b4a] [&_blockquote]:pl-5 [&_blockquote]:italic [&_blockquote]:text-[#272727]/70"
className="[&_a]:text-[#396817] [&_a]:underline [&_a]:underline-offset-2 [&_blockquote]:my-6 [&_blockquote]:border-l-4 [&_blockquote]:border-[#f28b4a] [&_blockquote]:pl-5 [&_blockquote]:text-[#272727]/70 [&_blockquote]:italic [&_em]:italic [&_h1]:mt-10 [&_h1]:mb-6 [&_h1]:text-[32px] [&_h1]:leading-tight [&_h1]:font-bold [&_h1]:text-[#223e0d] [&_h2]:mt-10 [&_h2]:mb-4 [&_h2]:border-b [&_h2]:border-[#396817]/20 [&_h2]:pb-2 [&_h2]:text-[26px] [&_h2]:leading-tight [&_h2]:font-bold [&_h2]:text-[#223e0d] [&_h3]:mt-8 [&_h3]:mb-3 [&_h3]:text-[20px] [&_h3]:font-semibold [&_h3]:text-[#272727] [&_li]:mb-2 [&_li]:text-[17px] [&_li]:leading-[1.75] [&_ol]:mb-5 [&_ol]:list-decimal [&_ol]:pl-7 [&_p]:mb-5 [&_p]:text-[17px] [&_p]:leading-[1.85] [&_p]:text-[#272727] [&_strong]:font-semibold [&_strong]:text-[#223e0d] [&_ul]:mb-5 [&_ul]:list-disc [&_ul]:pl-7"
style={{ fontFamily: 'var(--font-poppins, Poppins), sans-serif' }}
>
<RichText data={post.body} />
</div>
) : (
<p className="text-[18px] text-[#272727]/60" style={{ fontFamily: 'var(--font-montserrat, Montserrat), sans-serif' }}>
<p
className="text-[18px] text-[#272727]/60"
style={{ fontFamily: 'var(--font-montserrat, Montserrat), sans-serif' }}
>
Вміст статті незабаром з&apos;явиться тут.
</p>
)}
@ -117,7 +126,13 @@ export default async function BlogPostPage({ params }: Props) {
style={{ fontFamily: 'var(--font-montserrat, Montserrat), sans-serif' }}
>
<svg width="16" height="16" viewBox="0 0 16 16" fill="none">
<path d="M13 8H3M7 4L3 8l4 4" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round"/>
<path
d="M13 8H3M7 4L3 8l4 4"
stroke="currentColor"
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
Інші статті
</Link>

View file

@ -10,7 +10,7 @@ export const metadata: Metadata = {
description: 'Новини, статті та корисна інформація від парку Шуміленд.',
}
export const dynamic = "force-dynamic"
export const dynamic = 'force-dynamic'
interface Post {
id: string
@ -38,7 +38,11 @@ async function getPosts(): Promise<Post[]> {
}
function formatDate(iso: string) {
return new Date(iso).toLocaleDateString('uk-UA', { day: 'numeric', month: 'long', year: 'numeric' })
return new Date(iso).toLocaleDateString('uk-UA', {
day: 'numeric',
month: 'long',
year: 'numeric',
})
}
export default async function BlogPage() {
@ -51,7 +55,10 @@ export default async function BlogPage() {
<div className="mx-auto max-w-[1204px] px-8 py-16">
{posts.length === 0 ? (
<div className="py-20 text-center">
<p className="text-[20px] text-[#272727]/60" style={{ fontFamily: 'var(--font-montserrat, Montserrat), sans-serif' }}>
<p
className="text-[20px] text-[#272727]/60"
style={{ fontFamily: 'var(--font-montserrat, Montserrat), sans-serif' }}
>
Статті незабаром з&apos;являться тут.
</p>
</div>
@ -76,7 +83,9 @@ export default async function BlogPage() {
) : (
<div className="h-full w-full bg-gradient-to-br from-[#396817] to-[#74d22c]">
<div className="flex h-full items-center justify-center opacity-30">
<svg width="64" height="64" viewBox="0 0 24 24" fill="white"><path d="M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-5 14H7v-2h7v2zm3-4H7v-2h10v2zm0-4H7V7h10v2z"/></svg>
<svg width="64" height="64" viewBox="0 0 24 24" fill="white">
<path d="M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-5 14H7v-2h7v2zm3-4H7v-2h10v2zm0-4H7V7h10v2z" />
</svg>
</div>
</div>
)}
@ -105,8 +114,20 @@ export default async function BlogPage() {
)}
<div className="mt-auto flex items-center gap-2 pt-2 text-[14px] font-semibold text-[#396817]">
Читати далі
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" className="transition-transform duration-200 group-hover:translate-x-1">
<path d="M3 8h10M9 4l4 4-4 4" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round"/>
<svg
width="16"
height="16"
viewBox="0 0 16 16"
fill="none"
className="transition-transform duration-200 group-hover:translate-x-1"
>
<path
d="M3 8h10M9 4l4 4-4 4"
stroke="currentColor"
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
</div>
</div>

View file

@ -101,7 +101,10 @@ export default async function LocationsPage() {
<div className="mx-auto max-w-[1204px] px-8 py-16">
<div className="flex flex-col gap-10">
{locations.map((loc, i) => {
const imgUrl = getMediaUrl(loc.image) ?? FALLBACK_IMAGES[loc.slug] ?? '/images/figma/loc-dinopark.webp'
const imgUrl =
getMediaUrl(loc.image) ??
FALLBACK_IMAGES[loc.slug] ??
'/images/figma/loc-dinopark.webp'
const color = COLORS[i % COLORS.length]
const slug = loc.slug

View file

@ -65,7 +65,7 @@ async function findOrUploadMedia(
export async function POST(req: NextRequest) {
const forceLocations = req.nextUrl.searchParams.get('force') === 'locations'
const forcePosts = req.nextUrl.searchParams.get("force") === "posts"
const forcePosts = req.nextUrl.searchParams.get('force') === 'posts'
const payload = await getPayload({ config })
const results: string[] = []
@ -494,8 +494,13 @@ export async function POST(req: NextRequest) {
})
if (postCount === 0 || forcePosts) {
if (forcePosts && postCount > 0) {
const { docs: existingPosts } = await payload.find({ collection: 'blog-posts', limit: 100, overrideAccess: true })
for (const p of existingPosts) await payload.delete({ collection: 'blog-posts', id: p.id, overrideAccess: true })
const { docs: existingPosts } = await payload.find({
collection: 'blog-posts',
limit: 100,
overrideAccess: true,
})
for (const p of existingPosts)
await payload.delete({ collection: 'blog-posts', id: p.id, overrideAccess: true })
}
const postDefs = [
{

View file

@ -65,7 +65,7 @@ export function BirthdayPricing({ packages, title, intro }: BirthdayPricingProps
const activePackages = packages && packages.length > 0 ? packages : STATIC_PACKAGES
return (
<section className="bg-[#fffdfa] py-[20px] md:py-[60px] lg:py-[40px]">
<section className="bg-[#f1fbeb] py-[20px] md:py-[60px] lg:py-[40px]">
<div className="mx-auto max-w-[1204px] px-8">
<div className="mb-[40px] flex flex-col gap-5 md:mb-[60px]">
<h2

View file

@ -83,7 +83,10 @@ export function Gallery({ images, title }: GalleryProps) {
const items: GalleryImage[] =
images && images.length > 0
? images.map((img, i) => ({
src: getMediaUrl(img.image) ?? STATIC_IMAGES[i % STATIC_IMAGES.length]?.src ?? '/images/figma/gallery-2.webp',
src:
getMediaUrl(img.image) ??
STATIC_IMAGES[i % STATIC_IMAGES.length]?.src ??
'/images/figma/gallery-2.webp',
alt: img.alt ?? `Шуміленд — фото ${i + 1}`,
width: i % 2 === 0 ? 320 : 380,
height: 420,

View file

@ -49,10 +49,22 @@ const STATIC_REVIEWS: ReviewCMS[] = [
function GoogleIcon() {
return (
<svg width="16" height="16" viewBox="0 0 24 24" aria-hidden="true">
<path d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z" fill="#4285F4"/>
<path d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z" fill="#34A853"/>
<path d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z" fill="#FBBC05"/>
<path d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z" fill="#EA4335"/>
<path
d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z"
fill="#4285F4"
/>
<path
d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z"
fill="#34A853"
/>
<path
d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z"
fill="#FBBC05"
/>
<path
d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z"
fill="#EA4335"
/>
</svg>
)
}
@ -87,7 +99,14 @@ function VideoReviewCard({ pauseScroll }: { pauseScroll: () => void }) {
aria-label="Відтворити відеовідгук"
>
<div className="flex h-16 w-16 items-center justify-center rounded-full bg-white/90 shadow-lg transition-transform hover:scale-110">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" aria-hidden="true" className="ml-1">
<svg
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
aria-hidden="true"
className="ml-1"
>
<path d="M5 3L19 12L5 21V3Z" fill="#396817" />
</svg>
</div>
@ -101,7 +120,10 @@ function VideoReviewCard({ pauseScroll }: { pauseScroll: () => void }) {
{/* Footer */}
<div className="flex items-center justify-between px-5 py-4">
<div>
<p className="text-[15px] font-semibold text-white" style={{ fontFamily: 'var(--font-montserrat, Montserrat), sans-serif' }}>
<p
className="text-[15px] font-semibold text-white"
style={{ fontFamily: 'var(--font-montserrat, Montserrat), sans-serif' }}
>
Відеовідгук
</p>
<p className="mt-0.5 text-[12px] text-white/50">Шуміленд 2026</p>
@ -154,7 +176,13 @@ export function Reviews({ data, title }: ReviewsProps) {
aria-label="Попередній відгук"
>
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" aria-hidden="true">
<path d="M13 4L7 10L13 16" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
<path
d="M13 4L7 10L13 16"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
</button>
@ -178,7 +206,11 @@ export function Reviews({ data, title }: ReviewsProps) {
{/* Quote mark decoration */}
<div className="px-7 pt-6 pb-0">
<svg width="32" height="24" viewBox="0 0 32 24" fill="none" aria-hidden="true">
<path d="M0 24V14.4C0 10.4 0.933333 7.06667 2.8 4.4C4.66667 1.6 7.46667 0 11.2 0L12.8 2.4C10.1333 3.06667 8.13333 4.33333 6.8 6.2C5.6 8.06667 5 10.2667 5 12.8H9.6V24H0ZM19.2 24V14.4C19.2 10.4 20.1333 7.06667 22 4.4C23.8667 1.6 26.6667 0 30.4 0L32 2.4C29.3333 3.06667 27.3333 4.33333 26 6.2C24.8 8.06667 24.2 10.2667 24.2 12.8H28.8V24H19.2Z" fill="white" fillOpacity="0.15"/>
<path
d="M0 24V14.4C0 10.4 0.933333 7.06667 2.8 4.4C4.66667 1.6 7.46667 0 11.2 0L12.8 2.4C10.1333 3.06667 8.13333 4.33333 6.8 6.2C5.6 8.06667 5 10.2667 5 12.8H9.6V24H0ZM19.2 24V14.4C19.2 10.4 20.1333 7.06667 22 4.4C23.8667 1.6 26.6667 0 30.4 0L32 2.4C29.3333 3.06667 27.3333 4.33333 26 6.2C24.8 8.06667 24.2 10.2667 24.2 12.8H28.8V24H19.2Z"
fill="white"
fillOpacity="0.15"
/>
</svg>
</div>
@ -196,20 +228,38 @@ export function Reviews({ data, title }: ReviewsProps) {
{/* Author row */}
<div className="flex items-center gap-4 px-7 py-5">
<div className="relative h-12 w-12 flex-none overflow-hidden rounded-full">
<img src={avatarUrl} alt="" aria-hidden="true" className="absolute inset-0 h-full w-full object-cover" />
<img
src={avatarUrl}
alt=""
aria-hidden="true"
className="absolute inset-0 h-full w-full object-cover"
/>
<div className="absolute inset-0 flex items-center justify-center bg-[#f28b4a]/75">
<span className="text-[20px] font-semibold leading-none text-white" style={{ fontFamily: 'var(--font-inter, Inter), sans-serif' }}>
<span
className="text-[20px] leading-none font-semibold text-white"
style={{ fontFamily: 'var(--font-inter, Inter), sans-serif' }}
>
{review.initial ?? review.name[0]}
</span>
</div>
</div>
<div className="flex-1 min-w-0">
<p className="truncate text-[15px] font-semibold text-white" style={{ fontFamily: 'var(--font-inter, Inter), sans-serif' }}>
<div className="min-w-0 flex-1">
<p
className="truncate text-[15px] font-semibold text-white"
style={{ fontFamily: 'var(--font-inter, Inter), sans-serif' }}
>
{review.name}
</p>
<div className="mt-1 flex items-center gap-3">
<StarRating value={review.rating ?? 5} size={13} className="flex gap-[2px]" />
<span className="text-[12px] text-white/50" style={{ fontFamily: 'var(--font-inter, Inter), sans-serif' }}>
<StarRating
value={review.rating ?? 5}
size={13}
className="flex gap-[2px]"
/>
<span
className="text-[12px] text-white/50"
style={{ fontFamily: 'var(--font-inter, Inter), sans-serif' }}
>
{review.ago}
</span>
</div>
@ -234,7 +284,13 @@ export function Reviews({ data, title }: ReviewsProps) {
aria-label="Наступний відгук"
>
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" aria-hidden="true">
<path d="M7 4L13 10L7 16" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
<path
d="M7 4L13 10L7 16"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
</button>
</div>

View file

@ -135,7 +135,7 @@ export function WhyParents({ items, sideGallery, title }: WhyParentsProps) {
<button
key={i}
onClick={() => handleItemClick(i)}
className="flex w-full flex-col gap-2.5 rounded-[10px] bg-[#fffdfa] px-[50px] py-[20px] text-left shadow-[0_4px_60px_0_rgba(242,139,74,0.25)] transition-all duration-200 lg:w-[628px]"
className="flex w-full flex-col gap-2.5 rounded-[10px] bg-[#f1fbeb] px-[50px] py-[20px] text-left shadow-[0_4px_60px_0_rgba(242,139,74,0.25)] transition-all duration-200 lg:w-[628px]"
aria-expanded={isOpen}
>
<div className="flex items-center gap-5">