From b04cefa06f406672ff9f39d94167bd2700a7007b Mon Sep 17 00:00:00 2001 From: Taken Date: Thu, 26 Jun 2025 13:13:23 +0200 Subject: [PATCH] Added main dashboard --- src/app/(admin)/dashboard/page.tsx | 37 ++++++++++++++++++++++- src/components/dashboard/stats-card.tsx | 25 ++++++++++++++++ src/lib/dashboard/stats.ts | 40 +++++++++++++++++++++++++ 3 files changed, 101 insertions(+), 1 deletion(-) create mode 100644 src/components/dashboard/stats-card.tsx create mode 100644 src/lib/dashboard/stats.ts diff --git a/src/app/(admin)/dashboard/page.tsx b/src/app/(admin)/dashboard/page.tsx index 19f15f5..749c9d6 100644 --- a/src/app/(admin)/dashboard/page.tsx +++ b/src/app/(admin)/dashboard/page.tsx @@ -1,4 +1,7 @@ +import { StatsCard } from "@/components/dashboard/stats-card" import { getSession } from "@/lib/auth/session" +import { getDashboardStats } from "@/lib/dashboard/stats" +import { LinkIcon, MousePointerClick, TrendingUp } from "lucide-react" export default async function Dashboard() { const { session, redirect } = await getSession() @@ -7,10 +10,42 @@ export default async function Dashboard() { redirect("/sign-in") } + const stats = await getDashboardStats() + + // Determine the most visited URL display value + const mostVisitedDisplay = stats.mostVisitedUrl + ? `${stats.mostVisitedUrl.title} (${stats.mostVisitedUrl.visitCount})` + : "No URLs" + + const mostVisitedDescription = stats.mostVisitedUrl + ? `/${stats.mostVisitedUrl.slug} - ${stats.mostVisitedUrl.visitCount} visits` + : "Create your first shortened URL" + return (

Dashboard

-

Welcome to your dashboard! Use the sidebar to navigate to different sections.

+

Welcome to your dashboard! Use the sidebar to navigate to different sections.

+ +
+ } + description="Total number of shortened links" + /> + } + description="Combined visits across all links" + /> + } + description={mostVisitedDescription} + /> +
) } diff --git a/src/components/dashboard/stats-card.tsx b/src/components/dashboard/stats-card.tsx new file mode 100644 index 0000000..20ea170 --- /dev/null +++ b/src/components/dashboard/stats-card.tsx @@ -0,0 +1,25 @@ +import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card" + +interface StatsCardProps { + title: string + value: number | string + icon?: React.ReactNode + description?: string +} + +export function StatsCard({ title, value, icon, description }: StatsCardProps) { + const displayValue = typeof value === "number" ? value.toLocaleString() : value + + return ( + + + {title} + {icon} + + +
{displayValue}
+ {description &&

{description}

} +
+
+ ) +} diff --git a/src/lib/dashboard/stats.ts b/src/lib/dashboard/stats.ts new file mode 100644 index 0000000..97c1971 --- /dev/null +++ b/src/lib/dashboard/stats.ts @@ -0,0 +1,40 @@ +import { count, desc, eq } from "drizzle-orm" +import { db } from "../drizzle/db" +import { urls, visits } from "../drizzle/schema" + +export async function getDashboardStats() { + try { + // Get count of shortened URLs + const [urlsCount] = await db.select({ count: count() }).from(urls) + + // Get count of total visits + const [visitsCount] = await db.select({ count: count() }).from(visits) + + // Get most visited URL + const mostVisitedUrl = await db + .select({ + id: urls.id, + title: urls.title, + slug: urls.slug, + visitCount: count(visits.id) + }) + .from(urls) + .leftJoin(visits, eq(urls.id, visits.urlId)) + .groupBy(urls.id, urls.title, urls.slug) + .orderBy(desc(count(visits.id))) + .limit(1) + + return { + totalUrls: urlsCount.count, + totalVisits: visitsCount.count, + mostVisitedUrl: mostVisitedUrl[0] || null + } + } catch (error) { + console.error("Failed to fetch dashboard stats:", error) + return { + totalUrls: 0, + totalVisits: 0, + mostVisitedUrl: null + } + } +}