Update new check for max daily dose waring and error

This commit is contained in:
2026-02-08 12:08:18 +00:00
parent 7f8503387c
commit c41db99cba
4 changed files with 89 additions and 15 deletions

View File

@@ -99,13 +99,19 @@ const DaySchedule: React.FC<DayScheduleProps> = ({
// Get template day for comparison // Get template day for comparison
const templateDay = days.find(d => d.isTemplate); const templateDay = days.find(d => d.isTemplate);
// Calculate daily total
const dayTotal = day.doses.reduce((sum, dose) => sum + (parseFloat(dose.ldx) || 0), 0);
// Check for daily total warnings/errors
const isDailyTotalError = dayTotal > 200;
const isDailyTotalWarning = !isDailyTotalError && dayTotal > 70;
// Calculate differences for deviation days // Calculate differences for deviation days
let doseCountDiff = 0; let doseCountDiff = 0;
let totalMgDiff = 0; let totalMgDiff = 0;
if (!day.isTemplate && templateDay) { if (!day.isTemplate && templateDay) {
doseCountDiff = day.doses.length - templateDay.doses.length; doseCountDiff = day.doses.length - templateDay.doses.length;
const dayTotal = day.doses.reduce((sum, dose) => sum + (parseFloat(dose.ldx) || 0), 0);
const templateTotal = templateDay.doses.reduce((sum, dose) => sum + (parseFloat(dose.ldx) || 0), 0); const templateTotal = templateDay.doses.reduce((sum, dose) => sum + (parseFloat(dose.ldx) || 0), 0);
totalMgDiff = dayTotal - templateTotal; totalMgDiff = dayTotal - templateTotal;
} }
@@ -153,7 +159,7 @@ const DaySchedule: React.FC<DayScheduleProps> = ({
> >
<Badge <Badge
variant="outline" variant="outline"
className={`text-xs ${doseCountDiff > 0 ? 'bg-blue-100 dark:bg-blue-900/60 dark:text-blue-200' : 'bg-orange-100 dark:bg-orange-900/60 dark:text-orange-200'}`} className={`text-xs ${doseCountDiff > 0 ? 'badge-trend-up' : 'badge-trend-down'}`}
> >
{doseCountDiff > 0 ? <TrendingUp className="h-3 w-3 inline mr-1" /> : <TrendingDown className="h-3 w-3 inline mr-1" />} {doseCountDiff > 0 ? <TrendingUp className="h-3 w-3 inline mr-1" /> : <TrendingDown className="h-3 w-3 inline mr-1" />}
{day.doses.length} {day.doses.length === 1 ? t('dose') : t('doses')} {day.doses.length} {day.doses.length === 1 ? t('dose') : t('doses')}
@@ -180,27 +186,59 @@ const DaySchedule: React.FC<DayScheduleProps> = ({
> >
<Badge <Badge
variant="outline" variant="outline"
className={`text-xs ${totalMgDiff > 0 ? 'bg-blue-100 dark:bg-blue-900/60 dark:text-blue-200' : 'bg-orange-100 dark:bg-orange-900/60 dark:text-orange-200'}`} className={`text-xs ${
isDailyTotalError
? 'badge-error'
: isDailyTotalWarning
? 'badge-warning'
: totalMgDiff > 0
? 'badge-trend-up'
: 'badge-trend-down'
}`}
> >
{totalMgDiff > 0 ? <TrendingUp className="h-3 w-3 inline mr-1" /> : <TrendingDown className="h-3 w-3 inline mr-1" />} {!isDailyTotalError && !isDailyTotalWarning && (totalMgDiff > 0 ? <TrendingUp className="h-3 w-3 inline mr-1" /> : <TrendingDown className="h-3 w-3 inline mr-1" />)}
{day.doses.reduce((sum, dose) => sum + (parseFloat(dose.ldx) || 0), 0).toFixed(1)} mg {dayTotal.toFixed(1)} mg
</Badge> </Badge>
</button> </button>
</TooltipTrigger> </TooltipTrigger>
<TooltipContent> <TooltipContent>
<p className="text-xs"> <p className="text-xs">
{totalMgDiff > 0 ? '+' : ''}{totalMgDiff.toFixed(1)} mg {t('comparedToRegularPlan')} {isDailyTotalError
? `${t('errorDailyTotalAbove200mg').replace('{{total}}', dayTotal.toFixed(1))}`
: isDailyTotalWarning
? `${t('warningDailyTotalAbove70mg').replace('{{total}}', dayTotal.toFixed(1))}`
: `${totalMgDiff > 0 ? '+' : ''}${totalMgDiff.toFixed(1)} mg ${t('comparedToRegularPlan')}`
}
</p> </p>
</TooltipContent> </TooltipContent>
</Tooltip> </Tooltip>
) : ( ) : (
<Badge variant="outline" className="text-xs"> <Badge
{day.doses.reduce((sum, dose) => sum + (parseFloat(dose.ldx) || 0), 0).toFixed(1)} mg variant="outline"
className={`text-xs ${
isDailyTotalError
? 'badge-error'
: isDailyTotalWarning
? 'badge-warning'
: ''
}`}
>
{dayTotal.toFixed(1)} mg
</Badge> </Badge>
)} )}
</CollapsibleCardHeader> </CollapsibleCardHeader>
{!collapsedDays.has(day.id) && ( {!collapsedDays.has(day.id) && (
<CardContent className="space-y-3"> <CardContent className="space-y-3">
{/* Daily total warning/error box */}
{(isDailyTotalWarning || isDailyTotalError) && (
<div className={`p-3 rounded-md text-sm ${isDailyTotalError ? 'error-bg-box' : 'warning-bg-box'}`}>
{formatText(isDailyTotalError
? t('errorDailyTotalAbove200mg').replace('{{total}}', dayTotal.toFixed(1))
: t('warningDailyTotalAbove70mg').replace('{{total}}', dayTotal.toFixed(1))
)}
</div>
)}
{/* Dose table header */} {/* Dose table header */}
<div className="grid grid-cols-[120px_1fr_auto] sm:grid-cols-[120px_1fr_auto_auto] gap-2 text-sm font-medium text-muted-foreground"> <div className="grid grid-cols-[120px_1fr_auto] sm:grid-cols-[120px_1fr_auto_auto] gap-2 text-sm font-medium text-muted-foreground">
<div className="flex items-center gap-1"> <div className="flex items-center gap-1">
@@ -247,6 +285,23 @@ const DaySchedule: React.FC<DayScheduleProps> = ({
// Check for dose > 70 mg // Check for dose > 70 mg
const isHighDose = parseFloat(dose.ldx) > 70; const isHighDose = parseFloat(dose.ldx) > 70;
// Determine the error/warning message priority:
// 1. Daily total error (> 200mg) - ERROR
// 2. Daily total warning (> 70mg) - WARNING
// 3. Individual dose warning (zero dose or > 70mg) - WARNING
let doseErrorMessage;
let doseWarningMessage;
if (isDailyTotalError) {
doseErrorMessage = formatText(t('errorDailyTotalAbove200mg').replace('{{total}}', dayTotal.toFixed(1)));
} else if (isDailyTotalWarning) {
doseWarningMessage = formatText(t('warningDailyTotalAbove70mg').replace('{{total}}', dayTotal.toFixed(1)));
} else if (isZeroDose) {
doseWarningMessage = formatText(t('warningZeroDose'));
} else if (isHighDose) {
doseWarningMessage = formatText(t('warningDoseAbove70mg'));
}
return ( return (
<div key={dose.id} className="space-y-2"> <div key={dose.id} className="space-y-2">
<div className="grid grid-cols-[120px_1fr_auto] sm:grid-cols-[120px_1fr_auto_auto] gap-2 items-center"> <div className="grid grid-cols-[120px_1fr_auto] sm:grid-cols-[120px_1fr_auto_auto] gap-2 items-center">
@@ -266,13 +321,10 @@ const DaySchedule: React.FC<DayScheduleProps> = ({
max={200} max={200}
//unit="mg" //unit="mg"
required={true} required={true}
warning={isZeroDose || isHighDose} error={isDailyTotalError}
errorMessage={formatText(t('errorNumberRequired'))} warning={isDailyTotalWarning || isZeroDose || isHighDose}
warningMessage={ errorMessage={doseErrorMessage || formatText(t('errorNumberRequired'))}
isZeroDose ? formatText(t('warningZeroDose')) warningMessage={doseWarningMessage}
: isHighDose ? formatText(t('warningDoseAbove70mg'))
: undefined // should not happen since warning is false
}
inputWidth="w-[72px]" inputWidth="w-[72px]"
/> />
<div className="flex gap-2 sm:contents"> <div className="flex gap-2 sm:contents">

View File

@@ -296,6 +296,8 @@ export const de = {
warningConversionOutOfRange: "⚠️ Typischer Bereich: 0,7-1,2h. Aktueller Wert könnte außerhalb klinischer Normen liegen.", warningConversionOutOfRange: "⚠️ Typischer Bereich: 0,7-1,2h. Aktueller Wert könnte außerhalb klinischer Normen liegen.",
warningEliminationOutOfRange: "⚠️ Typischer Bereich: 9-12h (normaler pH). Erweiterter Bereich 7-15h (pH-Effekte). Aktueller Wert ist ungewöhnlich.", warningEliminationOutOfRange: "⚠️ Typischer Bereich: 9-12h (normaler pH). Erweiterter Bereich 7-15h (pH-Effekte). Aktueller Wert ist ungewöhnlich.",
warningDoseAbove70mg: "⚠️ FDA-zugelassenes Maximum: 70 mg. Höhere Dosen haben keine Sicherheitsdaten und erhöhen kardiovaskuläre Risiken.", warningDoseAbove70mg: "⚠️ FDA-zugelassenes Maximum: 70 mg. Höhere Dosen haben keine Sicherheitsdaten und erhöhen kardiovaskuläre Risiken.",
warningDailyTotalAbove70mg: "⚠️ **Tagesgesamtdosis überschreitet empfohlenes Maximum.**\\n\\n__FDA-zugelassenes Maximum:__ **70 mg/Tag**.\\nIhre Tagesgesamtdosis: **{{total}} mg**.\\nKonsultieren Sie Ihren Arzt, bevor Sie diese Dosis überschreiten.",
errorDailyTotalAbove200mg: "⛔ **Tagesgesamtdosis überschreitet sichere Grenzen erheblich!**\\n\\nIhre Tagesgesamtdosis **{{total}} mg** überschreitet 200 mg/Tag, was **deutlich über FDA-zugelassenen Grenzen** liegt. *Bitte konsultieren Sie Ihren Arzt.*",
// Day-based schedule // Day-based schedule
regularPlan: "Regulärer Plan", regularPlan: "Regulärer Plan",

View File

@@ -296,6 +296,8 @@ export const en = {
warningConversionOutOfRange: "⚠️ Current value may be outside clinical norms.\\n\\n__Typical range:__ **0.7-1.2h**.", warningConversionOutOfRange: "⚠️ Current value may be outside clinical norms.\\n\\n__Typical range:__ **0.7-1.2h**.",
warningEliminationOutOfRange: "⚠️ Current value may be outside clinical norms.\\n\\n__Typical range:__ **9-12h** (normal pH).\\nExtended range 7-15h (pH effects).", warningEliminationOutOfRange: "⚠️ Current value may be outside clinical norms.\\n\\n__Typical range:__ **9-12h** (normal pH).\\nExtended range 7-15h (pH effects).",
warningDoseAbove70mg: "⚠️ Higher doses lack safety data and increase cardiovascular risk.\\n\\n__FDA-approved maximum:__ **70 mg**.\\n\\nConsult your physician before exceeding this dose.", warningDoseAbove70mg: "⚠️ Higher doses lack safety data and increase cardiovascular risk.\\n\\n__FDA-approved maximum:__ **70 mg**.\\n\\nConsult your physician before exceeding this dose.",
warningDailyTotalAbove70mg: "⚠️ **Daily total exceeds recommended maximum.**\\n\\n__FDA-approved maximum:__ **70 mg/day**.\\nYour daily total: **{{total}} mg**.\\nConsult your physician before exceeding this dose.",
errorDailyTotalAbove200mg: "⛔ **Daily total far exceeds safe limits!**\\n\\nYour daily total **{{total}} mg** exceeds 200 mg/day which is **significantly beyond FDA-approved limits**. *Please consult your physician.*",
// Time picker // Time picker
timePickerHour: "Hour", timePickerHour: "Hour",

View File

@@ -123,4 +123,22 @@
.info-text { .info-text {
@apply text-[hsl(var(--foreground))]; @apply text-[hsl(var(--foreground))];
} }
/* Badge variants for validation states */
.badge-error {
@apply border-red-500 bg-red-500/20 text-red-700 dark:text-red-300;
}
.badge-warning {
@apply border-amber-500 bg-amber-500/20 text-amber-700 dark:text-amber-300;
}
/* Badge variants for trend indicators */
.badge-trend-up {
@apply bg-blue-100 dark:bg-blue-900/60 text-blue-700 dark:text-blue-200;
}
.badge-trend-down {
@apply bg-orange-100 dark:bg-orange-900/60 text-orange-700 dark:text-orange-200;
}
} }