From b189a1965139d2a4721274c68a31eee925b29b9a Mon Sep 17 00:00:00 2001 From: Kenan KARAER Date: Sun, 7 Dec 2025 19:05:59 +0300 Subject: [PATCH] feat: Implement dashboard navigation, payments list, and deployment docs --- COOLIFY.md | 51 +++++++++ src/app/dashboard/expenses/page.tsx | 11 ++ src/app/dashboard/page.tsx | 102 +++++++++--------- src/app/dashboard/payments/page.tsx | 131 ++++++++++++++++++++++++ src/app/dashboard/reservations/page.tsx | 2 +- 5 files changed, 248 insertions(+), 49 deletions(-) create mode 100644 COOLIFY.md create mode 100644 src/app/dashboard/payments/page.tsx diff --git a/COOLIFY.md b/COOLIFY.md new file mode 100644 index 0000000..50b4bd6 --- /dev/null +++ b/COOLIFY.md @@ -0,0 +1,51 @@ +# Coolify ile Düğün Salonu Uygulaması Yayınlama Rehberi + +Bu proje **Next.js** tabanlıdır ve Coolify üzerinde **Nixpacks** kullanılarak kolayca yayınlanabilir. + +## 1. Hazırlık +Projenizin kodlarının GitHub, GitLab veya Bitbucket üzerinde güncel olduğundan emin olun. + +## 2. Coolify Üzerinde Proje Oluşturma +1. Coolify panelinize giriş yapın. +2. **Projects** (Projeler) sekmesine gidin ve bir proje seçin (veya yeni oluşturun). +3. **New** -> **Public Repository** (veya Private Repository) seçeneğini tıklayın. +4. Git deposu bağlantısını yapıştırın (Örn: `https://github.com/kullanici/dugunsalonu`). +5. **Check Repository** diyerek ilerleyin. + +## 3. Yapılandırma (Configuration) + +Coolify genellikle Next.js projesini otomatik algılar, ancak şu ayarları kontrol edin: + +* **Build Pack**: `Nixpacks` (Önerilen) +* **Install Command**: `npm install` +* **Build Command**: `npm run build` +* **Start Command**: `npm run start` +* **Port**: `3000` + +## 4. Ortam Değişkenleri (Environment Variables) +Uygulamanın çalışması için `.env.local` dosyasındaki değerleri Coolify'a eklemeniz gerekir. + +1. Coolify'da projenizin **Environment Variables** (Secrets) sekmesine gidin. +2. Aşağıdaki anahtarları ve değerlerini ekleyin: + +```env +NEXT_PUBLIC_SUPABASE_URL=... (Supabase URL'iniz) +NEXT_PUBLIC_SUPABASE_ANON_KEY=... (Supabase Anon Key'iniz) +``` + +> **Not:** Eğer varsa diğer `.env` değişkenlerinizi de eklemeyi unutmayın. + +## 5. Domain Ayarları +1. **General** sekmesinde **Domains** bölümüne gidin. +2. Uygulamanızın çalışacağı alanı adını (URL) girin. (Örn: `https://panel.dugunsalonu.com`) +3. Domain sağlayıcınızdan (Cloudflare, GoDaddy vb.) `A` kaydını Coolify sunucunuzun IP adresine yönlendirin. + +## 6. Dağıtım (Deploy) +1. Sağ üstteki **Deploy** butonuna basın. +2. **Deployments** sekmesinden sürecin loglarını takip edebilirsiniz. +3. "Build Success" mesajını gördüğünüzde uygulamanız yayında olacaktır. + +## Olası Sorunlar ve Çözümler +* **Build Hatası:** Logları inceleyin. Genellikle bağımlılık (dependency) sorunları olabilir. `package-lock.json` dosyasının git reponuzda olduğundan emin olun. +* **Resimler Görünmüyor:** `next.config.ts` dosyasındaki `images.remotePatterns` ayarının Supabase URL'nizi kapsadığından emin olun. (Bu projede `apiilker.edoysoft.com` zaten ekli). +* **Sayfa Yenileyince 404:** Next.js SSR (Server Side Rendering) kullandığı için bu sorun genellikle yaşanmaz ancak Dockerfile kullanıyorsanız doğru yapılandırıldığından emin olun. Nixpacks bu ayarları otomatik yapar. diff --git a/src/app/dashboard/expenses/page.tsx b/src/app/dashboard/expenses/page.tsx index a37885c..c2b279d 100644 --- a/src/app/dashboard/expenses/page.tsx +++ b/src/app/dashboard/expenses/page.tsx @@ -6,6 +6,7 @@ import { Table, TableBody, TableCell, + TableFooter, TableHead, TableHeader, TableRow, @@ -83,6 +84,16 @@ export default async function ExpensesPage() { )) )} + + + Toplam Gider: + + - {new Intl.NumberFormat('tr-TR', { style: 'currency', currency: 'TRY' }).format( + expenses?.reduce((sum, expense) => sum + Number(expense.amount), 0) || 0 + )} + + + diff --git a/src/app/dashboard/page.tsx b/src/app/dashboard/page.tsx index c7404ec..7598b4e 100644 --- a/src/app/dashboard/page.tsx +++ b/src/app/dashboard/page.tsx @@ -72,39 +72,43 @@ export default async function DashboardPage() {
- - - - Toplam Gelir - -
- -
-
- -
₺{totalRevenue.toLocaleString('tr-TR')}
-

