This commit is contained in:
Your Name committed 2026-06-20 18:39:03 -04:00
1 parent 3c4bb06003
commit f7aa4d099f
36 files changed
+528 -180

No files matched your search

@@ -1,12 +1,11 @@
import { notFound } from 'next/navigation'
import type { Metadata } from 'next'
import DynamicPageRenderer from '../../components/dynamic/DynamicPageRenderer'
import { getCategoriesResult, getCmsPageResult } from '../../lib/cms'
import { splitCmsLayoutSections } from '../../lib/cms-sections'
const lang = 'all'
import DynamicPageRenderer from '@/components/dynamic/DynamicPageRenderer'
import { getCategoriesResult, getCmsPageResult } from '@/lib/cms'
import { splitCmsLayoutSections } from '@/lib/cms-sections'
// lang is extracted from params
const reservedSlugs = new Set([
'about',
'article',
@@ -32,8 +31,8 @@ function isReservedSlug(slug: string) {
return reservedSlugs.has(normalizeSlug(slug))
}
export async function generateMetadata({ params }: { params: Promise<{ slug: string }> }): Promise<Metadata> {
const { slug } = await params
export async function generateMetadata({ params }: { params: Promise<{ slug: string, lang: string }> }): Promise<Metadata> {
const { slug, lang } = await params
if (isReservedSlug(slug)) return { title: 'Page Not Found | HomAroma' }
const pageResult = await getCmsPageResult(slug, lang)
@@ -44,8 +43,8 @@ export async function generateMetadata({ params }: { params: Promise<{ slug: str
}
}
export default async function CmsCustomPage({ params }: { params: Promise<{ slug: string }> }) {
const { slug } = await params
export default async function CmsCustomPage({ params }: { params: Promise<{ slug: string, lang: string }> }) {
const { slug, lang } = await params
if (isReservedSlug(slug)) notFound()
const [pageResult, categoriesResult] = await Promise.all([
@@ -1,4 +1,4 @@
import Link from 'next/link'
import Link from '@/components/LocalizedLink'
const PILLARS = [
{
@@ -1,10 +1,10 @@
import Link from 'next/link'
import Link from '@/components/LocalizedLink'
import { notFound } from 'next/navigation'
import type { Metadata } from 'next'
import { ArrowLeft } from 'lucide-react'
import type { Article } from '@homaroma/types'
import { API_BASE } from '../../../lib/api'
import { API_BASE } from '@/lib/api'
async function getArticle(slug: string): Promise<Article | null> {
try {
@@ -1,30 +1,29 @@
import Link from 'next/link'
import Link from '@/components/LocalizedLink'
import { notFound } from 'next/navigation'
import type { Metadata } from 'next'
import type { Category, Product } from '@homaroma/types'
import { getCategoryImage } from '@homaroma/types'
import DynamicPageRenderer from '../../../components/dynamic/DynamicPageRenderer'
import ProductCard from '../../../components/ProductCard'
import { type ApiResult, fetchJsonResult, getCategoriesResult, getCmsPageResult } from '../../../lib/cms'
import { splitCmsLayoutSections } from '../../../lib/cms-sections'
import { resolveLocalized } from '../../../lib/localization'
const lang = 'all'
import DynamicPageRenderer from '@/components/dynamic/DynamicPageRenderer'
import ProductCard from '@/components/ProductCard'
import { type ApiResult, fetchJsonResult, getCategoriesResult, getCmsPageResult } from '@/lib/cms'
import { splitCmsLayoutSections } from '@/lib/cms-sections'
import { resolveLocalized } from '@/lib/localization'
// lang is extracted from params
type CategoryProductsResponse = {
category?: Category
data?: Product[]
}
async function getCategoryProducts(id: string): Promise<ApiResult<CategoryProductsResponse>> {
async function getCategoryProducts(id: string, lang: string): Promise<ApiResult<CategoryProductsResponse>> {
return await fetchJsonResult<CategoryProductsResponse>(`/categories/${id}/products?limit=1000&lang=${lang}`, {
next: { revalidate: 60 },
})
}
export async function generateMetadata({ params }: { params: Promise<{ id: string }> }): Promise<Metadata> {
const { id } = await params
const result = await getCategoryProducts(id)
export async function generateMetadata({ params }: { params: Promise<{ id: string, lang: string }> }): Promise<Metadata> {
const { id, lang } = await params
const result = await getCategoryProducts(id, lang)
const categoryName = result.ok && result.data.category ? resolveLocalized(result.data.category.name, lang) : ''
return {
@@ -33,10 +32,10 @@ export async function generateMetadata({ params }: { params: Promise<{ id: strin
}
}
export default async function CategoryPage({ params }: { params: Promise<{ id: string }> }) {
const { id } = await params
export default async function CategoryPage({ params }: { params: Promise<{ id: string, lang: string }> }) {
const { id, lang } = await params
const [categoryProductsResult, categoriesResult, cmsLayoutResult] = await Promise.all([
getCategoryProducts(id),
getCategoryProducts(id, lang),
getCategoriesResult(lang),
getCmsPageResult('category-layout', lang),
])
@@ -1,18 +1,18 @@
import { CreditCard } from 'lucide-react'
import CheckoutClient from '../../components/CheckoutClient'
import DynamicPageRenderer from '../../components/dynamic/DynamicPageRenderer'
import { getCategoriesResult, getCmsPageResult } from '../../lib/cms'
import { splitCmsLayoutSections } from '../../lib/cms-sections'
const lang = 'all'
import CheckoutClient from '@/components/CheckoutClient'
import DynamicPageRenderer from '@/components/dynamic/DynamicPageRenderer'
import { getCategoriesResult, getCmsPageResult } from '@/lib/cms'
import { splitCmsLayoutSections } from '@/lib/cms-sections'
// lang is extracted from params
export const metadata = {
title: 'Checkout | HomAroma',
description: 'ตรวจสอบรายการสินค้า กรอกข้อมูลจัดส่ง และสร้างคำสั่งซื้อ HomAroma',
}
export default async function CheckoutPage() {
export default async function CheckoutPage({ params }: { params: Promise<{ lang: string }> }) {
const { lang } = await params
const [categoriesResult, cmsLayoutResult] = await Promise.all([
getCategoriesResult(lang),
getCmsPageResult('checkout-layout', lang),
@@ -1,11 +1,10 @@
import Link from 'next/link'
import Link from '@/components/LocalizedLink'
import { Ticket, ArrowRight } from 'lucide-react'
import DynamicPageRenderer from '../../components/dynamic/DynamicPageRenderer'
import { getActiveCouponsResult, getCategoriesResult, getCmsPageResult } from '../../lib/cms'
import { splitCmsLayoutSections } from '../../lib/cms-sections'
const lang = 'all'
import DynamicPageRenderer from '@/components/dynamic/DynamicPageRenderer'
import { getActiveCouponsResult, getCategoriesResult, getCmsPageResult } from '@/lib/cms'
import { splitCmsLayoutSections } from '@/lib/cms-sections'
// lang is extracted from params
function formatDiscount(type: string, value: number) {
if (type === 'PERCENTAGE') return `${value}% OFF`
if (type === 'FIXED_AMOUNT') return `ลด ${value} ฿`
@@ -18,7 +17,8 @@ export const metadata = {
description: 'รวมรหัสส่วนลดและโปรโมชั่นพิเศษจาก HomAroma สำหรับการสั่งซื้อน้ำหอมและผลิตภัณฑ์อโรม่าที่คุณชื่นชอบ',
}
export default async function CouponsPage() {
export default async function CouponsPage({ params }: { params: Promise<{ lang: string }> }) {
const { lang } = await params
const [couponsResult, categoriesResult, cmsLayoutResult] = await Promise.all([
getActiveCouponsResult(),
getCategoriesResult(lang),
@@ -1,6 +1,6 @@
"use client";
import { useState } from 'react'
import Link from 'next/link'
import Link from '@/components/LocalizedLink'
const FAQS = [
{
@@ -1,21 +1,22 @@
import type { Metadata } from 'next';
import './globals.css';
import Navbar from '../components/Navbar';
import Footer from '../components/Footer';
import ClientProviders from '../components/ClientProviders';
import { API_BASE } from '../lib/api';
import type { MegaMenuPayload } from '../components/navigation/menuTypes';
import '../globals.css';
import Navbar from '@/components/Navbar';
import Footer from '@/components/Footer';
import ClientProviders from '@/components/ClientProviders';
import { API_BASE } from '@/lib/api';
import type { MegaMenuPayload } from '@/components/navigation/menuTypes';
import type { MenuItem, StoreSettings } from '@homaroma/types';
export const metadata: Metadata = {
title: 'HomAroma',
description: 'Pure Origins, Artisan Craft, Zero Compromise',
metadataBase: new URL(process.env.NEXT_PUBLIC_SITE_URL || 'http://localhost:3000'),
};
// Fetch categories for Navbar and Footer on the server (lang=all for client-side switching)
async function getCategories() {
// Fetch categories for Navbar and Footer on the server
async function getCategories(lang: string) {
try {
const res = await fetch(`${API_BASE}/categories?lang=all`, { next: { revalidate: 3600 } });
const res = await fetch(`${API_BASE}/categories?lang=${lang}`, { next: { revalidate: 3600 } });
if (!res.ok) return [];
return await res.json();
} catch (error) {
@@ -25,9 +26,9 @@ async function getCategories() {
}
// Fetch mega menu on the server — eliminates client-side flash
async function getMegaMenu(): Promise<MegaMenuPayload[]> {
async function getMegaMenu(lang: string): Promise<MegaMenuPayload[]> {
try {
const res = await fetch(`${API_BASE}/menus/main`, { next: { revalidate: 300 } });
const res = await fetch(`${API_BASE}/menus/main?lang=${lang}`, { next: { revalidate: 300 } });
if (!res.ok) return [];
const data = await res.json() as { payload?: MegaMenuPayload[] };
return Array.isArray(data.payload) ? data.payload : [];
@@ -38,10 +39,10 @@ async function getMegaMenu(): Promise<MegaMenuPayload[]> {
}
// Fetch footer menus (explore + support) on the server
async function getFooterMenus(): Promise<{ exploreMenu: MenuItem[]; supportMenu: MenuItem[] }> {
async function getFooterMenus(lang: string): Promise<{ exploreMenu: MenuItem[]; supportMenu: MenuItem[] }> {
const fetchMenu = async (slug: string): Promise<MenuItem[]> => {
try {
const res = await fetch(`${API_BASE}/menus/${slug}`, { next: { revalidate: 300 } });
const res = await fetch(`${API_BASE}/menus/${slug}?lang=${lang}`, { next: { revalidate: 300 } });
if (!res.ok) return [];
const data = await res.json() as { items?: MenuItem[] };
return Array.isArray(data.items)
@@ -61,9 +62,9 @@ async function getFooterMenus(): Promise<{ exploreMenu: MenuItem[]; supportMenu:
}
// Fetch store settings (social links, footer texts) on the server
async function getStoreSettings(): Promise<StoreSettings | null> {
async function getStoreSettings(lang: string): Promise<StoreSettings | null> {
try {
const res = await fetch(`${API_BASE}/settings`, { next: { revalidate: 600 } });
const res = await fetch(`${API_BASE}/settings?lang=${lang}`, { next: { revalidate: 600 } });
if (!res.ok) return null;
return await res.json() as StoreSettings;
} catch (error) {
@@ -74,18 +75,22 @@ async function getStoreSettings(): Promise<StoreSettings | null> {
export default async function RootLayout({
children,
params,
}: {
children: React.ReactNode;
params: Promise<{ lang: string }>;
}) {
const { lang } = await params;
const [categories, initialMenu, footerMenus, settings] = await Promise.all([
getCategories(),
getMegaMenu(),
getFooterMenus(),
getStoreSettings(),
getCategories(lang),
getMegaMenu(lang),
getFooterMenus(lang),
getStoreSettings(lang),
]);
return (
<html lang="th" className="scroll-smooth">
<html lang={lang} className="scroll-smooth">
<body className="min-h-screen bg-pop-bg text-pop-black font-sans antialiased">
<ClientProviders>
<Navbar categories={categories} initialMenu={initialMenu} settings={settings} />
@@ -1,15 +1,14 @@
import Link from 'next/link'
import Link from '@/components/LocalizedLink'
import { notFound } from 'next/navigation'
import type { Metadata } from 'next'
import { CheckCircle2, Clock, PackageCheck, Truck } from 'lucide-react'
import { formatPrice } from '@homaroma/types'
import DynamicPageRenderer from '../../../components/dynamic/DynamicPageRenderer'
import { getCategoriesResult, getCmsPageResult, getOrderResult } from '../../../lib/cms'
import { splitCmsLayoutSections } from '../../../lib/cms-sections'
const lang = 'all'
import DynamicPageRenderer from '@/components/dynamic/DynamicPageRenderer'
import { getCategoriesResult, getCmsPageResult, getOrderResult } from '@/lib/cms'
import { splitCmsLayoutSections } from '@/lib/cms-sections'
// lang is extracted from params
function orderLabel(id: number) {
return `#${id.toString().padStart(4, '0')}`
}
@@ -47,8 +46,8 @@ export async function generateMetadata({ params }: { params: Promise<{ id: strin
}
}
export default async function OrderStatusPage({ params }: { params: Promise<{ id: string }> }) {
const { id } = await params
export default async function OrderStatusPage({ params }: { params: Promise<{ id: string, lang: string }> }) {
const { id, lang } = await params
const [orderResult, categoriesResult, cmsLayoutResult] = await Promise.all([
getOrderResult(id),
getCategoriesResult(lang),
@@ -1,11 +1,14 @@
import type { Metadata } from 'next'
import DynamicPageRenderer from '../components/dynamic/DynamicPageRenderer'
import { getCategories, getCmsPage } from '../lib/cms'
import { splitCmsLayoutSections } from '../lib/cms-sections'
import DynamicPageRenderer from '@/components/dynamic/DynamicPageRenderer'
import { getCategories, getCmsPage } from '@/lib/cms'
import { splitCmsLayoutSections } from '@/lib/cms-sections'
const lang = 'all'
type Props = {
params: Promise<{ lang: string }>
}
export async function generateMetadata(): Promise<Metadata> {
export async function generateMetadata({ params }: Props): Promise<Metadata> {
const { lang } = await params
const page = await getCmsPage('home', lang)
return {
@@ -14,7 +17,9 @@ export async function generateMetadata(): Promise<Metadata> {
}
}
export default async function HomePage() {
export default async function HomePage({ params }: Props) {
const { lang } = await params
const [categories, page] = await Promise.all([
getCategories(lang),
getCmsPage('home', lang),
@@ -1,24 +1,22 @@
import Link from 'next/link'
import Link from '@/components/LocalizedLink'
import { notFound } from 'next/navigation'
import type { Metadata } from 'next'
import { getCategoryImage, type Product } from '@homaroma/types'
import AddToCartSection from '../../../components/AddToCartSection'
import DynamicPageRenderer from '../../../components/dynamic/DynamicPageRenderer'
import ProductCard from '../../../components/ProductCard'
import { getCategoriesResult, getCmsPageResult, getProduct, getProductResult, getProductsResult } from '../../../lib/cms'
import { splitCmsLayoutSections } from '../../../lib/cms-sections'
import { resolveCategoryName, resolveLocalized } from '../../../lib/localization'
import AddToCartSection from '@/components/AddToCartSection'
import DynamicPageRenderer from '@/components/dynamic/DynamicPageRenderer'
import ProductCard from '@/components/ProductCard'
import { getCategoriesResult, getCmsPageResult, getProduct, getProductResult, getProductsResult } from '@/lib/cms'
import { splitCmsLayoutSections } from '@/lib/cms-sections'
import { resolveCategoryName, resolveLocalized } from '@/lib/localization'
const lang = 'all'
async function getRelated(categoryId: number, excludeId: number): Promise<Product[]> {
async function getRelated(categoryId: number, excludeId: number, lang: string): Promise<Product[]> {
const result = await getProductsResult(`categoryId=${categoryId}`, lang)
if (!result.ok) return []
return result.data.filter((product) => product.id !== excludeId).slice(0, 6)
}
export async function generateMetadata({ params }: { params: Promise<{ id: string }> }): Promise<Metadata> {
const { id } = await params
export async function generateMetadata({ params }: { params: Promise<{ id: string, lang: string }> }): Promise<Metadata> {
const { id, lang } = await params
const product = await getProduct(id, lang)
if (!product) return { title: 'Product Not Found | HomAroma' }
@@ -38,8 +36,8 @@ export async function generateMetadata({ params }: { params: Promise<{ id: strin
}
}
export default async function ProductDetailPage({ params }: { params: Promise<{ id: string }> }) {
const { id } = await params
export default async function ProductDetailPage({ params }: { params: Promise<{ id: string, lang: string }> }) {
const { id, lang } = await params
const [productResult, categoriesResult, cmsLayoutResult] = await Promise.all([
getProductResult(id, lang),
getCategoriesResult(lang),
@@ -73,7 +71,7 @@ export default async function ProductDetailPage({ params }: { params: Promise<{
}
const product = productResult.data
const related = await getRelated(product.category.id, product.id)
const related = await getRelated(product.category.id, product.id, lang)
const productName = resolveLocalized(product.name, lang) || product.scent.name
const categoryName = resolveCategoryName(product.category, lang)
const details = resolveLocalized(product.details, lang)
@@ -1,18 +1,18 @@
import { User } from 'lucide-react'
import DynamicPageRenderer from '../../components/dynamic/DynamicPageRenderer'
import ProfileClient from '../../components/ProfileClient'
import { getCategoriesResult, getCmsPageResult } from '../../lib/cms'
import { splitCmsLayoutSections } from '../../lib/cms-sections'
const lang = 'all'
import DynamicPageRenderer from '@/components/dynamic/DynamicPageRenderer'
import ProfileClient from '@/components/ProfileClient'
import { getCategoriesResult, getCmsPageResult } from '@/lib/cms'
import { splitCmsLayoutSections } from '@/lib/cms-sections'
// lang is extracted from params
export const metadata = {
title: 'โปรไฟล์สมาชิก | HomAroma',
description: 'จัดการโปรไฟล์สมาชิก HomAroma ประวัติคำสั่งซื้อ คะแนนสะสม และสมุดที่อยู่จัดส่ง',
}
export default async function ProfilePage() {
export default async function ProfilePage({ params }: { params: Promise<{ lang: string }> }) {
const { lang } = await params
const [categoriesResult, cmsLayoutResult] = await Promise.all([
getCategoriesResult(lang),
getCmsPageResult('profile-layout', lang),
@@ -1,16 +1,16 @@
import Link from 'next/link'
import DynamicPageRenderer from '../../components/dynamic/DynamicPageRenderer'
import { getArticlesResult, getCategoriesResult, getCmsPageResult } from '../../lib/cms'
import { splitCmsLayoutSections } from '../../lib/cms-sections'
const lang = 'all'
import Link from '@/components/LocalizedLink'
import DynamicPageRenderer from '@/components/dynamic/DynamicPageRenderer'
import { getArticlesResult, getCategoriesResult, getCmsPageResult } from '@/lib/cms'
import { splitCmsLayoutSections } from '@/lib/cms-sections'
// lang is extracted from params
export const metadata = {
title: 'Scent Guide | HomAroma',
description: 'Explore our collection of aroma articles, guides, and sensory knowledge to find your perfect scent.',
}
export default async function ScentGuidePage() {
export default async function ScentGuidePage({ params }: { params: Promise<{ lang: string }> }) {
const { lang } = await params
const [articlesResult, categoriesResult, cmsLayoutResult] = await Promise.all([
getArticlesResult(),
getCategoriesResult(lang),
@@ -1,20 +1,19 @@
import Link from 'next/link'
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'
const lang = 'all'
import Link from '@/components/LocalizedLink'
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 const metadata = {
title: 'Search Products | HomAroma',
description: 'Search HomAroma products by scent, category, or keyword.',
}
export default async function SearchPage({ searchParams }: { searchParams: Promise<{ q?: string | string[] }> }) {
const params = await searchParams
const rawQuery = Array.isArray(params.q) ? params.q[0] : params.q
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),
@@ -1,21 +1,21 @@
import Link from 'next/link'
import DynamicPageRenderer from '../../components/dynamic/DynamicPageRenderer'
import ProductCard from '../../components/ProductCard'
import ShopCouponBridge from '../../components/ShopCouponBridge'
import { getCategoriesResult, getCmsPageResult, getProductsResult } from '../../lib/cms'
import { splitCmsLayoutSections } from '../../lib/cms-sections'
import { resolveLocalized } from '../../lib/localization'
const lang = 'all'
import Link from '@/components/LocalizedLink'
import DynamicPageRenderer from '@/components/dynamic/DynamicPageRenderer'
import ProductCard from '@/components/ProductCard'
import ShopCouponBridge from '@/components/ShopCouponBridge'
import { getCategoriesResult, getCmsPageResult, getProductsResult } from '@/lib/cms'
import { splitCmsLayoutSections } from '@/lib/cms-sections'
import { resolveLocalized } from '@/lib/localization'
// lang is extracted from params
export const metadata = {
title: 'Shop Aroma Products | HomAroma',
description: 'Browse HomAroma aroma oils, reed diffusers, room sprays, and premium scent products.',
}
export default async function ShopPage({ searchParams }: { searchParams: Promise<{ coupon?: string | string[] }> }) {
const params = await searchParams
const coupon = Array.isArray(params.coupon) ? params.coupon[0] : params.coupon
export default async function ShopPage({ searchParams, params }: { searchParams: Promise<{ coupon?: string | string[] }>, params: Promise<{ lang: string }> }) {
const { lang } = await params
const sParams = await searchParams
const coupon = Array.isArray(sParams.coupon) ? sParams.coupon[0] : sParams.coupon
const [categoriesResult, productsResult, cmsLayoutResult] = await Promise.all([
getCategoriesResult(lang),
getProductsResult('limit=1000', lang),
@@ -1,18 +1,18 @@
import { MapPin } from 'lucide-react'
import DynamicPageRenderer from '../../components/dynamic/DynamicPageRenderer'
import TrackOrderClient from '../../components/TrackOrderClient'
import { getCategoriesResult, getCmsPageResult } from '../../lib/cms'
import { splitCmsLayoutSections } from '../../lib/cms-sections'
const lang = 'all'
import DynamicPageRenderer from '@/components/dynamic/DynamicPageRenderer'
import TrackOrderClient from '@/components/TrackOrderClient'
import { getCategoriesResult, getCmsPageResult } from '@/lib/cms'
import { splitCmsLayoutSections } from '@/lib/cms-sections'
// lang is extracted from params
export const metadata = {
title: 'ติดตามคำสั่งซื้อ | HomAroma',
description: 'ตรวจสอบสถานะคำสั่งซื้อ HomAroma ด้วยหมายเลขคำสั่งซื้อและเบอร์โทรศัพท์ที่ใช้สั่งซื้อ',
}
export default async function TrackPage() {
export default async function TrackPage({ params }: { params: Promise<{ lang: string }> }) {
const { lang } = await params
const [categoriesResult, cmsLayoutResult] = await Promise.all([
getCategoriesResult(lang),
getCmsPageResult('track-order-layout', lang),
@@ -1,18 +1,18 @@
import { Heart } from 'lucide-react'
import DynamicPageRenderer from '../../components/dynamic/DynamicPageRenderer'
import WishlistClient from '../../components/WishlistClient'
import { getCategoriesResult, getCmsPageResult } from '../../lib/cms'
import { splitCmsLayoutSections } from '../../lib/cms-sections'
const lang = 'all'
import DynamicPageRenderer from '@/components/dynamic/DynamicPageRenderer'
import WishlistClient from '@/components/WishlistClient'
import { getCategoriesResult, getCmsPageResult } from '@/lib/cms'
import { splitCmsLayoutSections } from '@/lib/cms-sections'
// lang is extracted from params
export const metadata = {
title: 'Wishlist | HomAroma',
description: 'รายการสินค้าที่คุณบันทึกไว้ใน Wishlist ของ HomAroma',
}
export default async function WishlistPage() {
export default async function WishlistPage({ params }: { params: Promise<{ lang: string }> }) {
const { lang } = await params
const [categoriesResult, cmsLayoutResult] = await Promise.all([
getCategoriesResult(lang),
getCmsPageResult('wishlist-layout', lang),
+14
View File
@@ -0,0 +1,14 @@
import { MetadataRoute } from 'next';
const baseUrl = process.env.NEXT_PUBLIC_SITE_URL || 'http://localhost:3000';
export default function robots(): MetadataRoute.Robots {
return {
rules: {
userAgent: '*',
allow: '/',
disallow: ['/api/', '/_next/', '/profile/', '/checkout/', '/order/'],
},
sitemap: `${baseUrl}/sitemap.xml`,
};
}
+78
View File
@@ -0,0 +1,78 @@
import { MetadataRoute } from 'next';
import { getCategories, getProducts, getArticlesResult } from '../lib/cms';
const baseUrl = process.env.NEXT_PUBLIC_SITE_URL || 'http://localhost:3000';
const languages = ['th', 'en', 'ja', 'zh', 'vi', 'ko'];
function createAlternates(path: string) {
const alternates: Record<string, string> = {};
for (const lang of languages) {
alternates[lang] = `${baseUrl}/${lang}${path}`;
}
return alternates;
}
export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
const [categories, products, articlesResult] = await Promise.all([
getCategories(),
getProducts('limit=1000'),
getArticlesResult(),
]);
const staticPages = [
'',
'/shop',
'/scent-guide',
'/about',
'/contact',
'/faq',
];
const routes: MetadataRoute.Sitemap = staticPages.map((route) => ({
url: `${baseUrl}/th${route}`,
lastModified: new Date(),
changeFrequency: 'daily',
priority: route === '' ? 1 : 0.8,
alternates: {
languages: createAlternates(route),
},
}));
const categoryRoutes: MetadataRoute.Sitemap = categories.map((category) => ({
url: `${baseUrl}/th/category/${category.id}`,
lastModified: new Date(),
changeFrequency: 'weekly',
priority: 0.7,
alternates: {
languages: createAlternates(`/category/${category.id}`),
},
}));
const productRoutes: MetadataRoute.Sitemap = products.map((product) => ({
url: `${baseUrl}/th/products/${product.id}`,
lastModified: new Date(),
changeFrequency: 'daily',
priority: 0.9,
alternates: {
languages: createAlternates(`/products/${product.id}`),
},
}));
const articleRoutes: MetadataRoute.Sitemap = [];
if (articlesResult.ok && articlesResult.data) {
articlesResult.data.forEach((article) => {
articleRoutes.push({
url: `${baseUrl}/th/article/${article.slug}`,
lastModified: new Date(article.updatedAt || article.createdAt),
changeFrequency: 'monthly',
priority: 0.6,
alternates: {
languages: createAlternates(`/article/${article.slug}`),
},
});
});
}
return [...routes, ...categoryRoutes, ...productRoutes, ...articleRoutes];
}