import { useState, useEffect } from 'react' import { useParams, Link } from 'react-router-dom' import Navbar from '../components/Navbar' import Footer from '../components/Footer' import DynamicPageRenderer from '../components/DynamicPageRenderer' import { API_BASE, formatPrice, type Order, type StoreSettings, type Category, type PageSection } from '../types' import { CheckCircle2, UploadCloud, Loader2, Copy, CreditCard, QrCode, Landmark } from 'lucide-react' export default function OrderSuccessPage() { const { id } = useParams<{ id: string }>() const [order, setOrder] = useState(null) const [settings, setSettings] = useState(null) const [categories, setCategories] = useState([]) const [loading, setLoading] = useState(true) const [uploading, setUploading] = useState(false) const [copied, setCopied] = useState(false) const [cmsSections, setCmsSections] = useState([]) const [paymentMethod, setPaymentMethod] = useState<'bank' | 'promptpay' | 'card'>('bank') const [qrCodeUrl, setQrCodeUrl] = useState(null) const [generatingQr, setGeneratingQr] = useState(false) const [polling, setPolling] = useState(false) // Card Form State const [cardNumber, setCardNumber] = useState('') const [cardName, setCardName] = useState('') const [cardExpiry, setCardExpiry] = useState('') // MM/YY const [cardCvv, setCardCvv] = useState('') const [payingCard, setPayingCard] = useState(false) const [cardError, setCardError] = useState(null) useEffect(() => { let interval: any; if (polling && order && order.status === 'PENDING_PAYMENT') { interval = setInterval(() => { fetch(`${API_BASE}/orders/${order.id}`) .then(res => res.json()) .then(updatedOrder => { if (updatedOrder && updatedOrder.status === 'PAID') { setOrder(updatedOrder); setPolling(false); clearInterval(interval); } }) .catch(console.error); }, 5000); } return () => clearInterval(interval); }, [polling, order]); const handleGeneratePromptPay = async () => { if (!order) return setGeneratingQr(true) try { const res = await fetch(`${API_BASE}/payments/charge`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ orderId: order.id, amount: order.totalAmount, type: 'promptpay' }) }) const data = await res.json() if (res.ok && data.success && data.qrCodeUrl) { setQrCodeUrl(data.qrCodeUrl) setPolling(true) } else { alert(data.error || 'Failed to generate PromptPay QR') } } catch (err) { console.error(err) alert('Failed to connect to payment server') } finally { setGeneratingQr(false) } } const handleCardPay = async (e: React.FormEvent) => { e.preventDefault() if (!order) return setPayingCard(true) setCardError(null) try { const omise = (window as any).Omise if (!omise) { throw new Error('Omise library is loading, please try again.') } omise.setPublicKey(import.meta.env.VITE_OMISE_PUBLIC_KEY) const parts = cardExpiry.split('/') if (parts.length !== 2) { throw new Error('Expiry date must be in MM/YY format') } const expMonth = parseInt(parts[0], 10) let expYear = parseInt(parts[1], 10) if (expYear < 100) expYear += 2000 const cardData = { name: cardName, number: cardNumber.replace(/\s+/g, ''), expiration_month: expMonth, expiration_year: expYear, security_code: cardCvv } omise.createToken('card', cardData, async (statusCode: number, response: any) => { if (statusCode === 200) { try { const chargeRes = await fetch(`${API_BASE}/payments/charge`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ orderId: order.id, amount: order.totalAmount, type: 'card', token: response.id }) }) const chargeData = await chargeRes.json() if (chargeRes.ok && chargeData.success) { if (chargeData.authorizeUri) { window.location.href = chargeData.authorizeUri } else { window.location.reload() } } else { setCardError(chargeData.error || 'Card charge request failed') setPayingCard(false) } } catch (err: any) { setCardError('Failed to verify card charge on server') setPayingCard(false) } } else { setCardError(response.message || 'Validation failed for card details') setPayingCard(false) } }) } catch (err: any) { setCardError(err.message || 'An error occurred during payment processing') setPayingCard(false) } } useEffect(() => { Promise.all([ fetch(`${API_BASE}/orders/${id}`).then(r => r.json()), fetch(`${API_BASE}/settings`).then(r => r.json()), fetch(`${API_BASE}/categories`).then(r => r.json()), fetch(`${API_BASE}/pages/order-success-layout`).then(r => r.ok ? r.json() : null).catch(() => null) ]) .then(([orderData, settingsData, catData, pageData]) => { setOrder(orderData) setSettings(settingsData) setCategories(catData) if (pageData && pageData.sections) { setCmsSections(pageData.sections) } }) .catch(console.error) .finally(() => setLoading(false)) }, [id]) const copyToClipboard = () => { if (!settings) return navigator.clipboard.writeText(settings.bankAccountNumber) setCopied(true) setTimeout(() => setCopied(false), 2000) } const handleFileUpload = async (e: React.ChangeEvent) => { const file = e.target.files?.[0] if (!file || !order) return setUploading(true) const formData = new FormData() formData.append('file', file) try { // 1. Upload file const uploadRes = await fetch(`${API_BASE}/upload`, { method: 'POST', body: formData }) const uploadData = await uploadRes.json() if (uploadData.url) { // 2. Update order slip const updateRes = await fetch(`${API_BASE}/orders/${order.id}/slip`, { method: 'PATCH', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ paymentSlipUrl: uploadData.url }) }) const updatedOrder = await updateRes.json() setOrder(updatedOrder) } } catch (err) { console.error(err) alert("Failed to upload payment slip.") } finally { setUploading(false) } } if (loading) { return (
) } if (!order || (order as any).error) { return (

Order Not Found

Return to Shop
) } const announcementSections = cmsSections.filter(s => s.isActive && s.type.toUpperCase() === 'ANNOUNCEMENT_BAR'); const bottomSections = cmsSections.filter(s => s.isActive && s.type.toUpperCase() !== 'ANNOUNCEMENT_BAR' && s.type.toUpperCase() !== 'HERO' && s.type.toUpperCase() !== 'BANNER'); return (
{announcementSections.length > 0 && ( )}
{/* Header */}

Order Received

Thank you! Your order #{order.id.toString().padStart(4, '0')} has been placed.

{/* Order Summary */}

Order Items

{order.items.map(item => (
Product

{item.productName}

{item.variantSize} x {item.quantity}

{formatPrice(item.priceAtPurchase * item.quantity)}

))}
Subtotal {formatPrice(order.totalAmount - order.shippingFee)}
Shipping Fee {formatPrice(order.shippingFee)}
Total {formatPrice(order.totalAmount)}
{order.status === 'PENDING_PAYMENT' ? (
{/* Payment Method Selector */}
{/* Bank Transfer Layout */} {paymentMethod === 'bank' && settings && (

Payment Required

{formatPrice(order.totalAmount)}

Bank

{settings.bankName}

Account Number

{settings.bankAccountNumber}

Account Name

{settings.bankAccountName}

Please transfer the exact amount and upload your slip below.

Payment Slip Verification

{order.paymentSlipUrl ? (

Slip Uploaded Successfully

Status: {order.status}

We will verify your payment and process your order shortly.

Slip
) : (
{uploading ? ( ) : ( )}

{uploading ? 'Uploading...' : 'Click or drag to upload slip'}

Supports JPG, PNG (Max 5MB)

)}
)} {/* PromptPay Layout */} {paymentMethod === 'promptpay' && (

PromptPay QR Code

{formatPrice(order.totalAmount)}
{!qrCodeUrl ? (
) : (
PromptPay QR Code

สแกนจ่ายเงินเพื่อเสร็จสิ้นออเดอร์

1. กดบันทึกรูปภาพคิวอาร์โค้ดลงในมือถือของคุณ

2. เปิดแอป Mobile Banking และเลือกสแกนภาพจากอัลบั้ม

{polling ? "🔄 กำลังตรวจสอบยอดชำระเงินโดยอัตโนมัติ..." : "รอการจ่ายเงิน..."}

ดาวน์โหลดรูปภาพ QR Code
)}
)} {/* Credit Card Layout */} {paymentMethod === 'card' && (

Credit / Debit Card

{formatPrice(order.totalAmount)}
{cardError && (
{cardError}
)}
setCardName(e.target.value)} className="w-full bg-spa-light border border-spa-stone/20 rounded-lg p-3 text-pop-black focus:border-spa-forest outline-none transition-all placeholder:text-spa-stone/30" placeholder="Jane Doe" />
{ const val = e.target.value.replace(/\D/g, '').replace(/(.{4})/g, '$1 ').trim(); setCardNumber(val); }} className="w-full bg-spa-light border border-spa-stone/20 rounded-lg p-3 text-pop-black focus:border-spa-forest outline-none transition-all placeholder:text-spa-stone/30" placeholder="4242 4242 4242 4242" />
{ const val = e.target.value.replace(/\D/g, '').replace(/(\d{2})(\d{1,2})/, '$1/$2').trim(); setCardExpiry(val); }} className="w-full bg-spa-light border border-spa-stone/20 rounded-lg p-3 text-pop-black focus:border-spa-forest outline-none transition-all placeholder:text-spa-stone/30" placeholder="MM/YY" />
setCardCvv(e.target.value.replace(/\D/g, ''))} className="w-full bg-spa-light border border-spa-stone/20 rounded-lg p-3 text-pop-black focus:border-spa-forest outline-none transition-all placeholder:text-spa-stone/30" placeholder="123" />
)}
) : (

ชำระเงินเสร็จสิ้น

ทางแบรนด์ได้รับยอดชำระของคุณเรียบร้อย ออเดอร์กำลังอยู่ระหว่างจัดเตรียมสินค้า

ออเดอร์สถานะปัจจุบัน: {order.status}

)}
{bottomSections.length > 0 && ( )}
) }