Update consolidated and improved tooltips

This commit is contained in:
2026-01-17 13:06:56 +00:00
parent 3214c152dd
commit 3e7281e4db
6 changed files with 180 additions and 177 deletions

View File

@@ -19,6 +19,8 @@ import Settings from './components/settings';
import LanguageSelector from './components/language-selector'; import LanguageSelector from './components/language-selector';
import DisclaimerModal from './components/disclaimer-modal'; import DisclaimerModal from './components/disclaimer-modal';
import { Button } from './components/ui/button'; import { Button } from './components/ui/button';
import { TooltipProvider } from './components/ui/tooltip';
import { IconButtonWithTooltip } from './components/ui/icon-button-with-tooltip';
import { PROJECT_REPOSITORY_URL, APP_VERSION } from './constants/defaults'; import { PROJECT_REPOSITORY_URL, APP_VERSION } from './constants/defaults';
// Custom Hooks // Custom Hooks
@@ -100,6 +102,7 @@ const MedPlanAssistant = () => {
} = useSimulation(appState); } = useSimulation(appState);
return ( return (
<TooltipProvider>
<div className="min-h-screen bg-background p-4 sm:p-6 lg:p-8"> <div className="min-h-screen bg-background p-4 sm:p-6 lg:p-8">
{/* Disclaimer Modal */} {/* Disclaimer Modal */}
<DisclaimerModal <DisclaimerModal
@@ -147,15 +150,14 @@ const MedPlanAssistant = () => {
{t('both')} {t('both')}
</Button> </Button>
</div> </div>
<Button <IconButtonWithTooltip
onClick={() => updateUiSetting('stickyChart', !uiSettings.stickyChart)} onClick={() => updateUiSetting('stickyChart', !uiSettings.stickyChart)}
icon={uiSettings.stickyChart ? <Pin size={16} /> : <PinOff size={16} />}
tooltip={uiSettings.stickyChart ? t('unpinChart') : t('pinChart')}
variant={uiSettings.stickyChart ? 'default' : 'outline'} variant={uiSettings.stickyChart ? 'default' : 'outline'}
size="sm" size="sm"
className="shrink-0" className="shrink-0"
title={uiSettings.stickyChart ? t('unpinChart') : t('pinChart')} />
>
{uiSettings.stickyChart ? <Pin size={16} /> : <PinOff size={16} />}
</Button>
</div> </div>
<SimulationChart <SimulationChart
@@ -239,6 +241,7 @@ const MedPlanAssistant = () => {
</footer> </footer>
</div> </div>
</div> </div>
</TooltipProvider>
); );
}; };

View File

