Skip to content

Commit e75d5a2

Browse files
Boshenclaude
andcommitted
refactor: split cluttered pages into smaller, reusable components
- Extract DashboardPage components (StatCard, StatsGrid, charts, etc.) - Extract RolldownStats components (MetricNavigation, charts, StatsCards) - Extract MinificationBenchmarks components (LibraryBenchmarkCard, charts, stats) - Extract NpmDownloads components (PackageDownloadsList, NpmStatsCards) - Create shared PageHeader component for consistent page headers - Move data transformation logic to utility files - Improve code organization and maintainability 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
1 parent f8f6ed1 commit e75d5a2

25 files changed

+1023
-910
lines changed

apps/dashboard/src/MinificationBenchmarks.tsx

Lines changed: 8 additions & 225 deletions
Original file line numberDiff line numberDiff line change
@@ -1,236 +1,19 @@
1-
import { Bar, BarChart, CartesianGrid, LabelList, ResponsiveContainer, Tooltip, XAxis, YAxis } from 'recharts';
2-
import minificationData from '../../../minification-benchmarks-data.json';
1+
import { LibraryBenchmarkCard } from './components/minification/LibraryBenchmarkCard';
2+
import { MinificationStats } from './components/minification/MinificationStats';
3+
import { libraries } from './utils/minification-data';
34

