x
This commit is contained in:
1 parent
0ceee3dbff
commit
2d39edbd57
18 files changed
+804
-153
No files matched your search
@@ -4,7 +4,7 @@ import { notFound } from 'next/navigation'
|
||||
import type { Metadata } from 'next'
|
||||
import { generateSeoMetadata } from '@/lib/seo'
|
||||
import type { Category, Product } from '@homaroma/types'
|
||||
import { getCategoryImage } from '@homaroma/types'
|
||||
import { getCategoryImage, getCategoryMedia, resolveCategoryMediaUrl } from '@homaroma/types'
|
||||
import DynamicPageRenderer from '@/components/dynamic/DynamicPageRenderer'
|
||||
import ProductCard from '@/components/ProductCard'
|
||||
import { type ApiResult, fetchJsonResult, getCategoriesResult, getCmsPageResult, getStoreSettings } from '@/lib/cms'
|
||||
@@ -37,18 +37,21 @@ export async function generateMetadata({ params }: { params: Promise<{ id: strin
|
||||
: (brandName ? `Shop ${brandName} category products.` : 'Shop category products.')
|
||||
|
||||
const category = result.ok ? result.data.category : undefined
|
||||
const categoryImage = category?.imageUrl || (categoryName ? getCategoryImage(categoryName) : undefined) || settings?.defaultOgImage
|
||||
const categoryImage = category
|
||||
? resolveCategoryMediaUrl(category, 'social_share', categoryName ? getCategoryImage(categoryName) : '')
|
||||
: undefined
|
||||
const socialImage = categoryImage || settings?.defaultOgImage
|
||||
|
||||
return generateSeoMetadata({
|
||||
title: categoryName ? `${categoryName}` : 'Category',
|
||||
description,
|
||||
path: `/category/${id}`,
|
||||
lang,
|
||||
openGraph: categoryImage
|
||||
openGraph: socialImage
|
||||
? {
|
||||
title: categoryName || undefined,
|
||||
description,
|
||||
images: [categoryImage],
|
||||
images: [socialImage],
|
||||
}
|
||||
: undefined,
|
||||
})
|
||||
@@ -93,7 +96,13 @@ export default async function CategoryPage({ params }: { params: Promise<{ id: s
|
||||
const category = categoryProductsResult.data.category
|
||||
const products = categoryProductsResult.data.data ?? []
|
||||
const categoryName = resolveLocalized(category.name, lang)
|
||||
const categoryImageUrl = category.imageUrl || getCategoryImage(categoryName)
|
||||
const fallbackImageUrl = getCategoryImage(categoryName)
|
||||
const categoryThumbnailUrl = resolveCategoryMediaUrl(category, 'thumbnail', fallbackImageUrl)
|
||||
const heroDesktopUrl = resolveCategoryMediaUrl(category, 'hero_desktop', fallbackImageUrl)
|
||||
const heroMobileUrl = resolveCategoryMediaUrl(category, 'hero_mobile', heroDesktopUrl)
|
||||
const heroDesktopMedia = getCategoryMedia(category, 'hero_desktop')
|
||||
const heroMobileMedia = getCategoryMedia(category, 'hero_mobile')
|
||||
const hasArtDirectedHero = Boolean(category.media?.some((item) => item.role === 'hero_desktop' || item.role === 'hero_mobile'))
|
||||
const layoutType = category.layoutType || 'split_showcase'
|
||||
|
||||
// Determine Product Grid Class based on layout archetype
|
||||
@@ -114,19 +123,30 @@ export default async function CategoryPage({ params }: { params: Promise<{ id: s
|
||||
{announcementSections.length > 0 && (
|
||||
<DynamicPageRenderer sections={announcementSections} categories={categories} lang={lang} />
|
||||
)}
|
||||
{heroSections.length > 0 ? (
|
||||
{heroSections.length > 0 && !(layoutType === 'editorial_hero' && hasArtDirectedHero) ? (
|
||||
<DynamicPageRenderer sections={heroSections} categories={categories} lang={lang} />
|
||||
) : layoutType === 'editorial_hero' ? (
|
||||
/* ── Archetype 2: Editorial Hero Layout ── */
|
||||
<section className="relative overflow-hidden min-h-[380px] md:min-h-[460px] flex items-center justify-center border-b border-line-primary/10 bg-black">
|
||||
{categoryImageUrl && (
|
||||
{heroDesktopUrl && (
|
||||
<div className="absolute inset-0 z-0">
|
||||
<Image
|
||||
src={categoryImageUrl}
|
||||
src={heroDesktopUrl}
|
||||
alt={categoryName}
|
||||
fill
|
||||
unoptimized
|
||||
className="object-cover object-center filter brightness-[0.45] saturate-125 scale-105"
|
||||
sizes="(min-width: 768px) 100vw, 1px"
|
||||
className="hidden object-cover filter brightness-[0.45] saturate-125 scale-105 md:block"
|
||||
style={{ objectPosition: `${(heroDesktopMedia?.focalX ?? 0.5) * 100}% ${(heroDesktopMedia?.focalY ?? 0.5) * 100}%` }}
|
||||
/>
|
||||
<Image
|
||||
src={heroMobileUrl}
|
||||
alt={categoryName}
|
||||
fill
|
||||
unoptimized
|
||||
sizes="(max-width: 767px) 100vw, 1px"
|
||||
className="object-cover filter brightness-[0.45] saturate-125 scale-105 md:hidden"
|
||||
style={{ objectPosition: `${(heroMobileMedia?.focalX ?? 0.5) * 100}% ${(heroMobileMedia?.focalY ?? 0.5) * 100}%` }}
|
||||
/>
|
||||
<div className="absolute inset-0 bg-gradient-to-t from-black via-black/70 to-black/40" />
|
||||
</div>
|
||||
@@ -201,7 +221,7 @@ export default async function CategoryPage({ params }: { params: Promise<{ id: s
|
||||
</div>
|
||||
<div className="relative w-48 h-48 md:w-72 md:h-72 shrink-0 rounded-3xl overflow-hidden shadow-lg border border-line-primary/5">
|
||||
<Image
|
||||
src={categoryImageUrl}
|
||||
src={categoryThumbnailUrl}
|
||||
alt={categoryName}
|
||||
fill
|
||||
unoptimized
|
||||
|
||||
@@ -5,7 +5,7 @@ import Link from '../LocalizedLink'
|
||||
import { Clock, Mail, MapPin, MessageCircle, Send, ChevronLeft, ChevronRight, Leaf, Droplet, Sparkles, Gift, CheckCircle, ShieldCheck, Heart, Download, FileText } from 'lucide-react'
|
||||
import { useState, useRef, useEffect, useMemo } from 'react'
|
||||
import type { Category, PageSection, Product } from '@homaroma/types'
|
||||
import { getCategoryImage } from '@homaroma/types'
|
||||
import { getCategoryImage, resolveCategoryMediaUrl } from '@homaroma/types'
|
||||
import ProductCard from '../ProductCard'
|
||||
import { localizedFromRecord, resolveCategoryName, resolveLocalized, toRecord } from '../../lib/localization'
|
||||
import { useLang } from '../../hooks/useLang'
|
||||
@@ -1003,7 +1003,7 @@ function CategoriesSection({
|
||||
className="group relative h-72 overflow-hidden rounded-2xl shadow-card transition hover:-translate-y-1 hover:shadow-card-hover"
|
||||
>
|
||||
<Image
|
||||
src={category.imageUrl || getCategoryImage(name)}
|
||||
src={resolveCategoryMediaUrl(category, 'card_portrait', getCategoryImage(name))}
|
||||
alt={name}
|
||||
fill
|
||||
unoptimized
|
||||
|
||||
Reference in new issue
Block a user