final
This commit is contained in:
291
app/orders/page.tsx
Normal file
291
app/orders/page.tsx
Normal file
@@ -0,0 +1,291 @@
|
||||
'use client'
|
||||
|
||||
import { useState, useEffect } from 'react'
|
||||
import { useRouter } from 'next/navigation'
|
||||
import Image from 'next/image'
|
||||
import Nav from '../components/Nav'
|
||||
|
||||
interface Order {
|
||||
id: number
|
||||
drop_id: number
|
||||
buyer_id: number
|
||||
size: number
|
||||
payment_id: string
|
||||
created_at: string
|
||||
drop_item: string
|
||||
drop_unit: string
|
||||
drop_ppu: number
|
||||
drop_image_url: string | null
|
||||
buyer_fullname: string
|
||||
buyer_address: string
|
||||
buyer_phone: string
|
||||
}
|
||||
|
||||
export default function OrdersPage() {
|
||||
const router = useRouter()
|
||||
const [orders, setOrders] = useState<Order[]>([])
|
||||
const [loading, setLoading] = useState(true)
|
||||
const [error, setError] = useState<string | null>(null)
|
||||
const [user, setUser] = useState<any>(null)
|
||||
|
||||
useEffect(() => {
|
||||
checkAuth()
|
||||
fetchOrders()
|
||||
}, [])
|
||||
|
||||
const checkAuth = async () => {
|
||||
try {
|
||||
const response = await fetch('/api/auth/session', {
|
||||
credentials: 'include',
|
||||
})
|
||||
if (response.ok) {
|
||||
const data = await response.json()
|
||||
setUser(data.user)
|
||||
} else {
|
||||
// Not authenticated, redirect to home
|
||||
router.push('/')
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error checking auth:', error)
|
||||
router.push('/')
|
||||
}
|
||||
}
|
||||
|
||||
const fetchOrders = async () => {
|
||||
try {
|
||||
const response = await fetch('/api/orders', {
|
||||
credentials: 'include',
|
||||
})
|
||||
|
||||
if (response.status === 401) {
|
||||
router.push('/')
|
||||
return
|
||||
}
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json()
|
||||
throw new Error(errorData.error || 'Failed to fetch orders')
|
||||
}
|
||||
|
||||
const data = await response.json()
|
||||
setOrders(data)
|
||||
} catch (error) {
|
||||
console.error('Error fetching orders:', error)
|
||||
setError(error instanceof Error ? error.message : 'Failed to load orders')
|
||||
} finally {
|
||||
setLoading(false)
|
||||
}
|
||||
}
|
||||
|
||||
const formatDate = (dateString: string) => {
|
||||
const date = new Date(dateString)
|
||||
return date.toLocaleDateString('en-US', {
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: 'numeric',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
})
|
||||
}
|
||||
|
||||
const formatSize = (size: number, unit: string) => {
|
||||
if (unit === 'kg' && size >= 1000) {
|
||||
return `${(size / 1000).toFixed(1)}kg`
|
||||
}
|
||||
return `${size}${unit}`
|
||||
}
|
||||
|
||||
const calculatePrice = (order: Order) => {
|
||||
// ppu is stored as integer where 1000 = $1.00
|
||||
const pricePerGram = order.drop_ppu / 1000
|
||||
// Size is in grams
|
||||
return (order.size * pricePerGram).toFixed(2)
|
||||
}
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<>
|
||||
<Nav />
|
||||
<div className="container" style={{ paddingTop: '120px', textAlign: 'center' }}>
|
||||
<p style={{ color: 'var(--muted)' }}>Loading orders...</p>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Nav />
|
||||
<div className="container" style={{ paddingTop: '120px' }}>
|
||||
<button
|
||||
onClick={() => router.push('/')}
|
||||
style={{
|
||||
background: 'transparent',
|
||||
border: '1px solid var(--border)',
|
||||
color: 'var(--text)',
|
||||
padding: '8px 16px',
|
||||
borderRadius: '8px',
|
||||
cursor: 'pointer',
|
||||
fontSize: '14px',
|
||||
marginBottom: '24px',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: '8px',
|
||||
}}
|
||||
>
|
||||
← Back
|
||||
</button>
|
||||
<h1 style={{ marginBottom: '40px' }}>My Orders</h1>
|
||||
|
||||
{error && (
|
||||
<div
|
||||
style={{
|
||||
padding: '16px',
|
||||
background: 'rgba(255, 0, 0, 0.1)',
|
||||
border: '1px solid rgba(255, 0, 0, 0.3)',
|
||||
borderRadius: '12px',
|
||||
color: '#ff4444',
|
||||
marginBottom: '24px',
|
||||
}}
|
||||
>
|
||||
{error}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{orders.length === 0 ? (
|
||||
<div
|
||||
style={{
|
||||
padding: '60px 20px',
|
||||
textAlign: 'center',
|
||||
background: 'var(--card)',
|
||||
borderRadius: '16px',
|
||||
border: '1px solid var(--border)',
|
||||
}}
|
||||
>
|
||||
<p style={{ color: 'var(--muted)', fontSize: '18px', marginBottom: '12px' }}>
|
||||
No orders yet
|
||||
</p>
|
||||
<p style={{ color: 'var(--muted)', fontSize: '14px' }}>
|
||||
Your purchase history will appear here once you make your first order.
|
||||
</p>
|
||||
<a
|
||||
href="/"
|
||||
style={{
|
||||
display: 'inline-block',
|
||||
marginTop: '24px',
|
||||
padding: '12px 24px',
|
||||
background: 'var(--accent)',
|
||||
color: '#000',
|
||||
borderRadius: '12px',
|
||||
textDecoration: 'none',
|
||||
fontWeight: 500,
|
||||
}}
|
||||
>
|
||||
Browse Drops
|
||||
</a>
|
||||
</div>
|
||||
) : (
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: '20px' }}>
|
||||
{orders.map((order) => (
|
||||
<div
|
||||
key={order.id}
|
||||
style={{
|
||||
background: 'var(--card)',
|
||||
borderRadius: '16px',
|
||||
padding: '24px',
|
||||
border: '1px solid var(--border)',
|
||||
display: 'grid',
|
||||
gridTemplateColumns: '120px 1fr',
|
||||
gap: '24px',
|
||||
}}
|
||||
>
|
||||
{order.drop_image_url ? (
|
||||
<Image
|
||||
src={order.drop_image_url}
|
||||
alt={order.drop_item}
|
||||
width={120}
|
||||
height={120}
|
||||
style={{
|
||||
width: '100%',
|
||||
height: '120px',
|
||||
borderRadius: '12px',
|
||||
objectFit: 'cover',
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<div
|
||||
style={{
|
||||
width: '100%',
|
||||
height: '120px',
|
||||
background: 'var(--bg-soft)',
|
||||
borderRadius: '12px',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
color: 'var(--muted)',
|
||||
fontSize: '14px',
|
||||
}}
|
||||
>
|
||||
No Image
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div>
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'start', marginBottom: '12px' }}>
|
||||
<div>
|
||||
<h3 style={{ margin: 0, marginBottom: '8px', fontSize: '20px' }}>
|
||||
{order.drop_item}
|
||||
</h3>
|
||||
<p style={{ margin: 0, color: 'var(--muted)', fontSize: '14px' }}>
|
||||
Order #{order.id} · {formatDate(order.created_at)}
|
||||
</p>
|
||||
</div>
|
||||
<div style={{ textAlign: 'right' }}>
|
||||
<div style={{ fontSize: '18px', fontWeight: 500, marginBottom: '4px' }}>
|
||||
{calculatePrice(order)} CHF
|
||||
</div>
|
||||
<div style={{ fontSize: '14px', color: 'var(--muted)' }}>
|
||||
{formatSize(order.size, 'g')}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
style={{
|
||||
padding: '16px',
|
||||
background: 'var(--bg-soft)',
|
||||
borderRadius: '8px',
|
||||
marginTop: '16px',
|
||||
}}
|
||||
>
|
||||
<div style={{ fontSize: '13px', color: 'var(--muted)', marginBottom: '8px' }}>
|
||||
Delivery Information:
|
||||
</div>
|
||||
<div style={{ fontSize: '14px' }}>
|
||||
<div style={{ marginBottom: '4px' }}>
|
||||
<strong>{order.buyer_fullname}</strong>
|
||||
</div>
|
||||
<div style={{ color: 'var(--muted)', marginBottom: '4px' }}>
|
||||
{order.buyer_address}
|
||||
</div>
|
||||
<div style={{ color: 'var(--muted)' }}>
|
||||
{order.buyer_phone}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{order.payment_id && (
|
||||
<div style={{ marginTop: '12px', fontSize: '12px', color: 'var(--muted)' }}>
|
||||
Payment ID: {order.payment_id}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user