@@ -14,7 +14,8 @@ import { Card, CardContent } from './ui/card';
import { Badge } from './ui/badge'; import { Badge } from './ui/badge';
import { FormTimeInput } from './ui/form-time-input'; import { FormTimeInput } from './ui/form-time-input';
import { FormNumericInput } from './ui/form-numeric-input'; import { FormNumericInput } from './ui/form-numeric-input';
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from './ui/tooltip'; import { Tooltip, TooltipContent, TooltipTrigger } from './ui/tooltip';
import { IconButtonWithTooltip } from './ui/icon-button-with-tooltip';
import CollapsibleCardHeader from './ui/collapsible-card-header'; import CollapsibleCardHeader from './ui/collapsible-card-header';
import { Plus, Copy, Trash2, ArrowDownAZ, TrendingUp, TrendingDown } from 'lucide-react'; import { Plus, Copy, Trash2, ArrowDownAZ, TrendingUp, TrendingDown } from 'lucide-react';
import type { DayGroup } from '../constants/defaults'; import type { DayGroup } from '../constants/defaults';
@@ -116,25 +117,23 @@ const DaySchedule: React.FC<DayScheduleProps> = ({
rightSection={ rightSection={
<> <>
{canAddDay && ( {canAddDay && (
<Button <IconButtonWithTooltip
onClick={() => onAddDay(day.id)} onClick={() => onAddDay(day.id)}
icon={<Copy className="h-4 w-4" />}
tooltip={t('cloneDay')}
size="sm" size="sm"
variant="outline" variant="outline"
title={t('cloneDay')} />
>
<Copy className="h-4 w-4" />
</Button>
)} )}
{!day.isTemplate && ( {!day.isTemplate && (
<Button <IconButtonWithTooltip
onClick={() => onRemoveDay(day.id)} onClick={() => onRemoveDay(day.id)}
icon={<Trash2 className="h-4 w-4" />}
tooltip={t('removeDay')}
size="sm" size="sm"
variant="outline" variant="outline"
className="border-destructive text-destructive hover:bg-destructive hover:text-destructive-foreground" className="border-destructive text-destructive hover:bg-destructive hover:text-destructive-foreground"
title={t('removeDay')} />
>
<Trash2 className="h-4 w-4" />
</Button>
)} )}
</> </>
} }
@@ -143,7 +142,6 @@ const DaySchedule: React.FC<DayScheduleProps> = ({
{t('day')} {dayIndex + 1} {t('day')} {dayIndex + 1}
</Badge> </Badge>
{!day.isTemplate && doseCountDiff !== 0 ? ( {!day.isTemplate && doseCountDiff !== 0 ? (
<TooltipProvider>
<Tooltip> <Tooltip>
<TooltipTrigger asChild> <TooltipTrigger asChild>
<button <button
@@ -165,14 +163,12 @@ const DaySchedule: React.FC<DayScheduleProps> = ({
</p> </p>
</TooltipContent> </TooltipContent>
</Tooltip> </Tooltip>
</TooltipProvider>
) : ( ) : (
<Badge variant="outline" className="text-xs"> <Badge variant="outline" className="text-xs">
{day.doses.length} {day.doses.length === 1 ? t('dose') : t('doses')} {day.doses.length} {day.doses.length === 1 ? t('dose') : t('doses')}
</Badge> </Badge>
)} )}
{!day.isTemplate && Math.abs(totalMgDiff) > 0.1 ? ( {!day.isTemplate && Math.abs(totalMgDiff) > 0.1 ? (
<TooltipProvider>
<Tooltip> <Tooltip>
<TooltipTrigger asChild> <TooltipTrigger asChild>
<button <button
@@ -194,7 +190,6 @@ const DaySchedule: React.FC<DayScheduleProps> = ({
</p> </p>
</TooltipContent> </TooltipContent>
</Tooltip> </Tooltip>
</TooltipProvider>
) : ( ) : (
<Badge variant="outline" className="text-xs"> <Badge variant="outline" className="text-xs">
{day.doses.reduce((sum, dose) => sum + (parseFloat(dose.ldx) || 0), 0).toFixed(1)} mg {day.doses.reduce((sum, dose) => sum + (parseFloat(dose.ldx) || 0), 0).toFixed(1)} mg
@@ -207,7 +202,6 @@ const DaySchedule: React.FC<DayScheduleProps> = ({
<div className="grid grid-cols-[120px_1fr_auto] gap-3 text-sm font-medium text-muted-foreground"> <div className="grid grid-cols-[120px_1fr_auto] gap-3 text-sm font-medium text-muted-foreground">
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<span>{t('time')}</span> <span>{t('time')}</span>
<TooltipProvider>
<Tooltip> <Tooltip>
<TooltipTrigger asChild> <TooltipTrigger asChild>
<Button <Button
@@ -231,7 +225,6 @@ const DaySchedule: React.FC<DayScheduleProps> = ({
</p> </p>
</TooltipContent> </TooltipContent>
</Tooltip> </Tooltip>
</TooltipProvider>
</div> </div>
<div>{t('ldx')} (mg)</div> <div>{t('ldx')} (mg)</div>
<div></div> <div></div>
@@ -267,16 +260,15 @@ const DaySchedule: React.FC<DayScheduleProps> = ({
errorMessage={t('errorNumberRequired')} errorMessage={t('errorNumberRequired')}
warningMessage={t('warningZeroDose')} warningMessage={t('warningZeroDose')}
/> />
<Button <IconButtonWithTooltip
onClick={() => onRemoveDose(day.id, dose.id)} onClick={() => onRemoveDose(day.id, dose.id)}
icon={<Trash2 className="h-4 w-4" />}
tooltip={t('removeDose')}
size="sm" size="sm"
variant="ghost" variant="ghost"
disabled={day.isTemplate && day.doses.length === 1} disabled={day.isTemplate && day.doses.length === 1}
className="h-9 w-9 p-0" className="h-9 w-9 p-0"
title={t('removeDose')} />
>
<Trash2 className="h-4 w-4" />
</Button>
</div> </div>
); );
})} })}

View File

@@ -15,7 +15,7 @@ import { Label } from './ui/label';
import { Switch } from './ui/switch'; import { Switch } from './ui/switch';
import { Separator } from './ui/separator'; import { Separator } from './ui/separator';
import { Button } from './ui/button'; import { Button } from './ui/button';
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from './ui/tooltip'; import { Tooltip, TooltipContent, TooltipTrigger } from './ui/tooltip';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from './ui/select'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from './ui/select';
import { FormNumericInput } from './ui/form-numeric-input'; import { FormNumericInput } from './ui/form-numeric-input';
import CollapsibleCardHeader from './ui/collapsible-card-header'; import CollapsibleCardHeader from './ui/collapsible-card-header';
@@ -269,7 +269,6 @@ const Settings = ({
<Label htmlFor="showTemplateDay" className="font-medium"> <Label htmlFor="showTemplateDay" className="font-medium">
{t('showTemplateDayInChart')} {t('showTemplateDayInChart')}
</Label> </Label>
<TooltipProvider>
<Tooltip open={openTooltipId === 'showTemplateDay'} onOpenChange={(open) => setOpenTooltipId(open ? 'showTemplateDay' : null)}> <Tooltip open={openTooltipId === 'showTemplateDay'} onOpenChange={(open) => setOpenTooltipId(open ? 'showTemplateDay' : null)}>
<TooltipTrigger asChild> <TooltipTrigger asChild>
<button <button
@@ -286,7 +285,6 @@ const Settings = ({
<p className="text-xs max-w-xs">{tWithDefaults(t, 'showTemplateDayTooltip', defaultsForT)}</p> <p className="text-xs max-w-xs">{tWithDefaults(t, 'showTemplateDayTooltip', defaultsForT)}</p>
</TooltipContent> </TooltipContent>
</Tooltip> </Tooltip>
</TooltipProvider>
</div> </div>
</div> </div>
@@ -300,7 +298,6 @@ const Settings = ({
<Label htmlFor="showDayReferenceLines" className="font-medium"> <Label htmlFor="showDayReferenceLines" className="font-medium">
{t('showDayReferenceLines')} {t('showDayReferenceLines')}
</Label> </Label>
<TooltipProvider>
<Tooltip open={openTooltipId === 'showDayReferenceLines'} onOpenChange={(open) => setOpenTooltipId(open ? 'showDayReferenceLines' : null)}> <Tooltip open={openTooltipId === 'showDayReferenceLines'} onOpenChange={(open) => setOpenTooltipId(open ? 'showDayReferenceLines' : null)}>
<TooltipTrigger asChild> <TooltipTrigger asChild>
<button <button
@@ -317,7 +314,6 @@ const Settings = ({
<p className="text-xs max-w-xs">{tWithDefaults(t, 'showDayReferenceLinesTooltip', defaultsForT)}</p> <p className="text-xs max-w-xs">{tWithDefaults(t, 'showDayReferenceLinesTooltip', defaultsForT)}</p>
</TooltipContent> </TooltipContent>
</Tooltip> </Tooltip>
</TooltipProvider>
</div> </div>
</div> </div>
@@ -331,7 +327,6 @@ const Settings = ({
<Label htmlFor="showTherapeuticRange" className="font-medium"> <Label htmlFor="showTherapeuticRange" className="font-medium">
{t('showTherapeuticRangeLines')} {t('showTherapeuticRangeLines')}
</Label> </Label>
<TooltipProvider>
<Tooltip open={openTooltipId === 'showTherapeuticRangeLines'} onOpenChange={(open) => setOpenTooltipId(open ? 'showTherapeuticRangeLines' : null)}> <Tooltip open={openTooltipId === 'showTherapeuticRangeLines'} onOpenChange={(open) => setOpenTooltipId(open ? 'showTherapeuticRangeLines' : null)}>
<TooltipTrigger asChild> <TooltipTrigger asChild>
<button <button
@@ -348,7 +343,6 @@ const Settings = ({
<p className="text-xs max-w-xs">{tWithDefaults(t, 'showTherapeuticRangeLinesTooltip', defaultsForT)}</p> <p className="text-xs max-w-xs">{tWithDefaults(t, 'showTherapeuticRangeLinesTooltip', defaultsForT)}</p>
</TooltipContent> </TooltipContent>
</Tooltip> </Tooltip>
</TooltipProvider>
</div> </div>
{showTherapeuticRange && ( {showTherapeuticRange && (
<div className="ml-8 space-y-2"> <div className="ml-8 space-y-2">
@@ -356,7 +350,6 @@ const Settings = ({
<Label className="font-medium"> <Label className="font-medium">
{t('therapeuticRange')} {t('therapeuticRange')}
</Label> </Label>
<TooltipProvider>
<Tooltip open={openTooltipId === 'therapeuticRange'} onOpenChange={(open) => setOpenTooltipId(open ? 'therapeuticRange' : null)}> <Tooltip open={openTooltipId === 'therapeuticRange'} onOpenChange={(open) => setOpenTooltipId(open ? 'therapeuticRange' : null)}>
<TooltipTrigger asChild> <TooltipTrigger asChild>
<button <button
@@ -373,7 +366,6 @@ const Settings = ({
<p className="text-xs max-w-xs">{tWithDefaults(t, 'therapeuticRangeTooltip', defaultsForT)}</p> <p className="text-xs max-w-xs">{tWithDefaults(t, 'therapeuticRangeTooltip', defaultsForT)}</p>
</TooltipContent> </TooltipContent>
</Tooltip> </Tooltip>
</TooltipProvider>
</div> </div>
<div className="flex flex-wrap items-center gap-2"> <div className="flex flex-wrap items-center gap-2">
<FormNumericInput <FormNumericInput
@@ -406,7 +398,6 @@ const Settings = ({
<div className="space-y-2"> <div className="space-y-2">
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<Label className="font-medium">{t('displayedDays')}</Label> <Label className="font-medium">{t('displayedDays')}</Label>
<TooltipProvider>
<Tooltip open={openTooltipId === 'displayedDays'} onOpenChange={(open) => setOpenTooltipId(open ? 'displayedDays' : null)}> <Tooltip open={openTooltipId === 'displayedDays'} onOpenChange={(open) => setOpenTooltipId(open ? 'displayedDays' : null)}>
<TooltipTrigger asChild> <TooltipTrigger asChild>
<button <button
@@ -423,7 +414,6 @@ const Settings = ({
<p className="text-xs max-w-xs">{tWithDefaults(t, 'displayedDaysTooltip', defaultsForT)}</p> <p className="text-xs max-w-xs">{tWithDefaults(t, 'displayedDaysTooltip', defaultsForT)}</p>
</TooltipContent> </TooltipContent>
</Tooltip> </Tooltip>
</TooltipProvider>
</div> </div>
<FormNumericInput <FormNumericInput
value={displayedDays} value={displayedDays}
@@ -440,7 +430,6 @@ const Settings = ({
<div className="space-y-2"> <div className="space-y-2">
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<Label className="font-medium">{t('yAxisRange')}</Label> <Label className="font-medium">{t('yAxisRange')}</Label>
<TooltipProvider>
<Tooltip open={openTooltipId === 'yAxisRange'} onOpenChange={(open) => setOpenTooltipId(open ? 'yAxisRange' : null)}> <Tooltip open={openTooltipId === 'yAxisRange'} onOpenChange={(open) => setOpenTooltipId(open ? 'yAxisRange' : null)}>
<TooltipTrigger asChild> <TooltipTrigger asChild>
<button <button
@@ -457,7 +446,6 @@ const Settings = ({
<p className="text-xs max-w-xs">{tWithDefaults(t, 'yAxisRangeTooltip', defaultsForT)}</p> <p className="text-xs max-w-xs">{tWithDefaults(t, 'yAxisRangeTooltip', defaultsForT)}</p>
</TooltipContent> </TooltipContent>
</Tooltip> </Tooltip>
</TooltipProvider>
</div> </div>
<div className="flex flex-wrap items-center gap-2"> <div className="flex flex-wrap items-center gap-2">
<FormNumericInput <FormNumericInput
@@ -487,7 +475,6 @@ const Settings = ({
<div className="space-y-2"> <div className="space-y-2">
<Label className="font-medium">{t('xAxisTimeFormat')}</Label> <Label className="font-medium">{t('xAxisTimeFormat')}</Label>
<TooltipProvider>
<Select <Select
value={showDayTimeOnXAxis} value={showDayTimeOnXAxis}
onValueChange={value => onUpdateUiSetting('showDayTimeOnXAxis', value)} onValueChange={value => onUpdateUiSetting('showDayTimeOnXAxis', value)}
@@ -528,7 +515,6 @@ const Settings = ({
</Tooltip> </Tooltip>
</SelectContent> </SelectContent>
</Select> </Select>
</TooltipProvider>
</div> </div>
</CardContent> </CardContent>
)} )}
@@ -546,7 +532,6 @@ const Settings = ({
<div className="space-y-2"> <div className="space-y-2">
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<Label className="font-medium">{t('simulationDuration')}</Label> <Label className="font-medium">{t('simulationDuration')}</Label>
<TooltipProvider>
<Tooltip open={openTooltipId === 'simulationDuration'} onOpenChange={(open) => setOpenTooltipId(open ? 'simulationDuration' : null)}> <Tooltip open={openTooltipId === 'simulationDuration'} onOpenChange={(open) => setOpenTooltipId(open ? 'simulationDuration' : null)}>
<TooltipTrigger asChild> <TooltipTrigger asChild>
<button <button
@@ -563,7 +548,6 @@ const Settings = ({
<p className="text-xs max-w-xs">{tWithDefaults(t, 'simulationDurationTooltip', defaultsForT)}</p> <p className="text-xs max-w-xs">{tWithDefaults(t, 'simulationDurationTooltip', defaultsForT)}</p>
</TooltipContent> </TooltipContent>
</Tooltip> </Tooltip>
</TooltipProvider>
</div> </div>
<FormNumericInput <FormNumericInput
value={simulationDays} value={simulationDays}
@@ -598,7 +582,6 @@ const Settings = ({
<Label htmlFor="steadyStateDaysEnabled" className="font-medium"> <Label htmlFor="steadyStateDaysEnabled" className="font-medium">
{t('steadyStateDays')} {t('steadyStateDays')}
</Label> </Label>
<TooltipProvider>
<Tooltip open={openTooltipId === 'steadyStateDays'} onOpenChange={(open) => setOpenTooltipId(open ? 'steadyStateDays' : null)}> <Tooltip open={openTooltipId === 'steadyStateDays'} onOpenChange={(open) => setOpenTooltipId(open ? 'steadyStateDays' : null)}>
<TooltipTrigger asChild> <TooltipTrigger asChild>
<button <button
@@ -615,7 +598,6 @@ const Settings = ({
<p className="text-xs max-w-xs">{tWithDefaults(t, 'steadyStateDaysTooltip', defaultsForT)}</p> <p className="text-xs max-w-xs">{tWithDefaults(t, 'steadyStateDaysTooltip', defaultsForT)}</p>
</TooltipContent> </TooltipContent>
</Tooltip> </Tooltip>
</TooltipProvider>
</div> </div>
{steadyStateDaysEnabled && ( {steadyStateDaysEnabled && (
<div className="ml-8 space-y-2"> <div className="ml-8 space-y-2">
@@ -648,7 +630,6 @@ const Settings = ({
<div className="space-y-2"> <div className="space-y-2">
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<Label className="font-medium">{t('halfLife')}</Label> <Label className="font-medium">{t('halfLife')}</Label>
<TooltipProvider>
<Tooltip open={openTooltipId === 'halfLife'} onOpenChange={(open) => setOpenTooltipId(open ? 'halfLife' : null)}> <Tooltip open={openTooltipId === 'halfLife'} onOpenChange={(open) => setOpenTooltipId(open ? 'halfLife' : null)}>
<TooltipTrigger asChild> <TooltipTrigger asChild>
<button <button
@@ -665,7 +646,6 @@ const Settings = ({
<p className="text-xs max-w-xs">{renderTooltipWithLinks(tWithDefaults(t, 'halfLifeTooltip', defaultsForT))}</p> <p className="text-xs max-w-xs">{renderTooltipWithLinks(tWithDefaults(t, 'halfLifeTooltip', defaultsForT))}</p>
</TooltipContent> </TooltipContent>
</Tooltip> </Tooltip>
</TooltipProvider>
</div> </div>
<FormNumericInput <FormNumericInput
value={pkParams.damph.halfLife} value={pkParams.damph.halfLife}
@@ -688,7 +668,6 @@ const Settings = ({
<div className="space-y-2"> <div className="space-y-2">
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<Label className="font-medium">{t('conversionHalfLife')}</Label> <Label className="font-medium">{t('conversionHalfLife')}</Label>
<TooltipProvider>
<Tooltip open={openTooltipId === 'conversionHalfLife'} onOpenChange={(open) => setOpenTooltipId(open ? 'conversionHalfLife' : null)}> <Tooltip open={openTooltipId === 'conversionHalfLife'} onOpenChange={(open) => setOpenTooltipId(open ? 'conversionHalfLife' : null)}>
<TooltipTrigger asChild> <TooltipTrigger asChild>
<button <button
@@ -705,7 +684,6 @@ const Settings = ({
<p className="text-xs max-w-xs">{tWithDefaults(t, 'conversionHalfLifeTooltip', defaultsForT)}</p> <p className="text-xs max-w-xs">{tWithDefaults(t, 'conversionHalfLifeTooltip', defaultsForT)}</p>
</TooltipContent> </TooltipContent>
</Tooltip> </Tooltip>
</TooltipProvider>
</div> </div>
<FormNumericInput <FormNumericInput
value={pkParams.ldx.halfLife} value={pkParams.ldx.halfLife}
@@ -724,7 +702,6 @@ const Settings = ({
<div className="space-y-2"> <div className="space-y-2">
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<Label className="font-medium">{t('absorptionHalfLife')}</Label> <Label className="font-medium">{t('absorptionHalfLife')}</Label>
<TooltipProvider>
<Tooltip open={openTooltipId === 'absorptionHalfLife'} onOpenChange={(open) => setOpenTooltipId(open ? 'absorptionHalfLife' : null)}> <Tooltip open={openTooltipId === 'absorptionHalfLife'} onOpenChange={(open) => setOpenTooltipId(open ? 'absorptionHalfLife' : null)}>
<TooltipTrigger asChild> <TooltipTrigger asChild>
<button <button
@@ -741,7 +718,6 @@ const Settings = ({
<p className="text-xs max-w-xs">{tWithDefaults(t, 'absorptionHalfLifeTooltip', defaultsForT)}</p> <p className="text-xs max-w-xs">{tWithDefaults(t, 'absorptionHalfLifeTooltip', defaultsForT)}</p>
</TooltipContent> </TooltipContent>
</Tooltip> </Tooltip>
</TooltipProvider>
</div> </div>
<FormNumericInput <FormNumericInput
value={pkParams.ldx.absorptionHalfLife} value={pkParams.ldx.absorptionHalfLife}
@@ -784,7 +760,6 @@ const Settings = ({
<Label htmlFor="weightBasedVdEnabled" className="font-medium"> <Label htmlFor="weightBasedVdEnabled" className="font-medium">
{t('weightBasedVdScaling')} {t('weightBasedVdScaling')}
</Label> </Label>
<TooltipProvider>
<Tooltip open={openTooltipId === 'weightBasedVd'} onOpenChange={(open) => setOpenTooltipId(open ? 'weightBasedVd' : null)}> <Tooltip open={openTooltipId === 'weightBasedVd'} onOpenChange={(open) => setOpenTooltipId(open ? 'weightBasedVd' : null)}>
<TooltipTrigger asChild> <TooltipTrigger asChild>
<button <button
@@ -801,13 +776,11 @@ const Settings = ({
<p className="text-xs max-w-xs">{tWithDefaults(t, 'weightBasedVdTooltip', defaultsForT)}</p> <p className="text-xs max-w-xs">{tWithDefaults(t, 'weightBasedVdTooltip', defaultsForT)}</p>
</TooltipContent> </TooltipContent>
</Tooltip> </Tooltip>
</TooltipProvider>
</div> </div>
{pkParams.advanced.weightBasedVd.enabled && ( {pkParams.advanced.weightBasedVd.enabled && (
<div className="ml-8 space-y-2"> <div className="ml-8 space-y-2">
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<Label className="text-sm font-medium">{t('bodyWeight')}</Label> <Label className="text-sm font-medium">{t('bodyWeight')}</Label>
<TooltipProvider>
<Tooltip open={openTooltipId === 'bodyWeight'} onOpenChange={(open) => setOpenTooltipId(open ? 'bodyWeight' : null)}> <Tooltip open={openTooltipId === 'bodyWeight'} onOpenChange={(open) => setOpenTooltipId(open ? 'bodyWeight' : null)}>
<TooltipTrigger asChild> <TooltipTrigger asChild>
<button <button
@@ -824,7 +797,6 @@ const Settings = ({
<p className="text-xs max-w-xs">{renderTooltipWithLinks(tWithDefaults(t, 'bodyWeightTooltip', defaultsForT))}</p> <p className="text-xs max-w-xs">{renderTooltipWithLinks(tWithDefaults(t, 'bodyWeightTooltip', defaultsForT))}</p>
</TooltipContent> </TooltipContent>
</Tooltip> </Tooltip>
</TooltipProvider>
</div> </div>
<FormNumericInput <FormNumericInput
value={pkParams.advanced.weightBasedVd.bodyWeight} value={pkParams.advanced.weightBasedVd.bodyWeight}
@@ -852,7 +824,6 @@ const Settings = ({
<Label htmlFor="foodEffectEnabled" className="font-medium"> <Label htmlFor="foodEffectEnabled" className="font-medium">
{t('foodEffectEnabled')} {t('foodEffectEnabled')}
</Label> </Label>
<TooltipProvider>
<Tooltip open={openTooltipId === 'foodEffect'} onOpenChange={(open) => setOpenTooltipId(open ? 'foodEffect' : null)}> <Tooltip open={openTooltipId === 'foodEffect'} onOpenChange={(open) => setOpenTooltipId(open ? 'foodEffect' : null)}>
<TooltipTrigger asChild> <TooltipTrigger asChild>
<button <button
@@ -869,13 +840,11 @@ const Settings = ({
<p className="text-xs max-w-xs">{tWithDefaults(t, 'foodEffectTooltip', defaultsForT)}</p> <p className="text-xs max-w-xs">{tWithDefaults(t, 'foodEffectTooltip', defaultsForT)}</p>
</TooltipContent> </TooltipContent>
</Tooltip> </Tooltip>
</TooltipProvider>
</div> </div>
{pkParams.advanced.foodEffect.enabled && ( {pkParams.advanced.foodEffect.enabled && (
<div className="ml-8 space-y-2"> <div className="ml-8 space-y-2">
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<Label className="text-sm font-medium">{t('tmaxDelay')}</Label> <Label className="text-sm font-medium">{t('tmaxDelay')}</Label>
<TooltipProvider>
<Tooltip open={openTooltipId === 'tmaxDelay'} onOpenChange={(open) => setOpenTooltipId(open ? 'tmaxDelay' : null)}> <Tooltip open={openTooltipId === 'tmaxDelay'} onOpenChange={(open) => setOpenTooltipId(open ? 'tmaxDelay' : null)}>
<TooltipTrigger asChild> <TooltipTrigger asChild>
<button <button
@@ -892,7 +861,6 @@ const Settings = ({
<p className="text-xs max-w-xs">{renderTooltipWithLinks(tWithDefaults(t, 'tmaxDelayTooltip', defaultsForT))}</p> <p className="text-xs max-w-xs">{renderTooltipWithLinks(tWithDefaults(t, 'tmaxDelayTooltip', defaultsForT))}</p>
</TooltipContent> </TooltipContent>
</Tooltip> </Tooltip>
</TooltipProvider>
</div> </div>
<FormNumericInput <FormNumericInput
value={pkParams.advanced.foodEffect.tmaxDelay} value={pkParams.advanced.foodEffect.tmaxDelay}
@@ -920,7 +888,6 @@ const Settings = ({
<Label htmlFor="urinePHEnabled" className="font-medium"> <Label htmlFor="urinePHEnabled" className="font-medium">
{t('urinePHTendency')} {t('urinePHTendency')}
</Label> </Label>
<TooltipProvider>
<Tooltip open={openTooltipId === 'urinePH'} onOpenChange={(open) => setOpenTooltipId(open ? 'urinePH' : null)}> <Tooltip open={openTooltipId === 'urinePH'} onOpenChange={(open) => setOpenTooltipId(open ? 'urinePH' : null)}>
<TooltipTrigger asChild> <TooltipTrigger asChild>
<button <button
@@ -937,13 +904,11 @@ const Settings = ({
<p className="text-xs max-w-xs">{tWithDefaults(t, 'urinePHTooltip', defaultsForT)}</p> <p className="text-xs max-w-xs">{tWithDefaults(t, 'urinePHTooltip', defaultsForT)}</p>
</TooltipContent> </TooltipContent>
</Tooltip> </Tooltip>
</TooltipProvider>
</div> </div>
{pkParams.advanced.urinePh.enabled && ( {pkParams.advanced.urinePh.enabled && (
<div className="ml-8 space-y-2"> <div className="ml-8 space-y-2">
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<Label className="text-sm font-medium">{t('urinePHValue')}</Label> <Label className="text-sm font-medium">{t('urinePHValue')}</Label>
<TooltipProvider>
<Tooltip open={openTooltipId === 'urinePHValue'} onOpenChange={(open) => setOpenTooltipId(open ? 'urinePHValue' : null)}> <Tooltip open={openTooltipId === 'urinePHValue'} onOpenChange={(open) => setOpenTooltipId(open ? 'urinePHValue' : null)}>
<TooltipTrigger asChild> <TooltipTrigger asChild>
<button <button
@@ -960,7 +925,6 @@ const Settings = ({
<p className="text-xs max-w-xs">{tWithDefaults(t, 'urinePHValueTooltip', defaultsForT)}</p> <p className="text-xs max-w-xs">{tWithDefaults(t, 'urinePHValueTooltip', defaultsForT)}</p>
</TooltipContent> </TooltipContent>
</Tooltip> </Tooltip>
</TooltipProvider>
</div> </div>
<FormNumericInput <FormNumericInput
value={pkParams.advanced.urinePh.phTendency} value={pkParams.advanced.urinePh.phTendency}
@@ -981,7 +945,6 @@ const Settings = ({
<div className="space-y-2"> <div className="space-y-2">
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<Label className="font-medium">{t('oralBioavailability')}</Label> <Label className="font-medium">{t('oralBioavailability')}</Label>
<TooltipProvider>
<Tooltip open={openTooltipId === 'oralBioavailability'} onOpenChange={(open) => setOpenTooltipId(open ? 'oralBioavailability' : null)}> <Tooltip open={openTooltipId === 'oralBioavailability'} onOpenChange={(open) => setOpenTooltipId(open ? 'oralBioavailability' : null)}>
<TooltipTrigger asChild> <TooltipTrigger asChild>
<button <button
@@ -998,7 +961,6 @@ const Settings = ({
<p className="text-xs max-w-xs">{renderTooltipWithLinks(tWithDefaults(t, 'oralBioavailabilityTooltip', defaultsForT))}</p> <p className="text-xs max-w-xs">{renderTooltipWithLinks(tWithDefaults(t, 'oralBioavailabilityTooltip', defaultsForT))}</p>
</TooltipContent> </TooltipContent>
</Tooltip> </Tooltip>
</TooltipProvider>
</div> </div>
<FormNumericInput <FormNumericInput
value={pkParams.advanced.fOral} value={pkParams.advanced.fOral}

View File

@@ -25,7 +25,6 @@ import {
Tooltip as UiTooltip, Tooltip as UiTooltip,
TooltipTrigger as UiTooltipTrigger, TooltipTrigger as UiTooltipTrigger,
TooltipContent as UiTooltipContent, TooltipContent as UiTooltipContent,
TooltipProvider as UiTooltipProvider,
} from './ui/tooltip'; } from './ui/tooltip';
// Chart color scheme // Chart color scheme
@@ -310,12 +309,12 @@ const chartDomain = React.useMemo(() => {
? scrollableWidth ? scrollableWidth
: Math.ceil((scrollableWidth / dispDays) * simDays); : Math.ceil((scrollableWidth / dispDays) * simDays);
// Render legend with tooltips for full names (custom legend renderer)
const renderLegend = React.useCallback((props: any) => { const renderLegend = React.useCallback((props: any) => {
const { payload } = props; const { payload } = props;
if (!payload) return null; if (!payload) return null;
return ( return (
<UiTooltipProvider>
<ul className="flex flex-wrap gap-2 text-xs leading-tight"> <ul className="flex flex-wrap gap-2 text-xs leading-tight">
{payload.map((item: any) => { {payload.map((item: any) => {
const labelInfo = seriesLabels[item.dataKey] || { display: item.value, full: item.value }; const labelInfo = seriesLabels[item.dataKey] || { display: item.value, full: item.value };
@@ -331,7 +330,6 @@ const chartDomain = React.useMemo(() => {
<UiTooltipTrigger asChild> <UiTooltipTrigger asChild>
<span <span
className="px-1 py-0.5 rounded-sm bg-white text-black shadow-sm border border-muted truncate inline-block max-w-[100px]" className="px-1 py-0.5 rounded-sm bg-white text-black shadow-sm border border-muted truncate inline-block max-w-[100px]"
title={labelInfo.full}
> >
{labelInfo.display} {labelInfo.display}
</span> </span>
@@ -344,7 +342,6 @@ const chartDomain = React.useMemo(() => {
); );
})} })}
</ul> </ul>
</UiTooltipProvider>
); );
}, [seriesLabels]); }, [seriesLabels]);

View File

@@ -11,6 +11,7 @@
import * as React from "react" import * as React from "react"
import { Minus, Plus, X } from "lucide-react" import { Minus, Plus, X } from "lucide-react"
import { Button } from "./button" import { Button } from "./button"
import { IconButtonWithTooltip } from "./icon-button-with-tooltip"
import { Input } from "./input" import { Input } from "./input"
import { cn } from "../../lib/utils" import { cn } from "../../lib/utils"
import { useTranslation } from "react-i18next" import { useTranslation } from "react-i18next"
@@ -199,8 +200,10 @@ const FormNumericInput = React.forwardRef<HTMLInputElement, NumericInputProps>(
<Plus className="h-4 w-4" /> <Plus className="h-4 w-4" />
</Button> </Button>
{clearButton && allowEmpty && ( {clearButton && allowEmpty && (
<Button <IconButtonWithTooltip
type="button" type="button"
icon={<X className="h-4 w-4" />}
tooltip={t('buttonClear')}
variant="outline" variant="outline"
size="icon" size="icon"
className={cn( className={cn(
@@ -210,10 +213,7 @@ const FormNumericInput = React.forwardRef<HTMLInputElement, NumericInputProps>(
)} )}
onClick={() => onChange('')} onClick={() => onChange('')}
tabIndex={-1} tabIndex={-1}
title={ t('buttonClear') } />
>
<X className="h-4 w-4" />
</Button>
)} )}
</div> </div>
{unit && <span className="text-sm text-muted-foreground whitespace-nowrap">{unit}</span>} {unit && <span className="text-sm text-muted-foreground whitespace-nowrap">{unit}</span>}

View File

@@ -0,0 +1,49 @@
/**
* IconButtonWithTooltip
*
* A reusable button component that combines an icon button with a tooltip.
* Provides consistent tooltip behavior across the app for icon-only action buttons.
*
* @author Andreas Weyer
* @license MIT
*/
import React from 'react';
import { Button, ButtonProps } from './button';
import { Tooltip, TooltipContent, TooltipTrigger } from './tooltip';
interface IconButtonWithTooltipProps extends Omit<ButtonProps, 'children'> {
/** The icon element to display in the button */
icon: React.ReactNode;
/** The tooltip text to show on hover */
tooltip: string;
/** Optional side for tooltip positioning */
tooltipSide?: 'top' | 'right' | 'bottom' | 'left';
}
export const IconButtonWithTooltip: React.FC<IconButtonWithTooltipProps> = ({
icon,
tooltip,
tooltipSide = 'top',
disabled,
...buttonProps
}) => {
return (
<Tooltip>
<TooltipTrigger asChild>
<Button
disabled={disabled}
aria-label={tooltip}
{...buttonProps}
>
{icon}
</Button>
</TooltipTrigger>
{!disabled && (
<TooltipContent side={tooltipSide}>
<p className="text-xs">{tooltip}</p>
</TooltipContent>
)}
</Tooltip>
);
};