Files
weeding/src/app/dashboard/page.tsx

188 lines
9.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 { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
import { CalendarDays, CreditCard, Users, DollarSign, TrendingUp, ArrowUpRight, Calendar as CalendarIcon } from "lucide-react"
import { format } from "date-fns"
import { tr } from "date-fns/locale"
import Link from "next/link"
import { Badge } from "@/components/ui/badge"
export default async function DashboardPage() {
const supabase = await createClient()
// 1. Total Reservations (Count)
const { count: totalReservations } = await supabase
.from('reservations')
.select('*', { count: 'exact', head: true })
.neq('status', 'cancelled')
// 2. Active Customers (Count)
const { count: totalCustomers } = await supabase
.from('customers')
.select('*', { count: 'exact', head: true })
// 3. Pending Payments (Sum of remaining balances)
// This is complex to calculate in one query without a view or function,
// so we'll approximate or fetch pending payments directly if possible.
// For now, let's just count pending reservations as a proxy or fetch recent payments.
// Better: Sum of 'amount' from 'payments' where status = 'pending' (if we tracked pending payments that way)
// Or: Calculate total potential revenue vs paid revenue.
// Let's stick to "Total Revenue" (Paid) for now.
const { data: payments } = await supabase
.from('payments')
.select('amount')
.eq('status', 'paid')
const totalRevenue = payments?.reduce((sum, p) => sum + Number(p.amount), 0) || 0
// 4. Upcoming Events (Next 5)
const { data: upcomingEvents } = await supabase
.from('reservations')
.select(`
id,
start_time,
status,
halls (name),
customers (full_name)
`)
.gte('start_time', new Date().toISOString())
.neq('status', 'cancelled')
.order('start_time', { ascending: true })
.limit(5)
// 5. Recent Activities (Last 5 created reservations)
const { data: recentReservations } = await supabase
.from('reservations')
.select(`
id,
created_at,
customers (full_name)
`)
.order('created_at', { ascending: false })
.limit(5)
return (
<div className="space-y-8">
<div className="flex items-center justify-between">
<div>
<h2 className="text-3xl font-bold tracking-tight gradient-text">Hoş Geldiniz, Admin</h2>
<p className="text-muted-foreground mt-1">İşletmenizin durumu hakkında genel bakış.</p>
</div>
</div>
<div className="grid gap-6 md:grid-cols-2 lg:grid-cols-4">
<Card className="card-hover border-l-4 border-l-blue-500">
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
<CardTitle className="text-sm font-medium text-muted-foreground">
Toplam Rezervasyon
</CardTitle>
<div className="h-8 w-8 rounded-full bg-blue-100 dark:bg-blue-900/20 flex items-center justify-center">
<CalendarDays className="h-4 w-4 text-blue-600 dark:text-blue-400" />
</div>
</CardHeader>
<CardContent>
<div className="text-3xl font-bold mt-2">{totalReservations || 0}</div>
<p className="text-xs text-muted-foreground mt-1 flex items-center">
Aktif rezervasyonlar
</p>
</CardContent>
</Card>
<Card className="card-hover border-l-4 border-l-purple-500">
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
<CardTitle className="text-sm font-medium text-muted-foreground">
Toplam Müşteri
</CardTitle>
<div className="h-8 w-8 rounded-full bg-purple-100 dark:bg-purple-900/20 flex items-center justify-center">
<Users className="h-4 w-4 text-purple-600 dark:text-purple-400" />
</div>
</CardHeader>
<CardContent>
<div className="text-3xl font-bold mt-2">{totalCustomers || 0}</div>
<p className="text-xs text-muted-foreground mt-1 flex items-center">
Kayıtlı müşteri sayısı
</p>
</CardContent>
</Card>
<Card className="card-hover border-l-4 border-l-green-500">
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
<CardTitle className="text-sm font-medium text-muted-foreground">
Toplam Gelir
</CardTitle>
<div className="h-8 w-8 rounded-full bg-green-100 dark:bg-green-900/20 flex items-center justify-center">
<DollarSign className="h-4 w-4 text-green-600 dark:text-green-400" />
</div>
</CardHeader>
<CardContent>
<div className="text-3xl font-bold mt-2">{totalRevenue.toLocaleString('tr-TR')}</div>
<p className="text-xs text-muted-foreground mt-1 flex items-center">
Tahsil edilen ödemeler
</p>
</CardContent>
</Card>
</div>
<div className="grid gap-6 md:grid-cols-2 lg:grid-cols-7">
<Card className="col-span-4 shadow-md border-none">
<CardHeader>
<CardTitle>Yaklaşan Etkinlikler</CardTitle>
</CardHeader>
<CardContent>
{upcomingEvents?.length === 0 ? (
<div className="h-[200px] flex items-center justify-center bg-muted/20 rounded-lg border border-dashed">
<p className="text-muted-foreground">Yaklaşan etkinlik bulunmuyor.</p>
</div>
) : (
<div className="space-y-4">
{upcomingEvents?.map((event) => (
<div key={event.id} className="flex items-center justify-between p-4 border rounded-lg bg-card hover:bg-accent/50 transition-colors">
<div className="flex items-center gap-4">
<div className="h-10 w-10 rounded-full bg-primary/10 flex items-center justify-center text-primary">
<CalendarIcon className="h-5 w-5" />
</div>
<div>
<p className="font-medium">{event.customers?.full_name}</p>
<p className="text-sm text-muted-foreground">{event.halls?.name}</p>
</div>
</div>
<div className="text-right">
<p className="font-medium">{format(new Date(event.start_time), 'd MMM yyyy', { locale: tr })}</p>
<p className="text-sm text-muted-foreground">{format(new Date(event.start_time), 'HH:mm')}</p>
</div>
</div>
))}
</div>
)}
</CardContent>
</Card>
<Card className="col-span-3 shadow-md border-none">
<CardHeader>
<CardTitle>Son İşlemler</CardTitle>
</CardHeader>
<CardContent>
<div className="space-y-4">
{recentReservations?.map((res) => (
<div key={res.id} className="flex items-center gap-4 p-3 rounded-lg hover:bg-muted/50 transition-colors">
<div className="h-9 w-9 rounded-full bg-primary/10 flex items-center justify-center text-primary font-bold text-sm">
{res.customers?.full_name?.substring(0, 2).toUpperCase()}
</div>
<div className="flex-1">
<p className="text-sm font-medium">{res.customers?.full_name} rezervasyon oluşturdu</p>
<p className="text-xs text-muted-foreground">
{format(new Date(res.created_at), 'd MMM HH:mm', { locale: tr })}
</p>
</div>
</div>
))}
{recentReservations?.length === 0 && (
<p className="text-sm text-muted-foreground text-center py-4">Henüz işlem yok.</p>
)}
</div>
</CardContent>
</Card>
</div>
</div>
)
}