Fix isFed state for regular plan comparison line, simplified urin ph selection

This commit is contained in:
2026-02-02 11:51:39 +00:00
parent 8e74fe576f
commit 90b0806cec
8 changed files with 71 additions and 60 deletions

View File

@@ -46,7 +46,6 @@ const getDefaultsForTranslation = (pkParams: any, therapeuticRange: any, uiSetti
standardVdPreset: defaults.pkParams.advanced.standardVd?.preset || 'adult',
bodyWeight: defaults.pkParams.advanced.weightBasedVd.bodyWeight,
tmaxDelay: defaults.pkParams.advanced.foodEffect.tmaxDelay,
phTendency: defaults.pkParams.advanced.urinePh.phTendency,
fOral: defaults.pkParams.advanced.fOral,
fOralPercent: String((parseFloat(defaults.pkParams.advanced.fOral) * 100).toFixed(1)),
steadyStateDays: defaults.pkParams.advanced.steadyStateDays,
@@ -924,12 +923,7 @@ const Settings = ({
{/* Urine pH */}
<div className="space-y-3">
<div className="flex items-center gap-3">
<Switch
id="urinePHEnabled"
checked={pkParams.advanced.urinePh.enabled}
onCheckedChange={checked => updateAdvanced('urinePh', 'enabled', checked)}
/>
<Label htmlFor="urinePHEnabled" className="font-medium">
<Label htmlFor="urinePHMode" className="font-medium">
{t('urinePHTendency')}
</Label>
<Tooltip open={openTooltipId === 'urinePH'} onOpenChange={(open) => setOpenTooltipId(open ? 'urinePH' : null)}>
@@ -949,38 +943,23 @@ const Settings = ({
</TooltipContent>
</Tooltip>
</div>
{pkParams.advanced.urinePh.enabled && (
<div className="ml-8 space-y-2">
<div className="flex items-center gap-2">
<Label className="text-sm font-medium">{t('urinePHValue')}</Label>
<Tooltip open={openTooltipId === 'urinePHValue'} onOpenChange={(open) => setOpenTooltipId(open ? 'urinePHValue' : null)}>
<TooltipTrigger asChild>
<button
type="button"
onClick={handleTooltipToggle('urinePHValue')}
onTouchStart={handleTooltipToggle('urinePHValue')}
className="inline-flex items-center justify-center rounded-sm text-muted-foreground hover:text-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2"
aria-label={t('urinePHValueTooltip')}
>
<Info className="h-4 w-4" />
</button>
</TooltipTrigger>
<TooltipContent side={tooltipSide}>
<p className="text-xs max-w-xs">{tWithDefaults(t, 'urinePHValueTooltip', defaultsForT)}</p>
</TooltipContent>
</Tooltip>
</div>
<FormNumericInput
value={pkParams.advanced.urinePh.phTendency}
onChange={val => updateAdvanced('urinePh', 'phTendency', val)}
increment={0.1}
min={5.5}
max={8.0}
unit={t('phUnit')}
required={true}
/>
</div>
)}
<div>
<Select
value={pkParams.advanced.urinePh.mode}
onValueChange={(value: 'normal' | 'acidic' | 'alkaline') =>
updateAdvanced('urinePh', 'mode', value)
}
>
<SelectTrigger id="urinePHMode">
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="normal">{t('urinePHModeNormal')}</SelectItem>
<SelectItem value="acidic">{t('urinePHModeAcidic')}</SelectItem>
<SelectItem value="alkaline">{t('urinePHModeAlkaline')}</SelectItem>
</SelectContent>
</Select>
</div>
</div>
<Separator className="my-4" />

View File

@@ -26,7 +26,7 @@ const versionInfo = versionJsonDefault && Object.keys(versionJsonDefault).length
gitDate: 'unknown',
};
export const LOCAL_STORAGE_KEY = 'medPlanAssistantState_v8'; // Incremented for ageGroup + renalFunction fields
export const LOCAL_STORAGE_KEY = 'medPlanAssistantState_v9'; // Incremented for urinePh mode structure change
export const PROJECT_REPOSITORY_URL = 'https://git.11001001.org/cbaoth/med-plan-assistant';
export const APP_VERSION = versionInfo.version;
export const BUILD_INFO = versionInfo;
@@ -42,7 +42,7 @@ export interface AdvancedSettings {
standardVd: { preset: 'adult' | 'child' | 'custom'; customValue: string }; // Volume of distribution (L)
weightBasedVd: { enabled: boolean; bodyWeight: string }; // kg
foodEffect: { enabled: boolean; tmaxDelay: string }; // hours
urinePh: { enabled: boolean; phTendency: string }; // 5.5-8.0 range
urinePh: { mode: 'normal' | 'acidic' | 'alkaline' }; // pH effect on elimination
fOral: string; // bioavailability fraction
steadyStateDays: string; // days of medication history to simulate
// Age-specific pharmacokinetics (Research Section 5.2)
@@ -141,7 +141,7 @@ export const getDefaultState = (): AppState => ({
standardVd: { preset: 'adult', customValue: '377' }, // Adult: 377L (Roberts 2015), Child: ~150-200L
weightBasedVd: { enabled: false, bodyWeight: '70' }, // kg, adult average
foodEffect: { enabled: false, tmaxDelay: '1.0' }, // hours delay
urinePh: { enabled: false, phTendency: '6.0' }, // pH scale (5.5-8.0)
urinePh: { mode: 'normal' }, // 'normal' (6-7.5), 'acidic' (<6), 'alkaline' (>7.5)
fOral: String(DEFAULT_F_ORAL), // 0.96 bioavailability
steadyStateDays: '7' // days of prior medication history
}

View File

@@ -29,10 +29,35 @@ export const useAppState = () => {
migratedUiSettings.showDayTimeOnXAxis = migratedUiSettings.showDayTimeOnXAxis ? '24h' : 'continuous';
}
// Migrate urinePh from old {enabled, phTendency} to new {mode} structure
let migratedPkParams = {...defaults.pkParams, ...parsedState.pkParams};
if (migratedPkParams.advanced) {
const oldUrinePh = migratedPkParams.advanced.urinePh as any;
if (oldUrinePh && typeof oldUrinePh === 'object' && 'enabled' in oldUrinePh) {
// Old format detected: {enabled: boolean, phTendency: string}
if (!oldUrinePh.enabled) {
migratedPkParams.advanced.urinePh = { mode: 'normal' };
} else {
const phValue = parseFloat(oldUrinePh.phTendency);
if (!isNaN(phValue)) {
if (phValue < 6.0) {
migratedPkParams.advanced.urinePh = { mode: 'acidic' };
} else if (phValue > 7.5) {
migratedPkParams.advanced.urinePh = { mode: 'alkaline' };
} else {
migratedPkParams.advanced.urinePh = { mode: 'normal' };
}
} else {
migratedPkParams.advanced.urinePh = { mode: 'normal' };
}
}
}
}
setAppState({
...defaults,
...parsedState,
pkParams: {...defaults.pkParams, ...parsedState.pkParams},
pkParams: migratedPkParams,
days: parsedState.days || defaults.days,
uiSettings: migratedUiSettings,
});

View File

@@ -72,7 +72,8 @@ export const useSimulation = (appState: AppState) => {
doses: templateDay.doses.map(d => ({
id: `${d.id}-template-${i}`,
time: d.time,
ldx: d.ldx
ldx: d.ldx,
isFed: d.isFed // Preserve food-timing flag for proper absorption delay modeling
}))
}));

View File

@@ -135,7 +135,11 @@ export const de = {
tmaxDelayUnit: "h",
urinePHTendency: "Urin-pH-Effekte",
urinePHTooltip: "Urin-pH beeinflusst Nierenrückresorption von Amphetamin. Ermöglicht pH-abhängige Halbwertszeit-Variation (7-15h Bereich). Bei Deaktivierung: neutraler pH (~11h HWZ).",
urinePHTooltip: "Urin-pH beeinflusst Nierenrückresorption von Amphetamin. Saurer Urin (<6) erhöht Elimination (schnellere Ausscheidung, t½ ~7-9h). Normaler pH (6-7,5) hält Basis-Elimination (~11h). Alkalischer Urin (>7,5) reduziert Elimination (langsamere Ausscheidung, t½ ~13-15h). Typischer Bereich: 5,5-8,0. Standard: Normaler pH (6-7,5).",
urinePHMode: "pH-Effekt",
urinePHModeNormal: "Normal (pH 6-7,5, t½ 11h)",
urinePHModeAcidic: "Sauer (pH <6, schnellere Elimination)",
urinePHModeAlkaline: "Alkalisch (pH >7,5, langsamere Elimination)",
urinePHValue: "pH-Wert",
urinePHValueTooltip: "Dein typischer Urin-pH (sauer=schnellere Ausscheidung, alkalisch=langsamer). Standard: {{phTendency}}. Bereich: 5,5-8,0.",
phValue: "pH-Wert",

View File

@@ -133,7 +133,11 @@ export const en = {
tmaxDelayUnit: "h",
urinePHTendency: "Urine pH Effects",
urinePHTooltip: "Urine pH affects kidney reabsorption of amphetamine. Enables pH-dependent half-life variation (7-15h range). When disabled, assumes neutral pH (~11h HL).",
urinePHTooltip: "Urine pH affects kidney reabsorption of amphetamine. Acidic urine (<6) increases elimination (faster clearance, t½ ~7-9h). Normal pH (6-7.5) maintains baseline elimination (~11h). Alkaline urine (>7.5) reduces elimination (slower clearance, t½ ~13-15h). Typical range: 5.5-8.0. Default: Normal pH (6-7.5).",
urinePHMode: "pH Effect",
urinePHModeNormal: "Normal (pH 6-7.5, t½ 11h)",
urinePHModeAcidic: "Acidic (pH <6, faster elimination)",
urinePHModeAlkaline: "Alkaline (pH >7.5, slower elimination)",
urinePHValue: "pH Value",
urinePHValueTooltip: "Your typical urine pH (acidic=faster clearance, alkaline=slower). Default: {{phTendency}}. Range: 5.5-8.0.",
phValue: "pH Value",

View File

@@ -250,6 +250,7 @@ export const importSettings = (
time: dose.time || '12:00',
ldx: dose.ldx || '0',
damph: dose.damph,
isFed: dose.isFed, // Explicitly preserve food-timing flag
}))
}));
}