- Tahsil edilen ödemeler -

-
-
+ + + + + Toplam Gelir + +
+ +
+
+ +
₺{totalRevenue.toLocaleString('tr-TR')}
+

+ Tahsil edilen ödemeler +

+
+
+ - - - - Toplam Gider - -
- -
-
- -
₺{totalExpenses.toLocaleString('tr-TR')}
-

- İşletme giderleri -

-
-
+ + + + + Toplam Gider + +
+ +
+
+ +
₺{totalExpenses.toLocaleString('tr-TR')}
+

+ İşletme giderleri +

+
+
+ @@ -123,22 +127,24 @@ export default async function DashboardPage() { - - - - Toplam Rezervasyon - -
- -
-
- -
{totalReservations || 0}
-

- Aktif rezervasyonlar -

-
-
+ + + + + Toplam Rezervasyon + +
+ +
+
+ +
{totalReservations || 0}
+

+ Aktif rezervasyonlar +

+
+
+
diff --git a/src/app/dashboard/payments/page.tsx b/src/app/dashboard/payments/page.tsx new file mode 100644 index 0000000..9ec8784 --- /dev/null +++ b/src/app/dashboard/payments/page.tsx @@ -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 Ödendi + case 'pending': return Bekliyor + case 'refunded': return İade + case 'confirmed': return Onaylandı + case 'cancelled': return İptal + case 'completed': return Tamamlandı + default: return {status} + } + } + + 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 ( +
+
+

Ödemeler

+

Tüm alınan ödemelerin listesi.

+
+ +
+ + + + Tarih + Müşteri + Rez. Tarihi + Tutar + Ödeme Türü + Ödeme Yöntemi + Ödeme Durumu + Rez. Durumu + + + + {payments?.length === 0 ? ( + + + Henüz ödeme kaydı yok. + + + ) : ( + payments?.map((payment: any) => ( + + + {format(new Date(payment.created_at), 'd MMMM yyyy HH:mm', { locale: tr })} + + {getCustomerName(payment.reservations)} + {getReservationDate(payment.reservations)} + ₺{Number(payment.amount).toLocaleString('tr-TR')} + + {payment.payment_type === 'deposit' ? 'Kapora' : + payment.payment_type === 'full' ? 'Tam Ödeme' : + payment.payment_type === 'remaining' ? 'Kalan' : payment.payment_type} + + {payment.payment_method === 'credit_card' ? 'Kredi Kartı' : payment.payment_method === 'cash' ? 'Nakit' : 'Havale/EFT'} + {getStatusBadge(payment.status)} + {getStatusBadge(getReservationStatus(payment.reservations))} + + )) + )} + + + + Toplam: + + ₺{payments?.reduce((sum: number, p: any) => sum + Number(p.amount), 0).toLocaleString('tr-TR')} + + + + +
+
+
+ ) +} diff --git a/src/app/dashboard/reservations/page.tsx b/src/app/dashboard/reservations/page.tsx index 8c099e1..5185b22 100644 --- a/src/app/dashboard/reservations/page.tsx +++ b/src/app/dashboard/reservations/page.tsx @@ -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) {