Spaces:
Sleeping
Sleeping
File size: 5,243 Bytes
7601c3d a2ef1ee 7601c3d 22f54b5 7601c3d 8e23335 a2ef1ee 7601c3d a2ef1ee 7601c3d 22f54b5 7601c3d a2ef1ee 7601c3d a2ef1ee 7601c3d a2ef1ee 7601c3d 8e23335 7601c3d a2ef1ee 8e23335 7601c3d a2ef1ee 7601c3d 8e23335 | 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 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 | import { useEffect, useMemo, useState } from 'react';
import {
createSession,
deleteSession,
loadSessions,
updateSession,
} from './utils/storage';
import {
buildChartData,
buildGradeProgressData,
buildPainChartData,
computeMaxRoutesThreshold,
computeRecommendation,
computeWeeklySummary,
getWeekKey,
groupByWeek,
} from './utils/weekUtils';
import ClimbForm from './components/ClimbForm';
import WeeklySummary from './components/WeeklySummary';
import Charts from './components/Charts';
import SessionLog from './components/SessionLog';
import './App.css';
function App() {
const [sessions, setSessions] = useState([]);
const [isLoading, setIsLoading] = useState(true);
const [syncError, setSyncError] = useState('');
useEffect(() => {
let active = true;
async function bootstrapSessions() {
try {
const loaded = await loadSessions();
if (active) {
setSessions(Array.isArray(loaded) ? loaded : []);
setSyncError('');
}
} catch {
if (active) {
setSyncError('Could not load shared session data.');
}
} finally {
if (active) setIsLoading(false);
}
}
bootstrapSessions();
return () => {
active = false;
};
}, []);
const today = new Date().toISOString().split('T')[0];
const currentWeekKey = getWeekKey(today);
const weeklyGroups = useMemo(() => groupByWeek(sessions), [sessions]);
const chartData = useMemo(() => buildChartData(sessions), [sessions]);
const gradeProgressData = useMemo(() => buildGradeProgressData(sessions), [sessions]);
const painData = useMemo(() => buildPainChartData(sessions), [sessions]);
const allWeekKeys = useMemo(
() => [...new Set([...Object.keys(weeklyGroups), currentWeekKey])].sort(),
[weeklyGroups, currentWeekKey]
);
const [selectedWeekKey, setSelectedWeekKey] = useState(currentWeekKey);
useEffect(() => {
if (!allWeekKeys.includes(selectedWeekKey)) {
setSelectedWeekKey(currentWeekKey);
}
}, [allWeekKeys, selectedWeekKey, currentWeekKey]);
const selectedIdx = allWeekKeys.indexOf(selectedWeekKey);
const hasPrevWeek = selectedIdx > 0;
const hasNextWeek = selectedIdx >= 0 && selectedIdx < allWeekKeys.length - 1;
const selectedWeekSessions = weeklyGroups[selectedWeekKey] || [];
const selectedWeekSummary = computeWeeklySummary(selectedWeekSessions);
const weeksBeforeSelected = allWeekKeys.slice(0, Math.max(selectedIdx + 1, 1)).slice(-4);
const recentSummaries = weeksBeforeSelected.map((key) => computeWeeklySummary(weeklyGroups[key] || []));
const recommendation = computeRecommendation(recentSummaries);
const maxRoutes = computeMaxRoutesThreshold(sessions, today);
async function handleAddSession(newSession) {
const sessionWithId = { ...newSession, id: crypto.randomUUID() };
try {
const created = await createSession(sessionWithId);
setSessions((prev) => [created, ...prev.filter((session) => session.id !== created.id)]);
setSyncError('');
} catch {
setSyncError('Could not save session.');
}
}
async function handleEditSession(id, updatedFields) {
try {
const updated = await updateSession(id, updatedFields);
setSessions((prev) => prev.map((session) => (session.id === id ? updated : session)));
setSyncError('');
} catch {
setSyncError('Could not update session.');
}
}
async function handleDeleteSession(id) {
try {
await deleteSession(id);
setSessions((prev) => prev.filter((session) => session.id !== id));
setSyncError('');
} catch {
setSyncError('Could not delete session.');
}
}
return (
<div className="dashboard">
<header className="dashboard-header">
<h1>Climbing Dashboard</h1>
<p>Log each climbing session and track routes, load, and RPE over time.</p>
{syncError && <p className="sync-error">{syncError}</p>}
</header>
<main className="dashboard-main">
{isLoading ? (
<p className="loading-message">Loading shared sessions...</p>
) : (
<>
<div className="dashboard-top">
<ClimbForm onAddSession={handleAddSession} />
<WeeklySummary
summary={selectedWeekSummary}
recommendation={recommendation}
weekKey={selectedWeekKey}
isCurrentWeek={selectedWeekKey === currentWeekKey}
weeksOfData={weeksBeforeSelected.length}
maxRoutes={maxRoutes}
hasPrevWeek={hasPrevWeek}
hasNextWeek={hasNextWeek}
onPrevWeek={() => hasPrevWeek && setSelectedWeekKey(allWeekKeys[selectedIdx - 1])}
onNextWeek={() => hasNextWeek && setSelectedWeekKey(allWeekKeys[selectedIdx + 1])}
/>
</div>
<Charts data={chartData} gradeData={gradeProgressData} painData={painData} />
<SessionLog
sessions={sessions}
onEditSession={handleEditSession}
onDeleteSession={handleDeleteSession}
/>
</>
)}
</main>
</div>
);
}
export default App;
|