Skip to content

Commit 9519527

Browse files
Copilotrajbos
andauthored
[Feature] Show last date available in CSV and limit chart domain (#12)
* Initial plan * Implement last date display and chart domain limits Co-authored-by: rajbos <6085745+rajbos@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: rajbos <6085745+rajbos@users.noreply.github.com>
1 parent a6ad1b4 commit 9519527

4 files changed

Lines changed: 86 additions & 6 deletions

File tree

src/App.tsx

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@ import {
2424
getModelUsageSummary,
2525
getDailyModelData,
2626
getPowerUsers,
27-
getPowerUserDailyData
27+
getPowerUserDailyData,
28+
getLastDateFromData
2829
} from "@/lib/utils";
2930

3031
function App() {
@@ -34,6 +35,7 @@ function App() {
3435
const [modelSummary, setModelSummary] = useState<ModelUsageSummary[]>([]);
3536
const [dailyModelData, setDailyModelData] = useState<DailyModelData[]>([]);
3637
const [powerUserSummary, setPowerUserSummary] = useState<PowerUserSummary | null>(null);
38+
const [lastDateAvailable, setLastDateAvailable] = useState<string | null>(null);
3739
const [isDragging, setIsDragging] = useState(false);
3840
const [isProcessing, setIsProcessing] = useState(false);
3941
const fileInputRef = useRef<HTMLInputElement>(null);
@@ -94,6 +96,10 @@ function App() {
9496
const powerUsers = getPowerUsers(parsedData);
9597
setPowerUserSummary(powerUsers);
9698

99+
// Get the last date available in the CSV
100+
const lastDate = getLastDateFromData(parsedData);
101+
setLastDateAvailable(lastDate);
102+
97103
setIsProcessing(false);
98104
toast.success(`Loaded ${parsedData.length} records successfully`);
99105
} catch (error) {
@@ -133,6 +139,7 @@ function App() {
133139
setModelSummary([]);
134140
setDailyModelData([]);
135141
setPowerUserSummary(null);
142+
setLastDateAvailable(null);
136143
}
137144
};
138145

@@ -453,7 +460,8 @@ function App() {
453460
<XAxis
454461
dataKey="date"
455462
tick={{ fill: 'var(--foreground)' }}
456-
tickLine={{ stroke: 'var(--border)' }}
463+
tickLine={{ stroke: 'var(--border)' }}
464+
domain={['dataMin', lastDateAvailable || 'dataMax']}
457465
/>
458466
<YAxis
459467
tick={{ fill: 'var(--foreground)' }}
@@ -559,7 +567,14 @@ function App() {
559567
</div>
560568

561569
<div>
562-
<h2 className="text-2xl font-semibold mb-2">Daily Usage Overview</h2>
570+
<div className="flex justify-between items-center mb-2">
571+
<h2 className="text-2xl font-semibold">Daily Usage Overview</h2>
572+
{lastDateAvailable && (
573+
<div className="text-sm text-muted-foreground">
574+
Data available through: <span className="font-medium">{lastDateAvailable}</span>
575+
</div>
576+
)}
577+
</div>
563578
<Separator className="mb-6" />
564579
<div className="bg-card p-4 rounded-lg border mb-8">
565580
<ChartContainer
@@ -574,7 +589,8 @@ function App() {
574589
<XAxis
575590
dataKey="date"
576591
tick={{ fill: 'var(--foreground)' }}
577-
tickLine={{ stroke: 'var(--border)' }}
592+
tickLine={{ stroke: 'var(--border)' }}
593+
domain={['dataMin', lastDateAvailable || 'dataMax']}
578594
/>
579595
<YAxis
580596
tick={{ fill: 'var(--foreground)' }}
@@ -640,7 +656,14 @@ function App() {
640656
</div>
641657

642658
{/* Bar Chart - Requests per Model per Day */}
643-
<h2 className="text-2xl font-semibold mb-2">Requests per Model per Day</h2>
659+
<div className="flex justify-between items-center mb-2">
660+
<h2 className="text-2xl font-semibold">Requests per Model per Day</h2>
661+
{lastDateAvailable && (
662+
<div className="text-sm text-muted-foreground">
663+
Data available through: <span className="font-medium">{lastDateAvailable}</span>
664+
</div>
665+
)}
666+
</div>
644667
<Separator className="mb-6" />
645668
<div className="bg-card p-4 rounded-lg border">
646669
<ChartContainer
@@ -653,6 +676,7 @@ function App() {
653676
dataKey="date"
654677
tick={{ fill: 'var(--foreground)' }}
655678
tickLine={{ stroke: 'var(--border)' }}
679+
domain={['dataMin', lastDateAvailable || 'dataMax']}
656680
/>
657681
<YAxis
658682
tick={{ fill: 'var(--foreground)' }}

src/lib/utils.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -317,3 +317,14 @@ export function getPowerUserDailyData(powerUsers: PowerUserData[]): Array<{
317317
.map(([date, requests]) => ({ date, requests }))
318318
.sort((a, b) => a.date.localeCompare(b.date));
319319
}
320+
321+
// Function to get the last date from CSV data
322+
export function getLastDateFromData(data: CopilotUsageData[]): string | null {
323+
if (!data.length) return null;
324+
325+
// Get all dates and find the maximum
326+
const dates = data.map(item => item.timestamp.toISOString().split('T')[0]);
327+
const sortedDates = dates.sort((a, b) => a.localeCompare(b));
328+
329+
return sortedDates[sortedDates.length - 1];
330+
}

src/main.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import App from './App.tsx'
44
import "./main.css"
55
import "./styles/theme.css"
66
import "./index.css"
7-
import "@github/spark/spark"
7+
// @github/spark/spark import is excluded and handled externally
88

99
createRoot(document.getElementById('root')!).render(
1010
<App />
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import { describe, it, expect } from 'vitest';
2+
import { getLastDateFromData, parseCSV } from '../lib/utils';
3+
4+
describe('Last Date Functionality', () => {
5+
const csvContent = `"Timestamp","User","Model","Requests Used","Exceeds Monthly Quota","Total Monthly Quota"
6+
"2025-06-10T05:13:27.8766440Z","user1","gpt-4.1-2025-04-14","1","False","Unlimited"
7+
"2025-06-11T05:09:40.8432110Z","user2","gpt-4.1-2025-04-14","1","False","Unlimited"
8+
"2025-06-09T10:30:00.0000000Z","user3","gpt-4.1-2025-04-14","2","True","Unlimited"
9+
"2025-06-12T15:45:00.0000000Z","user1","gpt-3.5-turbo","3","False","Unlimited"`;
10+
11+
it('should return the last date from CSV data', () => {
12+
const parsedData = parseCSV(csvContent);
13+
const lastDate = getLastDateFromData(parsedData);
14+
15+
expect(lastDate).toBe('2025-06-12');
16+
});
17+
18+
it('should return null for empty data', () => {
19+
const lastDate = getLastDateFromData([]);
20+
21+
expect(lastDate).toBeNull();
22+
});
23+
24+
it('should handle single record data', () => {
25+
const singleRecordCsv = `"Timestamp","User","Model","Requests Used","Exceeds Monthly Quota","Total Monthly Quota"
26+
"2025-06-15T08:00:00.0000000Z","user1","gpt-4.1-2025-04-14","1","False","Unlimited"`;
27+
28+
const parsedData = parseCSV(singleRecordCsv);
29+
const lastDate = getLastDateFromData(parsedData);
30+
31+
expect(lastDate).toBe('2025-06-15');
32+
});
33+
34+
it('should handle multiple records on the same day', () => {
35+
const sameDayCsv = `"Timestamp","User","Model","Requests Used","Exceeds Monthly Quota","Total Monthly Quota"
36+
"2025-06-10T05:13:27.8766440Z","user1","gpt-4.1-2025-04-14","1","False","Unlimited"
37+
"2025-06-10T15:30:00.0000000Z","user2","gpt-4.1-2025-04-14","2","False","Unlimited"
38+
"2025-06-10T23:59:59.9999999Z","user3","gpt-3.5-turbo","1","True","Unlimited"`;
39+
40+
const parsedData = parseCSV(sameDayCsv);
41+
const lastDate = getLastDateFromData(parsedData);
42+
43+
expect(lastDate).toBe('2025-06-10');
44+
});
45+
});

0 commit comments

Comments
 (0)