Skip to content

Commit e823b03

Browse files
authored
Merge pull request #1491 from AshishViradiya153/PM/140
Custome time input on dashboard
2 parents f369734 + 28afd22 commit e823b03

File tree

11 files changed

+605
-83
lines changed

11 files changed

+605
-83
lines changed

components/analytics/dashboard-views-chart.tsx

Lines changed: 124 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { useMemo } from "react";
22

3-
import { format, subDays, subHours } from "date-fns";
3+
import { format } from "date-fns";
44
import {
55
Bar,
66
BarChart,
@@ -15,23 +15,59 @@ import { TimeRange } from "./time-range-select";
1515

1616
interface DashboardViewsChartProps {
1717
timeRange: TimeRange;
18-
data?: {
19-
date: string;
20-
views: number;
21-
}[];
18+
data?: { date: string; views: number }[];
19+
startDate?: Date;
20+
endDate?: Date;
2221
}
2322

2423
export default function DashboardViewsChart({
2524
timeRange,
2625
data = [],
26+
startDate,
27+
endDate,
2728
}: DashboardViewsChartProps) {
29+
const totalDays =
30+
startDate && endDate
31+
? Math.ceil(
32+
(endDate.getTime() - startDate.getTime()) / (1000 * 60 * 60 * 24),
33+
)
34+
: 0;
2835
// Format the data for display
2936
const formattedData = useMemo(() => {
3037
// Generate all possible time slots
3138
const now = new Date();
3239
const slots: { date: Date; views: number }[] = [];
40+
if (timeRange === "custom" && startDate && endDate) {
41+
if (totalDays > 365) {
42+
// More than a year: Group by months
43+
let current = new Date(
44+
startDate.getFullYear(),
45+
startDate.getMonth(),
46+
1,
47+
);
3348

34-
if (timeRange === "24h") {
49+
while (current <= endDate) {
50+
slots.push({ date: new Date(current), views: 0 });
51+
current.setMonth(current.getMonth() + 1);
52+
}
53+
} else if (totalDays > 30) {
54+
// More than a month but less than a year: Group by weeks
55+
for (let i = 0; i <= totalDays; i += 7) {
56+
const date = new Date(startDate);
57+
date.setDate(date.getDate() + i);
58+
date.setHours(0, 0, 0, 0);
59+
slots.push({ date, views: 0 });
60+
}
61+
} else {
62+
// Less than a month: Show daily data
63+
for (let i = 0; i <= totalDays; i++) {
64+
const date = new Date(startDate);
65+
date.setDate(date.getDate() + i);
66+
date.setHours(0, 0, 0, 0);
67+
slots.push({ date, views: 0 });
68+
}
69+
}
70+
} else if (timeRange === "24h") {
3571
// Generate 24 hourly slots
3672
for (let i = 23; i >= 0; i--) {
3773
const date = new Date(now);
@@ -54,26 +90,64 @@ export default function DashboardViewsChart({
5490
if (data) {
5591
data.forEach((point) => {
5692
const pointDate = new Date(point.date);
57-
const slotIndex = slots.findIndex((slot) => {
58-
if (timeRange === "24h") {
59-
return slot.date.getHours() === pointDate.getHours();
93+
94+
let slotIndex = -1;
95+
96+
if (timeRange === "24h") {
97+
slotIndex = slots.findIndex(
98+
(slot) => slot.date.getHours() === pointDate.getHours(),
99+
);
100+
} else if (timeRange === "custom") {
101+
if (totalDays > 365) {
102+
// If range is more than a year, match by month
103+
slotIndex = slots.findIndex(
104+
(slot) =>
105+
slot.date.getFullYear() === pointDate.getFullYear() &&
106+
slot.date.getMonth() === pointDate.getMonth(),
107+
);
108+
} else if (totalDays > 30) {
109+
// If range is more than a month but less than a year, match by week
110+
slotIndex = slots.findIndex(
111+
(slot) =>
112+
pointDate >= slot.date &&
113+
pointDate <
114+
new Date(slot.date.getTime() + 7 * 24 * 60 * 60 * 1000), // Within the week
115+
);
60116
} else {
61-
return slot.date.toDateString() === pointDate.toDateString();
117+
// If range is less than a month, match by exact day
118+
slotIndex = slots.findIndex(
119+
(slot) => slot.date.toDateString() === pointDate.toDateString(),
120+
);
62121
}
63-
});
122+
} else {
123+
// Default case: match by exact day for '7d' and '30d'
124+
slotIndex = slots.findIndex(
125+
(slot) => slot.date.toDateString() === pointDate.toDateString(),
126+
);
127+
}
128+
64129
if (slotIndex !== -1) {
65-
slots[slotIndex].views = point.views;
130+
slots[slotIndex].views += point.views;
66131
}
67132
});
68133
}
69134

70135
// Format for display
71136
return slots.map((slot) => ({
72137
date: slot.date,
73-
name: format(slot.date, timeRange === "24h" ? "h:mm aa" : "EEE, MMM d"),
138+
name: format(
139+
slot.date,
140+
timeRange === "24h"
141+
? "h:mm aa"
142+
: totalDays > 365
143+
? "MMM yyyy"
144+
: totalDays > 30
145+
? "MMM d"
146+
: "EEE, MMM d",
147+
),
74148
views: slot.views,
75149
}));
76-
}, [data, timeRange]);
150+
}, [data, timeRange, startDate, endDate, totalDays]);
77151

78152
// Calculate tick values based on time range
79153
const ticks = useMemo(() => {
@@ -96,17 +170,41 @@ export default function DashboardViewsChart({
96170
tickIndices.unshift(i);
97171
}
98172
return tickIndices.map((i) => formattedData[i].name);
173+
} else if (timeRange === "custom") {
174+
if (totalDays > 365) {
175+
// Show every 2rd month
176+
return formattedData.filter((_, i) => i % 2 === 0).map((d) => d.name);
177+
}
178+
179+
if (totalDays > 30) {
180+
// Show every 2nd week
181+
return formattedData.filter((_, i) => i % 2 === 0).map((d) => d.name);
182+
}
183+
return formattedData.map((d) => d.name);
99184
}
100185
return formattedData.map((d) => d.name);
101-
}, [timeRange, formattedData]);
186+
}, [timeRange, formattedData, totalDays]);
187+
188+
const barSize = useMemo(() => {
189+
if (timeRange === "24h") return 8;
190+
if (timeRange === "7d") return 24;
191+
if (timeRange === "30d") return 12;
192+
193+
if (startDate && endDate) {
194+
if (totalDays > 365) return 24;
195+
if (totalDays > 30) return 16;
196+
}
197+
198+
return 12;
199+
}, [timeRange, startDate, endDate, totalDays]);
102200

103201
return (
104202
<div className="h-[300px] w-full">
105203
<ResponsiveContainer width="100%" height="100%">
106204
<BarChart
107205
data={formattedData}
108206
margin={{ top: 10, right: 30, left: 0, bottom: 0 }}
109-
barSize={timeRange === "24h" ? 8 : timeRange === "7d" ? 24 : 12}
207+
barSize={barSize}
110208
>
111209
<XAxis
112210
dataKey="name"
@@ -137,9 +235,16 @@ export default function DashboardViewsChart({
137235
Time
138236
</span>
139237
<span className="font-bold text-muted-foreground">
140-
{timeRange === "24h"
141-
? format(data.date, "h:mm a")
142-
: format(data.date, "MMM d, yyyy")}
238+
{format(
239+
data.date,
240+
timeRange === "24h"
241+
? "h:mm aa"
242+
: totalDays > 365
243+
? "MMM yyyy"
244+
: totalDays > 30
245+
? "'Week of' MMM d"
246+
: "MMM d, yyyy",
247+
)}
143248
</span>
144249
</div>
145250
<div className="flex flex-col">

components/analytics/documents-table.tsx

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {
1313
getSortedRowModel,
1414
useReactTable,
1515
} from "@tanstack/react-table";
16+
import { format } from "date-fns";
1617
import {
1718
ChevronDownIcon,
1819
ChevronUpIcon,
@@ -154,7 +155,13 @@ const columns: ColumnDef<Document>[] = [
154155
},
155156
];
156157

157-
export default function DocumentsTable() {
158+
export default function DocumentsTable({
159+
startDate,
160+
endDate,
161+
}: {
162+
startDate: Date;
163+
endDate: Date;
164+
}) {
158165
const router = useRouter();
159166
const teamInfo = useTeam();
160167
const [sorting, setSorting] = useState<SortingState>([
@@ -163,7 +170,7 @@ export default function DocumentsTable() {
163170

164171
const interval = router.query.interval || "24h";
165172
const { data: documents, isLoading } = useSWR<Document[]>(
166-
`/api/analytics?type=documents&interval=${interval}&teamId=${teamInfo?.currentTeam?.id}`,
173+
`/api/analytics?type=documents&interval=${interval}&teamId=${teamInfo?.currentTeam?.id}${interval === "custom" ? `&startDate=${format(startDate, "MM-dd-yyyy")}&endDate=${format(endDate, "MM-dd-yyyy")}` : ""}`,
167174
fetcher,
168175
{
169176
keepPreviousData: true,

components/analytics/links-table.tsx

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {
1313
getSortedRowModel,
1414
useReactTable,
1515
} from "@tanstack/react-table";
16+
import { format } from "date-fns";
1617
import {
1718
Check,
1819
ChevronDownIcon,
@@ -216,7 +217,13 @@ const columns: ColumnDef<Link>[] = [
216217
},
217218
];
218219

219-
export default function LinksTable() {
220+
export default function LinksTable({
221+
startDate,
222+
endDate,
223+
}: {
224+
startDate: Date;
225+
endDate: Date;
226+
}) {
220227
const router = useRouter();
221228

222229
const teamInfo = useTeam();
@@ -226,7 +233,7 @@ export default function LinksTable() {
226233

227234
const interval = router.query.interval || "7d";
228235
const { data: links, isLoading } = useSWR<Link[]>(
229-
`/api/analytics?type=links&interval=${interval}&teamId=${teamInfo?.currentTeam?.id}`,
236+
`/api/analytics?type=links&interval=${interval}&teamId=${teamInfo?.currentTeam?.id}${interval === "custom" ? `&startDate=${format(startDate, "MM-dd-yyyy")}&endDate=${format(endDate, "MM-dd-yyyy")}` : ""}`,
230237
fetcher,
231238
{
232239
keepPreviousData: true,
@@ -327,7 +334,12 @@ export default function LinksTable() {
327334
<Link2Icon className="size-6" />
328335
</div>
329336
</div>
330-
<p>No visited links in the last {interval}</p>
337+
<p>
338+
No visited links in the last{" "}
339+
{interval === "custom"
340+
? `From ${format(startDate, "PP")} to ${format(endDate, "PP")}`
341+
: interval}
342+
</p>
331343
</div>
332344
</TableCell>
333345
</TableRow>

0 commit comments

Comments
 (0)