Skip to content

Commit 1b02a89

Browse files
committed
[TOOL-3124] Dashboard: Show 8 top projects, Add show more button for full list
1 parent 9f79277 commit 1b02a89

File tree

2 files changed

+63
-21
lines changed

2 files changed

+63
-21
lines changed

apps/dashboard/src/app/team/[team_slug]/(team)/page.tsx

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,12 @@ import { getWalletConnections } from "@/api/analytics";
22
import { type Project, getProjects } from "@/api/projects";
33
import { getTeamBySlug } from "@/api/team";
44
import { Changelog } from "components/dashboard/Changelog";
5+
import { subDays } from "date-fns";
56
import { redirect } from "next/navigation";
6-
import { TeamProjectsPage } from "./~/projects/TeamProjectsPage";
7+
import {
8+
type ProjectWithAnalytics,
9+
TeamProjectsPage,
10+
} from "./~/projects/TeamProjectsPage";
711

812
export default async function Page(props: {
913
params: Promise<{ team_slug: string }>;
@@ -16,7 +20,7 @@ export default async function Page(props: {
1620
}
1721

1822
const projects = await getProjects(params.team_slug);
19-
const projectsWithTotalWallets = await getProjectsWithTotalWallets(projects);
23+
const projectsWithTotalWallets = await getProjectsWithAnalytics(projects);
2024

2125
return (
2226
<div className="container flex grow flex-col gap-12 py-8 lg:flex-row">
@@ -34,30 +38,35 @@ export default async function Page(props: {
3438
);
3539
}
3640

37-
async function getProjectsWithTotalWallets(
41+
async function getProjectsWithAnalytics(
3842
projects: Project[],
39-
): Promise<Array<Project & { totalConnections: number }>> {
43+
): Promise<Array<ProjectWithAnalytics>> {
4044
return Promise.all(
4145
projects.map(async (p) => {
4246
try {
47+
const today = new Date();
48+
const thirtyDaysAgo = subDays(today, 30);
49+
4350
const data = await getWalletConnections({
4451
clientId: p.publishableKey,
4552
period: "all",
53+
from: thirtyDaysAgo,
54+
to: today,
4655
});
4756

48-
let totalConnections = 0;
57+
let uniqueWalletsConnected = 0;
4958
for (const d of data) {
50-
totalConnections += d.totalConnections;
59+
uniqueWalletsConnected += d.uniqueWalletsConnected;
5160
}
5261

5362
return {
5463
...p,
55-
totalConnections,
64+
monthlyActiveUsers: uniqueWalletsConnected,
5665
};
5766
} catch {
5867
return {
5968
...p,
60-
totalConnections: 0,
69+
monthlyActiveUsers: 0,
6170
};
6271
}
6372
}),

apps/dashboard/src/app/team/[team_slug]/(team)/~/projects/TeamProjectsPage.tsx

Lines changed: 46 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import {
2323
SelectTrigger,
2424
} from "@/components/ui/select";
2525
import { useDashboardRouter } from "@/lib/DashboardRouter";
26+
import { cn } from "@/lib/utils";
2627
import { LazyCreateAPIKeyDialog } from "components/settings/ApiKeys/Create/LazyCreateAPIKeyDialog";
2728
import {
2829
ChevronDownIcon,
@@ -33,22 +34,25 @@ import {
3334
import Link from "next/link";
3435
import { useMemo, useState } from "react";
3536

36-
type SortById = "name" | "createdAt" | "totalConnections";
37+
type SortById = "name" | "createdAt" | "monthlyActiveUsers";
3738

38-
type ProjectWithTotalConnections = Project & { totalConnections: number };
39+
export type ProjectWithAnalytics = Project & {
40+
monthlyActiveUsers: number;
41+
};
3942

4043
export function TeamProjectsPage(props: {
41-
projects: ProjectWithTotalConnections[];
44+
projects: ProjectWithAnalytics[];
4245
team: Team;
4346
}) {
4447
const { projects } = props;
4548
const [searchTerm, setSearchTerm] = useState("");
46-
const [sortBy, setSortBy] = useState<SortById>("totalConnections");
49+
const [sortBy, setSortBy] = useState<SortById>("monthlyActiveUsers");
4750
const [isCreateProjectDialogOpen, setIsCreateProjectDialogOpen] =
4851
useState(false);
4952
const router = useDashboardRouter();
53+
const [isExpanded, setIsExpanded] = useState(false);
5054

51-
const projectsToShow = useMemo(() => {
55+
const filteredProjects = useMemo(() => {
5256
let _projectsToShow = !searchTerm
5357
? projects
5458
: projects.filter(
@@ -68,15 +72,23 @@ export function TeamProjectsPage(props: {
6872
(a, b) =>
6973
new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime(),
7074
);
71-
} else if (sortBy === "totalConnections") {
75+
} else if (sortBy === "monthlyActiveUsers") {
7276
_projectsToShow = _projectsToShow.sort(
73-
(a, b) => b.totalConnections - a.totalConnections,
77+
(a, b) => b.monthlyActiveUsers - a.monthlyActiveUsers,
7478
);
7579
}
7680

7781
return _projectsToShow;
7882
}, [searchTerm, sortBy, projects]);
7983

84+
const maxProjectsToShow = 8;
85+
86+
const projectsToShow = isExpanded
87+
? filteredProjects
88+
: filteredProjects.slice(0, maxProjectsToShow);
89+
90+
const showExpandCollapseButton = filteredProjects.length > maxProjectsToShow;
91+
8092
return (
8193
<div className="flex grow flex-col">
8294
{/* Filters + Add New */}
@@ -132,6 +144,27 @@ export function TeamProjectsPage(props: {
132144
</div>
133145
)}
134146

147+
{showExpandCollapseButton && (
148+
<div className="mt-4 flex items-center justify-end">
149+
<Button
150+
size="sm"
151+
variant="ghost"
152+
className="gap-2"
153+
onClick={() => {
154+
setIsExpanded((v) => !v);
155+
}}
156+
>
157+
<ChevronDownIcon
158+
className={cn(
159+
"size-4 transition-transform",
160+
isExpanded && "rotate-180",
161+
)}
162+
/>
163+
{isExpanded ? "Show less" : "Show more"}
164+
</Button>
165+
</div>
166+
)}
167+
135168
<LazyCreateAPIKeyDialog
136169
open={isCreateProjectDialogOpen}
137170
onOpenChange={setIsCreateProjectDialogOpen}
@@ -148,7 +181,7 @@ export function TeamProjectsPage(props: {
148181
}
149182

150183
function ProjectCard(props: {
151-
project: ProjectWithTotalConnections;
184+
project: ProjectWithAnalytics;
152185
team_slug: string;
153186
}) {
154187
const { project, team_slug } = props;
@@ -160,7 +193,7 @@ function ProjectCard(props: {
160193
{/* TODO - set image */}
161194
<ProjectAvatar className="size-10 rounded-full" src="" />
162195

163-
<div className="flex-grow flex-col gap-1.5">
196+
<div className="flex flex-grow flex-col gap-1">
164197
<Link
165198
className="group static before:absolute before:top-0 before:right-0 before:bottom-0 before:left-0 before:z-0"
166199
// remove /connect when we have overview page
@@ -170,8 +203,8 @@ function ProjectCard(props: {
170203
</Link>
171204

172205
<p className="flex items-center gap-1 text-muted-foreground text-sm">
173-
<span>{project.totalConnections}</span>
174-
Total Users
206+
<span>{project.monthlyActiveUsers}</span>
207+
Monthly Active Users
175208
</p>
176209
</div>
177210

@@ -256,11 +289,11 @@ function SelectBy(props: {
256289
value: SortById;
257290
onChange: (value: SortById) => void;
258291
}) {
259-
const values: SortById[] = ["name", "createdAt", "totalConnections"];
292+
const values: SortById[] = ["name", "createdAt", "monthlyActiveUsers"];
260293
const valueToLabel: Record<SortById, string> = {
261294
name: "Name",
262295
createdAt: "Creation Date",
263-
totalConnections: "Total Users",
296+
monthlyActiveUsers: "Monthly Active Users",
264297
};
265298

266299
return (

0 commit comments

Comments
 (0)