Files
webshop/shop/app/my-orders/page.tsx
DanielS bffaca1d63
All checks were successful
Staging Build / build (push) Successful in 2m49s
feat(orders): add rejected status to order schema and admin table views
2026-07-08 10:13:21 +02:00

181 lines
7.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { createClient } from '@/lib/supabase/server'
import { redirect } from 'next/navigation'
import Link from 'next/link'
import { Download, ExternalLink, ShoppingBag, ArrowLeft, Package } from 'lucide-react'
import { Button } from '@/components/ui/button'
import { Card, CardContent } from '@/components/ui/card'
import { Badge } from '@/components/ui/badge'
import type { Order } from '@/lib/types'
const statusLabel: Record<string, string> = {
pending: 'Eingegangen',
active: 'In Bearbeitung',
completed: 'Abgeschlossen',
cancelled: 'Storniert',
rejected: 'Abgelehnt',
}
const statusClass: Record<string, string> = {
pending: 'bg-amber-500/20 text-amber-400 border-amber-500/30',
active: 'bg-green-500/20 text-green-400 border-green-500/30',
completed: 'bg-blue-500/20 text-blue-400 border-blue-500/30',
cancelled: 'bg-red-500/20 text-red-400 border-red-500/30',
rejected: 'bg-rose-500/20 text-rose-400 border-rose-500/30',
}
export default async function MyOrdersPage() {
const supabase = await createClient()
const { data: { user } } = await supabase.auth.getUser()
if (!user) redirect('/auth/login')
// Get user's company_id and fetch all company orders
const { data: dbUser } = await supabase
.from('users')
.select('company_id')
.eq('id', user.id)
.single()
let query = supabase.from('orders').select('*')
if (dbUser?.company_id) {
query = query.eq('company_id', dbUser.company_id)
} else {
query = query.eq('user_id', user.id)
}
const { data: orders, error } = await query.order('created_at', { ascending: false })
return (
<div className="min-h-screen bg-[#020617] text-white px-4 py-12">
<div className="max-w-4xl mx-auto space-y-8">
{/* Header */}
<div className="flex items-center gap-4">
<Link href="/">
<Button variant="ghost" size="sm" className="text-slate-400 hover:text-white">
<ArrowLeft className="w-4 h-4 mr-2" /> Startseite
</Button>
</Link>
<div>
<h1 className="text-3xl font-extrabold tracking-tight flex items-center gap-3">
<ShoppingBag className="w-8 h-8 text-primary" />
Meine Anfragen
</h1>
<p className="text-slate-400 text-sm mt-1">
Alle Ihre Anfragen auf einen Blick inkl. aktuellem Status.
</p>
</div>
</div>
{/* Fehler */}
{error && (
<div className="bg-red-500/10 border border-red-500/20 text-red-400 rounded-xl p-4 text-sm">
Fehler beim Laden der Anfragen: {error.message}
</div>
)}
{/* Keine Anfragen */}
{!error && (!orders || orders.length === 0) && (
<Card className="glass-dark border-white/10">
<CardContent className="flex flex-col items-center justify-center py-16 gap-4">
<Package className="w-12 h-12 text-slate-600" />
<p className="text-slate-400 text-lg">Sie haben noch keine Anfragen aufgegeben.</p>
<Link href="/order">
<Button>Jetzt konfigurieren</Button>
</Link>
</CardContent>
</Card>
)}
{/* Anfragenliste */}
<div className="space-y-4">
{(orders ?? []).map((order) => {
const o = order as Order
const items = o.order_data?.items ?? []
return (
<Card key={o.id} className="glass-dark border-white/10 hover:border-white/20 transition-colors">
<CardContent className="p-6 space-y-4">
{/* Kopfzeile */}
<div className="flex items-start justify-between gap-4 flex-wrap">
<div className="space-y-1">
<p className="text-xs text-slate-500 uppercase tracking-wider">Anfragenummer</p>
<p className="font-mono font-bold text-primary text-lg">#{o.order_number}</p>
</div>
<div className="space-y-1 text-right">
<p className="text-xs text-slate-500 uppercase tracking-wider">Datum</p>
<p className="text-white text-sm">
{new Date(o.created_at).toLocaleDateString('de-DE', {
day: '2-digit', month: '2-digit', year: 'numeric',
})}
</p>
</div>
<div className="space-y-1">
<p className="text-xs text-slate-500 uppercase tracking-wider">Status</p>
<Badge className={statusClass[o.status] ?? 'bg-slate-500/20 text-slate-300'}>
{statusLabel[o.status] ?? o.status}
</Badge>
</div>
<div className="space-y-1 text-right">
<p className="text-xs text-slate-500 uppercase tracking-wider">Gesamt</p>
<p className="font-bold text-white text-lg">
{new Intl.NumberFormat('de-DE', { style: 'currency', currency: 'EUR' }).format(o.total_price)}
</p>
</div>
</div>
{/* Produkte kurz */}
{items.length > 0 && (
<div className="flex flex-wrap gap-2">
{items.map((item, i) => (
<span
key={item.product_id}
className="text-xs bg-white/5 border border-white/10 rounded-full px-3 py-1 text-slate-300"
>
{item.product_name}
{item.selected_modules.length > 0 && (
<span className="text-slate-500 ml-1">+{item.selected_modules.length} Module</span>
)}
</span>
))}
</div>
)}
{/* Aktionen */}
<div className="flex gap-2 flex-wrap pt-1">
<Link href={`/order/success?id=${o.id}`}>
<Button variant="outline" size="sm" className="border-white/10 hover:bg-white/10 text-xs">
Details ansehen
</Button>
</Link>
{o.status !== 'completed' && (
<Link href={`/order?id=${o.id}`}>
<Button variant="outline" size="sm" className="border-amber-500/20 hover:bg-amber-500/10 hover:border-amber-500/40 text-amber-400 text-xs">
Bearbeiten
</Button>
</Link>
)}
{o.pdf_url && (
<>
<Button variant="ghost" size="sm" asChild className="text-xs text-slate-400 hover:text-white">
<a href={`/api/orders/${o.id}/download?inline=true`} target="_blank" rel="noopener noreferrer">
<ExternalLink className="w-3 h-3 mr-1" /> Anfrage ansehen
</a>
</Button>
<Button variant="ghost" size="sm" asChild className="text-xs text-slate-400 hover:text-white">
<a href={`/api/orders/${o.id}/download`} download>
<Download className="w-3 h-3 mr-1" /> PDF
</a>
</Button>
</>
)}
</div>
</CardContent>
</Card>
)
})}
</div>
</div>
</div>
)
}