From 5486a19d572cfefb16e7d9a9089bf8794a9f9733 Mon Sep 17 00:00:00 2001 From: Poyraz <78071274+poyrazavsever@users.noreply.github.com> Date: Tue, 16 Jun 2026 18:36:54 +0300 Subject: [PATCH] feat: implement data fetching logic and skeleton loading states for dashboard and analytics modules --- app/(dashboard)/analytics/loading.tsx | 30 ++++++ app/(dashboard)/analytics/page.tsx | 27 +++--- app/(dashboard)/clients/loading.tsx | 68 +++++++++++++ app/(dashboard)/loading.tsx | 70 ++++++++++++++ app/(dashboard)/page.tsx | 66 ++++++------- app/(dashboard)/projects/[id]/loading.tsx | 111 ++++++++++++++++++++++ app/(dashboard)/projects/loading.tsx | 89 +++++++++++++++++ app/(dashboard)/tasks/loading.tsx | 56 +++++++++++ 8 files changed, 470 insertions(+), 47 deletions(-) create mode 100644 app/(dashboard)/analytics/loading.tsx create mode 100644 app/(dashboard)/clients/loading.tsx create mode 100644 app/(dashboard)/loading.tsx create mode 100644 app/(dashboard)/projects/[id]/loading.tsx create mode 100644 app/(dashboard)/projects/loading.tsx create mode 100644 app/(dashboard)/tasks/loading.tsx diff --git a/app/(dashboard)/analytics/loading.tsx b/app/(dashboard)/analytics/loading.tsx new file mode 100644 index 0000000..fba49f2 --- /dev/null +++ b/app/(dashboard)/analytics/loading.tsx @@ -0,0 +1,30 @@ +import { Skeleton } from "@/components/ui/skeleton"; +import { Card, CardContent } from "poyraz-ui/atoms"; + +export default function AnalyticsLoading() { + return ( +
+ {/* Header */} +
+
+ + + +
+ +
+ + {/* Charts */} +
+ {Array.from({ length: 2 }).map((_, i) => ( + + + + + + + ))} +
+
+ ); +} diff --git a/app/(dashboard)/analytics/page.tsx b/app/(dashboard)/analytics/page.tsx index 1f5a40d..4c2cbda 100644 --- a/app/(dashboard)/analytics/page.tsx +++ b/app/(dashboard)/analytics/page.tsx @@ -14,20 +14,19 @@ export default async function AnalyticsPage() { redirect("/login"); } - // 1. Fetch tasks - const { data: tasks } = await supabase - .from("tasks") - .select("*"); - - // 2. Fetch projects - const { data: projects } = await supabase - .from("projects") - .select("*"); - - // 3. Fetch finances - const { data: finances } = await supabase - .from("finance_transactions") - .select("*"); + // Fetch all analytics data in parallel with only needed columns + const [{ data: tasks }, { data: projects }, { data: finances }] = + await Promise.all([ + supabase + .from("tasks") + .select("id, status, created_at, due_at"), + supabase + .from("projects") + .select("id, name"), + supabase + .from("finance_transactions") + .select("id, type, amount, transaction_date, project_id"), + ]); const analyticsData = { tasks: tasks || [], diff --git a/app/(dashboard)/clients/loading.tsx b/app/(dashboard)/clients/loading.tsx new file mode 100644 index 0000000..c0a1494 --- /dev/null +++ b/app/(dashboard)/clients/loading.tsx @@ -0,0 +1,68 @@ +import { Skeleton } from "@/components/ui/skeleton"; +import { Card, CardContent } from "poyraz-ui/atoms"; + +export default function ClientsLoading() { + return ( +
+ {/* Header */} +
+
+ + + +
+ +
+ + {/* Stat Cards */} +
+ {Array.from({ length: 4 }).map((_, i) => ( + + +
+
+ + +
+ +
+
+
+ ))} +
+ + {/* Tabs + Search */} +
+ + +
+ + {/* Pipeline Kanban Columns */} +
+ {Array.from({ length: 5 }).map((_, colIdx) => ( +
+
+
+ + +
+ +
+ {Array.from({ length: colIdx < 2 ? 3 : colIdx < 4 ? 2 : 1 }).map((_, cardIdx) => ( + + +
+ + +
+ + +
+
+ ))} +
+ ))} +
+
+ ); +} diff --git a/app/(dashboard)/loading.tsx b/app/(dashboard)/loading.tsx new file mode 100644 index 0000000..ce5e869 --- /dev/null +++ b/app/(dashboard)/loading.tsx @@ -0,0 +1,70 @@ +import { Skeleton } from "@/components/ui/skeleton"; +import { Card, CardContent } from "poyraz-ui/atoms"; + +export default function DashboardLoading() { + return ( +
+ {/* Header */} +
+
+ + + +
+ +
+ + {/* KPI Cards */} +
+ {Array.from({ length: 4 }).map((_, i) => ( + + +
+ + +
+ +
+
+ ))} +
+ + {/* Charts */} +
+ {Array.from({ length: 2 }).map((_, i) => ( + + + + + + + ))} +
+ + {/* Recent Items */} +
+ {Array.from({ length: 2 }).map((_, i) => ( + + +
+ + +
+
+ {Array.from({ length: 4 }).map((_, j) => ( +
+ +
+ + +
+
+ ))} +
+
+
+ ))} +
+
+ ); +} diff --git a/app/(dashboard)/page.tsx b/app/(dashboard)/page.tsx index bb91b43..270ad51 100644 --- a/app/(dashboard)/page.tsx +++ b/app/(dashboard)/page.tsx @@ -22,39 +22,39 @@ export default async function DashboardPage() { const todayStr = today.toISOString(); const thirtyDaysAgoStr = thirtyDaysAgo.toISOString(); - // 1. Fetch tasks - const { data: tasks } = await supabase - .from("tasks") - .select("*") - .or(`due_at.gte.${thirtyDaysAgoStr},created_at.gte.${thirtyDaysAgoStr}`) - .order("due_at", { ascending: true }); - - // 2. Fetch projects - const { data: projects } = await supabase - .from("projects") - .select("*") - .order("created_at", { ascending: false }); - - // 3. Fetch finances - const { data: finances } = await supabase - .from("finance_transactions") - .select("*") - .gte("transaction_date", thirtyDaysAgoStr) - .order("transaction_date", { ascending: true }); - - // 4. Fetch daily logs - const { data: logs } = await supabase - .from("daily_logs") - .select("*") - .gte("log_date", thirtyDaysAgoStr) - .order("log_date", { ascending: true }); - - // 5. Fetch recent clients - const { data: clients } = await supabase - .from("clients") - .select("*") - .order("created_at", { ascending: false }) - .limit(5); + // Fetch all dashboard data in parallel with only needed columns + const [ + { data: tasks }, + { data: projects }, + { data: finances }, + { data: logs }, + { data: clients }, + ] = await Promise.all([ + supabase + .from("tasks") + .select("id, status, created_at, updated_at, due_at") + .or(`due_at.gte.${thirtyDaysAgoStr},created_at.gte.${thirtyDaysAgoStr}`) + .order("due_at", { ascending: true }), + supabase + .from("projects") + .select("id, status, name, created_at") + .order("created_at", { ascending: false }), + supabase + .from("finance_transactions") + .select("id, type, amount, transaction_date") + .gte("transaction_date", thirtyDaysAgoStr) + .order("transaction_date", { ascending: true }), + supabase + .from("daily_logs") + .select("id, log_date, mood_score, energy_score") + .gte("log_date", thirtyDaysAgoStr) + .order("log_date", { ascending: true }), + supabase + .from("clients") + .select("id, name, company_name, created_at") + .order("created_at", { ascending: false }) + .limit(5), + ]); const dashboardData = { tasks: tasks || [], diff --git a/app/(dashboard)/projects/[id]/loading.tsx b/app/(dashboard)/projects/[id]/loading.tsx new file mode 100644 index 0000000..af52d0f --- /dev/null +++ b/app/(dashboard)/projects/[id]/loading.tsx @@ -0,0 +1,111 @@ +import { Skeleton } from "@/components/ui/skeleton"; +import { Card, CardContent } from "poyraz-ui/atoms"; + +export default function ProjectDetailLoading() { + return ( +
+ {/* Back link + Header */} +
+ +
+
+ + +
+ + +
+
+
+ + +
+
+
+ + {/* Cover Image */} + + + {/* Tabs */} +
+ {Array.from({ length: 5 }).map((_, i) => ( + + ))} +
+ + {/* Tab Content: Overview-like skeleton */} +
+ {/* Main Content */} +
+ + + +
+ {Array.from({ length: 5 }).map((_, i) => ( +
+ + +
+ ))} +
+
+
+ + + + +
+ {Array.from({ length: 4 }).map((_, i) => ( + + + + + + + ))} +
+
+
+
+ + {/* Sidebar */} +
+ + + +
+
+ + +
+ +
+
+ {Array.from({ length: 5 }).map((_, i) => ( +
+ + +
+ ))} +
+
+
+ + + + +
+ {Array.from({ length: 3 }).map((_, i) => ( +
+ + +
+ ))} +
+
+
+
+
+
+ ); +} diff --git a/app/(dashboard)/projects/loading.tsx b/app/(dashboard)/projects/loading.tsx new file mode 100644 index 0000000..ea8399b --- /dev/null +++ b/app/(dashboard)/projects/loading.tsx @@ -0,0 +1,89 @@ +import { Skeleton } from "@/components/ui/skeleton"; +import { Card, CardContent } from "poyraz-ui/atoms"; + +export default function ProjectsLoading() { + return ( +
+ {/* Header */} +
+
+ + + +
+
+ + +
+
+ + {/* Stat Cards */} +
+ {Array.from({ length: 4 }).map((_, i) => ( + + +
+ + +
+ +
+
+ ))} +
+ + {/* Project List Card */} + + +
+
+ + +
+
+ + +
+
+ + {/* Project Cards Grid */} +
+ {Array.from({ length: 6 }).map((_, i) => ( + + + +
+
+ + +
+ +
+
+ + + +
+
+
+ + +
+ +
+
+ +
+ + +
+
+
+
+ ))} +
+
+
+
+ ); +} diff --git a/app/(dashboard)/tasks/loading.tsx b/app/(dashboard)/tasks/loading.tsx new file mode 100644 index 0000000..57fa88b --- /dev/null +++ b/app/(dashboard)/tasks/loading.tsx @@ -0,0 +1,56 @@ +import { Skeleton } from "@/components/ui/skeleton"; +import { Card, CardContent } from "poyraz-ui/atoms"; + +export default function TasksLoading() { + return ( +
+ {/* Header */} +
+
+ + + +
+
+ +
+
+ + {/* View toggle + filters */} +
+ +
+ + +
+
+ + {/* Kanban columns */} +
+ {Array.from({ length: 3 }).map((_, colIdx) => ( +
+
+ + +
+ {Array.from({ length: colIdx === 0 ? 4 : colIdx === 1 ? 3 : 2 }).map((_, cardIdx) => ( + + +
+ + +
+ +
+ + +
+
+
+ ))} +
+ ))} +
+
+ ); +}