diff --git a/src/components/day-schedule.tsx b/src/components/day-schedule.tsx index 0bb1b59..7acbc60 100644 --- a/src/components/day-schedule.tsx +++ b/src/components/day-schedule.tsx @@ -99,13 +99,19 @@ const DaySchedule: React.FC = ({ // Get template day for comparison 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 let doseCountDiff = 0; let totalMgDiff = 0; if (!day.isTemplate && templateDay) { 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); totalMgDiff = dayTotal - templateTotal; } @@ -153,7 +159,7 @@ const DaySchedule: React.FC = ({ > 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 ? : } {day.doses.length} {day.doses.length === 1 ? t('dose') : t('doses')} @@ -180,27 +186,59 @@ const DaySchedule: React.FC = ({ > 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 ? : } - {day.doses.reduce((sum, dose) => sum + (parseFloat(dose.ldx) || 0), 0).toFixed(1)} mg + {!isDailyTotalError && !isDailyTotalWarning && (totalMgDiff > 0 ? : )} + {dayTotal.toFixed(1)} mg

- {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')}` + }

) : ( - - {day.doses.reduce((sum, dose) => sum + (parseFloat(dose.ldx) || 0), 0).toFixed(1)} mg + + {dayTotal.toFixed(1)} mg )} {!collapsedDays.has(day.id) && ( + {/* Daily total warning/error box */} + {(isDailyTotalWarning || isDailyTotalError) && ( +
+ + {formatText(isDailyTotalError + ? t('errorDailyTotalAbove200mg').replace('{{total}}', dayTotal.toFixed(1)) + : t('warningDailyTotalAbove70mg').replace('{{total}}', dayTotal.toFixed(1)) + )} +
+ )} {/* Dose table header */}
@@ -247,6 +285,23 @@ const DaySchedule: React.FC = ({ // Check for dose > 70 mg 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 (
@@ -266,13 +321,10 @@ const DaySchedule: React.FC = ({ max={200} //unit="mg" required={true} - warning={isZeroDose || isHighDose} - errorMessage={formatText(t('errorNumberRequired'))} - warningMessage={ - isZeroDose ? formatText(t('warningZeroDose')) - : isHighDose ? formatText(t('warningDoseAbove70mg')) - : undefined // should not happen since warning is false - } + error={isDailyTotalError} + warning={isDailyTotalWarning || isZeroDose || isHighDose} + errorMessage={doseErrorMessage || formatText(t('errorNumberRequired'))} + warningMessage={doseWarningMessage} inputWidth="w-[72px]" />
diff --git a/src/locales/de.ts b/src/locales/de.ts index adc9e3a..0c40c91 100644 --- a/src/locales/de.ts +++ b/src/locales/de.ts @@ -296,6 +296,8 @@ export const de = { 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.", 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 regularPlan: "Regulärer Plan", diff --git a/src/locales/en.ts b/src/locales/en.ts index b256c07..317d215 100644 --- a/src/locales/en.ts +++ b/src/locales/en.ts @@ -296,6 +296,8 @@ export const en = { 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).", 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 timePickerHour: "Hour", diff --git a/src/styles/global.css b/src/styles/global.css index d58db7b..1f45813 100644 --- a/src/styles/global.css +++ b/src/styles/global.css @@ -123,4 +123,22 @@ .info-text { @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; + } }