View File

@@ -108,8 +108,7 @@ export const calculateSingleDoseConcentration = (
// Per-dose food effect takes precedence over global setting
const foodEnabled = isFed !== undefined ? isFed : pkParams.advanced.foodEffect.enabled;
const tmaxDelay = foodEnabled ? parseFloat(pkParams.advanced.foodEffect.tmaxDelay) : 0;
const urinePHEnabled = pkParams.advanced.urinePh.enabled;
const phTendency = urinePHEnabled ? parseFloat(pkParams.advanced.urinePh.phTendency) : 6.0;
const urinePHMode = pkParams.advanced.urinePh.mode;
// Validate base parameters
if (isNaN(absorptionHalfLife) || absorptionHalfLife <= 0 ||
@@ -125,20 +124,18 @@ export const calculateSingleDoseConcentration = (
const calculationTime = adjustedTime; // Use delayed time for all kinetic calculations
// Apply urine pH effect on elimination half-life
// pH < 6: acidic (faster elimination, HL ~7-9h)
// pH 6-7: normal (HL ~10-12h)
// pH > 7: alkaline (slower elimination, HL ~13-15h up to 34h extreme)
// Acidic: pH < 6 (faster elimination, HL ~7-9h)
// Normal: pH 6-7.5 (baseline elimination, HL ~10-12h)
// Alkaline: pH > 7.5 (slower elimination, HL ~13-15h up to 34h extreme)
let adjustedDamphHL = damphHalfLife;
if (urinePHEnabled) {
if (phTendency < 6.0) {
// Acidic: reduce HL by ~30%
adjustedDamphHL = damphHalfLife * 0.7;
} else if (phTendency > 7.5) {
// Alkaline: increase HL by ~30-40%
adjustedDamphHL = damphHalfLife * 1.35;
}
// else: normal pH 6-7.5, no adjustment
if (urinePHMode === 'acidic') {
// Acidic: reduce HL by ~30%
adjustedDamphHL = damphHalfLife * 0.7;
} else if (urinePHMode === 'alkaline') {
// Alkaline: increase HL by ~30-40%
adjustedDamphHL = damphHalfLife * 1.35;
}
// else: normal mode, no adjustment
// Calculate rate constants
const ka_ldx = Math.log(2) / absorptionHalfLife;