Files
homaroma/apps/website/src/app/[lang]/search/page.tsx
T
2026-06-20 23:51:57 -04:00

126 lines
5.7 KiB
TypeScript

import Link from '@/components/LocalizedLink'
import { generateSeoMetadata } from '@/lib/seo'
import DynamicPageRenderer from '@/components/dynamic/DynamicPageRenderer'
import ProductCard from '@/components/ProductCard'
import { getCategoriesResult, getCmsPageResult, getProductsResult } from '@/lib/cms'
import { splitCmsLayoutSections } from '@/lib/cms-sections'
import { resolveLocalized } from '@/lib/localization'
export async function generateMetadata({ params }: { params: Promise<{ lang: string }> }) {
const { lang } = await params;
return generateSeoMetadata({
title: 'Search Products | HomAroma',
description: 'Search HomAroma products by scent, category, or keyword.',
path: '/search',
lang,
});
}
export default async function SearchPage({ searchParams, params }: { searchParams: Promise<{ q?: string | string[] }>, params: Promise<{ lang: string }> }) {
const { lang } = await params
const sParams = await searchParams
const rawQuery = Array.isArray(sParams.q) ? sParams.q[0] : sParams.q
const query = rawQuery?.trim() ?? ''
const [categoriesResult, cmsLayoutResult, productsResult] = await Promise.all([
getCategoriesResult(lang),
getCmsPageResult('search-layout', lang),
query ? getProductsResult(`search=${encodeURIComponent(query)}&limit=1000`, lang) : Promise.resolve({ ok: true as const, data: [] }),
])
const categories = categoriesResult.ok ? categoriesResult.data : []
const cmsSections = cmsLayoutResult.ok ? cmsLayoutResult.data.sections ?? [] : []
const { announcementSections, heroSections, bottomSections } = splitCmsLayoutSections(cmsSections)
const products = productsResult.ok ? productsResult.data : []
if (!cmsLayoutResult.ok && cmsLayoutResult.status !== 404) {
console.error(`[website-cms] search-layout unavailable: ${cmsLayoutResult.error}`)
}
return (
<div className="min-h-screen bg-surface-page text-text-main">
{announcementSections.length > 0 && (
<DynamicPageRenderer sections={announcementSections} categories={categories} lang={lang} />
)}
{heroSections.length > 0 ? (
<DynamicPageRenderer sections={heroSections} categories={categories} lang={lang} />
) : (
<section className="border-b border-line-primary/20 bg-surface-card px-6 py-16 text-center">
<p className="ui-eyebrow">Search</p>
<h1 className="mt-4 text-5xl font-semibold tracking-tight text-text-main">Find Your Scent</h1>
</section>
)}
<section className="border-b border-line-primary/20 bg-surface-card/70 px-6 py-8">
<form className="mx-auto flex max-w-xl overflow-hidden rounded-full border border-line-primary/20 bg-surface-card shadow-card" action="/search">
<input
name="q"
defaultValue={query}
placeholder="Search by scent or product name..."
className="min-w-0 flex-1 px-5 py-3 text-sm text-text-main outline-none placeholder:text-muted-text/70"
/>
<button className="bg-text-main px-6 py-3 text-sm font-bold uppercase tracking-widest text-white transition hover:bg-brand-primary">
Search
</button>
</form>
{categories.length > 0 && (
<div className="mx-auto mt-5 flex max-w-3xl flex-wrap justify-center gap-2">
{categories.map((category) => {
const name = resolveLocalized(category.name, lang)
if (!name) return null
return (
<Link
key={category.id}
href={`/search?q=${encodeURIComponent(name)}`}
className="rounded-full border border-line-primary/20 bg-surface-card px-4 py-1.5 text-xs font-semibold text-muted-text transition hover:border-brand-primary hover:text-brand-primary"
>
{name}
</Link>
)
})}
</div>
)}
{query && (
<p className="mt-5 text-center text-sm text-muted-text">
{products.length > 0
? `Found ${products.length} result${products.length !== 1 ? 's' : ''} for "${query}"`
: `No results for "${query}"`}
</p>
)}
</section>
<div className="mx-auto max-w-7xl px-6 py-12">
{!query ? (
<div className="ui-panel px-6 py-20 text-center">
<p className="text-lg font-semibold text-text-main">Type something to search</p>
<Link href="/shop" className="mt-4 inline-flex font-semibold text-brand-primary">
Browse All Products &rarr;
</Link>
</div>
) : !productsResult.ok ? (
<div className="ui-panel border border-rose-100 px-6 py-20 text-center">
<p className="text-lg font-semibold text-text-main">Unable to search products</p>
<p className="mt-2 text-sm text-rose-500">{productsResult.error}</p>
</div>
) : products.length === 0 ? (
<div className="ui-panel px-6 py-20 text-center">
<p className="text-lg font-semibold text-text-main">No products found</p>
<p className="mt-2 text-sm text-muted-text">Try a different scent name or keyword.</p>
<Link href="/shop" className="mt-4 inline-flex font-semibold text-brand-primary">
Browse All Products &rarr;
</Link>
</div>
) : (
<div className="grid grid-cols-1 gap-8 md:grid-cols-3">
{products.map((product) => <ProductCard key={product.id} product={product} />)}
</div>
)}
</div>
{bottomSections.length > 0 && (
<DynamicPageRenderer sections={bottomSections} categories={categories} lang={lang} />
)}
</div>
)
}