Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(saas): Admin Dashboard #20

Merged
merged 12 commits into from
May 3, 2024
Prev Previous commit
Next Next commit
feat(saas): Admin dynamic users analytics data
  • Loading branch information
alifarooq9 committed May 1, 2024
commit f6c27cfa53d3a5090ab1fdfee4bb92b6c5b706fb
Original file line number Diff line number Diff line change
@@ -1,25 +1,37 @@
"use client";

import { LineChart } from "@/components/charts";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { formatDate, thousandToK } from "@/lib/utils";
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from "@/components/ui/card";
import { thousandToK } from "@/lib/utils";

type UsersChartProps = {
data: unknown[];
data: {
Date: string;
Users: number;
}[];
};

export function UsersChart({ data }: UsersChartProps) {
return (
<Card>
<CardHeader>
<CardTitle>Users Analytics</CardTitle>
<CardDescription>
Count of users joined each month for last 6 months
</CardDescription>
</CardHeader>
<CardContent>
<LineChart
data={data}
xAxisDataKey="Date"
yAxisDataKey="Users"
lineDataKeys={["Users", "Active Users"]}
lineDataKeys={["Users"]}
lineProps={[
{ stroke: "hsl(var(--primary))" },
{ stroke: "green" },
@@ -33,9 +45,6 @@ export function UsersChart({ data }: UsersChartProps) {
}
},
}}
xAxisProps={{
tickFormatter: formatDate,
}}
/>
</CardContent>
</Card>
39 changes: 13 additions & 26 deletions starterkits/saas/src/app/(app)/admin/dashboard/page.tsx
Original file line number Diff line number Diff line change
@@ -5,28 +5,15 @@ import { adminDashConfig } from "@/app/(app)/admin/dashboard/_constants/page-con
import { buttonVariants } from "@/components/ui/button";
import { siteUrls } from "@/config/urls";
import { cn } from "@/lib/utils";
import { formatDate } from "@/lib/utils";
import { getUsersCount } from "@/server/actions/user/queries";
import { DollarSignIcon, Users2Icon } from "lucide-react";
import Link from "next/link";

const data = [
{ Date: formatDate("2023-01-01"), Users: 1000, "Active Users": 100 },
{ Date: formatDate("2023-02-01"), Users: 1040, "Active Users": 120 },
{ Date: formatDate("2023-03-01"), Users: 1190, "Active Users": 140 },
{ Date: formatDate("2023-04-01"), Users: 1340, "Active Users": 160 },
{ Date: formatDate("2023-05-01"), Users: 1390, "Active Users": 180 },
{ Date: formatDate("2023-06-01"), Users: 1440, "Active Users": 200 },
{ Date: formatDate("2023-07-01"), Users: 1490, "Active Users": 220 },
{ Date: formatDate("2023-08-01"), Users: 1540, "Active Users": 240 },
{ Date: formatDate("2023-09-01"), Users: 1890, "Active Users": 260 },
{ Date: formatDate("2023-10-01"), Users: 2040, "Active Users": 280 },
{ Date: formatDate("2023-11-01"), Users: 4000, "Active Users": 300 },
{ Date: formatDate("2023-12-01"), Users: 10040, "Active Users": 320 },
{ Date: formatDate("2024-01-01"), Users: 19090, "Active Users": 340 },
{ Date: formatDate("2024-02-01"), Users: 25040, "Active Users": 360 },
];

export default async function AdminDashPage() {
const usersCountData = await getUsersCount();

const usersChartData = usersCountData.usersCountByMonth;

return (
<AppPageShell
title={adminDashConfig.title}
@@ -52,29 +39,29 @@ export default async function AdminDashPage() {

<div className="grid grid-cols-3 gap-4">
<StatsCard
title="Total Users"
value="1000"
title="Users"
value={String(usersCountData.totalCount)}
Icon={Users2Icon}
subText="+20.1% from last month"
subText="Total users joined"
/>

<StatsCard
title="Total Revenue"
title="Revenue"
value="$10,000"
Icon={DollarSignIcon}
subText="+20.1% from last month"
subText="Total revenue generated"
/>

<StatsCard
title="Total Subscriptions"
title="Subscriptions"
value="100"
Icon={DollarSignIcon}
subText="+20.1% from last month"
subText="Total subscriptions made"
/>
</div>

<div className="grid grid-cols-2 gap-4">
<UsersChart data={data} />
<UsersChart data={usersChartData} />
</div>
</div>
</AppPageShell>
2 changes: 1 addition & 1 deletion starterkits/saas/src/lib/utils.ts
Original file line number Diff line number Diff line change
@@ -25,6 +25,6 @@ export function thousandToK(value: number) {
return value / 1000;
}

export function formatDate(date: string) {
export function formatDate(date: string | number | Date) {
return format(new Date(date), "PP");
}
45 changes: 43 additions & 2 deletions starterkits/saas/src/server/actions/user/queries.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
"use server";
import "server-only";

import { db } from "@/server/db";
import { users } from "@/server/db/schema";
import { adminProcedure } from "@/server/procedures";
import { asc, count, desc, ilike, inArray, or } from "drizzle-orm";
import { asc, count, desc, gt, ilike, inArray, or } from "drizzle-orm";
import { unstable_noStore as noStore } from "next/cache";
import { z } from "zod";
import { eachMonthOfInterval, format, startOfMonth, subMonths } from "date-fns";

/**
* Get paginated users
@@ -94,3 +95,43 @@ export async function getPaginatedUsersQuery(

return { data, pageCount, total };
}

export async function getUsersCount() {
await adminProcedure();

const dateBeforeMonths = subMonths(new Date(), 6);

const startDateOfTheMonth = startOfMonth(dateBeforeMonths);

const { total, data } = await db.transaction(async (tx) => {
const data = await db.query.users.findMany({
where: gt(users.createdAt, startDateOfTheMonth),
});

const total = await tx
.select({ count: count() })
.from(users)
.execute()
.then((res) => res[0]?.count ?? 0);

return { total, data };
});

const months = eachMonthOfInterval({
start: startDateOfTheMonth,
end: new Date(),
});

const usersCountByMonth = months.map((month) => {
const monthStr = format(month, "MMM-yyy");
const count = data.filter(
(user) => format(new Date(user.createdAt), "MMM-yyy") === monthStr,
).length;
return { Date: monthStr, Users: count };
});

return {
usersCountByMonth,
totalCount: total,
};
}
Loading