feat: Implement dashboard navigation, payments list, and deployment docs
This commit is contained in:
@@ -6,6 +6,7 @@ import {
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableFooter,
|
||||
TableHead,
|
||||
TableHeader,
|
||||
TableRow,
|
||||
@@ -83,6 +84,16 @@ export default async function ExpensesPage() {
|
||||
))
|
||||
)}
|
||||
</TableBody>
|
||||
<TableFooter>
|
||||
<TableRow>
|
||||
<TableCell colSpan={3} className="font-bold text-right">Toplam Gider:</TableCell>
|
||||
<TableCell className="text-right font-bold text-red-600 dark:text-red-400">
|
||||
- {new Intl.NumberFormat('tr-TR', { style: 'currency', currency: 'TRY' }).format(
|
||||
expenses?.reduce((sum, expense) => sum + Number(expense.amount), 0) || 0
|
||||
)}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
</TableFooter>
|
||||
</Table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -72,39 +72,43 @@ export default async function DashboardPage() {
|
||||
</div>
|
||||
|
||||
<div className="grid gap-6 md:grid-cols-2 lg:grid-cols-4">
|
||||
<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-2xl 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>
|
||||
<Link href="/dashboard/payments" className="block">
|
||||
<Card className="card-hover border-l-4 border-l-green-500 h-full cursor-pointer hover:bg-muted/5 transition-colors">
|
||||
<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-2xl 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>
|
||||
</Link>
|
||||
|
||||
<Card className="card-hover border-l-4 border-l-red-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 Gider
|
||||
</CardTitle>
|
||||
<div className="h-8 w-8 rounded-full bg-red-100 dark:bg-red-900/20 flex items-center justify-center">
|
||||
<TrendingUp className="h-4 w-4 text-red-600 dark:text-red-400 rotate-180" />
|
||||
</div>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="text-2xl font-bold mt-2">₺{totalExpenses.toLocaleString('tr-TR')}</div>
|
||||
<p className="text-xs text-muted-foreground mt-1 flex items-center">
|
||||
İşletme giderleri
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
<Link href="/dashboard/expenses" className="block">
|
||||
<Card className="card-hover border-l-4 border-l-red-500 h-full cursor-pointer hover:bg-muted/5 transition-colors">
|
||||
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
|
||||
<CardTitle className="text-sm font-medium text-muted-foreground">
|
||||
Toplam Gider
|
||||
</CardTitle>
|
||||
<div className="h-8 w-8 rounded-full bg-red-100 dark:bg-red-900/20 flex items-center justify-center">
|
||||
<TrendingUp className="h-4 w-4 text-red-600 dark:text-red-400 rotate-180" />
|
||||
</div>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="text-2xl font-bold mt-2">₺{totalExpenses.toLocaleString('tr-TR')}</div>
|
||||
<p className="text-xs text-muted-foreground mt-1 flex items-center">
|
||||
İşletme giderleri
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</Link>
|
||||
|
||||
<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">
|
||||
@@ -123,22 +127,24 @@ export default async function DashboardPage() {
|
||||
</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 Rezervasyon
|
||||
</CardTitle>
|
||||
<div className="h-8 w-8 rounded-full bg-purple-100 dark:bg-purple-900/20 flex items-center justify-center">
|
||||
<CalendarDays className="h-4 w-4 text-purple-600 dark:text-purple-400" />
|
||||
</div>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="text-2xl font-bold mt-2">{totalReservations || 0}</div>
|
||||
<p className="text-xs text-muted-foreground mt-1 flex items-center">
|
||||
Aktif rezervasyonlar
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
<Link href="/dashboard/reservations" className="block">
|
||||
<Card className="card-hover border-l-4 border-l-purple-500 h-full cursor-pointer hover:bg-muted/5 transition-colors">
|
||||
<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-purple-100 dark:bg-purple-900/20 flex items-center justify-center">
|
||||
<CalendarDays className="h-4 w-4 text-purple-600 dark:text-purple-400" />
|
||||
</div>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="text-2xl font-bold mt-2">{totalReservations || 0}</div>
|
||||
<p className="text-xs text-muted-foreground mt-1 flex items-center">
|
||||
Aktif rezervasyonlar
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
<div className="grid gap-6 md:grid-cols-2 lg:grid-cols-7">
|
||||
|
||||
131
src/app/dashboard/payments/page.tsx
Normal file
131
src/app/dashboard/payments/page.tsx
Normal file
@@ -0,0 +1,131 @@
|
||||
import { createClient } from "@/lib/supabase/server"
|
||||
import {
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableFooter,
|
||||
TableHead,
|
||||
TableHeader,
|
||||
TableRow,
|
||||
} from "@/components/ui/table"
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
|
||||
import { format } from "date-fns"
|
||||
import { tr } from "date-fns/locale"
|
||||
import { Badge } from "@/components/ui/badge"
|
||||
|
||||
export default async function PaymentsPage() {
|
||||
const supabase = await createClient()
|
||||
|
||||
const { data: payments } = await supabase
|
||||
.from('payments')
|
||||
.select(`
|
||||
id,
|
||||
amount,
|
||||
created_at,
|
||||
payment_method,
|
||||
payment_type,
|
||||
status,
|
||||
reservations (
|
||||
id,
|
||||
start_time,
|
||||
status,
|
||||
customers (full_name)
|
||||
)
|
||||
`)
|
||||
.order('created_at', { ascending: false })
|
||||
|
||||
const getStatusBadge = (status: string) => {
|
||||
switch (status) {
|
||||
case 'paid': return <Badge className="bg-green-600">Ödendi</Badge>
|
||||
case 'pending': return <Badge variant="secondary">Bekliyor</Badge>
|
||||
case 'refunded': return <Badge variant="destructive">İade</Badge>
|
||||
case 'confirmed': return <Badge className="bg-blue-600">Onaylandı</Badge>
|
||||
case 'cancelled': return <Badge variant="destructive">İptal</Badge>
|
||||
case 'completed': return <Badge variant="outline">Tamamlandı</Badge>
|
||||
default: return <Badge>{status}</Badge>
|
||||
}
|
||||
}
|
||||
|
||||
const getCustomerName = (reservation: any) => {
|
||||
if (!reservation) return "-"
|
||||
const res = Array.isArray(reservation) ? reservation[0] : reservation
|
||||
const customer = res?.customers
|
||||
const cust = Array.isArray(customer) ? customer[0] : customer
|
||||
return cust?.full_name || "-"
|
||||
}
|
||||
|
||||
const getReservationDate = (reservation: any) => {
|
||||
if (!reservation) return "-"
|
||||
const res = Array.isArray(reservation) ? reservation[0] : reservation
|
||||
return res?.start_time ? format(new Date(res.start_time), 'd MMM yyyy', { locale: tr }) : "-"
|
||||
}
|
||||
|
||||
const getReservationStatus = (reservation: any) => {
|
||||
if (!reservation) return "-"
|
||||
const res = Array.isArray(reservation) ? reservation[0] : reservation
|
||||
return res?.status || "-"
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<div>
|
||||
<h2 className="text-3xl font-bold tracking-tight">Ödemeler</h2>
|
||||
<p className="text-muted-foreground">Tüm alınan ödemelerin listesi.</p>
|
||||
</div>
|
||||
|
||||
<div className="rounded-md border bg-card">
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead>Tarih</TableHead>
|
||||
<TableHead>Müşteri</TableHead>
|
||||
<TableHead>Rez. Tarihi</TableHead>
|
||||
<TableHead>Tutar</TableHead>
|
||||
<TableHead>Ödeme Türü</TableHead>
|
||||
<TableHead>Ödeme Yöntemi</TableHead>
|
||||
<TableHead>Ödeme Durumu</TableHead>
|
||||
<TableHead>Rez. Durumu</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{payments?.length === 0 ? (
|
||||
<TableRow>
|
||||
<TableCell colSpan={8} className="text-center h-24 text-muted-foreground">
|
||||
Henüz ödeme kaydı yok.
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
) : (
|
||||
payments?.map((payment: any) => (
|
||||
<TableRow key={payment.id}>
|
||||
<TableCell className="font-medium">
|
||||
{format(new Date(payment.created_at), 'd MMMM yyyy HH:mm', { locale: tr })}
|
||||
</TableCell>
|
||||
<TableCell>{getCustomerName(payment.reservations)}</TableCell>
|
||||
<TableCell>{getReservationDate(payment.reservations)}</TableCell>
|
||||
<TableCell>₺{Number(payment.amount).toLocaleString('tr-TR')}</TableCell>
|
||||
<TableCell className="capitalize">
|
||||
{payment.payment_type === 'deposit' ? 'Kapora' :
|
||||
payment.payment_type === 'full' ? 'Tam Ödeme' :
|
||||
payment.payment_type === 'remaining' ? 'Kalan' : payment.payment_type}
|
||||
</TableCell>
|
||||
<TableCell className="capitalize">{payment.payment_method === 'credit_card' ? 'Kredi Kartı' : payment.payment_method === 'cash' ? 'Nakit' : 'Havale/EFT'}</TableCell>
|
||||
<TableCell>{getStatusBadge(payment.status)}</TableCell>
|
||||
<TableCell>{getStatusBadge(getReservationStatus(payment.reservations))}</TableCell>
|
||||
</TableRow>
|
||||
))
|
||||
)}
|
||||
</TableBody>
|
||||
<TableFooter>
|
||||
<TableRow>
|
||||
<TableCell colSpan={3} className="font-bold text-right">Toplam:</TableCell>
|
||||
<TableCell className="font-bold">
|
||||
₺{payments?.reduce((sum: number, p: any) => sum + Number(p.amount), 0).toLocaleString('tr-TR')}
|
||||
</TableCell>
|
||||
<TableCell colSpan={4}></TableCell>
|
||||
</TableRow>
|
||||
</TableFooter>
|
||||
</Table>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -29,7 +29,7 @@ export default async function ReservationsPage() {
|
||||
customers (full_name),
|
||||
packages (name, price)
|
||||
`)
|
||||
.order('start_time', { ascending: true })
|
||||
.order('created_at', { ascending: false })
|
||||
|
||||
const getStatusBadge = (status: string) => {
|
||||
switch (status) {
|
||||
|
||||
Reference in New Issue
Block a user