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) => (
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ))}
+
+ ))}
+
+
+ );
+}