Skip to content

Commit 45446f6

Browse files
feat(web): improve analytics
1 parent f7f3090 commit 45446f6

File tree

12 files changed

+572
-78
lines changed

12 files changed

+572
-78
lines changed

apps/web/src/app/(home)/_components/stats-section.tsx

Lines changed: 20 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -14,38 +14,35 @@ import {
1414
} from "lucide-react";
1515
import Link from "next/link";
1616

17-
type EventRow = {
18-
_creationTime: number;
19-
};
17+
type DailyStats = { date: string; count: number };
2018

21-
function computeStats(events: EventRow[] | undefined) {
22-
if (!events || events.length === 0) {
19+
function computeStats(
20+
dailyStats: DailyStats[] | undefined,
21+
lastEventTime: number | undefined,
22+
) {
23+
if (!dailyStats || dailyStats.length === 0) {
2324
return { totalProjects: 0, avgProjectsPerDay: "0", lastUpdated: null };
2425
}
2526

26-
const byDay = new Set<string>();
27-
let maxTime = 0;
28-
29-
for (const ev of events) {
30-
const day = new Date(ev._creationTime).toISOString().slice(0, 10);
31-
byDay.add(day);
32-
if (ev._creationTime > maxTime) maxTime = ev._creationTime;
33-
}
34-
35-
const totalProjects = events.length;
27+
const totalProjects = dailyStats.reduce((sum, d) => sum + d.count, 0);
3628
const avgProjectsPerDay =
37-
byDay.size === 0 ? "0" : (totalProjects / byDay.size).toFixed(2);
38-
const lastUpdated = new Date(maxTime).toLocaleDateString("en-US", {
39-
month: "short",
40-
day: "numeric",
41-
year: "numeric",
42-
});
29+
dailyStats.length === 0
30+
? "0"
31+
: (totalProjects / dailyStats.length).toFixed(2);
32+
const lastUpdated = lastEventTime
33+
? new Date(lastEventTime).toLocaleDateString("en-US", {
34+
month: "short",
35+
day: "numeric",
36+
year: "numeric",
37+
})
38+
: null;
4339

4440
return { totalProjects, avgProjectsPerDay, lastUpdated };
4541
}
4642

4743
export default function StatsSection() {
48-
const events = useQuery(api.analytics.getAllEvents, { range: "30d" });
44+
const dailyStats = useQuery(api.analytics.getDailyStats, { days: 30 });
45+
const stats = useQuery(api.analytics.getStats, {});
4946
const githubRepo = useQuery(api.stats.getGithubRepo, {
5047
name: "AmanVarshney01/create-better-t-stack",
5148
});
@@ -54,7 +51,7 @@ export default function StatsSection() {
5451
});
5552

5653
const liveNpmDownloadCount = useNpmDownloadCounter(npmPackages);
57-
const analyticsData = computeStats(events as EventRow[] | undefined);
54+
const analyticsData = computeStats(dailyStats, stats?.lastEventTime);
5855

5956
return (
6057
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3">

apps/web/src/app/(home)/analytics/_components/analytics-header.tsx

Lines changed: 0 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
import { format } from "date-fns";
22
import { Terminal } from "lucide-react";
3-
import Image from "next/image";
43
import Link from "next/link";
5-
import discordIcon from "@/public/icon/discord.svg";
64

75
export function AnalyticsHeader({
86
totalProjects,
@@ -91,32 +89,6 @@ export function AnalyticsHeader({
9189
</span>
9290
</div>
9391
</div>
94-
95-
<Link
96-
href="https://discord.gg/ZYsbjpDaM5"
97-
target="_blank"
98-
rel="noopener noreferrer"
99-
className="block rounded rounded-t-none border border-border border-t-0 transition-colors hover:bg-muted/5"
100-
>
101-
<div className="flex items-center justify-between p-3">
102-
<div className="flex items-center gap-3">
103-
<Image
104-
src={discordIcon}
105-
alt="Discord"
106-
className="h-4 w-4 invert-0 dark:invert"
107-
/>
108-
<div>
109-
<span className="font-semibold text-sm">Join Discord</span>
110-
<p className="text-muted-foreground text-xs">
111-
Get live project creation notifications
112-
</p>
113-
</div>
114-
</div>
115-
<div className="rounded border border-border bg-primary/10 px-2 py-1 font-semibold text-primary text-xs">
116-
JOIN
117-
</div>
118-
</div>
119-
</Link>
12092
</div>
12193
);
12294
}

apps/web/src/app/(home)/analytics/_components/analytics-page.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ export default function AnalyticsPage({
2525
};
2626
}) {
2727
return (
28-
<div className="mx-auto min-h-svh max-w-[1280px]">
28+
<div className="mx-auto min-h-svh">
2929
<div className="container mx-auto space-y-10 px-4 py-8 pt-16">
3030
<AnalyticsHeader
3131
totalProjects={data.totalProjects}

apps/web/src/app/(home)/analytics/_components/metrics-cards.tsx

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
"use client";
2+
3+
import NumberFlow from "@number-flow/react";
14
import {
25
Code2,
36
Database,
@@ -16,6 +19,7 @@ type MetricCardProps = {
1619
subtitle: string;
1720
icon: React.ReactNode;
1821
highlight?: boolean;
22+
animate?: boolean;
1923
};
2024

2125
function MetricCard({
@@ -24,6 +28,7 @@ function MetricCard({
2428
subtitle,
2529
icon,
2630
highlight,
31+
animate,
2732
}: MetricCardProps) {
2833
return (
2934
<div className="rounded border border-border">
@@ -36,11 +41,21 @@ function MetricCard({
3641
</div>
3742
</div>
3843
<div className="p-4">
39-
<div
40-
className={`truncate font-bold text-xl ${highlight ? "text-primary" : "text-accent"}`}
41-
>
42-
{value}
43-
</div>
44+
{animate && typeof value === "number" ? (
45+
<NumberFlow
46+
value={value}
47+
className={`truncate font-bold text-xl ${highlight ? "text-primary" : "text-accent"}`}
48+
transformTiming={{ duration: 800, easing: "ease-out" }}
49+
willChange
50+
isolate
51+
/>
52+
) : (
53+
<div
54+
className={`truncate font-bold text-xl ${highlight ? "text-primary" : "text-accent"}`}
55+
>
56+
{typeof value === "number" ? value.toLocaleString() : value}
57+
</div>
58+
)}
4459
<p className="mt-1 text-muted-foreground text-xs">{subtitle}</p>
4560
</div>
4661
</div>
@@ -60,17 +75,19 @@ export function MetricsCards({ data }: { data: AggregatedAnalyticsData }) {
6075
<div className="grid gap-4 sm:grid-cols-2 lg:grid-cols-4">
6176
<MetricCard
6277
title="TOTAL_PROJECTS"
63-
value={totalProjects.toLocaleString()}
78+
value={totalProjects}
6479
subtitle="Projects created with CLI"
6580
icon={<Terminal className="h-4 w-4" />}
6681
highlight
82+
animate
6783
/>
6884
<MetricCard
6985
title="AVG_PER_DAY"
70-
value={avgProjectsPerDay.toFixed(1)}
86+
value={Number(avgProjectsPerDay.toFixed(1))}
7187
subtitle="Average daily creations"
7288
icon={<TrendingUp className="h-4 w-4" />}
7389
highlight
90+
animate
7491
/>
7592
<MetricCard
7693
title="TOP_FRONTEND"

0 commit comments

Comments
 (0)