Skip to content

Commit a23e73c

Browse files
committed
Fix settings form: prevent raceType/computedPlanWeeks from overwriting loaded values (peak mileage, max long run, start mileage)
1 parent a40f168 commit a23e73c

1 file changed

Lines changed: 39 additions & 20 deletions

File tree

Web/src/components/PlanSetupForm.tsx

Lines changed: 39 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,7 @@ export default function PlanSetupForm({
201201
const hasExistingCalibration = useRef(false);
202202
// Track whether we've loaded settings from the server (to prevent race-type defaults from overriding them)
203203
const settingsLoadedRef = useRef(false);
204-
const initialRaceTypeRef = useRef<string | null>(null);
204+
205205

206206
const [message, setMessage] = useState('');
207207
const [formErrors, setFormErrors] = useState<Record<string, string>>({});
@@ -265,17 +265,16 @@ export default function PlanSetupForm({
265265
const shapePercent = propShapePercent > 0 ? propShapePercent : (internalStatsData?.marathonShape?.shape || 0);
266266

267267
// Auto-initialize startWeeklyMileage from the user's last 4 weeks of running
268-
// Skip in settings mode — the goal's saved value will be loaded instead
268+
// startWeeklyMileage is not persisted in the Goal model, so we always derive it
269269
useEffect(() => {
270-
if (mode === 'settings') return;
271270
if (!startMileageInitialized.current && internalStatsData?.avgWeeklyKmLast4Weeks > 0) {
272271
const avg = internalStatsData.avgWeeklyKmLast4Weeks;
273272
// Round down to nearest 5 km for a clean slider value
274273
const rounded = Math.floor(avg / 5) * 5;
275274
setStartWeeklyMileage(Math.max(5, rounded));
276275
startMileageInitialized.current = true;
277276
}
278-
}, [internalStatsData, mode]);
277+
}, [internalStatsData]);
279278

280279
// Fetch existing settings (for settings mode)
281280
const { data: settingsData } = useQuery({
@@ -393,28 +392,30 @@ export default function PlanSetupForm({
393392
? (parseInt(durationWeeks) || 12)
394393
: Math.max(4, Math.floor((new Date(raceDate).getTime() - new Date(planStartDate).getTime()) / msPerWeek));
395394

395+
// Track whether the user has explicitly changed raceType after settings loaded
396+
const userChangedRaceTypeRef = useRef(false);
397+
// Track whether the user has explicitly changed weeklyMileage or raceType after settings loaded
398+
const userChangedVolumeRef = useRef(false);
399+
396400
const maxLongRunLoadedRef = useRef(false);
397401
useEffect(() => {
398-
// In settings mode, skip the first recalculation — the API value was loaded
399-
if (mode === 'settings' && settingsLoadedRef.current && !maxLongRunLoadedRef.current) {
400-
maxLongRunLoadedRef.current = true;
401-
return;
402+
// In settings mode, only recalculate maxLongRunKm when the user explicitly changed volume/race
403+
if (mode === 'settings') {
404+
if (!userChangedVolumeRef.current) {
405+
return; // Don't recalculate from defaults — API value is correct
406+
}
402407
}
403408
setMaxLongRunKm(getDefaultMaxLongRunKm(raceType, weeklyMileage));
404409
}, [raceType, weeklyMileage, mode]);
405410

406411
useEffect(() => {
407412
// In settings mode, only apply race-type defaults when the user explicitly changes the race type.
408413
// The settingsData effect already loaded the correct saved values from the API.
409-
if (mode === 'settings' && !settingsLoadedRef.current) return;
410-
if (mode === 'settings' && initialRaceTypeRef.current !== null) {
411-
// First raceType change from goalsData — skip and clear the guard
412-
if (raceType === initialRaceTypeRef.current) {
413-
initialRaceTypeRef.current = null;
414-
return;
414+
// We block ALL default overwrites until the user explicitly changes raceType.
415+
if (mode === 'settings') {
416+
if (!userChangedRaceTypeRef.current) {
417+
return; // Skip — settings were loaded from API, no user change yet
415418
}
416-
// Different raceType — user changed it, allow defaults to apply
417-
initialRaceTypeRef.current = null;
418419
}
419420
let defaults = getRaceDefaults(raceType);
420421
if (effectiveVO2max > 0) {
@@ -436,6 +437,8 @@ export default function PlanSetupForm({
436437
setTargetLaps(defaults.targetLaps);
437438
}
438439
setTriGoalTimeSeconds(null);
440+
// Mark volume as changed too, so maxLongRunKm recalculates on next render
441+
userChangedVolumeRef.current = true;
439442
}, [raceType, mode, computedPlanWeeks]);
440443

441444
// Auto-prefill threshold values from calibration data while still allowing manual overrides.
@@ -497,7 +500,7 @@ export default function PlanSetupForm({
497500
if (goalsData?.goals?.length > 0) {
498501
const activeGoal = goalsData.goals.find((g: any) => g.isActive);
499502
if (activeGoal) {
500-
initialRaceTypeRef.current = activeGoal.raceType || 'MARATHON';
503+
501504
setGoalName(activeGoal.name || 'My Race');
502505
setRaceType(activeGoal.raceType || 'MARATHON');
503506
if (activeGoal.raceDate) {
@@ -875,7 +878,13 @@ export default function PlanSetupForm({
875878
goalName={goalName}
876879
setGoalName={setGoalName}
877880
raceType={raceType}
878-
setRaceType={setRaceType}
881+
setRaceType={(val: string) => {
882+
if (mode === 'settings' && settingsLoadedRef.current) {
883+
userChangedRaceTypeRef.current = true;
884+
userChangedVolumeRef.current = true;
885+
}
886+
setRaceType(val);
887+
}}
879888
raceDate={raceDate}
880889
setRaceDate={setRaceDate}
881890
planStartDate={planStartDate}
@@ -1004,10 +1013,20 @@ export default function PlanSetupForm({
10041013
strengthPerWeek={strengthPerWeek}
10051014
setStrengthPerWeek={setStrengthPerWeek}
10061015
weeklyMileage={weeklyMileage}
1007-
setWeeklyMileage={setWeeklyMileage}
1016+
setWeeklyMileage={(val: number) => {
1017+
if (mode === 'settings' && settingsLoadedRef.current) {
1018+
userChangedVolumeRef.current = true;
1019+
}
1020+
setWeeklyMileage(val);
1021+
}}
10081022
raceType={raceType}
10091023
maxLongRunKm={maxLongRunKm}
1010-
setMaxLongRunKm={setMaxLongRunKm}
1024+
setMaxLongRunKm={(val: number) => {
1025+
if (mode === 'settings' && settingsLoadedRef.current) {
1026+
userChangedVolumeRef.current = true;
1027+
}
1028+
setMaxLongRunKm(val);
1029+
}}
10111030
taperWeeks={taperWeeks}
10121031
setTaperWeeks={setTaperWeeks}
10131032
peakWeeks={peakWeeks}

0 commit comments

Comments
 (0)