Feat: Connect Dashboard to real Supabase data
This commit is contained in:
@@ -1,7 +1,66 @@
|
||||
import { createClient } from "@/lib/supabase/server"
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
|
||||
import { CalendarDays, CreditCard, Users, DollarSign, TrendingUp, ArrowUpRight } from "lucide-react"
|
||||
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)
|
||||
|
||||
export default function DashboardPage() {
|
||||
return (
|
||||
<div className="space-y-8">
|
||||
<div className="flex items-center justify-between">
|
||||
@@ -22,9 +81,9 @@ export default function DashboardPage() {
|
||||
</div>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="text-3xl font-bold mt-2">12</div>
|
||||
<div className="text-3xl font-bold mt-2">{totalReservations || 0}</div>
|
||||
<p className="text-xs text-muted-foreground mt-1 flex items-center">
|
||||
<span className="text-green-600 flex items-center mr-1"><TrendingUp className="h-3 w-3 mr-1" /> +2</span> geçen aydan beri
|
||||
Aktif rezervasyonlar
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
@@ -32,33 +91,16 @@ export default function DashboardPage() {
|
||||
<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">
|
||||
Aktif Müşteriler
|
||||
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">50</div>
|
||||
<div className="text-3xl font-bold mt-2">{totalCustomers || 0}</div>
|
||||
<p className="text-xs text-muted-foreground mt-1 flex items-center">
|
||||
<span className="text-green-600 flex items-center mr-1"><ArrowUpRight className="h-3 w-3 mr-1" /> +4</span> geçen aydan beri
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card className="card-hover border-l-4 border-l-orange-500">
|
||||
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
|
||||
<CardTitle className="text-sm font-medium text-muted-foreground">
|
||||
Bekleyen Ödemeler
|
||||
</CardTitle>
|
||||
<div className="h-8 w-8 rounded-full bg-orange-100 dark:bg-orange-900/20 flex items-center justify-center">
|
||||
<CreditCard className="h-4 w-4 text-orange-600 dark:text-orange-400" />
|
||||
</div>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="text-3xl font-bold mt-2">₺12,000</div>
|
||||
<p className="text-xs text-muted-foreground mt-1">
|
||||
3 rezervasyon için
|
||||
Kayıtlı müşteri sayısı
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
@@ -66,16 +108,16 @@ export default function DashboardPage() {
|
||||
<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">
|
||||
Aylık Gelir
|
||||
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">₺45,000</div>
|
||||
<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">
|
||||
<span className="text-green-600 flex items-center mr-1"><TrendingUp className="h-3 w-3 mr-1" /> +10%</span> geçen aydan beri
|
||||
Tahsil edilen ödemeler
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
@@ -87,28 +129,55 @@ export default function DashboardPage() {
|
||||
<CardTitle>Yaklaşan Etkinlikler</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="h-[300px] flex items-center justify-center bg-muted/20 rounded-lg border border-dashed">
|
||||
<p className="text-muted-foreground">Takvim önizlemesi buraya gelecek.</p>
|
||||
</div>
|
||||
{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 Aktiviteler</CardTitle>
|
||||
<CardTitle>Son İşlemler</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="space-y-4">
|
||||
{[1, 2, 3].map((i) => (
|
||||
<div key={i} className="flex items-center gap-4 p-3 rounded-lg hover:bg-muted/50 transition-colors">
|
||||
{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">
|
||||
AH
|
||||
{res.customers?.full_name?.substring(0, 2).toUpperCase()}
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<p className="text-sm font-medium">Ahmet Hakan rezervasyon yaptı</p>
|
||||
<p className="text-xs text-muted-foreground">2 dakika önce</p>
|
||||
<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>
|
||||
|
||||
Reference in New Issue
Block a user