199 lines
9.6 KiB
TypeScript
199 lines
9.6 KiB
TypeScript
import Link from 'next/link'
|
|
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 = 'th'
|
|
|
|
function orderLabel(id: number) {
|
|
return `#${id.toString().padStart(4, '0')}`
|
|
}
|
|
|
|
function statusLabel(status: string) {
|
|
const labels: Record<string, string> = {
|
|
PENDING_PAYMENT: 'รอชำระเงิน',
|
|
PAID: 'ชำระเงินแล้ว',
|
|
PROCESSING: 'กำลังจัดเตรียมสินค้า',
|
|
SHIPPED: 'จัดส่งแล้ว',
|
|
DELIVERED: 'จัดส่งสำเร็จ',
|
|
CANCELLED: 'ยกเลิกแล้ว',
|
|
}
|
|
|
|
return labels[status] ?? status.replaceAll('_', ' ')
|
|
}
|
|
|
|
function statusTone(status: string) {
|
|
if (status === 'PENDING_PAYMENT') return 'bg-amber-50 text-amber-700 border-amber-100'
|
|
if (status === 'PAID' || status === 'PROCESSING') return 'bg-emerald-50 text-emerald-700 border-emerald-100'
|
|
if (status === 'SHIPPED' || status === 'DELIVERED') return 'bg-sky-50 text-sky-700 border-sky-100'
|
|
if (status === 'CANCELLED') return 'bg-rose-50 text-rose-700 border-rose-100'
|
|
return 'bg-gray-50 text-gray-700 border-gray-100'
|
|
}
|
|
|
|
export async function generateMetadata({ params }: { params: Promise<{ id: string }> }): Promise<Metadata> {
|
|
const { id } = await params
|
|
const result = await getOrderResult(id)
|
|
|
|
if (!result.ok) return { title: 'Order Not Found | HomAroma' }
|
|
|
|
return {
|
|
title: `Order ${orderLabel(result.data.id)} | HomAroma`,
|
|
description: `ตรวจสอบสถานะคำสั่งซื้อ ${orderLabel(result.data.id)} จาก HomAroma`,
|
|
}
|
|
}
|
|
|
|
export default async function OrderStatusPage({ params }: { params: Promise<{ id: string }> }) {
|
|
const { id } = await params
|
|
const [orderResult, categoriesResult, cmsLayoutResult] = await Promise.all([
|
|
getOrderResult(id),
|
|
getCategoriesResult(lang),
|
|
getCmsPageResult('order-success-layout', lang),
|
|
])
|
|
const categories = categoriesResult.ok ? categoriesResult.data : []
|
|
const cmsSections = cmsLayoutResult.ok ? cmsLayoutResult.data.sections ?? [] : []
|
|
const { announcementSections, bottomSections } = splitCmsLayoutSections(cmsSections)
|
|
|
|
if (!cmsLayoutResult.ok && cmsLayoutResult.status !== 404) {
|
|
console.error(`[website-cms] order-success-layout unavailable: ${cmsLayoutResult.error}`)
|
|
}
|
|
|
|
if (!orderResult.ok) {
|
|
if (orderResult.status === 404) notFound()
|
|
|
|
return (
|
|
<main className="min-h-[70vh] bg-pop-bg">
|
|
{announcementSections.length > 0 && (
|
|
<DynamicPageRenderer sections={announcementSections} categories={categories} lang={lang} />
|
|
)}
|
|
<section className="mx-auto max-w-3xl px-6 py-24 text-center">
|
|
<div className="rounded-3xl border border-rose-100 bg-white p-10 shadow-brutal">
|
|
<h1 className="text-2xl font-semibold text-pop-black">ไม่สามารถโหลดคำสั่งซื้อนี้ได้</h1>
|
|
<p className="mt-3 text-sm text-rose-500">{orderResult.error}</p>
|
|
<Link href="/track" className="mt-6 inline-flex font-semibold text-pop-pink">กลับไปค้นหาคำสั่งซื้อ</Link>
|
|
</div>
|
|
</section>
|
|
</main>
|
|
)
|
|
}
|
|
|
|
const order = orderResult.data
|
|
const subtotal = order.subtotal ?? Math.max(0, order.totalAmount - order.shippingFee)
|
|
|
|
return (
|
|
<main className="min-h-[70vh] bg-pop-bg">
|
|
{announcementSections.length > 0 && (
|
|
<DynamicPageRenderer sections={announcementSections} categories={categories} lang={lang} />
|
|
)}
|
|
|
|
<section className="mx-auto max-w-4xl space-y-8 px-6 py-16 md:py-20">
|
|
<div className="text-center">
|
|
<div className="mx-auto mb-5 flex h-20 w-20 items-center justify-center rounded-full bg-white text-pop-pink shadow-brutal">
|
|
<CheckCircle2 className="h-10 w-10" />
|
|
</div>
|
|
<p className="text-xs font-bold uppercase tracking-[0.28em] text-pop-pink">Order Status</p>
|
|
<h1 className="mt-4 text-4xl font-semibold text-pop-black md:text-5xl">
|
|
{orderLabel(order.id)}
|
|
</h1>
|
|
<p className="mt-3 text-gray-600">คำสั่งซื้อของคุณถูกบันทึกแล้ว สามารถตรวจสอบสถานะล่าสุดได้จากหน้านี้</p>
|
|
</div>
|
|
|
|
<section className="grid gap-5 md:grid-cols-3">
|
|
<article className="rounded-3xl bg-white p-6 shadow-brutal">
|
|
<Clock className="mb-4 h-6 w-6 text-pop-pink" />
|
|
<p className="text-xs font-bold uppercase tracking-[0.22em] text-gray-400">Created</p>
|
|
<p className="mt-2 font-semibold text-pop-black">
|
|
{new Date(order.createdAt).toLocaleDateString('th-TH')}
|
|
</p>
|
|
</article>
|
|
<article className="rounded-3xl bg-white p-6 shadow-brutal">
|
|
<PackageCheck className="mb-4 h-6 w-6 text-pop-pink" />
|
|
<p className="text-xs font-bold uppercase tracking-[0.22em] text-gray-400">Status</p>
|
|
<span className={`mt-3 inline-flex rounded-full border px-3 py-1 text-xs font-bold uppercase tracking-[0.14em] ${statusTone(order.status)}`}>
|
|
{statusLabel(order.status)}
|
|
</span>
|
|
</article>
|
|
<article className="rounded-3xl bg-white p-6 shadow-brutal">
|
|
<Truck className="mb-4 h-6 w-6 text-pop-pink" />
|
|
<p className="text-xs font-bold uppercase tracking-[0.22em] text-gray-400">Tracking No.</p>
|
|
<p className="mt-2 font-semibold text-pop-black">{order.trackingNo || 'อยู่ระหว่างจัดเตรียมพัสดุ'}</p>
|
|
</article>
|
|
</section>
|
|
|
|
<section className="rounded-3xl bg-white p-8 shadow-brutal">
|
|
<h2 className="border-b border-gray-100 pb-4 text-sm font-bold uppercase tracking-[0.22em] text-pop-pink">
|
|
Order Items
|
|
</h2>
|
|
<div className="mt-6 space-y-4">
|
|
{order.items.map((item) => (
|
|
<div key={item.id} className="flex items-center gap-4 rounded-2xl bg-pop-bg p-4">
|
|
<div className="h-14 w-14 shrink-0 overflow-hidden rounded-xl bg-white">
|
|
<img
|
|
src={item.productImage || '/images/oil_ozone.png'}
|
|
alt={item.productName}
|
|
className="h-full w-full object-cover"
|
|
/>
|
|
</div>
|
|
<div className="min-w-0 flex-1">
|
|
<p className="font-semibold text-pop-black">{item.productName}</p>
|
|
<p className="text-sm text-gray-500">
|
|
{item.variantSize ? `${item.variantSize} · ` : ''}x {item.quantity}
|
|
</p>
|
|
</div>
|
|
<p className="font-bold text-pop-pink">{formatPrice(item.priceAtPurchase * item.quantity)}</p>
|
|
</div>
|
|
))}
|
|
</div>
|
|
|
|
<div className="mt-6 space-y-3 border-t border-gray-100 pt-5 text-sm">
|
|
<div className="flex justify-between text-gray-500">
|
|
<span>Subtotal</span>
|
|
<span>{formatPrice(subtotal)}</span>
|
|
</div>
|
|
<div className="flex justify-between text-gray-500">
|
|
<span>Shipping Fee</span>
|
|
<span>{formatPrice(order.shippingFee)}</span>
|
|
</div>
|
|
{(order.discountAmount ?? 0) > 0 && (
|
|
<div className="flex justify-between text-emerald-600">
|
|
<span>Discount{order.couponCode ? ` (${order.couponCode})` : ''}</span>
|
|
<span>-{formatPrice(order.discountAmount)}</span>
|
|
</div>
|
|
)}
|
|
<div className="flex justify-between border-t border-gray-100 pt-4 text-lg font-bold text-pop-black">
|
|
<span>Total</span>
|
|
<span className="text-pop-pink">{formatPrice(order.totalAmount)}</span>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
{order.status === 'PENDING_PAYMENT' && (
|
|
<section className="rounded-3xl border border-amber-100 bg-amber-50 p-6 text-amber-800">
|
|
<h2 className="font-semibold">คำสั่งซื้อนี้ยังรอการชำระเงิน</h2>
|
|
<p className="mt-2 text-sm leading-relaxed">
|
|
หน้านี้รองรับการตรวจสอบสถานะคำสั่งซื้อแล้ว ส่วนการชำระเงินและอัปโหลดสลิปบน website จะย้ายมาในรอบถัดไป เพื่อไม่ทำ workaround ที่เสี่ยงกับ payment flow
|
|
</p>
|
|
</section>
|
|
)}
|
|
|
|
<div className="flex flex-wrap justify-center gap-3">
|
|
<Link href="/track" className="rounded-full bg-white px-6 py-3 font-semibold text-pop-black shadow-brutal transition hover:text-pop-pink">
|
|
ค้นหาออเดอร์อื่น
|
|
</Link>
|
|
<Link href="/shop" className="rounded-full bg-pop-pink px-6 py-3 font-semibold text-white transition hover:bg-pop-red">
|
|
กลับไปช้อปต่อ
|
|
</Link>
|
|
</div>
|
|
</section>
|
|
|
|
{bottomSections.length > 0 && (
|
|
<DynamicPageRenderer sections={bottomSections} categories={categories} lang={lang} />
|
|
)}
|
|
</main>
|
|
)
|
|
}
|