Spaces:
Running
Running
File size: 4,539 Bytes
3d4aa49 e72c2c6 3d4aa49 ac30c1e 359f0ff 3d4aa49 dbc0348 e72c2c6 3d4aa49 e72c2c6 3d4aa49 7b6976e 49699c7 ac30c1e 359f0ff 49699c7 e72c2c6 3d4aa49 e72c2c6 49699c7 e72c2c6 3d4aa49 dbc0348 3d4aa49 e72c2c6 dbc0348 3d4aa49 e72c2c6 3d4aa49 dbc0348 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 | 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 (
<div className="dashboard">
<header className="dashboard-header">
<h1>Running Dashboard</h1>
{syncError && <p className="sync-error">{syncError}</p>}
</header>
<main className="dashboard-main">
{isLoading ? (
<p className="loading-message">Loading shared runs...</p>
) : (
<>
<div className="dashboard-top">
<RunForm onAddRun={handleAddRun} />
<WeeklySummary
summary={selectedWeekSummary}
recommendation={recommendation}
weekKey={selectedWeekKey}
isCurrentWeek={selectedWeekKey === currentWeekKey}
weeksOfData={weeksBeforeSelected.length}
longestRun={longestRunData}
hasPrevWeek={hasPrevWeek}
hasNextWeek={hasNextWeek}
onPrevWeek={() => hasPrevWeek && setSelectedWeekKey(allWeekKeys[selectedIdx - 1])}
onNextWeek={() => hasNextWeek && setSelectedWeekKey(allWeekKeys[selectedIdx + 1])}
/>
</div>
<Charts data={chartData} painData={painChartData} />
<RunLog runs={runs} onEditRun={handleEditRun} onDeleteRun={handleDeleteRun} />
</>
)}
</main>
</div>
);
}
export default App;
|