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 { 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 (
|
return (
|
||||||
<div className="space-y-8">
|
<div className="space-y-8">
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
@@ -22,9 +81,9 @@ export default function DashboardPage() {
|
|||||||
</div>
|
</div>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent>
|
<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">
|
<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>
|
</p>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
@@ -32,33 +91,16 @@ export default function DashboardPage() {
|
|||||||
<Card className="card-hover border-l-4 border-l-purple-500">
|
<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">
|
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
|
||||||
<CardTitle className="text-sm font-medium text-muted-foreground">
|
<CardTitle className="text-sm font-medium text-muted-foreground">
|
||||||
Aktif Müşteriler
|
Toplam Müşteri
|
||||||
</CardTitle>
|
</CardTitle>
|
||||||
<div className="h-8 w-8 rounded-full bg-purple-100 dark:bg-purple-900/20 flex items-center justify-center">
|
<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" />
|
<Users className="h-4 w-4 text-purple-600 dark:text-purple-400" />
|
||||||
</div>
|
</div>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent>
|
<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">
|
<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
|
Kayıtlı müşteri sayısı
|
||||||
</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
|
|
||||||
</p>
|
</p>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
@@ -66,16 +108,16 @@ export default function DashboardPage() {
|
|||||||
<Card className="card-hover border-l-4 border-l-green-500">
|
<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">
|
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
|
||||||
<CardTitle className="text-sm font-medium text-muted-foreground">
|
<CardTitle className="text-sm font-medium text-muted-foreground">
|
||||||
Aylık Gelir
|
Toplam Gelir
|
||||||
</CardTitle>
|
</CardTitle>
|
||||||
<div className="h-8 w-8 rounded-full bg-green-100 dark:bg-green-900/20 flex items-center justify-center">
|
<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" />
|
<DollarSign className="h-4 w-4 text-green-600 dark:text-green-400" />
|
||||||
</div>
|
</div>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent>
|
<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">
|
<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>
|
</p>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
@@ -87,28 +129,55 @@ export default function DashboardPage() {
|
|||||||
<CardTitle>Yaklaşan Etkinlikler</CardTitle>
|
<CardTitle>Yaklaşan Etkinlikler</CardTitle>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent>
|
<CardContent>
|
||||||
<div className="h-[300px] flex items-center justify-center bg-muted/20 rounded-lg border border-dashed">
|
{upcomingEvents?.length === 0 ? (
|
||||||
<p className="text-muted-foreground">Takvim önizlemesi buraya gelecek.</p>
|
<div className="h-[200px] flex items-center justify-center bg-muted/20 rounded-lg border border-dashed">
|
||||||
</div>
|
<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>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
<Card className="col-span-3 shadow-md border-none">
|
<Card className="col-span-3 shadow-md border-none">
|
||||||
<CardHeader>
|
<CardHeader>
|
||||||
<CardTitle>Son Aktiviteler</CardTitle>
|
<CardTitle>Son İşlemler</CardTitle>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent>
|
<CardContent>
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
{[1, 2, 3].map((i) => (
|
{recentReservations?.map((res) => (
|
||||||
<div key={i} className="flex items-center gap-4 p-3 rounded-lg hover:bg-muted/50 transition-colors">
|
<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">
|
<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>
|
||||||
<div className="flex-1">
|
<div className="flex-1">
|
||||||
<p className="text-sm font-medium">Ahmet Hakan rezervasyon yaptı</p>
|
<p className="text-sm font-medium">{res.customers?.full_name} rezervasyon oluşturdu</p>
|
||||||
<p className="text-xs text-muted-foreground">2 dakika önce</p>
|
<p className="text-xs text-muted-foreground">
|
||||||
|
{format(new Date(res.created_at), 'd MMM HH:mm', { locale: tr })}
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
|
{recentReservations?.length === 0 && (
|
||||||
|
<p className="text-sm text-muted-foreground text-center py-4">Henüz işlem yok.</p>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|||||||
Reference in New Issue
Block a user