Update settings add min<=max validation to ranges, minor text changes

This commit is contained in:
2026-02-02 13:17:35 +00:00
parent 90b0806cec
commit 02b1209c2d
4 changed files with 42 additions and 7 deletions

View File

@@ -134,6 +134,10 @@ const Settings = ({
// Track which tooltip is currently open (for mobile touch interaction) // Track which tooltip is currently open (for mobile touch interaction)
const [openTooltipId, setOpenTooltipId] = React.useState<string | null>(null); const [openTooltipId, setOpenTooltipId] = React.useState<string | null>(null);
// Validation state for range inputs
const [therapeuticRangeError, setTherapeuticRangeError] = React.useState<string>('');
const [yAxisRangeError, setYAxisRangeError] = React.useState<string>('');
// Track window width for responsive tooltip positioning // Track window width for responsive tooltip positioning
const [isNarrowScreen, setIsNarrowScreen] = React.useState( const [isNarrowScreen, setIsNarrowScreen] = React.useState(
typeof window !== 'undefined' ? window.innerWidth < 640 : false typeof window !== 'undefined' ? window.innerWidth < 640 : false
@@ -226,6 +230,27 @@ const Settings = ({
// Create defaults object for translation interpolation // Create defaults object for translation interpolation
const defaultsForT = getDefaultsForTranslation(pkParams, therapeuticRange, uiSettings); const defaultsForT = getDefaultsForTranslation(pkParams, therapeuticRange, uiSettings);
// Validate range inputs
React.useEffect(() => {
// Therapeutic range validation (blocking error)
const minTherapeutic = parseFloat(therapeuticRange.min);
const maxTherapeutic = parseFloat(therapeuticRange.max);
if (!isNaN(minTherapeutic) && !isNaN(maxTherapeutic) && minTherapeutic >= maxTherapeutic) {
setTherapeuticRangeError(t('errorTherapeuticRangeInvalid'));
} else {
setTherapeuticRangeError('');
}
// Y-axis range validation (non-blocking warning)
const minYAxis = parseFloat(yAxisMin);
const maxYAxis = parseFloat(yAxisMax);
if (!isNaN(minYAxis) && !isNaN(maxYAxis) && minYAxis >= maxYAxis) {
setYAxisRangeError(t('errorYAxisRangeInvalid'));
} else {
setYAxisRangeError('');
}
}, [therapeuticRange.min, therapeuticRange.max, yAxisMin, yAxisMax, t]);
// Helper to update nested advanced settings // Helper to update nested advanced settings
const updateAdvanced = (category: string, key: string, value: any) => { const updateAdvanced = (category: string, key: string, value: any) => {
onUpdatePkParams('advanced', { onUpdatePkParams('advanced', {
@@ -380,7 +405,8 @@ const Settings = ({
min={0} min={0}
placeholder={t('min')} placeholder={t('min')}
required={true} required={true}
errorMessage={t('errorTherapeuticRangeMinRequired') || 'Minimum therapeutic range is required'} error={!!therapeuticRangeError || !therapeuticRange.min}
errorMessage={therapeuticRangeError || t('errorTherapeuticRangeMinRequired') || 'Minimum therapeutic range is required'}
/> />
<span className="text-muted-foreground">-</span> <span className="text-muted-foreground">-</span>
<FormNumericInput <FormNumericInput
@@ -391,7 +417,8 @@ const Settings = ({
placeholder={t('max')} placeholder={t('max')}
unit="ng/ml" unit="ng/ml"
required={true} required={true}
errorMessage={t('errorTherapeuticRangeMaxRequired') || 'Maximum therapeutic range is required'} error={!!therapeuticRangeError || !therapeuticRange.max}
errorMessage={therapeuticRangeError || t('errorTherapeuticRangeMaxRequired') || 'Maximum therapeutic range is required'}
/> />
</div> </div>
</div> </div>
@@ -461,6 +488,8 @@ const Settings = ({
placeholder={t('auto')} placeholder={t('auto')}
allowEmpty={true} allowEmpty={true}
clearButton={true} clearButton={true}
warning={!!yAxisRangeError}
warningMessage={yAxisRangeError}
/> />
<span className="text-muted-foreground">-</span> <span className="text-muted-foreground">-</span>
<FormNumericInput <FormNumericInput
@@ -472,6 +501,8 @@ const Settings = ({
unit="ng/ml" unit="ng/ml"
allowEmpty={true} allowEmpty={true}
clearButton={true} clearButton={true}
warning={!!yAxisRangeError}
warningMessage={yAxisRangeError}
/> />
</div> </div>
</div> </div>

View File

@@ -159,7 +159,7 @@ export const getDefaultState = (): AppState => ({
} }
], ],
steadyStateConfig: { daysOnMedication: '7' }, // kept for backwards compatibility, now sourced from pkParams.advanced steadyStateConfig: { daysOnMedication: '7' }, // kept for backwards compatibility, now sourced from pkParams.advanced
therapeuticRange: { min: '', max: '' }, // Empty by default - users should personalize based on their response therapeuticRange: { min: '10', max: '120' }, // min for adults and max for children for maximum range (users should personalize based on their response)
doseIncrement: '2.5', doseIncrement: '2.5',
uiSettings: { uiSettings: {
showDayTimeOnXAxis: '24h', showDayTimeOnXAxis: '24h',
@@ -168,7 +168,7 @@ export const getDefaultState = (): AppState => ({
yAxisMin: '', yAxisMin: '',
yAxisMax: '', yAxisMax: '',
simulationDays: '5', simulationDays: '5',
displayedDays: '2', displayedDays: '1',
showTherapeuticRange: false, showTherapeuticRange: false,
steadyStateDaysEnabled: true, steadyStateDaysEnabled: true,
stickyChart: false, stickyChart: false,

View File

@@ -97,7 +97,7 @@ export const de = {
showDayReferenceLines: "Tagestrenner anzeigen", showDayReferenceLines: "Tagestrenner anzeigen",
showDayReferenceLinesTooltip: "Vertikale Linien und Statusanzeigen zwischen Tagen anzeigen (Standard: aktiviert).", showDayReferenceLinesTooltip: "Vertikale Linien und Statusanzeigen zwischen Tagen anzeigen (Standard: aktiviert).",
showTherapeuticRangeLines: "Therapeutischen Bereich anzeigen", showTherapeuticRangeLines: "Therapeutischen Bereich anzeigen ",
showTherapeuticRangeLinesTooltip: "Horizontale Referenzlinien für therapeutisches Min/Max anzeigen (Standard: aktiviert).", showTherapeuticRangeLinesTooltip: "Horizontale Referenzlinien für therapeutisches Min/Max anzeigen (Standard: aktiviert).",
simulationDuration: "Simulationsdauer", simulationDuration: "Simulationsdauer",
simulationDurationTooltip: "Anzahl der zu simulierenden Tage. Längere Zeiträume zeigen Steady-State. Standard: {{simulationDays}} Tage.", simulationDurationTooltip: "Anzahl der zu simulierenden Tage. Längere Zeiträume zeigen Steady-State. Standard: {{simulationDays}} Tage.",
@@ -108,7 +108,7 @@ export const de = {
yAxisRangeAutoButton: "A", yAxisRangeAutoButton: "A",
yAxisRangeAutoButtonTitle: "Bereich automatisch anhand des Datenbereichs bestimmen", yAxisRangeAutoButtonTitle: "Bereich automatisch anhand des Datenbereichs bestimmen",
auto: "Auto", auto: "Auto",
therapeuticRange: "Therapeutischer Bereich", therapeuticRange: "Therapeutischer Bereich (d-Amphetamin)",
therapeuticRangeTooltip: "Personalisierte Konzentrationsziele basierend auf DEINER individuellen Reaktion. Setze diese nachdem du beobachtet hast, welche Werte Symptomkontrolle vs. Nebenwirkungen bieten. Referenzbereiche (stark variabel): Erwachsene ~10-80 ng/mL, Kinder ~20-120 ng/mL (aufgrund geringeren Körpergewichts/Vd). Leer lassen wenn unsicher. Konsultiere deinen Arzt.", therapeuticRangeTooltip: "Personalisierte Konzentrationsziele basierend auf DEINER individuellen Reaktion. Setze diese nachdem du beobachtet hast, welche Werte Symptomkontrolle vs. Nebenwirkungen bieten. Referenzbereiche (stark variabel): Erwachsene ~10-80 ng/mL, Kinder ~20-120 ng/mL (aufgrund geringeren Körpergewichts/Vd). Leer lassen wenn unsicher. Konsultiere deinen Arzt.",
dAmphetamineParameters: "d-Amphetamin Parameter", dAmphetamineParameters: "d-Amphetamin Parameter",
halfLife: "Eliminations-Halbwertszeit", halfLife: "Eliminations-Halbwertszeit",
@@ -269,6 +269,8 @@ export const de = {
errorConversionHalfLifeRequired: "⛔ Umwandlungs-Halbwertszeit ist erforderlich.", errorConversionHalfLifeRequired: "⛔ Umwandlungs-Halbwertszeit ist erforderlich.",
errorTherapeuticRangeMinRequired: "⛔ Minimaler therapeutischer Bereich ist erforderlich.", errorTherapeuticRangeMinRequired: "⛔ Minimaler therapeutischer Bereich ist erforderlich.",
errorTherapeuticRangeMaxRequired: "⛔ Maximaler therapeutischer Bereich ist erforderlich.", errorTherapeuticRangeMaxRequired: "⛔ Maximaler therapeutischer Bereich ist erforderlich.",
errorTherapeuticRangeInvalid: "⛔ Maximum muss größer als Minimum sein.",
errorYAxisRangeInvalid: "⚠️ Maximum muss größer als Minimum sein.",
errorEliminationHalfLifeRequired: "⛔ Eliminations-Halbwertszeit ist erforderlich.", errorEliminationHalfLifeRequired: "⛔ Eliminations-Halbwertszeit ist erforderlich.",
// Field validation - Warnings // Field validation - Warnings

View File

@@ -106,7 +106,7 @@ export const en = {
yAxisRangeAutoButton: "A", yAxisRangeAutoButton: "A",
yAxisRangeAutoButtonTitle: "Determine range automatically based on data range", yAxisRangeAutoButtonTitle: "Determine range automatically based on data range",
auto: "Auto", auto: "Auto",
therapeuticRange: "Therapeutic Range", therapeuticRange: "Therapeutic Range (d-Amphetamine)",
therapeuticRangeTooltip: "Personalized concentration targets based on YOUR individual response. Set these after observing which levels provide symptom control vs. side effects. Reference ranges (highly variable): Adults ~10-80 ng/mL, Children ~20-120 ng/mL (due to lower body weight/Vd). Leave empty if unsure. Consult your physician.", therapeuticRangeTooltip: "Personalized concentration targets based on YOUR individual response. Set these after observing which levels provide symptom control vs. side effects. Reference ranges (highly variable): Adults ~10-80 ng/mL, Children ~20-120 ng/mL (due to lower body weight/Vd). Leave empty if unsure. Consult your physician.",
dAmphetamineParameters: "d-Amphetamine Parameters", dAmphetamineParameters: "d-Amphetamine Parameters",
halfLife: "Elimination Half-life", halfLife: "Elimination Half-life",
@@ -268,6 +268,8 @@ export const en = {
errorAbsorptionRateRequired: "⛔ Absorption rate is required.", errorAbsorptionRateRequired: "⛔ Absorption rate is required.",
errorTherapeuticRangeMinRequired: "⛔ Minimum therapeutic range is required.", errorTherapeuticRangeMinRequired: "⛔ Minimum therapeutic range is required.",
errorTherapeuticRangeMaxRequired: "⛔ Maximum therapeutic range is required.", errorTherapeuticRangeMaxRequired: "⛔ Maximum therapeutic range is required.",
errorTherapeuticRangeInvalid: "⛔ Maximum must be greater than minimum.",
errorYAxisRangeInvalid: "⚠️ Maximum must be greater than minimum.",
errorEliminationHalfLifeRequired: "⛔ Elimination half-life is required.", errorEliminationHalfLifeRequired: "⛔ Elimination half-life is required.",