116 lines
5.1 KiB
TypeScript
116 lines
5.1 KiB
TypeScript
import Link from 'next/link'
|
|
import { ArrowRight, CircleCheck, Package } from 'lucide-react'
|
|
|
|
export const dynamic = 'force-dynamic'
|
|
|
|
const BACKEND_URL = process.env.VULA_ECOMMERCE_BACKEND_URL || 'http://vula-ecommerce:9000'
|
|
const PUB_KEY = process.env.NEXT_PUBLIC_MEDUSA_PUBLISHABLE_KEY || ''
|
|
const CURRENCY_SYMBOLS: Record<string, string> = { zar: 'R', usd: '$', eur: '€', gbp: '£', kes: 'KSh', ngn: '₦', ghs: '₵' }
|
|
const formatPrice = (amount: number, code?: string) => {
|
|
const sym = CURRENCY_SYMBOLS[(code ?? '').toLowerCase()] ?? (code?.toUpperCase() ?? '')
|
|
return `${sym}${(amount / 100).toFixed(2)}`
|
|
}
|
|
|
|
async function getOrder(orderId: string) {
|
|
try {
|
|
const res = await fetch(`${BACKEND_URL}/store/orders/${orderId}`, {
|
|
headers: { 'x-publishable-api-key': PUB_KEY },
|
|
cache: 'no-store',
|
|
signal: AbortSignal.timeout(8000),
|
|
})
|
|
if (!res.ok) return null
|
|
const { order } = await res.json()
|
|
return order
|
|
} catch {
|
|
return null
|
|
}
|
|
}
|
|
|
|
interface PageProps { params: { orderId: string } }
|
|
|
|
export default async function OrderConfirmationPage({ params }: PageProps) {
|
|
const order = await getOrder(params.orderId)
|
|
const currencyCode = order?.currency_code
|
|
const items = order?.items ?? []
|
|
|
|
return (
|
|
<main className="min-h-screen bg-background">
|
|
<div className="container mx-auto px-4 py-16 max-w-2xl">
|
|
<div className="text-center mb-10">
|
|
<div className="w-20 h-20 rounded-full bg-green-100 dark:bg-green-900/30 flex items-center justify-center mx-auto mb-6">
|
|
<CircleCheck className="h-10 w-10 text-green-600" />
|
|
</div>
|
|
<h1 className="text-3xl font-bold text-foreground mb-2">Order Confirmed!</h1>
|
|
<p className="text-muted-foreground">
|
|
{order?.email ? `A confirmation has been sent to ${order.email}.` : 'Thank you for your order.'}
|
|
</p>
|
|
</div>
|
|
|
|
<div className="bg-card border border-border rounded-xl p-6 mb-6">
|
|
<div className="flex items-center justify-between mb-4">
|
|
<h2 className="text-base font-semibold text-foreground">Order Details</h2>
|
|
{order && (
|
|
<span className="text-xs font-mono bg-muted text-muted-foreground px-3 py-1 rounded-lg">
|
|
#{order.display_id ?? params.orderId.slice(0, 8).toUpperCase()}
|
|
</span>
|
|
)}
|
|
</div>
|
|
{items.length > 0 ? (
|
|
<ul className="space-y-4">
|
|
{items.map((item: any) => (
|
|
<li key={item.id} className="flex gap-3 items-start">
|
|
{item.thumbnail && (
|
|
<img src={item.thumbnail} alt={item.product_title || item.title} className="w-14 h-14 object-cover rounded-lg flex-shrink-0" />
|
|
)}
|
|
<div className="flex-1 min-w-0">
|
|
<p className="text-sm font-medium text-foreground">{item.product_title || item.title}</p>
|
|
{item.variant?.title && item.variant.title !== 'Default Title' && (
|
|
<p className="text-xs text-muted-foreground">{item.variant.title}</p>
|
|
)}
|
|
<p className="text-xs text-muted-foreground">Qty: {item.quantity}</p>
|
|
</div>
|
|
<p className="text-sm font-semibold text-foreground flex-shrink-0">
|
|
{formatPrice((item.unit_price ?? 0) * item.quantity, currencyCode)}
|
|
</p>
|
|
</li>
|
|
))}
|
|
</ul>
|
|
) : (
|
|
<div className="flex items-center gap-3 text-muted-foreground">
|
|
<Package className="h-5 w-5" />
|
|
<p className="text-sm">Your items are being prepared.</p>
|
|
</div>
|
|
)}
|
|
{order?.total && (
|
|
<div className="border-t border-border mt-4 pt-4 flex justify-between font-bold text-foreground">
|
|
<span>Total</span>
|
|
<span className="text-primary">{formatPrice(order.total, currencyCode)}</span>
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
{order?.shipping_address && (
|
|
<div className="bg-card border border-border rounded-xl p-6 mb-6">
|
|
<h2 className="text-base font-semibold text-foreground mb-3">Delivering to</h2>
|
|
<address className="not-italic text-sm text-muted-foreground space-y-1">
|
|
<p>{order.shipping_address.first_name} {order.shipping_address.last_name}</p>
|
|
<p>{order.shipping_address.address_1}</p>
|
|
<p>{order.shipping_address.city}, {order.shipping_address.postal_code}</p>
|
|
</address>
|
|
</div>
|
|
)}
|
|
|
|
<div className="flex flex-col sm:flex-row gap-3">
|
|
<Link href="/products" className="flex-1 flex items-center justify-center gap-2 bg-primary text-primary-foreground py-3 rounded-xl font-semibold hover:bg-primary/90 transition-colors">
|
|
Continue Shopping
|
|
<ArrowRight className="h-4 w-4" />
|
|
</Link>
|
|
<Link href="/" className="flex-1 flex items-center justify-center border border-border text-foreground py-3 rounded-xl font-medium hover:bg-muted transition-colors">
|
|
Back to Home
|
|
</Link>
|
|
</div>
|
|
</div>
|
|
</main>
|
|
)
|
|
}
|