Fix various issues with pharmacokinetics, improved parameters, distinction between adult/child

This commit is contained in:
2026-01-17 20:27:00 +00:00
parent b911fa1e16
commit 6983ce3853
13 changed files with 1505 additions and 115 deletions

View File

@@ -43,6 +43,8 @@ const getDefaultsForTranslation = (pkParams: any, therapeuticRange: any, uiSetti
ldxAbsorptionHalfLife: defaults.pkParams.ldx.absorptionHalfLife,
// 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,
tmaxDelay: defaults.pkParams.advanced.foodEffect.tmaxDelay,
phTendency: defaults.pkParams.advanced.urinePh.phTendency,
@@ -886,8 +888,74 @@ const Settings = ({
<p className="text-yellow-800 dark:text-yellow-200">{t('advancedSettingsWarning')}</p>
</div>
{/* Standard Volume of Distribution */}
<div className="space-y-3">
<div className="flex items-center gap-2">
<Label className="text-sm font-medium">{t('standardVolumeOfDistribution')}</Label>
<Tooltip open={openTooltipId === 'standardVd'} onOpenChange={(open) => setOpenTooltipId(open ? 'standardVd' : null)}>
<TooltipTrigger asChild>
<button
type="button"
onClick={handleTooltipToggle('standardVd')}
onTouchStart={handleTooltipToggle('standardVd')}
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('standardVdTooltip')}
>
<Info className="h-4 w-4" />
</button>
</TooltipTrigger>
<TooltipContent side={tooltipSide}>
<p className="text-xs max-w-xs">{renderTooltipWithLinks(tWithDefaults(t, 'standardVdTooltip', {
...defaultsForT,
standardVdValue: pkParams.advanced.standardVd?.preset === 'adult' ? '377' : pkParams.advanced.standardVd?.preset === 'child' ? '175' : pkParams.advanced.standardVd?.customValue || '377',
standardVdPreset: t(`standardVdPreset${pkParams.advanced.standardVd?.preset?.charAt(0).toUpperCase()}${pkParams.advanced.standardVd?.preset?.slice(1)}` || 'standardVdPresetAdult')
}))}</p>
</TooltipContent>
</Tooltip>
</div>
<Select
value={pkParams.advanced.standardVd?.preset || 'adult'}
onValueChange={(value: 'adult' | 'child' | 'custom') => updateAdvanced('standardVd', 'preset', value)}
>
<SelectTrigger>
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="adult">{t('standardVdPresetAdult')}</SelectItem>
<SelectItem value="child">{t('standardVdPresetChild')}</SelectItem>
<SelectItem value="custom">{t('standardVdPresetCustom')}</SelectItem>
</SelectContent>
</Select>
{pkParams.advanced.weightBasedVd.enabled && (
<div className="ml-0 mt-2 p-2 bg-blue-50 dark:bg-blue-900/20 border border-blue-200 dark:border-blue-800 rounded text-xs text-blue-800 dark:text-blue-200">
Weight-based Vd is enabled below. This setting is currently overridden.
</div>
)}
{pkParams.advanced.standardVd?.preset === 'custom' && (
<div className="ml-8 mt-2">
<Label className="text-sm font-medium">{t('customVdValue')}</Label>
<FormNumericInput
value={pkParams.advanced.standardVd?.customValue || '377'}
onChange={val => updateAdvanced('standardVd', 'customValue', val)}
increment={10}
min={50}
max={800}
unit="L"
required={true}
/>
</div>
)}
</div>
<Separator className="my-4" />
{/* Weight-Based Vd */}
<div className="space-y-3">
{pkParams.advanced.weightBasedVd.enabled && (
<div className="p-2 bg-blue-50 dark:bg-blue-900/20 border border-blue-200 dark:border-blue-800 rounded text-xs text-blue-800 dark:text-blue-200 mb-3">
When enabled, this overrides the Standard Vd setting above. Disable to use Standard Vd presets (Adult/Child/Custom).
</div>
)}
<div className="flex items-center gap-3">
<Switch
id="weightBasedVdEnabled"
@@ -950,66 +1018,38 @@ const Settings = ({
<Separator className="my-4" />
{/* Food Effect */}
<Separator className="my-4" />
{/* Food Effect Absorption Delay */}
<div className="space-y-3">
<div className="flex items-center gap-3">
<Switch
id="foodEffectEnabled"
checked={pkParams.advanced.foodEffect.enabled}
onCheckedChange={checked => updateAdvanced('foodEffect', 'enabled', checked)}
/>
<Label htmlFor="foodEffectEnabled" className="font-medium">
{t('foodEffectEnabled')}
</Label>
<Tooltip open={openTooltipId === 'foodEffect'} onOpenChange={(open) => setOpenTooltipId(open ? 'foodEffect' : null)}>
<TooltipTrigger asChild>
<button
type="button"
onClick={handleTooltipToggle('foodEffect')}
onTouchStart={handleTooltipToggle('foodEffect')}
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('foodEffectTooltip')}
>
<Info className="h-4 w-4" />
</button>
</TooltipTrigger>
<TooltipContent side={tooltipSide}>
<p className="text-xs max-w-xs">{tWithDefaults(t, 'foodEffectTooltip', defaultsForT)}</p>
</TooltipContent>
</Tooltip>
<div className="flex items-center gap-2">
<Label className="text-sm font-medium">{t('foodEffectDelay')}</Label>
<Tooltip open={openTooltipId === 'tmaxDelay'} onOpenChange={(open) => setOpenTooltipId(open ? 'tmaxDelay' : null)}>
<TooltipTrigger asChild>
<button
type="button"
onClick={handleTooltipToggle('tmaxDelay')}
onTouchStart={handleTooltipToggle('tmaxDelay')}
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('tmaxDelayTooltip')}
>
<Info className="h-4 w-4" />
</button>
</TooltipTrigger>
<TooltipContent side={tooltipSide}>
<p className="text-xs max-w-xs">{renderTooltipWithLinks(tWithDefaults(t, 'tmaxDelayTooltip', defaultsForT))}</p>
</TooltipContent>
</Tooltip>
</div>
{pkParams.advanced.foodEffect.enabled && (
<div className="ml-8 space-y-2">
<div className="flex items-center gap-2">
<Label className="text-sm font-medium">{t('tmaxDelay')}</Label>
<Tooltip open={openTooltipId === 'tmaxDelay'} onOpenChange={(open) => setOpenTooltipId(open ? 'tmaxDelay' : null)}>
<TooltipTrigger asChild>
<button
type="button"
onClick={handleTooltipToggle('tmaxDelay')}
onTouchStart={handleTooltipToggle('tmaxDelay')}
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('tmaxDelayTooltip')}
>
<Info className="h-4 w-4" />
</button>
</TooltipTrigger>
<TooltipContent side={tooltipSide}>
<p className="text-xs max-w-xs">{renderTooltipWithLinks(tWithDefaults(t, 'tmaxDelayTooltip', defaultsForT))}</p>
</TooltipContent>
</Tooltip>
</div>
<FormNumericInput
value={pkParams.advanced.foodEffect.tmaxDelay}
onChange={val => updateAdvanced('foodEffect', 'tmaxDelay', val)}
increment={0.1}
min={0}
max={2}
unit={t('tmaxDelayUnit')}
required={true}
/>
</div>
)}
<FormNumericInput
value={pkParams.advanced.foodEffect.tmaxDelay}
onChange={val => updateAdvanced('foodEffect', 'tmaxDelay', val)}
increment={0.1}
min={0}
max={2}
unit={t('tmaxDelayUnit')}
required={true}
/>
</div>
<Separator className="my-4" />
@@ -1078,6 +1118,108 @@ const Settings = ({
<Separator className="my-4" />
{/* Age Group Selection */}
<div className="space-y-3">
<div className="flex items-center gap-2">
<Label className="text-sm font-medium">{t('ageGroup')}</Label>
<Tooltip open={openTooltipId === 'ageGroup'} onOpenChange={(open) => setOpenTooltipId(open ? 'ageGroup' : null)}>
<TooltipTrigger asChild>
<button
type="button"
onClick={handleTooltipToggle('ageGroup')}
onTouchStart={handleTooltipToggle('ageGroup')}
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('ageGroupTooltip')}
>
<Info className="h-4 w-4" />
</button>
</TooltipTrigger>
<TooltipContent side={tooltipSide}>
<p className="text-xs max-w-xs">{renderTooltipWithLinks(tWithDefaults(t, 'ageGroupTooltip', defaultsForT))}</p>
</TooltipContent>
</Tooltip>
</div>
<Select
value={pkParams.advanced.ageGroup?.preset || 'adult'}
onValueChange={(value: 'child' | 'adult' | 'custom') => {
updateAdvancedDirect('ageGroup', { preset: value });
}}
>
<SelectTrigger className="w-full">
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="adult">{t('ageGroupAdult')}</SelectItem>
<SelectItem value="child">{t('ageGroupChild')}</SelectItem>
<SelectItem value="custom">{t('ageGroupCustom')}</SelectItem>
</SelectContent>
</Select>
</div>
<Separator className="my-4" />
{/* Renal Function */}
<div className="space-y-3">
<div className="flex items-center gap-3">
<Switch
id="renalFunctionEnabled"
checked={pkParams.advanced.renalFunction?.enabled || false}
onCheckedChange={checked => {
updateAdvancedDirect('renalFunction', {
enabled: checked,
severity: pkParams.advanced.renalFunction?.severity || 'normal'
});
}}
/>
<Label htmlFor="renalFunctionEnabled" className="font-medium">
{t('renalFunction')}
</Label>
<Tooltip open={openTooltipId === 'renalFunction'} onOpenChange={(open) => setOpenTooltipId(open ? 'renalFunction' : null)}>
<TooltipTrigger asChild>
<button
type="button"
onClick={handleTooltipToggle('renalFunction')}
onTouchStart={handleTooltipToggle('renalFunction')}
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('renalFunctionTooltip')}
>
<Info className="h-4 w-4" />
</button>
</TooltipTrigger>
<TooltipContent side={tooltipSide}>
<p className="text-xs max-w-xs">{renderTooltipWithLinks(tWithDefaults(t, 'renalFunctionTooltip', defaultsForT))}</p>
</TooltipContent>
</Tooltip>
</div>
{(pkParams.advanced.renalFunction?.enabled) && (
<div className="ml-8 space-y-2">
<div className="flex items-center gap-2">
<Label className="text-sm font-medium">{t('renalFunctionSeverity')}</Label>
</div>
<Select
value={pkParams.advanced.renalFunction?.severity || 'normal'}
onValueChange={(value: 'normal' | 'mild' | 'severe') => {
updateAdvancedDirect('renalFunction', {
enabled: true,
severity: value
});
}}
>
<SelectTrigger className="w-full">
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="normal">{t('renalFunctionNormal')}</SelectItem>
<SelectItem value="mild">{t('renalFunctionMild')}</SelectItem>
<SelectItem value="severe">{t('renalFunctionSevere')}</SelectItem>
</SelectContent>
</Select>
</div>
)}
</div>
<Separator className="my-4" />
{/* Oral Bioavailability */}
<div className="space-y-2">
<div className="flex items-center gap-2">