Files
webshop/shop/app/admin/page.tsx

99 lines
4.2 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 { createAdminClient } from '@/lib/supabase/admin'
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
import { Package, ShoppingCart, Users, TrendingUp } from 'lucide-react'
import { AdminRecentOrders } from '@/components/admin/recent-orders'
// ─── KPI-Daten aus DB ────────────────────────────────────────────────────────
async function getKpis() {
const supabase = await createClient()
const adminDb = createAdminClient()
const [
{ data: orders },
{ count: userCount },
{ count: productCount },
] = await Promise.all([
adminDb.from('orders').select('total_price, status, created_at'),
adminDb.from('profiles').select('*', { count: 'exact', head: true }),
supabase.from('products').select('*', { count: 'exact', head: true }),
])
const now = new Date()
const currentYear = now.getFullYear()
const currentMonth = now.getMonth()
const completedOrdersThisMonth = (orders ?? []).filter(o => {
if (o.status !== 'completed') return false
const orderDate = new Date(o.created_at)
return orderDate.getFullYear() === currentYear && orderDate.getMonth() === currentMonth
})
const totalRevenue = completedOrdersThisMonth.reduce((sum, o) => sum + Number(o.total_price), 0)
const orderCount = (orders ?? []).length
return { totalRevenue, orderCount, userCount: userCount ?? 0, productCount: productCount ?? 0 }
}
export default async function AdminDashboard() {
const { totalRevenue, orderCount, userCount, productCount } = await getKpis()
return (
<div className="p-8 space-y-8">
<h2 className="text-3xl font-bold tracking-tight text-gradient">Dashboard</h2>
{/* KPI Cards echte DB-Daten */}
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-4">
<Card className="glass-dark border-white/5">
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
<CardTitle className="text-sm font-medium text-slate-300">Umsatz aktueller Monat</CardTitle>
<TrendingUp className="h-4 w-4 text-primary" />
</CardHeader>
<CardContent>
<div className="text-2xl font-bold text-white">
{new Intl.NumberFormat('de-DE', { style: 'currency', currency: 'EUR' }).format(totalRevenue)}
</div>
<p className="text-xs text-slate-400">Nur fertige Bestellungen</p>
</CardContent>
</Card>
<Card className="glass-dark border-white/5">
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
<CardTitle className="text-sm font-medium text-slate-300">Bestellungen</CardTitle>
<ShoppingCart className="h-4 w-4 text-primary" />
</CardHeader>
<CardContent>
<div className="text-2xl font-bold text-white">{orderCount}</div>
<p className="text-xs text-slate-400">Gesamt</p>
</CardContent>
</Card>
<Card className="glass-dark border-white/5">
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
<CardTitle className="text-sm font-medium text-slate-300">Produkte</CardTitle>
<Package className="h-4 w-4 text-primary" />
</CardHeader>
<CardContent>
<div className="text-2xl font-bold text-white">{productCount}</div>
<p className="text-xs text-slate-400">Im Katalog</p>
</CardContent>
</Card>
<Card className="glass-dark border-white/5">
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
<CardTitle className="text-sm font-medium text-slate-300">Kunden</CardTitle>
<Users className="h-4 w-4 text-primary" />
</CardHeader>
<CardContent>
<div className="text-2xl font-bold text-white">{userCount}</div>
<p className="text-xs text-slate-400">Registrierte User</p>
</CardContent>
</Card>
</div>
{/* Letzte Bestellungen Client Component mit 30s-Polling */}
<AdminRecentOrders />
</div>
)
}