From 1bc51ffd21e049db402721191cb351add3fd095c Mon Sep 17 00:00:00 2001 From: Poyraz Avsever Date: Mon, 8 Jun 2026 15:43:59 +0300 Subject: [PATCH] feat: add revisions and tasks pages to client portal with integrated sidebar navigation --- .../projects/[id]/portal-project-client.tsx | 116 +++++++++--------- app/portal/projects/page.tsx | 90 ++++++++++++++ app/portal/revisions/page.tsx | 106 +++++++++++++--- app/portal/tasks/page.tsx | 100 ++++++++++++--- config/portal-sidebar.ts | 4 +- 5 files changed, 328 insertions(+), 88 deletions(-) create mode 100644 app/portal/projects/page.tsx diff --git a/app/portal/projects/[id]/portal-project-client.tsx b/app/portal/projects/[id]/portal-project-client.tsx index 22e97d5..44ec8be 100644 --- a/app/portal/projects/[id]/portal-project-client.tsx +++ b/app/portal/projects/[id]/portal-project-client.tsx @@ -157,70 +157,68 @@ export function PortalProjectClient({ project, sections, tasks, revisions, clien - - - -

Proje Planı & Aşamalar

- {sections.length === 0 ? ( -

Henüz bir plan yüklenmemiş.

- ) : ( -
- {sections.map((section: any) => ( -
-
-

{section.title}

- - {section.type === 'milestone' ? 'Aşama' : section.type === 'deliverable' ? 'Teslimat' : 'Not'} - -
-

{section.content}

+ + {sections.length === 0 ? ( +
+ Henüz bir plan yüklenmemiş. +
+ ) : ( +
+ {sections.map((section: any) => ( + + +
+

{section.title}

+ + {section.type === 'milestone' ? 'Aşama' : section.type === 'deliverable' ? 'Teslimat' : 'Not'} +
- ))} -
- )} - - +

{section.content}

+ + + ))} +
+ )} - - - -
-

Revizyon Talepleriniz

-
Kalan Hak: {project.revision_quota !== null ? project.revision_quota : 'Sınırsız'}
-
- - {revisions.length === 0 ? ( -
- -

Henüz bir revizyon talebi oluşturmadınız.

- -
- ) : ( -
- {revisions.map((rev: any) => ( -
-
-
- - {format(new Date(rev.created_at), "d MMM yyyy, HH:mm", { locale: tr })} -
- - {rev.status === 'pending' ? 'Bekliyor' : - rev.status === 'in_progress' ? 'İşleniyor' : - rev.status === 'completed' ? 'Tamamlandı' : 'Reddedildi'} - + +
+
+ Kalan Hak: {project.revision_quota !== null ? project.revision_quota : 'Sınırsız'} +
+
+ + {revisions.length === 0 ? ( +
+ +

Henüz bir revizyon talebi oluşturmadınız.

+ +
+ ) : ( +
+ {revisions.map((rev: any) => ( + + +
+
+ + {format(new Date(rev.created_at), "d MMM yyyy, HH:mm", { locale: tr })}
-

{rev.description}

+ + {rev.status === 'pending' ? 'Bekliyor' : + rev.status === 'in_progress' ? 'İşleniyor' : + rev.status === 'completed' ? 'Tamamlandı' : 'Reddedildi'} +
- ))} -
- )} - - +

{rev.description}

+ + + ))} +
+ )}
diff --git a/app/portal/projects/page.tsx b/app/portal/projects/page.tsx new file mode 100644 index 0000000..fd37954 --- /dev/null +++ b/app/portal/projects/page.tsx @@ -0,0 +1,90 @@ +import { createClient } from "@/lib/supabase/server"; +import { Card, CardContent, Badge } from "poyraz-ui/atoms"; +import { FolderKanban, Clock } from "lucide-react"; +import Link from "next/link"; +import { format } from "date-fns"; +import { tr } from "date-fns/locale"; + +export default async function PortalProjectsPage() { + const supabase = await createClient(); + const { data: { user } } = await supabase.auth.getUser(); + + if (!user) return null; + + const { data: clientData } = await supabase + .from("clients") + .select("id") + .eq("client_auth_id", user.id) + .single(); + + if (!clientData) { + return ( +
+

Hesabınız Henüz Aktif Değil

+
+ ); + } + + const { data: projectsData } = await supabase + .from("projects") + .select("id, name, status, progress, due_date, budget_amount, currency") + .eq("client_id", clientData.id) + .order("created_at", { ascending: false }); + + const projects = projectsData || []; + + return ( +
+
+

Projeleriniz

+

Size atanan tüm projeleri buradan inceleyebilirsiniz.

+
+ +
+ {projects.length === 0 ? ( +
+ + Henüz size atanmış bir proje bulunmuyor. +
+ ) : ( + projects.map(project => ( + + + +
+
+

{project.name}

+ + {project.status} + +
+ + {project.due_date && ( +
+ + Son Teslim: {format(new Date(project.due_date), 'd MMM yyyy', { locale: tr })} +
+ )} +
+ +
+
+ İlerleme + %{project.progress} +
+
+
+
+
+ + + + )) + )} +
+
+ ); +} diff --git a/app/portal/revisions/page.tsx b/app/portal/revisions/page.tsx index 578e1e0..25f63d9 100644 --- a/app/portal/revisions/page.tsx +++ b/app/portal/revisions/page.tsx @@ -1,25 +1,103 @@ -import { MessageSquareDiff } from "lucide-react"; +import { createClient } from "@/lib/supabase/server"; +import { Card, CardContent, Badge } from "poyraz-ui/atoms"; +import { Clock, MessageSquare } from "lucide-react"; import Link from "next/link"; +import { format } from "date-fns"; +import { tr } from "date-fns/locale"; + +export default async function PortalRevisionsPage() { + const supabase = await createClient(); + const { data: { user } } = await supabase.auth.getUser(); + + if (!user) return null; + + const { data: clientData } = await supabase + .from("clients") + .select("id") + .eq("client_auth_id", user.id) + .single(); + + if (!clientData) { + return ( +
+

Hesabınız Henüz Aktif Değil

+
+ ); + } + + const { data: projectsData } = await supabase + .from("projects") + .select("id, name") + .eq("client_id", clientData.id); + + const projectIds = projectsData?.map(p => p.id) || []; + + let revisions = []; + if (projectIds.length > 0) { + const { data: revisionsData } = await supabase + .from("project_revisions") + .select("id, description, status, project_id, created_at") + .in("project_id", projectIds) + .order("created_at", { ascending: false }); + revisions = revisionsData || []; + } + + const getProjectName = (id: string) => projectsData?.find(p => p.id === id)?.name || "Bilinmeyen Proje"; -export default function PortalRevisionsPage() { return (

Revizyon Taleplerim

-

İlettiğiniz tüm revizyon taleplerinin durumunu buradan takip edebilirsiniz.

+

İlettiğiniz tüm revizyon taleplerinin güncel durumunu buradan takip edebilirsiniz.

-
- -

- Revizyon modülü yapım aşamasında -

-

- Yakında tüm revizyon taleplerinizi buradan yönetebileceksiniz. Şimdilik proje detay sayfasından revizyon talep edebilirsiniz. -

- - Dashboard'a dön - +
+ {revisions.length === 0 ? ( +
+ + Henüz bir revizyon talebinde bulunmadınız. +
+ ) : ( + revisions.map(rev => ( + + +
+
+
+ + {format(new Date(rev.created_at), "d MMM yyyy, HH:mm", { locale: tr })} +
+ + {rev.status === 'pending' ? 'Bekliyor' : + rev.status === 'in_progress' ? 'İşleniyor' : + rev.status === 'completed' ? 'Tamamlandı' : 'Reddedildi'} + +
+ +
+ Proje: + + {getProjectName(rev.project_id)} + +
+ +

+ {rev.description} +

+
+ +
+ + Projeye Git → + +
+
+
+ )) + )}
); diff --git a/app/portal/tasks/page.tsx b/app/portal/tasks/page.tsx index b6e167c..7e82c3a 100644 --- a/app/portal/tasks/page.tsx +++ b/app/portal/tasks/page.tsx @@ -1,25 +1,97 @@ -import { FolderKanban } from "lucide-react"; +import { createClient } from "@/lib/supabase/server"; +import { Card, CardContent, Badge } from "poyraz-ui/atoms"; +import { CheckCircle2, Clock, CalendarDays, KanbanSquare } from "lucide-react"; import Link from "next/link"; +import { format } from "date-fns"; +import { tr } from "date-fns/locale"; + +export default async function PortalTasksPage() { + const supabase = await createClient(); + const { data: { user } } = await supabase.auth.getUser(); + + if (!user) return null; + + const { data: clientData } = await supabase + .from("clients") + .select("id") + .eq("client_auth_id", user.id) + .single(); + + if (!clientData) { + return ( +
+

Hesabınız Henüz Aktif Değil

+
+ ); + } + + const { data: projectsData } = await supabase + .from("projects") + .select("id, name") + .eq("client_id", clientData.id); + + const projectIds = projectsData?.map(p => p.id) || []; + + let tasks = []; + if (projectIds.length > 0) { + const { data: tasksData } = await supabase + .from("tasks") + .select("id, title, status, project_id, created_at, date, priority") + .in("project_id", projectIds) + .eq("is_public_to_client", true) + .order("created_at", { ascending: false }); + tasks = tasksData || []; + } + + const getProjectName = (id: string) => projectsData?.find(p => p.id === id)?.name || "Bilinmeyen Proje"; -export default function PortalTasksPage() { return (

Yapılan Görevler

-

Projeniz kapsamında üzerinde çalışılan ve tamamlanan görevler.

+

Sizinle paylaşılan aktif ve tamamlanmış görevleri buradan takip edebilirsiniz.

-
- -

- Görev detayları yapım aşamasında -

-

- Yakında tüm görevlerin anlık durumlarını detaylıca buradan görebileceksiniz. Şimdilik proje detay sayfasından takip edebilirsiniz. -

- - Dashboard'a dön - +
+ {tasks.length === 0 ? ( +
+ + Henüz sizinle paylaşılan bir görev bulunmuyor. +
+ ) : ( + tasks.map(task => ( + + +
+
+

+ {task.title} +

+ + {task.status === 'todo' ? 'Bekliyor' : task.status === 'in_progress' ? 'İşleniyor' : 'Tamamlandı'} + +
+ +
+ {getProjectName(task.project_id)} +
+
+ +
+
+ + {task.date ? format(new Date(task.date), 'd MMM yyyy', { locale: tr }) : 'Tarih yok'} +
+ {task.status === 'completed' || task.status === 'done' ? ( + + ) : ( + + )} +
+
+
+ )) + )}
); diff --git a/config/portal-sidebar.ts b/config/portal-sidebar.ts index d409911..f21b77d 100644 --- a/config/portal-sidebar.ts +++ b/config/portal-sidebar.ts @@ -1,6 +1,7 @@ import { FolderKanban, Sparkles, + CheckSquare2, } from "lucide-react"; export type PortalSidebarNavItem = { @@ -25,7 +26,8 @@ export const portalSidebarData: PortalSidebarNavGroup[] = [ { title: "SÜREÇLER", items: [ - { title: "Yapılan Görevler", href: "/portal/tasks", icon: FolderKanban }, + { title: "Projeleriniz", href: "/portal/projects", icon: FolderKanban }, + { title: "Yapılan Görevler", href: "/portal/tasks", icon: CheckSquare2 }, { title: "Revizyon Talepleri", href: "/portal/revisions", icon: Sparkles }, ], }