fix: resolve hydration and blocking route issues and add admin order archive

This commit is contained in:
DanielS
2026-05-01 00:52:43 +02:00
parent 316d9510a8
commit 1dd2756e22
4 changed files with 125 additions and 8 deletions

View File

@@ -0,0 +1,108 @@
import { createClient } from '@/lib/supabase/server'
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table'
import { Badge } from '@/components/ui/badge'
import { Button } from '@/components/ui/button'
import { FileText, Download, ExternalLink } from 'lucide-react'
import Link from 'next/link'
import { Suspense } from 'react'
export default async function AdminOrdersPage() {
return (
<div className="space-y-6">
<div className="flex justify-between items-center">
<div>
<h1 className="text-3xl font-bold tracking-tight">Bestellungen & Rechnungen</h1>
<p className="text-muted-foreground">Verwalten Sie alle Kundenbestellungen und greifen Sie auf generierte PDFs zu.</p>
</div>
</div>
<Suspense fallback={<div className="h-40 w-full animate-pulse bg-white/5 rounded-xl" />}>
<OrdersData />
</Suspense>
</div>
)
}
async function OrdersData() {
const supabase = await createClient()
const { data: orders, error } = await supabase
.from('orders')
.select('*')
.order('created_at', { ascending: false })
if (error) {
return <div>Fehler beim Laden der Bestellungen: {error.message}</div>
}
return (
<Card className="glass-dark border-white/10">
<CardContent className="p-0">
<Table>
<TableHeader>
<TableRow className="border-white/10 hover:bg-transparent">
<TableHead className="w-[100px]">ID</TableHead>
<TableHead>Datum</TableHead>
<TableHead>Kunde</TableHead>
<TableHead>Betrag</TableHead>
<TableHead>Status</TableHead>
<TableHead className="text-right">Rechnung</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{orders?.length === 0 ? (
<TableRow>
<TableCell colSpan={6} className="text-center py-12 text-muted-foreground">
Noch keine Bestellungen vorhanden.
</TableCell>
</TableRow>
) : (
orders?.map((order) => (
<TableRow key={order.id} className="border-white/5 hover:bg-white/5">
<TableCell className="font-mono text-xs text-muted-foreground">
{order.id.slice(0, 8)}...
</TableCell>
<TableCell>
{new Date(order.created_at).toLocaleDateString('de-DE')}
</TableCell>
<TableCell>
<div className="font-medium">{order.customer_data?.company_name || 'Privatkunde'}</div>
<div className="text-xs text-muted-foreground">{order.customer_data?.first_name} {order.customer_data?.last_name}</div>
</TableCell>
<TableCell className="font-semibold">
{new Intl.NumberFormat('de-DE', { style: 'currency', currency: 'EUR' }).format(order.total_price)}
</TableCell>
<TableCell>
<Badge variant={order.status === 'completed' ? 'default' : 'secondary'} className="capitalize">
{order.status}
</Badge>
</TableCell>
<TableCell className="text-right">
{order.pdf_url ? (
<div className="flex justify-end gap-2">
<Button variant="outline" size="sm" asChild className="h-8 border-white/10 hover:bg-primary/20">
<a href={order.pdf_url} target="_blank" rel="noopener noreferrer">
<ExternalLink className="w-4 h-4 mr-2" /> Ansehen
</a>
</Button>
<Button variant="secondary" size="sm" asChild className="h-8">
<a href={order.pdf_url} download>
<Download className="w-4 h-4" />
</a>
</Button>
</div>
) : (
<span className="text-xs text-muted-foreground italic">Kein PDF</span>
)}
</TableCell>
</TableRow>
))
)}
</TableBody>
</Table>
</CardContent>
</Card>
)
}

View File

@@ -1,4 +1,4 @@
import { getProducts } from '@/lib/actions/products'
import { getProducts, getCategories } from '@/lib/actions/products'
import { ProductList } from '@/components/admin/product-list'
import { CreateProductDialog } from '@/components/admin/create-product-dialog'
@@ -19,11 +19,9 @@ export default async function AdminProductsPage() {
</p>
</div>
<div className="flex items-center space-x-2">
<CreateProductDialog>
<button className="inline-flex items-center justify-center rounded-md bg-primary px-4 py-2 text-sm font-medium text-primary-foreground shadow transition-colors hover:bg-primary/90 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50">
<Plus className="mr-2 h-4 w-4" /> Produkt hinzufügen
</button>
</CreateProductDialog>
<Suspense fallback={<div className="h-10 w-32 animate-pulse bg-white/5 rounded-md" />}>
<CreateProductAction />
</Suspense>
</div>
</div>
<Suspense fallback={<div className="h-40 w-full animate-pulse bg-white/5 rounded-xl" />}>
@@ -35,5 +33,13 @@ export default async function AdminProductsPage() {
async function ProductDataWrapper() {
const products = await getProducts()
return <ProductList initialProducts={products} />
const categories = await getCategories()
return <ProductList initialProducts={products} categories={categories} />
}
async function CreateProductAction() {
const categories = await getCategories()
return (
<CreateProductDialog categories={categories} />
)
}

View File

@@ -9,7 +9,7 @@ const defaultUrl = process.env.VERCEL_URL
export const metadata: Metadata = {
metadataBase: new URL(defaultUrl),
title: "CloudShop | Modulare Software-Lösungen",
title: "CASPOSShop | Modulare Software-Lösungen",
description: "Konfigurieren und bestellen Sie Ihre maßgeschneiderte Business-Software in Minuten.",
};