diff --git a/src/components/settings.tsx b/src/components/settings.tsx index 15099dd..3239b5b 100644 --- a/src/components/settings.tsx +++ b/src/components/settings.tsx @@ -44,7 +44,7 @@ const getDefaultsForTranslation = (pkParams: any, therapeuticRange: any, uiSetti // Advanced Settings standardVdValue: defaults.pkParams.advanced.standardVd?.preset === 'adult' ? '377' : defaults.pkParams.advanced.standardVd?.preset === 'child' ? '175' : defaults.pkParams.advanced.standardVd?.customValue || '377', standardVdPreset: defaults.pkParams.advanced.standardVd?.preset || 'adult', - bodyWeight: defaults.pkParams.advanced.weightBasedVd.bodyWeight, + bodyWeight: defaults.pkParams.advanced.standardVd.bodyWeight, tmaxDelay: defaults.pkParams.advanced.foodEffect.tmaxDelay, fOral: defaults.pkParams.advanced.fOral, fOralPercent: String((parseFloat(defaults.pkParams.advanced.fOral) * 100).toFixed(1)), @@ -374,7 +374,7 @@ const Settings = ({ {showTherapeuticRange && ( -
+
{steadyStateDaysEnabled && ( -
+
updateAdvancedDirect('steadyStateDays', val)} @@ -827,7 +827,7 @@ const Settings = ({
- {pkParams.advanced.weightBasedVd.enabled && ( + {pkParams.advanced.standardVd?.preset === 'weight-based' && (
- ⓘ Weight-based Vd is enabled below. This setting is currently overridden. + ⓘ {t('weightBasedVdInfo')}
)} {pkParams.advanced.standardVd?.preset === 'custom' && ( -
+
)} -
- - - - {/* Weight-Based Vd */} -
- {pkParams.advanced.weightBasedVd.enabled && ( -
- ⓘ When enabled, this overrides the Standard Vd setting above. Disable to use Standard Vd presets (Adult/Child/Custom). -
- )} -
- updateAdvanced('weightBasedVd', 'enabled', checked)} - /> - - setOpenTooltipId(open ? 'weightBasedVd' : null)}> - - - - -

{tWithDefaults(t, 'weightBasedVdTooltip', defaultsForT)}

-
-
-
- {pkParams.advanced.weightBasedVd.enabled && ( -
+ {pkParams.advanced.standardVd?.preset === 'weight-based' && ( +
- setOpenTooltipId(open ? 'bodyWeight' : null)}> - - - - -

{renderTooltipWithLinks(tWithDefaults(t, 'bodyWeightTooltip', defaultsForT))}

-
-
+ setOpenTooltipId(open ? 'bodyWeight' : null)}> + + + + +

{renderTooltipWithLinks(tWithDefaults(t, 'bodyWeightTooltip', defaultsForT))}

+
+
updateAdvanced('weightBasedVd', 'bodyWeight', val)} + value={pkParams.advanced.standardVd?.bodyWeight || '70'} + onChange={val => updateAdvanced('standardVd', 'bodyWeight', val)} increment={1} min={20} max={300} @@ -930,8 +894,6 @@ const Settings = ({ - - {/* Food Effect Absorption Delay */}
@@ -1084,7 +1046,7 @@ const Settings = ({
{(pkParams.advanced.renalFunction?.enabled) && ( -
+
diff --git a/src/constants/defaults.ts b/src/constants/defaults.ts index 8d42746..8f70dda 100644 --- a/src/constants/defaults.ts +++ b/src/constants/defaults.ts @@ -39,8 +39,7 @@ export const DEFAULT_F_ORAL = 0.96; // Type definitions export interface AdvancedSettings { - standardVd: { preset: 'adult' | 'child' | 'custom'; customValue: string }; // Volume of distribution (L) - weightBasedVd: { enabled: boolean; bodyWeight: string }; // kg + standardVd: { preset: 'adult' | 'child' | 'custom' | 'weight-based'; customValue: string; bodyWeight: string }; // Volume of distribution (L) foodEffect: { enabled: boolean; tmaxDelay: string }; // hours urinePh: { mode: 'normal' | 'acidic' | 'alkaline' }; // pH effect on elimination fOral: string; // bioavailability fraction @@ -138,8 +137,7 @@ export const getDefaultState = (): AppState => ({ absorptionHalfLife: '0.7' // Updated from 0.9 for better ~1h Tmax of prodrug }, advanced: { - standardVd: { preset: 'adult', customValue: '377' }, // Adult: 377L (Roberts 2015), Child: ~150-200L - weightBasedVd: { enabled: false, bodyWeight: '70' }, // kg, adult average + standardVd: { preset: 'adult', customValue: '377', bodyWeight: '70' }, // Adult: 377L (Roberts 2015), Child: ~150-200L, Weight-based: ~5.4 L/kg foodEffect: { enabled: false, tmaxDelay: '1.0' }, // hours delay urinePh: { mode: 'normal' }, // 'normal' (6-7.5), 'acidic' (<6), 'alkaline' (>7.5) fOral: String(DEFAULT_F_ORAL), // 0.96 bioavailability diff --git a/src/hooks/useAppState.ts b/src/hooks/useAppState.ts index 538f8eb..a3062ae 100644 --- a/src/hooks/useAppState.ts +++ b/src/hooks/useAppState.ts @@ -52,6 +52,38 @@ export const useAppState = () => { } } } + + // Migrate weightBasedVd from old {enabled, bodyWeight} to new standardVd structure + const oldWeightBasedVd = (migratedPkParams.advanced as any).weightBasedVd; + if (oldWeightBasedVd && typeof oldWeightBasedVd === 'object' && 'enabled' in oldWeightBasedVd) { + // Old format detected: {enabled: boolean, bodyWeight: string} + if (oldWeightBasedVd.enabled) { + // Convert to new weight-based preset + migratedPkParams.advanced.standardVd = { + preset: 'weight-based', + customValue: migratedPkParams.advanced.standardVd?.customValue || '377', + bodyWeight: oldWeightBasedVd.bodyWeight || '70' + }; + } else { + // Keep existing standardVd, but ensure bodyWeight is present + if (!migratedPkParams.advanced.standardVd?.bodyWeight) { + migratedPkParams.advanced.standardVd = { + ...migratedPkParams.advanced.standardVd, + bodyWeight: oldWeightBasedVd.bodyWeight || '70' + }; + } + } + // Remove old weightBasedVd property + delete (migratedPkParams.advanced as any).weightBasedVd; + } + + // Ensure bodyWeight exists in standardVd (for new installations or old formats) + if (!migratedPkParams.advanced.standardVd?.bodyWeight) { + migratedPkParams.advanced.standardVd = { + ...migratedPkParams.advanced.standardVd, + bodyWeight: '70' + }; + } } // Validate numeric fields and replace empty/invalid values with defaults diff --git a/src/locales/de.ts b/src/locales/de.ts index 6e7d1ea..6d604a6 100644 --- a/src/locales/de.ts +++ b/src/locales/de.ts @@ -79,11 +79,13 @@ export const de = { advancedSettings: "Erweiterte Einstellungen", advancedSettingsWarning: "⚠️ Diese Parameter beeinflussen die Simulationsgenauigkeit und können von Bevölkerungsdurchschnitten abweichen. Nur anpassen, wenn spezifische klinische Daten oder Forschungsreferenzen vorliegen.", standardVolumeOfDistribution: "Verteilungsvolumen (Vd)", - standardVdTooltip: "Definiert wie sich der Wirkstoff im Körper verteilt. Erwachsene: 377L (Roberts 2015), Kinder: ~150-200L. Beeinflusst alle Konzentrationsberechnungen. Nur für pädiatrische oder spezialisierte Simulationen ändern. Standard: {{standardVdValue}}L ({{standardVdPreset}}).", + standardVdTooltip: "Definiert wie sich der Wirkstoff im Körper verteilt. Erwachsene: 377L (Roberts 2015), Kinder: ~150-200L. Gewichtsbasierte Skalierung: ~5,4 L/kg (für Erwachsene >18 Jahre basierend auf [Populations-Pharmakokinetik](https://pmc.ncbi.nlm.nih.gov/articles/PMC5572767/)). Beeinflusst alle Konzentrationsberechnungen. Nur für pädiatrische oder spezialisierte Simulationen ändern. Standard: {{standardVdValue}}L ({{standardVdPreset}}).", standardVdPresetAdult: "Erwachsene (377L)", standardVdPresetChild: "Kinder (175L)", standardVdPresetCustom: "Benutzerdefiniert", + standardVdPresetWeightBased: "Gewichtsbasiert (~5,4 L/kg)", customVdValue: "Benutzerdefiniertes Vd (L)", + weightBasedVdInfo: "Gewichtsbasiertes Vd passt Plasmakonzentrationen basierend auf Körpergewicht an (~5,4 L/kg). Leichtere Personen → höhere Spitzen, schwerere → niedrigere Spitzen. Diese Option ist für Erwachsene (>18 Jahre) basierend auf der Populations-PK-Studie vorgesehen. Für pädiatrische Patienten verwenden Sie die Voreinstellung 'Kinder'.", xAxisTimeFormat: "Zeitformat", xAxisFormatContinuous: "Fortlaufend", xAxisFormatContinuousDesc: "Endlose Sequenz (0h, 6h, 12h...)", diff --git a/src/locales/en.ts b/src/locales/en.ts index ae91308..2cedaf6 100644 --- a/src/locales/en.ts +++ b/src/locales/en.ts @@ -78,11 +78,13 @@ export const en = { advancedSettings: "Advanced Settings", advancedSettingsWarning: "⚠️ These parameters affect simulation accuracy and may deviate from population averages. Adjust only if you have specific clinical data or research references.", standardVolumeOfDistribution: "Volume of Distribution (Vd)", - standardVdTooltip: "Defines how drug disperses in body. Adult: 377L (Roberts 2015), Child: ~150-200L. Affects all concentration calculations. Change only for pediatric or specialized simulations. Default: {{standardVdValue}}L ({{standardVdPreset}}).", + standardVdTooltip: "Defines how drug disperses in body. Adult: 377L (Roberts 2015), Child: ~150-200L. Weight-based scaling: ~5.4 L/kg (intended for adults >18 years based on [population PK analysis](https://pmc.ncbi.nlm.nih.gov/articles/PMC5572767/)). Affects all concentration calculations. Change only for pediatric or specialized simulations. Default: {{standardVdValue}}L ({{standardVdPreset}}).", standardVdPresetAdult: "Adult (377L)", standardVdPresetChild: "Child (175L)", standardVdPresetCustom: "Custom", + standardVdPresetWeightBased: "Weight-Based (~5.4 L/kg)", customVdValue: "Custom Vd (L)", + weightBasedVdInfo: "Weight-based Vd adjusts plasma concentrations based on body weight (~5.4 L/kg). Lighter persons → higher peaks, heavier → lower peaks. This option is intended for adults (>18 years) based on the population PK study. For pediatric patients, use the 'Child' preset.", xAxisTimeFormat: "Time Format", xAxisFormatContinuous: "Continuous", xAxisFormatContinuousDesc: "Endless sequence (0h, 6h, 12h...)", diff --git a/src/utils/exportImport.ts b/src/utils/exportImport.ts index 921d227..75e926b 100644 --- a/src/utils/exportImport.ts +++ b/src/utils/exportImport.ts @@ -242,7 +242,7 @@ export const validateImportData = (data: any): ImportValidationResult => { // Validate advanced settings if (importData.advancedSettings !== undefined) { - const validCategories = ['standardVd', 'weightBasedVd', 'foodEffect', 'urinePh', 'fOral', 'steadyStateDays', 'ageGroup', 'renalFunction']; + const validCategories = ['standardVd', 'foodEffect', 'urinePh', 'fOral', 'steadyStateDays', 'ageGroup', 'renalFunction']; const importedCategories = Object.keys(importData.advancedSettings); const unknownCategories = importedCategories.filter(c => !validCategories.includes(c)); if (unknownCategories.length > 0) { diff --git a/src/utils/pharmacokinetics.ts b/src/utils/pharmacokinetics.ts index a83a575..465add0 100644 --- a/src/utils/pharmacokinetics.ts +++ b/src/utils/pharmacokinetics.ts @@ -186,13 +186,13 @@ export const calculateSingleDoseConcentration = ( } } - // Weight-based Vd scaling (OVERRIDES preset if enabled) + // Weight-based Vd scaling (selected as 'weight-based' preset) // Research Section 8.1: Vd_damph ≈ 5.4 L/kg body weight // Lighter person → smaller Vd → higher concentration // Heavier person → larger Vd → lower concentration let effectiveVd_damph = baseVd_damph; - if (pkParams.advanced.weightBasedVd.enabled) { - const bodyWeight = parseFloat(pkParams.advanced.weightBasedVd.bodyWeight); + if (pkParams.advanced.standardVd && pkParams.advanced.standardVd.preset === 'weight-based') { + const bodyWeight = parseFloat(pkParams.advanced.standardVd.bodyWeight); if (!isNaN(bodyWeight) && bodyWeight > 0) { effectiveVd_damph = bodyWeight * 5.4; // L/kg factor from literature }