This commit is contained in:
root
2025-12-21 12:46:27 +01:00
parent 5e65144934
commit bb1c5b43d6
18 changed files with 1375 additions and 691 deletions

291
app/orders/page.tsx Normal file
View 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>
</>
)
}