Skip to content

Commit 50f4b7e

Browse files
committed
feat: start of creating filtering for statistics
1 parent b8a8615 commit 50f4b7e

File tree

6 files changed

+164
-103
lines changed

6 files changed

+164
-103
lines changed

src/actions/statistics/get-stats-chart-data.ts

Lines changed: 57 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,9 @@ export const getStatsChartData = async (opts: {
1414
userUid: string;
1515
to: string;
1616
from: string;
17+
step: 'month' | 'week' | 'day';
1718
}) => {
18-
const { userUid, to, from } = opts;
19+
const { userUid, to, from, step } = opts;
1920

2021
if (!userUid) {
2122
return null;
@@ -44,27 +45,74 @@ export const getStatsChartData = async (opts: {
4445

4546
const data: StatsChartData = {};
4647

48+
// Helper function to format day label
49+
const formatDayLabel = (date: Date) => {
50+
const days = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
51+
const months = [
52+
'Jan',
53+
'Feb',
54+
'Mar',
55+
'Apr',
56+
'May',
57+
'Jun',
58+
'Jul',
59+
'Aug',
60+
'Sep',
61+
'Oct',
62+
'Nov',
63+
'Dec'
64+
];
65+
66+
const dayOfWeek = days[date.getDay()];
67+
const month = months[date.getMonth()];
68+
const dayOfMonth = date.getDate();
69+
70+
return `${dayOfWeek}, ${month} ${dayOfMonth}`;
71+
};
72+
4773
questions.forEach((answer) => {
48-
console.log(answer.createdAt);
49-
const month = answer.createdAt.toISOString().slice(0, 7);
74+
let key: string;
75+
switch (step) {
76+
case 'month':
77+
key = answer.createdAt.toISOString().slice(0, 7);
78+
break;
79+
case 'week':
80+
// Get the start of the week (first day of the week)
81+
const weekStart = new Date(answer.createdAt);
82+
weekStart.setDate(
83+
answer.createdAt.getDate() - answer.createdAt.getDay()
84+
);
85+
key = weekStart.toISOString().slice(0, 10);
86+
break;
87+
case 'day':
88+
key = formatDayLabel(answer.createdAt);
89+
break;
90+
}
91+
5092
const tags = answer.question.tags.map((tag) => tag.tag.name);
5193

52-
if (data[month]) {
53-
data[month].totalQuestions++;
94+
if (data[key]) {
95+
data[key].totalQuestions++;
5496
tags.forEach((tag) => {
55-
data[month].tagCounts[tag] = (data[month].tagCounts[tag] || 0) + 1;
97+
data[key].tagCounts[tag] = (data[key].tagCounts[tag] || 0) + 1;
98+
// Ensure tags array is updated
99+
if (!data[key].tags.includes(tag)) {
100+
data[key].tags.push(tag);
101+
}
56102
});
57103
} else {
58104
const tagCounts: Record<string, number> = {};
59105
tags.forEach((tag) => {
60106
tagCounts[tag] = 1;
61107
});
62-
data[month] = { totalQuestions: 1, tagCounts };
108+
data[key] = {
109+
totalQuestions: 1,
110+
tagCounts,
111+
tags: [...tags]
112+
};
63113
}
64114
});
65115

66-
console.log(data['2024-12']);
67-
68116
revalidateTag('statistics');
69117

70118
return data;

src/app/(dashboard)/(default_layout)/statistics/page.tsx

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {
44
getTotalTimeTaken,
55
getHighestScoringTag
66
} from '@/actions/statistics/get-stats-chart-data';
7+
import StatsRangePicker from '@/components/statistics/range-picker';
78
import QuestionChart from '@/components/statistics/total-question-chart';
89
import TotalStatsCard from '@/components/statistics/total-stats-card';
910
import { Button } from '@/components/ui/button';
@@ -26,7 +27,8 @@ export default async function StatisticsPage() {
2627
getStatsChartData({
2728
userUid: user.uid,
2829
from: '2024-01-01',
29-
to: '2024-12-31'
30+
to: '2024-12-31',
31+
step: 'day'
3032
}),
3133
getTotalQuestionCount(user.uid),
3234
getTotalTimeTaken(user.uid),
@@ -40,10 +42,7 @@ export default async function StatisticsPage() {
4042
Statistics
4143
</h1>
4244
<div className="flex gap-3">
43-
<Button>
44-
<Calendar className="size-4 mr-2" />
45-
Date Range
46-
</Button>
45+
<StatsRangePicker />
4746
<Button variant="default">
4847
<Stars className="size-4 text-yellow-300 fill-yellow-300" />
4948
</Button>
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
'use client';
2+
3+
import {
4+
DropdownMenu,
5+
DropdownMenuContent,
6+
DropdownMenuGroup,
7+
DropdownMenuItem,
8+
DropdownMenuTrigger
9+
} from '@/components/ui/dropdown-menu';
10+
import { Button } from '../ui/button';
11+
import { Calendar } from 'lucide-react';
12+
import { useRouter, useSearchParams } from 'next/navigation';
13+
import { STATISTICS_STEPS } from '@/utils/constants/statistics-filters';
14+
15+
export default function StatsRangePicker() {
16+
const router = useRouter();
17+
const searchParams = useSearchParams();
18+
19+
const updateDateRange = (range: string) => {
20+
const params = new URLSearchParams(searchParams.toString());
21+
params.set('range', range);
22+
router.push(`?${params.toString()}`);
23+
};
24+
25+
return (
26+
<DropdownMenu>
27+
<DropdownMenuTrigger asChild>
28+
<Button
29+
variant="default"
30+
padding="sm"
31+
size="sm"
32+
className="flex items-center gap-x-2.5 text-xs group"
33+
>
34+
<Calendar className="size-4 mr-2" />
35+
Date Range
36+
</Button>
37+
</DropdownMenuTrigger>
38+
<DropdownMenuContent
39+
align="center"
40+
className="!p-0 w-40 bg-black border border-black-50 text-white text-sm"
41+
>
42+
<DropdownMenuGroup className="p-1">
43+
{STATISTICS_STEPS.map((step) => (
44+
<DropdownMenuItem
45+
key={step.value}
46+
className="hover:!text-white"
47+
onClick={() => updateDateRange(step.value)}
48+
>
49+
{step.label}
50+
</DropdownMenuItem>
51+
))}
52+
</DropdownMenuGroup>
53+
</DropdownMenuContent>
54+
</DropdownMenu>
55+
);
56+
}

src/components/statistics/total-question-chart.tsx

Lines changed: 29 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -27,91 +27,52 @@ const chartConfig = {
2727
}
2828
} satisfies ChartConfig;
2929

30+
export interface StatsChartData {
31+
[key: string]: {
32+
totalQuestions: number;
33+
tagCounts: Record<string, number>;
34+
tags: string[];
35+
};
36+
}
37+
3038
export default function QuestionChart({
3139
questionData
3240
}: {
3341
questionData: StatsChartData;
3442
}) {
3543
const chartData = useMemo(() => {
3644
const entries = Object.entries(questionData);
37-
const sortedEntries = entries.sort(
38-
(a, b) => new Date(a[0]).getTime() - new Date(b[0]).getTime()
39-
);
40-
const monthCount = sortedEntries.length;
41-
42-
if (monthCount < 3) {
43-
// Convert to weekly or daily data
44-
const allDates = sortedEntries.flatMap(([month, data]) => {
45-
const [year, monthNum] = month.split('-');
46-
const daysInMonth = new Date(
47-
Number(year),
48-
Number(monthNum),
49-
0
50-
).getDate();
51-
return Array.from({ length: daysInMonth }, (_, i) => {
52-
const day = i + 1;
53-
const date = `${year}-${monthNum.padStart(2, '0')}-${day
54-
.toString()
55-
.padStart(2, '0')}`;
56-
return [date, data.totalQuestions / daysInMonth];
57-
});
58-
});
5945

60-
if (monthCount < 1) {
61-
// Daily data
62-
return allDates.map(([date, questions]) => ({
63-
date: new Date(date).toLocaleDateString('en-US', {
64-
month: 'short',
65-
day: 'numeric'
66-
}),
67-
questions: Math.round(Number(questions))
68-
}));
69-
} else {
70-
// Weekly data
71-
const weeklyData: { [week: string]: number } = {};
72-
allDates.forEach(([date, questions]) => {
73-
const week = getWeekNumber(new Date(date));
74-
weeklyData[week] = (weeklyData[week] || 0) + Number(questions);
75-
});
76-
return Object.entries(weeklyData).map(([week, questions]) => ({
77-
date: `Week ${week}`,
78-
questions: Math.round(questions)
79-
}));
80-
}
81-
} else {
82-
// Monthly data (original logic)
83-
return sortedEntries.map(([month, data]) => ({
84-
date: new Date(month).toLocaleDateString('en-US', {
85-
month: 'short',
86-
year: 'numeric'
87-
}),
88-
questions: data.totalQuestions
89-
}));
90-
}
46+
// Directly use the keys as they should now be pre-formatted
47+
return entries.map(([date, data]) => ({
48+
date: date,
49+
questions: data.totalQuestions
50+
}));
9151
}, [questionData]);
9252

9353
const trend = useMemo(() => {
54+
if (chartData.length < 2) {
55+
return { percentage: 0, isUp: true };
56+
}
57+
9458
const lastPeriod = chartData[chartData.length - 1];
9559
const previousPeriod = chartData[chartData.length - 2];
9660

97-
if (lastPeriod && previousPeriod) {
98-
const percentageChange =
99-
((lastPeriod.questions - previousPeriod.questions) /
100-
previousPeriod.questions) *
101-
100;
102-
return {
103-
percentage: Math.abs(percentageChange).toFixed(2),
104-
isUp: percentageChange > 0
105-
};
106-
}
61+
const percentageChange =
62+
((lastPeriod.questions - previousPeriod.questions) /
63+
previousPeriod.questions) *
64+
100;
10765

108-
return { percentage: 0, isUp: true };
66+
return {
67+
percentage: Math.abs(percentageChange).toFixed(2),
68+
isUp: percentageChange > 0
69+
};
10970
}, [chartData]);
11071

11172
const periodText = useMemo(() => {
112-
const monthCount = Object.keys(questionData).length;
113-
if (monthCount < 1) return 'days';
114-
if (monthCount < 3) return 'weeks';
73+
const entryKeys = Object.keys(questionData);
74+
if (entryKeys[0]?.includes(',')) return 'days';
75+
if (entryKeys[0]?.length === 10) return 'weeks';
11576
return 'months';
11677
}, [questionData]);
11778

@@ -163,7 +124,7 @@ export default function QuestionChart({
163124
tickLine={false}
164125
axisLine={false}
165126
tickMargin={8}
166-
tickFormatter={(value) => value.split(' ')[0]}
127+
tickFormatter={(value) => value.split(',')[0]}
167128
/>
168129
<YAxis
169130
tickLine={false}
@@ -194,21 +155,3 @@ export default function QuestionChart({
194155
</Card>
195156
);
196157
}
197-
198-
// Helper function to get week number
199-
function getWeekNumber(date: Date) {
200-
const d = new Date(
201-
Date.UTC(date.getFullYear(), date.getMonth(), date.getDate())
202-
);
203-
const dayNum = d.getUTCDay() || 7;
204-
d.setUTCDate(d.getUTCDate() + 4 - dayNum);
205-
const yearStart = new Date(Date.UTC(d.getUTCFullYear(), 0, 1));
206-
return Math.ceil(((d.getTime() - yearStart.getTime()) / 86400000 + 1) / 7);
207-
}
208-
209-
export interface StatsChartData {
210-
[month: string]: {
211-
totalQuestions: number;
212-
tagCounts: Record<string, number>;
213-
};
214-
}

src/types/Stats.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
export interface StatsChartData {
2-
[month: string]: {
1+
export type StatsChartData = {
2+
[key: string]: {
33
totalQuestions: number;
44
tagCounts: Record<string, number>;
5+
tags: string[];
56
};
6-
}
7+
};
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
export const STATISTICS_STEPS = [
2+
{
3+
value: 'last-7-days',
4+
label: 'Last 7 days'
5+
},
6+
{
7+
value: 'last-30-days',
8+
label: 'Last 30 days'
9+
},
10+
{
11+
value: 'last-90-days',
12+
label: 'Last 90 days'
13+
}
14+
];

0 commit comments

Comments
 (0)