4-
// Transform minification data for charts
5-
// Get popular minifiers for comparison
6-
const popularMinifiers = ['terser', 'esbuild', '@swc/core', 'uglify-js', 'oxc-minify'];
7-
8-
// Get library names from the data, sorted by size (largest first)
9-
const libraries = Object.entries(minificationData)
10-
.map(([name, data]: [string, any]) => ({ name, size: data.size }))
11-
.sort((a, b) => b.size - a.size)
12-
.map(item => item.name);
13-
14-
// Transform minification data for individual library charts
15-
const getLibraryData = (library: string, metric: 'time' | 'compression') => {
16-
const libraryData = (minificationData as any)[library];
17-
const data: any[] = [];
18-
19-
popularMinifiers.forEach(minifier => {
20-
const minifierData = libraryData.minified?.[minifier];
21-
if (minifierData?.result?.data) {
22-
let value: number;
23-
let minzippedBytes = 0;
24-
if (metric === 'time') {
25-
value = Math.round(minifierData.result.data.time || 0);
26-
} else {
27-
// compression ratio
28-
const originalSize = libraryData.size;
29-
minzippedBytes = minifierData.result.data.minzippedBytes || 0;
30-
value = Math.round(((originalSize - minzippedBytes) / originalSize) * 100 * 10) / 10;
31-
}
32-
33-
data.push({
34-
name: minifier === '@swc/core'
35-
? 'SWC'
36-
: minifier === 'uglify-js'
37-
? 'UglifyJS'
38-
: minifier === 'oxc-minify'
39-
? 'OXC'
40-
: minifier === 'esbuild'
41-
? 'ESBuild'
42-
: minifier === 'terser'
43-
? 'Terser'
44-
: minifier,
45-
value,
46-
minzippedBytes: minzippedBytes || 0,
47-
fill: minifier === 'terser'
48-
? '#a78bfa'
49-
: minifier === 'esbuild'
50-
? '#10b981'
51-
: minifier === '@swc/core'
52-
? '#38bdf8'
53-
: minifier === 'uglify-js'
54-
? '#f87171'
55-
: minifier === 'oxc-minify'
56-
? '#fbbf24'
57-
: '#9ca3af',
58-
});
59-
}
60-
});
61-
62-
// Sort data: time from smallest to largest (fastest to slowest), compression from largest to smallest (best to worst)
63-
return data.sort((a, b) => metric === 'time' ? a.value - b.value : a.minzippedBytes - b.minzippedBytes);
64-
};
65-
66-
interface MinificationBenchmarksProps {
67-
// Remove selectedMetric and setSelectedMetric since we'll show both metrics together
68-
}
69-
70-
function MinificationBenchmarks({}: MinificationBenchmarksProps) {
5+
function MinificationBenchmarks() {
716
return (
727
<>
738
<main className='max-w-6xl mx-auto px-8 py-8 flex flex-col gap-8'>
749
{/* Combined Charts for Each Library - Time and Compression Side by Side */}
7510
<div className='flex flex-col gap-8'>
76-
{libraries.map(library => {
77-
const timeData = getLibraryData(library, 'time');
78-
const compressionData = getLibraryData(library, 'compression');
79-
80-
return (
81-
<div
82-
key={library}
83-
className='bg-white dark:bg-slate-800 border border-gray-200 dark:border-slate-700 rounded-xl px-6 py-6 shadow-sm transition-all duration-300 hover:shadow-md hover:-translate-y-0.5'
84-
>
85-
<h3 className='mb-4 text-xl font-bold text-slate-800 dark:text-slate-100 capitalize text-center pb-2 border-b-2 border-slate-200 dark:border-slate-700'>
86-
{library}
87-
</h3>
88-
<div className='grid grid-cols-1 lg:grid-cols-2 gap-8 mt-4'>
89-
{/* Left column - Minification Time */}
90-
<div className='bg-slate-50 dark:bg-slate-900 border border-gray-200 dark:border-slate-700 rounded-lg p-4'>
91-
<h4 className='mb-4 text-base font-medium text-gray-700 dark:text-gray-300 text-center pb-2 border-b border-gray-200 dark:border-gray-700'>
92-
Minification Time
93-
</h4>
94-
<ResponsiveContainer width='100%' height={300}>
95-
<BarChart data={timeData} margin={{ top: 20, right: 30, left: 20, bottom: 5 }}>
96-
<CartesianGrid strokeDasharray='3 3' className='stroke-slate-200 dark:stroke-slate-700' />
97-
<XAxis
98-
dataKey='name'
99-
tick={{ fill: '#6b7280', fontSize: 11 }}
100-
axisLine={{ stroke: '#e5e7eb' }}
101-
tickLine={{ stroke: '#e5e7eb' }}
102-
angle={-45}
103-
textAnchor='end'
104-
height={60}
105-
/>
106-
<YAxis
107-
tick={{ fill: '#6b7280', fontSize: 11 }}
108-
axisLine={{ stroke: '#e5e7eb' }}
109-
tickLine={{ stroke: '#e5e7eb' }}
110-
/>
111-
<Tooltip
112-
contentStyle={{
113-
backgroundColor: 'var(--tooltip-bg)',
114-
border: '1px solid var(--tooltip-border)',
115-
borderRadius: '0.5rem',
116-
boxShadow: '0 4px 8px rgba(0, 0, 0, 0.1)',
117-
color: 'var(--tooltip-text)',
118-
}}
119-
labelStyle={{ color: 'var(--tooltip-text)' }}
120-
formatter={(value: any) => [`${value}ms`, 'Minification Time']}
121-
/>
122-
<Bar dataKey='value' fill='#60a5fa'>
123-
<LabelList
124-
dataKey='value'
125-
position='top'
126-
formatter={(label: React.ReactNode) => `${label}ms`}
127-
style={{ fontSize: '12px', fill: '#6b7280' }}
128-
/>
129-
</Bar>
130-
</BarChart>
131-
</ResponsiveContainer>
132-
</div>
133-
134-
{/* Right column - Compression Ratio */}
135-
<div className='bg-slate-50 dark:bg-slate-900 border border-gray-200 dark:border-slate-700 rounded-lg p-4'>
136-
<h4 className='mb-4 text-base font-medium text-gray-700 dark:text-gray-300 text-center pb-2 border-b border-gray-200 dark:border-gray-700'>
137-
Compression Ratio
138-
</h4>
139-
<ResponsiveContainer width='100%' height={300}>
140-
<BarChart data={compressionData} margin={{ top: 20, right: 30, left: 20, bottom: 5 }}>
141-
<CartesianGrid strokeDasharray='3 3' className='stroke-slate-200 dark:stroke-slate-700' />
142-
<XAxis
143-
dataKey='name'
144-
tick={{ fill: '#6b7280', fontSize: 11 }}
145-
axisLine={{ stroke: '#e5e7eb' }}
146-
tickLine={{ stroke: '#e5e7eb' }}
147-
angle={-45}
148-
textAnchor='end'
149-
height={60}
150-
/>
151-
<YAxis
152-
tick={{ fill: '#6b7280', fontSize: 11 }}
153-
axisLine={{ stroke: '#e5e7eb' }}
154-
tickLine={{ stroke: '#e5e7eb' }}
155-
/>
156-
<Tooltip
157-
contentStyle={{
158-
backgroundColor: 'var(--tooltip-bg)',
159-
border: '1px solid var(--tooltip-border)',
160-
borderRadius: '0.5rem',
161-
boxShadow: '0 4px 8px rgba(0, 0, 0, 0.1)',
162-
color: 'var(--tooltip-text)',
163-
}}
164-
labelStyle={{ color: 'var(--tooltip-text)' }}
165-
formatter={(value: any, _name: any, props: any) => [
166-
`${value}% (${props.payload.minzippedBytes} bytes)`,
167-
'Compression Ratio',
168-
]}
169-
/>
170-
<Bar dataKey='value' fill='#4ade80'>
171-
<LabelList
172-
dataKey='value'
173-
position='top'
174-
formatter={(label: React.ReactNode) => `${label}%`}
175-
style={{ fontSize: '10px', fill: '#6b7280' }}
176-
/>
177-
</Bar>
178-
</BarChart>
179-
</ResponsiveContainer>
180-
</div>
181-
</div>
182-
</div>
183-
);
184-
})}
11+
{libraries.map(library => (
12+
<LibraryBenchmarkCard key={library} library={library} />
13+
))}
18514
</div>
18615

187-
<div className='grid grid-cols-1 md:grid-cols-2 xl:grid-cols-4 gap-6'>
188-
{/* Minification statistics */}
189-
<div className='bg-white dark:bg-slate-800 border border-slate-200 dark:border-slate-700 px-7 py-7 rounded-xl shadow-sm border-l-4 border-l-blue-500 transition-all duration-200 hover:-translate-y-0.5 hover:shadow-md'>
190-
<h3 className='mb-3 text-slate-500 dark:text-slate-400 text-sm font-semibold uppercase tracking-widest'>
191-
Libraries Tested
192-
</h3>
193-
<p className='mb-3 text-4xl font-bold text-slate-800 dark:text-slate-100 tracking-tight leading-tight'>
194-
{Object.keys(minificationData).length}
195-
</p>
196-
<span className='text-sm font-semibold px-3 py-1.5 rounded-lg inline-flex items-center gap-1 text-emerald-700 bg-emerald-100 bg-opacity-100 border border-emerald-200 border-opacity-200'>
197-
JavaScript Libraries
198-
</span>
199-
</div>
200-
<div className='bg-white dark:bg-slate-800 border border-slate-200 dark:border-slate-700 px-7 py-7 rounded-xl shadow-sm border-l-4 border-l-blue-500 transition-all duration-200 hover:-translate-y-0.5 hover:shadow-md'>
201-
<h3 className='mb-3 text-slate-500 dark:text-slate-400 text-sm font-semibold uppercase tracking-widest'>
202-
Minifiers Compared
203-
</h3>
204-
<p className='mb-3 text-4xl font-bold text-slate-800 dark:text-slate-100 tracking-tight leading-tight'>
205-
{popularMinifiers.length}
206-
</p>
207-
<span className='text-sm font-semibold px-3 py-1.5 rounded-lg inline-flex items-center gap-1 text-emerald-700 bg-emerald-100 bg-opacity-100 border border-emerald-200 border-opacity-200'>
208-
Popular Tools
209-
</span>
210-
</div>
211-
<div className='bg-white dark:bg-slate-800 border border-slate-200 dark:border-slate-700 px-7 py-7 rounded-xl shadow-sm border-l-4 border-l-blue-500 transition-all duration-200 hover:-translate-y-0.5 hover:shadow-md'>
212-
<h3 className='mb-3 text-slate-500 dark:text-slate-400 text-sm font-semibold uppercase tracking-widest'>
213-
Fastest Minifier
214-
</h3>
215-
<p className='mb-3 text-4xl font-bold text-slate-800 dark:text-slate-100 tracking-tight leading-tight'>
216-
OXC
217-
</p>
218-
<span className='text-sm font-semibold px-3 py-1.5 rounded-lg inline-flex items-center gap-1 text-emerald-700 bg-emerald-100 bg-opacity-100 border border-emerald-200 border-opacity-200'>
219-
Rust-based
220-
</span>
221-
</div>
222-
<div className='bg-white dark:bg-slate-800 border border-slate-200 dark:border-slate-700 px-7 py-7 rounded-xl shadow-sm border-l-4 border-l-blue-500 transition-all duration-200 hover:-translate-y-0.5 hover:shadow-md'>
223-
<h3 className='mb-3 text-slate-500 dark:text-slate-400 text-sm font-semibold uppercase tracking-widest'>
224-
Best Compression
225-
</h3>
226-
<p className='mb-3 text-4xl font-bold text-slate-800 dark:text-slate-100 tracking-tight leading-tight'>
227-
UglifyJS
228-
</p>
229-
<span className='text-sm font-semibold px-3 py-1.5 rounded-lg inline-flex items-center gap-1 text-emerald-700 bg-emerald-100 bg-opacity-100 border border-emerald-200 border-opacity-200'>
230-
Traditional Leader
231-
</span>
232-
</div>
233-
</div>
16+
<MinificationStats />
23417
</main>
23518
</>
23619
);

apps/dashboard/src/NpmDownloads.tsx

Lines changed: 5 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
interface NpmDownloadsProps {}
1+
import { NpmStatsCards } from './components/npm/NpmStatsCards';
2+
import { PackageDownloadsList } from './components/npm/PackageDownloadsList';
23

34
// List of npm packages to display download counts for
45
const packages = [
@@ -13,11 +14,7 @@ const packages = [
1314
'oxc-resolver',
1415
];
1516

16-
function NpmDownloads({}: NpmDownloadsProps) {
17-
const handleCardClick = (packageName: string) => {
18-
const npmUrl = `https://www.npmjs.com/package/${packageName}`;
19-
window.open(npmUrl, '_blank', 'noopener,noreferrer');
20-
};
17+
function NpmDownloads() {
2118

2219
return (
2320
<>
@@ -27,83 +24,10 @@ function NpmDownloads({}: NpmDownloadsProps) {
2724
NPM Weekly Downloads
2825
</h2>
2926

30-
<div className='mx-auto mb-8 rounded-lg border border-slate-200 dark:border-slate-700 bg-white dark:bg-slate-800 shadow-sm max-w-lg w-fit'>
31-
<ul className='list-none p-0 m-0'>
32-
{packages.map((packageName) => (
33-
<li
34-
key={packageName}
35-
className='flex items-center justify-between px-4 py-2 border-b border-slate-200 dark:border-slate-700 transition-all duration-200 cursor-pointer gap-3 min-w-fit hover:bg-slate-50 dark:hover:bg-slate-700 focus:outline-2 focus:outline-blue-500 focus:outline-offset-[-2px] focus:bg-slate-50 dark:focus:bg-slate-700 last:border-b-0'
36-
onClick={() => handleCardClick(packageName)}
37-
role='button'
38-
tabIndex={0}
39-
onKeyDown={(e) => {
40-
if (e.key === 'Enter' || e.key === ' ') {
41-
e.preventDefault();
42-
handleCardClick(packageName);
43-
}
44-
}}
45-
>
46-
<span className='font-mono text-sm font-medium text-gray-700 dark:text-gray-300 bg-slate-100 dark:bg-slate-700 px-2 py-1 rounded border border-slate-300 dark:border-slate-600 min-w-fit whitespace-nowrap'>
47-
{packageName}
48-
</span>
49-
<img
50-
className='h-auto max-h-5 flex-shrink-0'
51-
src={`https://img.shields.io/npm/dw/${packageName}?label=npm`}
52-
alt={`Weekly downloads for ${packageName}`}
53-
loading='lazy'
54-
/>
55-
</li>
56-
))}
57-
</ul>
58-
</div>
27+
<PackageDownloadsList packages={packages} />
5928
</div>
6029

61-
<div className='grid grid-cols-1 md:grid-cols-2 xl:grid-cols-4 gap-6'>
62-
<div className='bg-white dark:bg-slate-800 border border-slate-200 dark:border-slate-700 px-7 py-7 rounded-xl shadow-sm border-l-4 border-l-blue-500 transition-all duration-200 hover:-translate-y-0.5 hover:shadow-md'>
63-
<h3 className='mb-3 text-slate-500 dark:text-slate-400 text-sm font-semibold uppercase tracking-widest'>
64-
Total Packages
65-
</h3>
66-
<p className='mb-3 text-4xl font-bold text-slate-800 dark:text-slate-100 tracking-tight leading-tight'>
67-
{packages.length}
68-
</p>
69-
<span className='text-sm font-semibold px-3 py-1.5 rounded-lg inline-flex items-center gap-1 text-emerald-700 bg-emerald-100 bg-opacity-100 border border-emerald-200 border-opacity-200'>
70-
NPM Packages
71-
</span>
72-
</div>
73-
<div className='bg-white dark:bg-slate-800 border border-slate-200 dark:border-slate-700 px-7 py-7 rounded-xl shadow-sm border-l-4 border-l-blue-500 transition-all duration-200 hover:-translate-y-0.5 hover:shadow-md'>
74-
<h3 className='mb-3 text-slate-500 dark:text-slate-400 text-sm font-semibold uppercase tracking-widest'>
75-
Registry
76-
</h3>
77-
<p className='mb-3 text-4xl font-bold text-slate-800 dark:text-slate-100 tracking-tight leading-tight'>
78-
NPM
79-
</p>
80-
<span className='text-sm font-semibold px-3 py-1.5 rounded-lg inline-flex items-center gap-1 text-emerald-700 bg-emerald-100 bg-opacity-100 border border-emerald-200 border-opacity-200'>
81-
Public Registry
82-
</span>
83-
</div>
84-
<div className='bg-white dark:bg-slate-800 border border-slate-200 dark:border-slate-700 px-7 py-7 rounded-xl shadow-sm border-l-4 border-l-blue-500 transition-all duration-200 hover:-translate-y-0.5 hover:shadow-md'>
85-
<h3 className='mb-3 text-slate-500 dark:text-slate-400 text-sm font-semibold uppercase tracking-widest'>
86-
Update Frequency
87-
</h3>
88-
<p className='mb-3 text-4xl font-bold text-slate-800 dark:text-slate-100 tracking-tight leading-tight'>
89-
Weekly
90-
</p>
91-
<span className='text-sm font-semibold px-3 py-1.5 rounded-lg inline-flex items-center gap-1 text-emerald-700 bg-emerald-100 bg-opacity-100 border border-emerald-200 border-opacity-200'>
92-
Auto Updated
93-
</span>
94-
</div>
95-
<div className='bg-white dark:bg-slate-800 border border-slate-200 dark:border-slate-700 px-7 py-7 rounded-xl shadow-sm border-l-4 border-l-blue-500 transition-all duration-200 hover:-translate-y-0.5 hover:shadow-md'>
96-
<h3 className='mb-3 text-slate-500 dark:text-slate-400 text-sm font-semibold uppercase tracking-widest'>
97-
Data Source
98-
</h3>
99-
<p className='mb-3 text-4xl font-bold text-slate-800 dark:text-slate-100 tracking-tight leading-tight'>
100-
Shields.io
101-
</p>
102-
<span className='text-sm font-semibold px-3 py-1.5 rounded-lg inline-flex items-center gap-1 text-emerald-700 bg-emerald-100 bg-opacity-100 border border-emerald-200 border-opacity-200'>
103-
Live Data
104-
</span>
105-
</div>
106-
</div>
30+
<NpmStatsCards packagesCount={packages.length} />
10731
</main>
10832
</>
10933
);

0 commit comments

Comments
 (0)