Skip to content

Commit 1f17d93

Browse files
clucraftclaude
andcommitted
Fix User Search trend to use selected year filter
- Trend API now accepts year parameter - Changed from 6-month rolling window to full 12-month year view - YTD renamed to "Year Total" showing full year for selected filter - Chart title shows selected year (e.g., "2024 Monthly Cost Trend") - Increased chart height to accommodate 12 months Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent f76e032 commit 1f17d93

3 files changed

Lines changed: 36 additions & 43 deletions

File tree

client/src/pages/UserSearch.tsx

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ interface TrendMonth {
3636

3737
interface TrendData {
3838
monthlyTrend: TrendMonth[];
39-
ytd: {
39+
yearTotal: {
4040
year: number;
4141
totalCost: number;
4242
totalCalls: number;
@@ -115,7 +115,7 @@ export default function UserSearch() {
115115
try {
116116
const [userResponse, trendResponse] = await Promise.all([
117117
usageApi.searchUser(searchTerm, month, year),
118-
usageApi.getUserTrend(searchTerm),
118+
usageApi.getUserTrend(searchTerm, year),
119119
]);
120120
setUserData(userResponse.data);
121121
setTrendData(trendResponse.data);
@@ -362,35 +362,35 @@ export default function UserSearch() {
362362
</div>
363363
</div>
364364

365-
{/* YTD Totals */}
365+
{/* Year Totals */}
366366
{trendData && (
367367
<div className="bg-gradient-to-br from-indigo-500 to-purple-600 p-6 rounded-lg shadow">
368368
<h2 className="text-sm font-medium text-indigo-100 mb-1">
369-
{trendData.ytd.year} Year-to-Date
369+
{trendData.yearTotal.year} Year Total
370370
</h2>
371371
<p className="text-3xl font-bold text-white mb-4">
372-
{formatCurrency(trendData.ytd.totalCost)}
372+
{formatCurrency(trendData.yearTotal.totalCost)}
373373
</p>
374374
<div className="grid grid-cols-2 gap-4">
375375
<div>
376376
<p className="text-xs text-indigo-200">Calls</p>
377-
<p className="text-lg font-semibold text-white">{trendData.ytd.totalCalls.toLocaleString()}</p>
377+
<p className="text-lg font-semibold text-white">{trendData.yearTotal.totalCalls.toLocaleString()}</p>
378378
</div>
379379
<div>
380380
<p className="text-xs text-indigo-200">Minutes</p>
381-
<p className="text-lg font-semibold text-white">{trendData.ytd.totalMinutes.toLocaleString()}</p>
381+
<p className="text-lg font-semibold text-white">{trendData.yearTotal.totalMinutes.toLocaleString()}</p>
382382
</div>
383383
</div>
384384
</div>
385385
)}
386386

387-
{/* 6-Month Trend Sparkline */}
387+
{/* Annual Cost Trend */}
388388
{convertedTrendData && (
389389
<div className="bg-white dark:bg-gray-800 p-6 rounded-lg shadow transition-colors">
390-
<h2 className="text-sm font-medium text-gray-500 dark:text-gray-400 mb-2">6-Month Cost Trend</h2>
391-
<ResponsiveContainer width="100%" height={100}>
390+
<h2 className="text-sm font-medium text-gray-500 dark:text-gray-400 mb-2">{year} Monthly Cost Trend</h2>
391+
<ResponsiveContainer width="100%" height={120}>
392392
<LineChart data={convertedTrendData.monthlyTrend}>
393-
<XAxis dataKey="monthName" tick={{ fontSize: 10, fill: theme === 'dark' ? '#9ca3af' : '#6b7280' }} axisLine={false} tickLine={false} />
393+
<XAxis dataKey="monthName" tick={{ fontSize: 9, fill: theme === 'dark' ? '#9ca3af' : '#6b7280' }} axisLine={false} tickLine={false} interval={0} />
394394
<YAxis hide />
395395
<Tooltip
396396
formatter={(value: number) => [formatCurrency(value), 'Cost']}

client/src/services/api.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,8 @@ export const usageApi = {
3939
api.get('/usage/top10', { params: { month, year } }),
4040
searchUser: (email: string, month?: number, year?: number) =>
4141
api.get(`/usage/user/${encodeURIComponent(email)}`, { params: { month, year } }),
42-
getUserTrend: (email: string) =>
43-
api.get(`/usage/user/${encodeURIComponent(email)}/trend`),
42+
getUserTrend: (email: string, year?: number) =>
43+
api.get(`/usage/user/${encodeURIComponent(email)}/trend`, { params: { year } }),
4444
getMonthlyCosts: (year?: number) =>
4545
api.get('/usage/monthly-costs', { params: { year } }),
4646
getDashboardStats: (month?: number, year?: number) =>

src/routes/usage.ts

Lines changed: 23 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -256,26 +256,19 @@ router.get('/user/:email', async (req: AuthRequest, res: Response) => {
256256
}
257257
});
258258

259-
// Get user trend data (6-month history and YTD)
259+
// Get user trend data (12-month history and yearly totals)
260260
router.get('/user/:email/trend', async (req: AuthRequest, res: Response) => {
261261
try {
262262
const email = req.params.email as string;
263+
const { year } = req.query;
263264
const rates = await prisma.rateMatrix.findMany();
264-
const currentYear = new Date().getFullYear();
265-
const currentMonth = new Date().getMonth() + 1;
265+
const selectedYear = year ? Number(year) : new Date().getFullYear();
266266

267-
// Get 6-month trend data
267+
// Get 12-month trend data for the selected year
268268
const monthlyData = [];
269-
for (let i = 5; i >= 0; i--) {
270-
let targetMonth = currentMonth - i;
271-
let targetYear = currentYear;
272-
if (targetMonth <= 0) {
273-
targetMonth += 12;
274-
targetYear -= 1;
275-
}
276-
277-
const startDate = new Date(targetYear, targetMonth - 1, 1);
278-
const endDate = new Date(targetYear, targetMonth, 0, 23, 59, 59);
269+
for (let month = 1; month <= 12; month++) {
270+
const startDate = new Date(selectedYear, month - 1, 1);
271+
const endDate = new Date(selectedYear, month, 0, 23, 59, 59);
279272

280273
const calls = await prisma.callRecord.findMany({
281274
where: {
@@ -303,28 +296,28 @@ router.get('/user/:email/trend', async (req: AuthRequest, res: Response) => {
303296
}
304297

305298
monthlyData.push({
306-
month: targetMonth,
307-
year: targetYear,
299+
month,
300+
year: selectedYear,
308301
monthName: startDate.toLocaleString('default', { month: 'short' }),
309302
cost: Math.round(totalCost * 100) / 100,
310303
calls: calls.length,
311304
minutes: Math.round(calls.reduce((sum, c) => sum + c.duration, 0) / 60),
312305
});
313306
}
314307

315-
// Get YTD totals
316-
const ytdStartDate = new Date(currentYear, 0, 1);
317-
const ytdEndDate = new Date();
308+
// Get yearly totals for the selected year
309+
const yearStartDate = new Date(selectedYear, 0, 1);
310+
const yearEndDate = new Date(selectedYear, 11, 31, 23, 59, 59);
318311

319-
const ytdCalls = await prisma.callRecord.findMany({
312+
const yearCalls = await prisma.callRecord.findMany({
320313
where: {
321314
userEmail: {
322315
contains: email,
323316
mode: 'insensitive',
324317
},
325318
callDate: {
326-
gte: ytdStartDate,
327-
lte: ytdEndDate,
319+
gte: yearStartDate,
320+
lte: yearEndDate,
328321
},
329322
},
330323
select: {
@@ -335,19 +328,19 @@ router.get('/user/:email/trend', async (req: AuthRequest, res: Response) => {
335328
},
336329
});
337330

338-
let ytdCost = 0;
339-
for (const call of ytdCalls) {
331+
let yearCost = 0;
332+
for (const call of yearCalls) {
340333
const rate = await findRateForCall(rates, call.originCountry, call.destCountry, call.callType);
341-
ytdCost += (call.duration / 60) * rate;
334+
yearCost += (call.duration / 60) * rate;
342335
}
343336

344337
res.json({
345338
monthlyTrend: monthlyData,
346-
ytd: {
347-
year: currentYear,
348-
totalCost: Math.round(ytdCost * 100) / 100,
349-
totalCalls: ytdCalls.length,
350-
totalMinutes: Math.round(ytdCalls.reduce((sum, c) => sum + c.duration, 0) / 60),
339+
yearTotal: {
340+
year: selectedYear,
341+
totalCost: Math.round(yearCost * 100) / 100,
342+
totalCalls: yearCalls.length,
343+
totalMinutes: Math.round(yearCalls.reduce((sum, c) => sum + c.duration, 0) / 60),
351344
},
352345
});
353346
} catch (error) {

0 commit comments

Comments
 (0)