feat(layout): wire CMS logo to Header and Footer with SVG fallback

This commit is contained in:
Vadym Samoilenko 2026-05-11 13:19:57 +01:00
parent bcdd2a3a2d
commit 0895f25434
3 changed files with 89 additions and 39 deletions

View file

@ -1,8 +1,19 @@
/* eslint-disable @next/next/no-img-element */
import Image from 'next/image'
import Link from 'next/link'
import { getGlobal } from '@/lib/payload'
import type { FooterGlobal } from '@/types/globals'
function resolveLogoUrl(logo: unknown): string | null {
if (!logo) return null
if (typeof logo === 'string') return logo
if (typeof logo === 'object' && logo !== null && 'url' in logo) {
const url = (logo as { url?: string | null }).url
return url ?? null
}
return null
}
const IMG_BG = '/images/figma/footer-bg.webp'
const LOGO_G1 = '/images/figma/logo-g1-lg.svg'
const LOGO_G2 = '/images/figma/logo-g2-lg.svg'
@ -41,6 +52,8 @@ export async function Footer() {
const navLinks = footer?.navLinks?.length ? footer.navLinks : STATIC_NAV
const contacts = footer?.contacts
const socials = footer?.socials?.filter((s) => s.url)
const logoUrl = resolveLogoUrl(footer?.logo)
const cmsLogo = logoUrl ? { url: logoUrl, alt: footer?.logoAlt ?? 'Шуміленд' } : null
return (
<footer className="relative overflow-hidden bg-[#223e0d]">
@ -55,26 +68,36 @@ export async function Footer() {
<div className="flex flex-col items-center gap-12 lg:flex-row lg:items-start lg:justify-between">
{/* Logo */}
<Link href="/" aria-label="Шуміленд — на головну" className="shrink-0">
<div className="relative" style={{ width: '120px', height: '104px' }}>
<div
className="absolute"
style={{ top: '91.32%', right: '21.81%', bottom: '0.97%', left: '22.26%' }}
>
<img src={LOGO_G1} alt="" aria-hidden="true" className="block h-full w-full" />
{cmsLogo ? (
<Image
src={cmsLogo.url}
alt={cmsLogo.alt}
width={140}
height={50}
className="object-contain"
/>
) : (
<div className="relative" style={{ width: '120px', height: '104px' }}>
<div
className="absolute"
style={{ top: '91.32%', right: '21.81%', bottom: '0.97%', left: '22.26%' }}
>
<img src={LOGO_G1} alt="" aria-hidden="true" className="block h-full w-full" />
</div>
<div
className="absolute"
style={{ top: '71.76%', right: '2.82%', bottom: '7.3%', left: '1.41%' }}
>
<img src={LOGO_G2} alt="" aria-hidden="true" className="block h-full w-full" />
</div>
<div
className="absolute"
style={{ top: '1.61%', right: '2.82%', bottom: '38.73%', left: '21.27%' }}
>
<img src={LOGO_G3} alt="" aria-hidden="true" className="block h-full w-full" />
</div>
</div>
<div
className="absolute"
style={{ top: '71.76%', right: '2.82%', bottom: '7.3%', left: '1.41%' }}
>
<img src={LOGO_G2} alt="" aria-hidden="true" className="block h-full w-full" />
</div>
<div
className="absolute"
style={{ top: '1.61%', right: '2.82%', bottom: '38.73%', left: '21.27%' }}
>
<img src={LOGO_G3} alt="" aria-hidden="true" className="block h-full w-full" />
</div>
</div>
)}
</Link>
{/* Nav */}

View file

@ -4,6 +4,16 @@ import { safeCmsHref } from '@/lib/cn'
import { HeaderClient, type NavLinkItem } from './HeaderClient'
import type { HeaderGlobal } from '@/types/globals'
function resolveLogoUrl(logo: unknown): string | null {
if (!logo) return null
if (typeof logo === 'string') return logo
if (typeof logo === 'object' && logo !== null && 'url' in logo) {
const url = (logo as { url?: string | null }).url
return url ?? null
}
return null
}
const DEFAULT_NAV: NavLinkItem[] = [
{ label: 'Головна', href: '/' },
{
@ -26,6 +36,7 @@ export async function Header() {
let navLinks: NavLinkItem[] = DEFAULT_NAV
let ctaHref = '/kvytky'
let ctaLabel = 'Купити квиток'
let logo: { url: string; alt?: string } | null = null
try {
const [header, menuLocations] = await Promise.all([
@ -36,6 +47,9 @@ export async function Header() {
if (header?.ctaLabel) ctaLabel = header.ctaLabel
if (header?.ctaHref) ctaHref = safeCmsHref(header.ctaHref) ?? header.ctaHref
const logoUrl = resolveLogoUrl(header?.logo)
if (logoUrl) logo = { url: logoUrl, alt: header?.logoAlt ?? 'Шуміленд' }
if (header?.navLinks && header.navLinks.length > 0) {
const parsed: NavLinkItem[] = header.navLinks
.filter((l) => l.label && l.href)
@ -71,6 +85,7 @@ export async function Header() {
ctaLabel={ctaLabel}
ctaHref={ctaHref}
siteName="Шуміленд"
logo={logo}
/>
)
}

View file

@ -1,6 +1,7 @@
'use client'
/* eslint-disable @next/next/no-img-element */
import Image from 'next/image'
import Link from 'next/link'
import { useState } from 'react'
import { NavLink } from '@/components/ui/NavLink'
@ -21,6 +22,7 @@ interface HeaderClientProps {
ctaLabel: string
ctaHref: string
siteName: string
logo?: { url: string; alt?: string } | null
}
/**
@ -32,7 +34,7 @@ interface HeaderClientProps {
* - real backdrop-filter blur + saturate + a subtle dark-green tint
* so the foliage behind shows through but text stays readable
*/
export function HeaderClient({ navLinks, ctaLabel, ctaHref }: HeaderClientProps) {
export function HeaderClient({ navLinks, ctaLabel, ctaHref, logo }: HeaderClientProps) {
const [menuOpen, setMenuOpen] = useState(false)
return (
@ -62,26 +64,36 @@ export function HeaderClient({ navLinks, ctaLabel, ctaHref }: HeaderClientProps)
<div className="relative flex items-center justify-between h-[60px] lg:h-[120px] px-[20px] lg:px-[30px]">
{/* Logo */}
<Link href="/" aria-label="Шуміленд — на головну" className="shrink-0">
<div className="relative h-[44px] w-[50px] lg:h-[62px] lg:w-[71px]">
<div
className="absolute"
style={{ top: '91.32%', right: '21.81%', bottom: '0.97%', left: '22.26%' }}
>
<img src={LOGO_G1} alt="" aria-hidden="true" className="block w-full h-full" />
{logo ? (
<Image
src={logo.url}
alt={logo.alt ?? 'Шуміленд'}
width={140}
height={50}
className="object-contain"
/>
) : (
<div className="relative h-[44px] w-[50px] lg:h-[62px] lg:w-[71px]">
<div
className="absolute"
style={{ top: '91.32%', right: '21.81%', bottom: '0.97%', left: '22.26%' }}
>
<img src={LOGO_G1} alt="" aria-hidden="true" className="block w-full h-full" />
</div>
<div
className="absolute"
style={{ top: '71.76%', right: '2.82%', bottom: '7.3%', left: '1.41%' }}
>
<img src={LOGO_G2} alt="" aria-hidden="true" className="block w-full h-full" />
</div>
<div
className="absolute"
style={{ top: '1.61%', right: '2.82%', bottom: '38.73%', left: '21.27%' }}
>
<img src={LOGO_G3} alt="" aria-hidden="true" className="block w-full h-full" />
</div>
</div>
<div
className="absolute"
style={{ top: '71.76%', right: '2.82%', bottom: '7.3%', left: '1.41%' }}
>
<img src={LOGO_G2} alt="" aria-hidden="true" className="block w-full h-full" />
</div>
<div
className="absolute"
style={{ top: '1.61%', right: '2.82%', bottom: '38.73%', left: '21.27%' }}
>
<img src={LOGO_G3} alt="" aria-hidden="true" className="block w-full h-full" />
</div>
</div>
)}
</Link>
{/* Nav — desktop */}