Skip to content

Commit ebeb42f

Browse files
committed
Add Date Range Selector for Connect Analaytics
1 parent ec53366 commit ebeb42f

File tree

2 files changed

+163
-55
lines changed

2 files changed

+163
-55
lines changed
Lines changed: 132 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,156 @@
11
"use client";
2+
import { DatePickerWithRange } from "@/components/ui/DatePickerWithRange";
3+
import {
4+
Select,
5+
SelectContent,
6+
SelectItem,
7+
SelectTrigger,
8+
SelectValue,
9+
} from "@/components/ui/select";
210
import {
311
useWalletUsageAggregate,
412
useWalletUsagePeriod,
513
} from "@3rdweb-sdk/react/hooks/useApi";
6-
import { useMemo } from "react";
14+
import { format, subDays } from "date-fns";
15+
import { useState } from "react";
716
import { ConnectAnalyticsDashboardUI } from "./ConnectAnalyticsDashboardUI";
817

918
export function ConnectAnalyticsDashboard(props: {
1019
clientId: string;
1120
}) {
12-
const allTimeFrom = useMemo(
13-
() => new Date(Date.now() - 135 * 24 * 60 * 60 * 1000),
14-
[],
15-
);
16-
const from = useMemo(
17-
() => new Date(Date.now() - 135 * 24 * 60 * 60 * 1000),
18-
[],
21+
const [range, setRange] = useState<Range>(() =>
22+
getLastNDaysRange("last-120"),
1923
);
20-
const to = useMemo(() => new Date(), []);
24+
2125
const walletUsageQuery = useWalletUsagePeriod({
2226
clientId: props.clientId,
23-
from,
24-
to,
27+
from: range.from,
28+
to: range.to,
2529
period: "day",
2630
});
31+
2732
const walletUsageAggregateQuery = useWalletUsageAggregate({
2833
clientId: props.clientId,
29-
from: allTimeFrom,
30-
to,
34+
from: range.from,
35+
to: range.to,
3136
});
3237

3338
return (
34-
<ConnectAnalyticsDashboardUI
35-
walletUsage={walletUsageQuery.data || []}
36-
aggregateWalletUsage={walletUsageAggregateQuery.data || []}
37-
isPending={
38-
walletUsageQuery.isPending || walletUsageAggregateQuery.isPending
39+
<div>
40+
<Filters range={range} setRange={setRange} />
41+
<div className="h-2" />
42+
<ConnectAnalyticsDashboardUI
43+
walletUsage={walletUsageQuery.data || []}
44+
aggregateWalletUsage={walletUsageAggregateQuery.data || []}
45+
isPending={
46+
walletUsageQuery.isPending || walletUsageAggregateQuery.isPending
47+
}
48+
/>
49+
</div>
50+
);
51+
}
52+
53+
const durationPresets = [
54+
{
55+
name: "Last 7 Days",
56+
id: "last-7",
57+
days: 7,
58+
},
59+
{
60+
name: "Last 30 Days",
61+
id: "last-30",
62+
days: 30,
63+
},
64+
{
65+
name: "Last 60 Days",
66+
id: "last-60",
67+
days: 60,
68+
},
69+
{
70+
name: "Last 120 Days",
71+
id: "last-120",
72+
days: 120,
73+
},
74+
] as const;
75+
76+
type DurationId = (typeof durationPresets)[number]["id"];
77+
78+
type Range = {
79+
type: DurationId | "custom";
80+
label?: string;
81+
from: Date;
82+
to: Date;
83+
};
84+
85+
function getLastNDaysRange(id: DurationId) {
86+
const durationInfo = durationPresets.find((preset) => preset.id === id);
87+
if (!durationInfo) {
88+
throw new Error("Invalid duration id");
89+
}
90+
91+
const todayDate = new Date();
92+
93+
const value: Range = {
94+
type: id,
95+
from: subDays(todayDate, durationInfo.days),
96+
to: todayDate,
97+
label: durationInfo.name,
98+
};
99+
100+
return value;
101+
}
102+
103+
function Filters(props: { range: Range; setRange: (range: Range) => void }) {
104+
const { range, setRange } = props;
105+
106+
return (
107+
<DatePickerWithRange
108+
from={range.from}
109+
to={range.to}
110+
setFrom={(from) =>
111+
setRange({
112+
from,
113+
to: range.to,
114+
type: "custom",
115+
})
116+
}
117+
setTo={(to) =>
118+
setRange({
119+
from: range.from,
120+
to,
121+
type: "custom",
122+
})
123+
}
124+
header={
125+
<div className="mb-2 border-border border-b p-4">
126+
<Select
127+
value={range.type}
128+
onValueChange={(id: DurationId) => {
129+
setRange(getLastNDaysRange(id));
130+
}}
131+
>
132+
<SelectTrigger className="flex bg-transparent">
133+
<SelectValue placeholder="Select" />
134+
</SelectTrigger>
135+
<SelectContent position="popper">
136+
{durationPresets.map((preset) => (
137+
<SelectItem key={preset.id} value={preset.id}>
138+
{preset.name}
139+
</SelectItem>
140+
))}
141+
142+
{range.type === "custom" && (
143+
<SelectItem value="custom">
144+
{format(range.from, "LLL dd, y")} -{" "}
145+
{format(range.to, "LLL dd, y")}
146+
</SelectItem>
147+
)}
148+
</SelectContent>
149+
</Select>
150+
</div>
39151
}
152+
labelOverride={range.label}
153+
className="w-auto border-none p-0"
40154
/>
41155
);
42156
}

apps/dashboard/src/components/pay/PayAnalytics/PayAnalytics.tsx

Lines changed: 31 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import {
88
SelectTrigger,
99
SelectValue,
1010
} from "@/components/ui/select";
11-
import { format } from "date-fns";
11+
import { format, subDays } from "date-fns";
1212
import { useState } from "react";
1313
import { PayCustomersTable } from "./components/PayCustomersTable";
1414
import { PayNewCustomers } from "./components/PayNewCustomers";
@@ -125,19 +125,15 @@ export function PayAnalytics(props: { clientId: string }) {
125125
}
126126

127127
function getLastNDaysRange(id: DurationId) {
128-
const todayDate = new Date();
129-
const pastDate = new Date(todayDate);
130-
131128
const durationInfo = durationPresets.find((preset) => preset.id === id);
132129
if (!durationInfo) {
133130
throw new Error("Invalid duration id");
134131
}
135132

136-
pastDate.setDate(todayDate.getDate() - durationInfo.days);
137-
133+
const todayDate = new Date();
138134
const value: Range = {
139135
type: id,
140-
from: pastDate,
136+
from: subDays(todayDate, durationInfo.days),
141137
to: todayDate,
142138
label: durationInfo.name,
143139
};
@@ -148,35 +144,6 @@ function getLastNDaysRange(id: DurationId) {
148144
function Filters(props: { range: Range; setRange: (range: Range) => void }) {
149145
const { range, setRange } = props;
150146

151-
const presets = (
152-
<div className="mb-2 border-border border-b p-4">
153-
<Select
154-
value={range.type}
155-
onValueChange={(id: DurationId) => {
156-
setRange(getLastNDaysRange(id));
157-
}}
158-
>
159-
<SelectTrigger className="flex bg-transparent">
160-
<SelectValue placeholder="Select" />
161-
</SelectTrigger>
162-
<SelectContent position="popper">
163-
{durationPresets.map((preset) => (
164-
<SelectItem key={preset.id} value={preset.id}>
165-
{preset.name}
166-
</SelectItem>
167-
))}
168-
169-
{range.type === "custom" && (
170-
<SelectItem value="custom">
171-
{format(range.from, "LLL dd, y")} -{" "}
172-
{format(range.to, "LLL dd, y")}
173-
</SelectItem>
174-
)}
175-
</SelectContent>
176-
</Select>
177-
</div>
178-
);
179-
180147
return (
181148
<div className="flex gap-2">
182149
<DatePickerWithRange
@@ -196,7 +163,34 @@ function Filters(props: { range: Range; setRange: (range: Range) => void }) {
196163
type: "custom",
197164
})
198165
}
199-
header={presets}
166+
header={
167+
<div className="mb-2 border-border border-b p-4">
168+
<Select
169+
value={range.type}
170+
onValueChange={(id: DurationId) => {
171+
setRange(getLastNDaysRange(id));
172+
}}
173+
>
174+
<SelectTrigger className="flex bg-transparent">
175+
<SelectValue placeholder="Select" />
176+
</SelectTrigger>
177+
<SelectContent position="popper">
178+
{durationPresets.map((preset) => (
179+
<SelectItem key={preset.id} value={preset.id}>
180+
{preset.name}
181+
</SelectItem>
182+
))}
183+
184+
{range.type === "custom" && (
185+
<SelectItem value="custom">
186+
{format(range.from, "LLL dd, y")} -{" "}
187+
{format(range.to, "LLL dd, y")}
188+
</SelectItem>
189+
)}
190+
</SelectContent>
191+
</Select>
192+
</div>
193+
}
200194
labelOverride={range.label}
201195
className="w-auto border-none p-0"
202196
/>

0 commit comments

Comments
 (0)