Skip to content

Commit 98c2bef

Browse files
committed
add responsive design
1 parent 08e394c commit 98c2bef

6 files changed

Lines changed: 239 additions & 151 deletions

File tree

App.tsx

Lines changed: 87 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { ExerciseView } from './components/ExerciseView';
88
import { HistoryView } from './components/HistoryView';
99
import { CSVImportModal } from './components/CSVImportModal';
1010
import { saveCSVData, getCSVData, hasCSVData, clearCSVData } from './utils/localStorage';
11-
import { LayoutDashboard, Dumbbell, History, Upload, BarChart3, Filter, Loader2, CheckCircle2, X, Trash2 } from 'lucide-react';
11+
import { LayoutDashboard, Dumbbell, History, Upload, BarChart3, Filter, Loader2, CheckCircle2, X, Trash2, Menu } from 'lucide-react';
1212
import { format, isSameDay } from 'date-fns';
1313

1414
enum Tab {
@@ -22,6 +22,7 @@ const App: React.FC = () => {
2222
const [parsedData, setParsedData] = useState<WorkoutSet[]>([]);
2323
const [activeTab, setActiveTab] = useState<Tab>(Tab.DASHBOARD);
2424
const [showCSVModal, setShowCSVModal] = useState(false);
25+
const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false);
2526

2627
// Loading State
2728
const [isAnalyzing, setIsAnalyzing] = useState(false);
@@ -179,43 +180,59 @@ const App: React.FC = () => {
179180

180181
{/* Top Header Navigation */}
181182
<header className="bg-slate-900 border-b border-slate-800 flex-shrink-0">
182-
<div className="px-6 py-4 flex flex-col gap-4">
183+
<div className="px-4 sm:px-6 py-4 flex flex-col gap-4">
183184
{/* Top Row: Logo and Nav Buttons */}
184185
<div className="flex items-center justify-between">
185-
<div className="flex items-center gap-4">
186-
<img src="/HevyAnalytics.png" alt="HevyAnalytics Logo" className="w-8 h-8" />
187-
<span className="font-bold text-xl tracking-tight text-white">HevyAnalytics</span>
186+
<div className="flex items-center gap-3 sm:gap-4">
187+
<img src="/HevyAnalytics.png" alt="HevyAnalytics Logo" className="w-7 h-7 sm:w-8 sm:h-8" />
188+
<span className="font-bold text-lg sm:text-xl tracking-tight text-white">HevyAnalytics</span>
188189
</div>
189190

190-
<nav className="flex items-center gap-2">
191+
{/* Desktop Navigation */}
192+
<nav className="hidden md:flex items-center gap-2">
191193
<button
192194
onClick={() => {
193195
setActiveTab(Tab.DASHBOARD);
194-
setSelectedDay(null); // Clear day filter when going back to Dashboard
196+
setSelectedDay(null);
197+
setIsMobileMenuOpen(false);
195198
}}
196199
className={`flex items-center gap-2 px-4 py-2 rounded-lg transition-all duration-200 ${activeTab === Tab.DASHBOARD ? 'bg-blue-600 text-white shadow-lg shadow-blue-900/50' : 'text-slate-400 hover:bg-slate-800 hover:text-white'}`}
197200
>
198201
<LayoutDashboard className="w-5 h-5" />
199202
<span className="font-medium">Dashboard</span>
200203
</button>
201204
<button
202-
onClick={() => setActiveTab(Tab.EXERCISES)}
205+
onClick={() => {
206+
setActiveTab(Tab.EXERCISES);
207+
setIsMobileMenuOpen(false);
208+
}}
203209
className={`flex items-center gap-2 px-4 py-2 rounded-lg transition-all duration-200 ${activeTab === Tab.EXERCISES ? 'bg-blue-600 text-white shadow-lg shadow-blue-900/50' : 'text-slate-400 hover:bg-slate-800 hover:text-white'}`}
204210
>
205211
<Dumbbell className="w-5 h-5" />
206212
<span className="font-medium">Exercises</span>
207213
</button>
208214
<button
209-
onClick={() => setActiveTab(Tab.HISTORY)}
215+
onClick={() => {
216+
setActiveTab(Tab.HISTORY);
217+
setIsMobileMenuOpen(false);
218+
}}
210219
className={`flex items-center gap-2 px-4 py-2 rounded-lg transition-all duration-200 ${activeTab === Tab.HISTORY ? 'bg-blue-600 text-white shadow-lg shadow-blue-900/50' : 'text-slate-400 hover:bg-slate-800 hover:text-white'}`}
211220
>
212221
<History className="w-5 h-5" />
213222
<span className="font-medium">History</span>
214223
</button>
215224
</nav>
216225

217-
{/* Action Buttons */}
218-
<div className="flex items-center gap-2">
226+
{/* Mobile Menu Button */}
227+
<button
228+
onClick={() => setIsMobileMenuOpen(!isMobileMenuOpen)}
229+
className="md:hidden p-2 rounded-lg hover:bg-slate-800 transition-colors text-slate-400 hover:text-white"
230+
>
231+
<Menu className="w-6 h-6" />
232+
</button>
233+
234+
{/* Action Buttons - Desktop */}
235+
<div className="hidden md:flex items-center gap-2">
219236
<label className="cursor-pointer group flex items-center gap-2 px-4 py-2 rounded-lg border border-dashed border-slate-600 hover:border-slate-400 hover:bg-slate-800/50 transition-all">
220237
<Upload className="w-4 h-4 text-slate-400 group-hover:text-white" />
221238
<span className="text-sm text-slate-400 group-hover:text-white">Import</span>
@@ -231,21 +248,74 @@ const App: React.FC = () => {
231248
</div>
232249
</div>
233250

251+
{/* Mobile Navigation Menu */}
252+
{isMobileMenuOpen && (
253+
<nav className="md:hidden flex flex-col gap-2 border-t border-slate-800 pt-4 mt-2">
254+
<button
255+
onClick={() => {
256+
setActiveTab(Tab.DASHBOARD);
257+
setSelectedDay(null);
258+
setIsMobileMenuOpen(false);
259+
}}
260+
className={`flex items-center gap-2 px-4 py-2 rounded-lg transition-all duration-200 w-full text-left ${activeTab === Tab.DASHBOARD ? 'bg-blue-600 text-white' : 'text-slate-400 hover:bg-slate-800 hover:text-white'}`}
261+
>
262+
<LayoutDashboard className="w-5 h-5" />
263+
<span className="font-medium">Dashboard</span>
264+
</button>
265+
<button
266+
onClick={() => {
267+
setActiveTab(Tab.EXERCISES);
268+
setIsMobileMenuOpen(false);
269+
}}
270+
className={`flex items-center gap-2 px-4 py-2 rounded-lg transition-all duration-200 w-full text-left ${activeTab === Tab.EXERCISES ? 'bg-blue-600 text-white' : 'text-slate-400 hover:bg-slate-800 hover:text-white'}`}
271+
>
272+
<Dumbbell className="w-5 h-5" />
273+
<span className="font-medium">Exercises</span>
274+
</button>
275+
<button
276+
onClick={() => {
277+
setActiveTab(Tab.HISTORY);
278+
setIsMobileMenuOpen(false);
279+
}}
280+
className={`flex items-center gap-2 px-4 py-2 rounded-lg transition-all duration-200 w-full text-left ${activeTab === Tab.HISTORY ? 'bg-blue-600 text-white' : 'text-slate-400 hover:bg-slate-800 hover:text-white'}`}
281+
>
282+
<History className="w-5 h-5" />
283+
<span className="font-medium">History</span>
284+
</button>
285+
286+
{/* Mobile Action Buttons */}
287+
<div className="flex flex-col gap-2 pt-2 border-t border-slate-800 mt-2">
288+
<label className="cursor-pointer group flex items-center gap-2 px-4 py-2 rounded-lg border border-dashed border-slate-600 hover:border-slate-400 hover:bg-slate-800/50 transition-all">
289+
<Upload className="w-4 h-4 text-slate-400 group-hover:text-white" />
290+
<span className="text-sm text-slate-400 group-hover:text-white">Import</span>
291+
<input type="file" accept=".csv" onChange={handleFileUpload} className="hidden" />
292+
</label>
293+
<button
294+
onClick={handleClearCSV}
295+
className="flex items-center gap-2 px-4 py-2 rounded-lg border border-dashed border-slate-600 hover:border-red-500 hover:bg-red-950/30 transition-all group"
296+
>
297+
<Trash2 className="w-4 h-4 text-slate-400 group-hover:text-red-400" />
298+
<span className="text-sm text-slate-400 group-hover:text-red-400">Remove</span>
299+
</button>
300+
</div>
301+
</nav>
302+
)}
303+
234304
{/* Filter Controls Row */}
235305
<div className="flex flex-col sm:flex-row sm:justify-between sm:items-center gap-3">
236306
<div>
237-
<h1 className="text-2xl font-bold text-white">
307+
<h1 className="text-xl sm:text-2xl font-bold text-white">
238308
{activeTab === Tab.DASHBOARD && 'Overview'}
239309
{activeTab === Tab.EXERCISES && 'Exercise Analytics'}
240310
{activeTab === Tab.HISTORY && 'Workout History'}
241311
</h1>
242-
<p className="text-slate-400 text-sm">
312+
<p className="text-slate-400 text-xs sm:text-sm">
243313
{filteredData.length > 0 ? `Analyzing ${filteredData.length} sets based on filters.` : 'No data available for current filters.'}
244314
</p>
245315
</div>
246316

247317
{/* Filter Controls */}
248-
<div className="flex flex-col sm:flex-row gap-3 bg-slate-950 p-2 rounded-xl border border-slate-800 shadow-sm items-start sm:items-center">
318+
<div className="flex flex-col sm:flex-row gap-2 sm:gap-3 bg-slate-950 p-2 rounded-xl border border-slate-800 shadow-sm items-start sm:items-center">
249319
<div className="flex items-center px-2">
250320
<Filter className="w-4 h-4 text-slate-500 mr-2" />
251321
<span className="text-xs font-bold text-slate-500 uppercase tracking-wide">Filters</span>
@@ -255,7 +325,7 @@ const App: React.FC = () => {
255325
{selectedDay && (
256326
<button
257327
onClick={() => setSelectedDay(null)}
258-
className="flex items-center gap-2 bg-blue-600 text-white text-sm px-3 py-2 rounded-lg hover:bg-blue-700 transition-colors"
328+
className="flex items-center gap-2 bg-blue-600 text-white text-xs sm:text-sm px-2 sm:px-3 py-2 rounded-lg hover:bg-blue-700 transition-colors"
259329
>
260330
<span>{format(selectedDay, 'MMM d, yyyy')}</span>
261331
<X className="w-3 h-3" />
@@ -267,7 +337,7 @@ const App: React.FC = () => {
267337
<select
268338
value={selectedMonth}
269339
onChange={(e) => setSelectedMonth(e.target.value)}
270-
className="bg-slate-900 text-slate-200 text-sm border border-slate-700 rounded-lg px-3 py-2 focus:outline-none focus:border-blue-500 hover:border-slate-600 transition-colors cursor-pointer"
340+
className="bg-slate-900 text-slate-200 text-xs sm:text-sm border border-slate-700 rounded-lg px-2 sm:px-3 py-2 focus:outline-none focus:border-blue-500 hover:border-slate-600 transition-colors cursor-pointer"
271341
>
272342
<option value="all">All Months</option>
273343
{availableMonths.map(month => (
@@ -283,7 +353,7 @@ const App: React.FC = () => {
283353
</header>
284354

285355
{/* Main Content Area */}
286-
<main className="flex-1 overflow-x-hidden overflow-y-auto bg-slate-950 p-6 lg:p-8">
356+
<main className="flex-1 overflow-x-hidden overflow-y-auto bg-slate-950 p-3 sm:p-4 md:p-6 lg:p-8">
287357

288358
{activeTab === Tab.DASHBOARD && (
289359
<Dashboard

components/CSVImportModal.tsx

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -36,14 +36,14 @@ export const CSVImportModal: React.FC<CSVImportModalProps> = ({ onFileSelect, is
3636
};
3737

3838
return (
39-
<div className="fixed inset-0 z-50 bg-slate-950/90 backdrop-blur-sm flex items-center justify-center p-4">
40-
<div className="w-full max-w-md bg-slate-900 border border-slate-800 rounded-2xl shadow-2xl p-8 flex flex-col items-center">
39+
<div className="fixed inset-0 z-50 bg-slate-950/90 backdrop-blur-sm flex items-center justify-center p-3 sm:p-4">
40+
<div className="w-full max-w-md bg-slate-900 border border-slate-800 rounded-2xl shadow-2xl p-6 sm:p-8 flex flex-col items-center">
4141
<div className="w-12 h-12 rounded-full bg-blue-600/20 flex items-center justify-center mb-6">
4242
<Upload className="w-6 h-6 text-blue-500" />
4343
</div>
4444

45-
<h2 className="text-2xl font-bold text-white mb-2 text-center">Welcome to HevyAnalytics</h2>
46-
<p className="text-slate-400 mb-8 text-center text-sm">
45+
<h2 className="text-xl sm:text-2xl font-bold text-white mb-2 text-center">Welcome to HevyAnalytics</h2>
46+
<p className="text-slate-400 mb-6 sm:mb-8 text-center text-xs sm:text-sm">
4747
Let's get started! Import your Hevy workout CSV to begin tracking your fitness journey.
4848
</p>
4949

@@ -52,11 +52,11 @@ export const CSVImportModal: React.FC<CSVImportModalProps> = ({ onFileSelect, is
5252
onDragOver={handleDragOver}
5353
onDrop={handleDrop}
5454
onClick={() => fileInputRef.current?.click()}
55-
className="w-full p-8 mb-6 border-2 border-dashed border-slate-600 rounded-xl hover:border-slate-400 hover:bg-slate-800/50 transition-all cursor-pointer flex flex-col items-center justify-center"
55+
className="w-full p-6 sm:p-8 mb-6 border-2 border-dashed border-slate-600 rounded-xl hover:border-slate-400 hover:bg-slate-800/50 transition-all cursor-pointer flex flex-col items-center justify-center"
5656
>
57-
<Upload className="w-8 h-8 text-slate-500 mb-3" />
58-
<p className="text-slate-300 font-medium text-center">Drag and drop your CSV here</p>
59-
<p className="text-slate-500 text-sm mt-1">or click to browse</p>
57+
<Upload className="w-6 h-6 sm:w-8 sm:h-8 text-slate-500 mb-3" />
58+
<p className="text-slate-300 font-medium text-center text-sm sm:text-base">Drag and drop your CSV here</p>
59+
<p className="text-slate-500 text-xs sm:text-sm mt-1">or click to browse</p>
6060
</div>
6161

6262
<input
@@ -74,15 +74,15 @@ export const CSVImportModal: React.FC<CSVImportModalProps> = ({ onFileSelect, is
7474
<AlertCircle className="w-4 h-4 text-slate-500 flex-shrink-0 mt-0.5" />
7575
<div>
7676
<p className="text-xs font-semibold text-slate-300 mb-1">Expected CSV Format</p>
77-
<p className="text-xs text-slate-400">
77+
<p className="text-[11px] sm:text-xs text-slate-400">
7878
Your CSV should include: title, start_time, end_time, description, exercise_title, superset_id, exercise_notes, set_index, set_type, weight_kg, reps, distance_km, duration_seconds, rpe
7979
</p>
8080
</div>
8181
</div>
8282
</div>
8383

8484
{isLoading && (
85-
<p className="text-slate-400 text-sm">
85+
<p className="text-slate-400 text-xs sm:text-sm">
8686
Loading your data...
8787
</p>
8888
)}

0 commit comments

Comments
 (0)