This commit is contained in:
Your Name committed 2026-06-13 23:25:19 -04:00
1 parent 0d10c7a071
commit b867063efb
2 files changed
+25 -10

No files matched your search

+15 -7
View File
@@ -6,10 +6,18 @@ import AddToCartSection from '../../../components/AddToCartSection'
import ProductCard from '../../../components/ProductCard' import ProductCard from '../../../components/ProductCard'
import { API_BASE } from '../../../lib/api' import { API_BASE } from '../../../lib/api'
import type { TranslatableString } from '@homaroma/types'
/** Resolve TranslatableString → plain string (SSR: always defaults to 'th') */
function resolveStr(v: TranslatableString | null | undefined, lang = 'th'): string {
if (!v) return ''
if (typeof v === 'string') return v
return v[lang] ?? v['th'] ?? v['en'] ?? Object.values(v)[0] ?? ''
}
async function getProduct(id: string): Promise<Product | null> { async function getProduct(id: string): Promise<Product | null> {
try { try {
const res = await fetch(`${API_BASE}/products/${id}`, { next: { revalidate: 60 } }) const res = await fetch(`${API_BASE}/products/${id}?lang=th`, { next: { revalidate: 60 } })
return res.ok ? res.json() : null return res.ok ? res.json() : null
} catch { return null } } catch { return null }
} }
@@ -29,10 +37,10 @@ export async function generateMetadata({ params }: { params: Promise<{ id: strin
if (!product) return { title: 'Product Not Found | HomAroma' } if (!product) return { title: 'Product Not Found | HomAroma' }
return { return {
title: `${product.scent.name} ${product.category.name} | HomAroma`, title: `${product.scent.name} ${product.category.name} | HomAroma`,
description: product.description ?? `Premium ${product.category.name} — ${product.scent.name}. Shop HomAroma for high-quality botanical scents.`, description: resolveStr(product.description) ?? `Premium ${product.category.name} — ${product.scent.name}. Shop HomAroma for high-quality botanical scents.`,
openGraph: { openGraph: {
title: product.scent.name, title: product.scent.name,
description: product.description ?? '', description: resolveStr(product.description) ?? '',
images: product.images?.[0]?.url ? [product.images[0].url] : [], images: product.images?.[0]?.url ? [product.images[0].url] : [],
}, },
} }
@@ -85,12 +93,12 @@ export default async function ProductDetailPage({ params }: { params: Promise<{
</div> </div>
{/* Full Details Section */} {/* Full Details Section */}
{product.details && ( {resolveStr(product.details) && (
<div className="pt-16 border-t border-white/5 space-y-6"> <div className="pt-16 border-t border-white/10 space-y-6">
<h2 className="text-3xl font-bold">Product Details</h2> <h2 className="text-3xl font-bold">Product Details</h2>
<div <div
className="prose prose-invert prose-lg max-w-4xl product-editorial" className="text-slate-300 leading-relaxed prose prose-invert max-w-none"
dangerouslySetInnerHTML={{ __html: product.details }} dangerouslySetInnerHTML={{ __html: resolveStr(product.details) }}
/> />
</div> </div>
)} )}
@@ -1,9 +1,16 @@
"use client"; "use client";
import { useState } from 'react' import { useState } from 'react'
import Link from 'next/link' import Link from 'next/link'
import { getCategoryImage, formatPrice, type Product } from '@homaroma/types' import { getCategoryImage, formatPrice, type Product, type TranslatableString } from '@homaroma/types'
import { useCartStore } from '../store/cartStore' import { useCartStore } from '../store/cartStore'
/** Resolve TranslatableString → plain string */
function resolveStr(v: TranslatableString | null | undefined, lang = 'th'): string {
if (!v) return ''
if (typeof v === 'string') return v
return v[lang] ?? v['th'] ?? v['en'] ?? Object.values(v)[0] ?? ''
}
interface Props { interface Props {
product: Product product: Product
} }
@@ -40,8 +47,8 @@ export default function AddToCartSection({ product }: Props) {
<p className="text-slate-400 text-sm uppercase tracking-widest">Premium Collection</p> <p className="text-slate-400 text-sm uppercase tracking-widest">Premium Collection</p>
</div> </div>
{product.description && ( {resolveStr(product.description) && (
<p className="text-slate-300 leading-relaxed">{product.description}</p> <p className="text-slate-300 leading-relaxed">{resolveStr(product.description)}</p>
)} )}
{/* Price */} {/* Price */}