x
This commit is contained in:
1 parent
0d10c7a071
commit
b867063efb
2 files changed
+26
-11
No files matched your search
@@ -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 */}
|
||||||
|
|||||||
Reference in new issue
Block a user