import { useState, useEffect } from 'react'; import { createRun, deleteRun, loadRuns, updateRun } from './utils/storage'; import { getWeekKey, groupByWeek, computeWeeklySummary, computeRecommendation, buildChartData, computeLongestRunThreshold, buildPainChartData, } from './utils/weekUtils'; import RunForm from './components/RunForm'; import WeeklySummary from './components/WeeklySummary'; import Charts from './components/Charts'; import RunLog from './components/RunLog'; import './App.css'; function App() { const [runs, setRuns] = useState([]); const [isLoading, setIsLoading] = useState(true); const [syncError, setSyncError] = useState(''); useEffect(() => { let active = true; async function bootstrapRuns() { try { const loaded = await loadRuns(); if (active) { setRuns(Array.isArray(loaded) ? loaded : []); setSyncError(''); } } catch { if (active) { setSyncError('Could not load shared run data.'); } } finally { if (active) setIsLoading(false); } } bootstrapRuns(); return () => { active = false; }; }, []); const today = new Date().toISOString().split('T')[0]; const currentWeekKey = getWeekKey(today); const weeklyGroups = groupByWeek(runs); const chartData = buildChartData(runs); // Week navigation: all weeks that have data, plus the current week const allWeekKeys = [...new Set([...Object.keys(weeklyGroups), currentWeekKey])].sort(); const [selectedWeekKey, setSelectedWeekKey] = useState(currentWeekKey); const selectedIdx = allWeekKeys.indexOf(selectedWeekKey); const hasPrevWeek = selectedIdx > 0; const hasNextWeek = selectedIdx < allWeekKeys.length - 1; const selectedWeekRuns = weeklyGroups[selectedWeekKey] || []; const selectedWeekSummary = computeWeeklySummary(selectedWeekRuns); // ACWR: get up to 4 weeks before the selected week for recommendation const weeksBeforeSelected = allWeekKeys.slice(0, selectedIdx + 1).slice(-4); const recentSummaries = weeksBeforeSelected.map((key) => computeWeeklySummary(weeklyGroups[key] || [])); const recommendation = computeRecommendation(recentSummaries); const longestRunData = computeLongestRunThreshold(runs, today); const painChartData = buildPainChartData(runs); async function handleAddRun(newRun) { const runWithId = { ...newRun, id: crypto.randomUUID() }; try { const created = await createRun(runWithId); setRuns((prev) => [created, ...prev.filter((run) => run.id !== created.id)]); setSyncError(''); } catch { setSyncError('Could not save run.'); } } async function handleEditRun(id, updatedFields) { try { const updated = await updateRun(id, updatedFields); setRuns((prev) => prev.map((r) => (r.id === id ? updated : r)) ); setSyncError(''); } catch { setSyncError('Could not update run.'); } } async function handleDeleteRun(id) { try { await deleteRun(id); setRuns((prev) => prev.filter((r) => r.id !== id)); setSyncError(''); } catch { setSyncError('Could not delete run.'); } } return (

Running Dashboard

{syncError &&

{syncError}

}
{isLoading ? (

Loading shared runs...

) : ( <>
hasPrevWeek && setSelectedWeekKey(allWeekKeys[selectedIdx - 1])} onNextWeek={() => hasNextWeek && setSelectedWeekKey(allWeekKeys[selectedIdx + 1])} />
)}
); } export default App;