lewtun's picture
lewtun HF Staff
Persist
a2ef1ee
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;