Skip to content

Commit de51c73

Browse files
committed
Refactor code structure for improved readability and maintainability
1 parent a6b586a commit de51c73

File tree

7 files changed

+2322
-75
lines changed

7 files changed

+2322
-75
lines changed

src/app/page.tsx

Lines changed: 204 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
"use client";
22

3-
import { useState } from "react";
3+
import { useState, useCallback } from "react";
44
import { Navigation } from "@/components/ui/Navigation";
55
import { FileUpload } from "@/components/ui/FileUpload";
66
import { BillingChart } from "@/components/charts/BillingChart";
7-
import { GitHubBillingReport, BillingData } from "@/types/billing";
7+
import { ServiceChart } from "@/components/charts/ServiceChart";
8+
import { Tabs } from "@/components/ui/Tabs";
9+
import { DataFilters } from "@/components/ui/DataFilters";
10+
import { GitHubBillingReport, BillingData, CategorizedBillingData, ServiceData } from "@/types/billing";
811

912
const sampleBillingData: BillingData[] = [
1013
{ month: "Jan", actions: 120, packages: 80, storage: 40 },
@@ -18,55 +21,222 @@ const sampleBillingData: BillingData[] = [
1821
export default function Home() {
1922
const [billingData, setBillingData] =
2023
useState<BillingData[]>(sampleBillingData);
24+
const [categorizedData, setCategorizedData] = useState<CategorizedBillingData | null>(null);
25+
const [filteredData, setFilteredData] = useState<CategorizedBillingData | null>(null);
2126
const [hasUploadedData, setHasUploadedData] = useState(false);
2227

2328
const handleDataLoaded = (report: GitHubBillingReport) => {
2429
setBillingData(report.data);
30+
setCategorizedData(report.categorizedData || null);
31+
setFilteredData(report.categorizedData || null);
2532
setHasUploadedData(true);
2633
};
2734

35+
const handleFiltersChange = useCallback((serviceType: keyof CategorizedBillingData, filteredServiceData: ServiceData[]) => {
36+
setFilteredData(prev => {
37+
if (!prev) return null;
38+
return {
39+
...prev,
40+
[serviceType]: filteredServiceData
41+
};
42+
});
43+
}, []);
44+
45+
// Create tabs based on available data
46+
const createTabs = () => {
47+
if (!categorizedData || !filteredData) {
48+
return [
49+
{
50+
id: "overview",
51+
label: "Sample Overview",
52+
content: (
53+
<BillingChart
54+
data={billingData}
55+
title="Sample GitHub Billing Visualization"
56+
/>
57+
),
58+
},
59+
];
60+
}
61+
62+
return [
63+
{
64+
id: "actionsMinutes",
65+
label: "Actions Minutes",
66+
content: (
67+
<div>
68+
<DataFilters
69+
data={categorizedData.actionsMinutes}
70+
onFiltersChange={(filtered) => handleFiltersChange("actionsMinutes", filtered)}
71+
/>
72+
<ServiceChart
73+
data={filteredData.actionsMinutes}
74+
title="GitHub Actions Minutes"
75+
serviceType="actionsMinutes"
76+
useSkuAnalysis={(() => {
77+
// Use SKU analysis when all organizations are shown (no organization filter applied)
78+
const originalOrgs = new Set(categorizedData.actionsMinutes.map(item => item.organization).filter(Boolean));
79+
const filteredOrgs = new Set(filteredData.actionsMinutes.map(item => item.organization).filter(Boolean));
80+
return originalOrgs.size === filteredOrgs.size && originalOrgs.size > 1;
81+
})()}
82+
/>
83+
</div>
84+
),
85+
},
86+
{
87+
id: "actionsStorage",
88+
label: "Actions Storage",
89+
content: (
90+
<div>
91+
<DataFilters
92+
data={categorizedData.actionsStorage}
93+
onFiltersChange={(filtered) => handleFiltersChange("actionsStorage", filtered)}
94+
/>
95+
<ServiceChart
96+
data={filteredData.actionsStorage}
97+
title="GitHub Actions Storage"
98+
serviceType="actionsStorage"
99+
/>
100+
</div>
101+
),
102+
},
103+
{
104+
id: "packages",
105+
label: "Packages",
106+
content: (
107+
<div>
108+
<DataFilters
109+
data={categorizedData.packages}
110+
onFiltersChange={(filtered) => handleFiltersChange("packages", filtered)}
111+
/>
112+
<ServiceChart
113+
data={filteredData.packages}
114+
title="GitHub Packages"
115+
serviceType="packages"
116+
/>
117+
</div>
118+
),
119+
},
120+
{
121+
id: "copilot",
122+
label: "Copilot",
123+
content: (
124+
<div>
125+
<DataFilters
126+
data={categorizedData.copilot}
127+
onFiltersChange={(filtered) => handleFiltersChange("copilot", filtered)}
128+
/>
129+
<ServiceChart
130+
data={filteredData.copilot}
131+
title="GitHub Copilot"
132+
serviceType="copilot"
133+
/>
134+
</div>
135+
),
136+
},
137+
{
138+
id: "codespaces",
139+
label: "Codespaces",
140+
content: (
141+
<div>
142+
<DataFilters
143+
data={categorizedData.codespaces}
144+
onFiltersChange={(filtered) => handleFiltersChange("codespaces", filtered)}
145+
/>
146+
<ServiceChart
147+
data={filteredData.codespaces}
148+
title="GitHub Codespaces"
149+
serviceType="codespaces"
150+
/>
151+
</div>
152+
),
153+
},
154+
].filter(tab => {
155+
// Only show tabs with data
156+
switch (tab.id) {
157+
case "actionsMinutes":
158+
return categorizedData.actionsMinutes.length > 0;
159+
case "actionsStorage":
160+
return categorizedData.actionsStorage.length > 0;
161+
case "packages":
162+
return categorizedData.packages.length > 0;
163+
case "copilot":
164+
return categorizedData.copilot.length > 0;
165+
case "codespaces":
166+
return categorizedData.codespaces.length > 0;
167+
default:
168+
return true;
169+
}
170+
});
171+
};
172+
173+
const tabs = createTabs();
174+
28175
return (
29176
<div className="min-h-screen bg-gray-950 text-white">
30177
<Navigation />
31178

32179
{/* Main Content */}
33180
<section className="relative py-32 px-4 sm:px-6 lg:px-8">
34-
<div className="max-w-4xl mx-auto text-center">
35-
<h1 className="text-5xl md:text-7xl font-bold mb-8 bg-gradient-to-r from-white to-gray-400 bg-clip-text text-transparent">
36-
Understand Your
37-
<br />
38-
<span className="bg-gradient-to-r from-green-400 to-blue-400 bg-clip-text text-transparent">
39-
GitHub Costs
40-
</span>
41-
</h1>
181+
<div className="max-w-7xl mx-auto">
182+
{!hasUploadedData ? (
183+
/* Landing Content - Only shown when no data is uploaded */
184+
<div className="text-center">
185+
<h1 className="text-5xl md:text-7xl font-bold mb-8 bg-gradient-to-r from-white to-gray-400 bg-clip-text text-transparent">
186+
Understand Your
187+
<br />
188+
<span className="bg-gradient-to-r from-green-400 to-blue-400 bg-clip-text text-transparent">
189+
GitHub Costs
190+
</span>
191+
</h1>
42192

43-
<p className="text-xl text-gray-400 mb-12 max-w-2xl mx-auto leading-relaxed">
44-
Upload your GitHub billing report to visualize spending patterns and
45-
optimize costs.
46-
<br />
47-
<span className="text-sm text-gray-500 mt-2 block">
48-
Your data is processed locally and not stored on our servers.
49-
</span>
50-
</p>
193+
<p className="text-xl text-gray-400 mb-12 max-w-2xl mx-auto leading-relaxed">
194+
Upload your GitHub billing report to visualize spending patterns and
195+
optimize costs across all services.
196+
<br />
197+
<span className="text-sm text-gray-500 mt-2 block">
198+
Your data is processed locally and not stored on our servers.
199+
</span>
200+
</p>
51201

52-
{/* File Upload */}
53-
<div className="mb-16">
54-
<FileUpload onDataLoaded={handleDataLoaded} />
55-
</div>
202+
{/* File Upload */}
203+
<div className="mb-16">
204+
<FileUpload onDataLoaded={handleDataLoaded} />
205+
</div>
206+
</div>
207+
) : (
208+
/* Analysis Content - Shown after upload */
209+
<div>
210+
{/* Back Button */}
211+
<div className="mb-8">
212+
<button
213+
onClick={() => {
214+
setHasUploadedData(false);
215+
setCategorizedData(null);
216+
setFilteredData(null);
217+
setBillingData(sampleBillingData);
218+
}}
219+
className="inline-flex items-center px-4 py-2 text-sm font-medium text-gray-300 bg-gray-800/50 border border-gray-600 rounded-lg hover:bg-gray-700/50 hover:border-gray-500 transition-colors"
220+
>
221+
<svg className="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
222+
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M10 19l-7-7m0 0l7-7m-7 7h18" />
223+
</svg>
224+
Upload New File
225+
</button>
226+
</div>
56227

57-
{/* Chart Visualization */}
58-
<div className="max-w-6xl mx-auto">
59-
<div className="bg-gray-800/50 backdrop-blur-sm border border-gray-700 rounded-xl p-8">
60-
<BillingChart
61-
data={billingData}
62-
title={
63-
hasUploadedData
64-
? "Your GitHub Billing Data"
65-
: "Sample GitHub Billing Visualization"
66-
}
67-
/>
228+
{/* Full Width Visualization */}
229+
<div className="bg-gray-800/50 backdrop-blur-sm border border-gray-700 rounded-xl p-8">
230+
<div className="mb-6">
231+
<h2 className="text-2xl font-bold mb-2">Service Breakdown</h2>
232+
<p className="text-gray-400">
233+
Detailed cost and usage analysis by GitHub service
234+
</p>
235+
</div>
236+
<Tabs tabs={tabs} defaultTab="actionsMinutes" />
237+
</div>
68238
</div>
69-
</div>
239+
)}
70240
</div>
71241

72242
{/* Background decoration */}

0 commit comments

Comments
 (0)