Update App.js, new modular structure

This commit is contained in:
2025-10-18 21:19:17 +02:00
parent b666b35fed
commit 0fb168b050
16 changed files with 1118 additions and 453 deletions

83
src/hooks/useAppState.js Normal file
View File

@@ -0,0 +1,83 @@
import React from 'react';
import { LOCAL_STORAGE_KEY, getDefaultState } from '../constants/defaults.js';
export const useAppState = () => {
const [appState, setAppState] = React.useState(getDefaultState);
const [isLoaded, setIsLoaded] = React.useState(false);
React.useEffect(() => {
try {
const savedState = window.localStorage.getItem(LOCAL_STORAGE_KEY);
if (savedState) {
const parsedState = JSON.parse(savedState);
const defaults = getDefaultState();
setAppState({
...defaults,
...parsedState,
pkParams: {...defaults.pkParams, ...parsedState.pkParams},
uiSettings: {...defaults.uiSettings, ...parsedState.uiSettings},
});
}
} catch (error) {
console.error("Failed to load state", error);
}
setIsLoaded(true);
}, []);
React.useEffect(() => {
if (isLoaded) {
try {
const stateToSave = {
pkParams: appState.pkParams,
doses: appState.doses,
steadyStateConfig: appState.steadyStateConfig,
therapeuticRange: appState.therapeuticRange,
doseIncrement: appState.doseIncrement,
uiSettings: appState.uiSettings,
};
window.localStorage.setItem(LOCAL_STORAGE_KEY, JSON.stringify(stateToSave));
} catch (error) {
console.error("Failed to save state", error);
}
}
}, [appState, isLoaded]);
const updateState = (key, value) => {
setAppState(prev => ({ ...prev, [key]: value }));
};
const updateNestedState = (parentKey, childKey, value) => {
setAppState(prev => ({
...prev,
[parentKey]: { ...prev[parentKey], [childKey]: value }
}));
};
const updateUiSetting = (key, value) => {
const newUiSettings = { ...appState.uiSettings, [key]: value };
if (key === 'simulationDays') {
const simDaysNum = parseInt(value, 10) || 1;
const dispDaysNum = parseInt(newUiSettings.displayedDays, 10) || 1;
if (dispDaysNum > simDaysNum) {
newUiSettings.displayedDays = String(simDaysNum);
}
}
setAppState(prev => ({ ...prev, uiSettings: newUiSettings }));
};
const handleReset = () => {
if (window.confirm("Bist du sicher, dass du alle Einstellungen auf die Standardwerte zurücksetzen möchtest? Dies kann nicht rückgängig gemacht werden.")) {
window.localStorage.removeItem(LOCAL_STORAGE_KEY);
window.location.reload();
}
};
return {
appState,
isLoaded,
updateState,
updateNestedState,
updateUiSetting,
handleReset
};
};

100
src/hooks/useSimulation.js Normal file
View File

@@ -0,0 +1,100 @@
import React from 'react';
import { calculateCombinedProfile } from '../utils/calculations.js';
import { generateSuggestion } from '../utils/suggestions.js';
import { timeToMinutes } from '../utils/timeUtils.js';
export const useSimulation = (appState) => {
const { pkParams, doses, steadyStateConfig, doseIncrement, uiSettings } = appState;
const { simulationDays } = uiSettings;
const [deviations, setDeviations] = React.useState([]);
const [suggestion, setSuggestion] = React.useState(null);
const calculateCombinedProfileMemo = React.useCallback(
(doseSchedule, deviationList = [], correction = null) =>
calculateCombinedProfile(
doseSchedule,
deviationList,
correction,
steadyStateConfig,
simulationDays,
pkParams
),
[steadyStateConfig, simulationDays, pkParams]
);
const generateSuggestionMemo = React.useCallback(() => {
const newSuggestion = generateSuggestion(
doses,
deviations,
doseIncrement,
simulationDays,
steadyStateConfig,
pkParams
);
setSuggestion(newSuggestion);
}, [doses, deviations, doseIncrement, simulationDays, steadyStateConfig, pkParams]);
React.useEffect(() => {
generateSuggestionMemo();
}, [generateSuggestionMemo]);
const idealProfile = React.useMemo(() =>
calculateCombinedProfileMemo(doses),
[doses, calculateCombinedProfileMemo]
);
const deviatedProfile = React.useMemo(() =>
deviations.length > 0 ? calculateCombinedProfileMemo(doses, deviations) : null,
[doses, deviations, calculateCombinedProfileMemo]
);
const correctedProfile = React.useMemo(() =>
suggestion && suggestion.dose ? calculateCombinedProfileMemo(doses, deviations, suggestion) : null,
[doses, deviations, suggestion, calculateCombinedProfileMemo]
);
const addDeviation = () => {
const sortedDoses = [...doses].sort((a,b) => timeToMinutes(a.time) - timeToMinutes(b.time));
let nextDose = sortedDoses[0] || { time: '08:00', dose: '25' };
if (deviations.length > 0) {
const lastDev = deviations[deviations.length - 1];
const lastDevTime = timeToMinutes(lastDev.time) + (lastDev.dayOffset || 0) * 24 * 60;
const nextPlanned = sortedDoses.find(d => timeToMinutes(d.time) > (lastDevTime % (24*60)));
if (nextPlanned) {
nextDose = { ...nextPlanned, dayOffset: lastDev.dayOffset };
} else {
nextDose = { ...sortedDoses[0], dayOffset: (lastDev.dayOffset || 0) + 1 };
}
}
setDeviations([...deviations, { ...nextDose, isAdditional: false, dayOffset: nextDose.dayOffset || 0 }]);
};
const removeDeviation = (index) => {
setDeviations(deviations.filter((_, i) => i !== index));
};
const handleDeviationChange = (index, field, value) => {
const newDeviations = [...deviations];
newDeviations[index][field] = value;
setDeviations(newDeviations);
};
const applySuggestion = () => {
if (!suggestion || !suggestion.dose) return;
setDeviations([...deviations, suggestion]);
setSuggestion(null);
};
return {
deviations,
suggestion,
idealProfile,
deviatedProfile,
correctedProfile,
addDeviation,
removeDeviation,
handleDeviationChange,
applySuggestion
};
};