Update day-schedule add folding and summary badges with trend indicator

This commit is contained in:
2025-12-04 01:45:11 +00:00
parent abae3d54e6
commit d64b9eabfa
3 changed files with 110 additions and 6 deletions

View File

@@ -15,7 +15,7 @@ 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, TooltipProvider, TooltipTrigger } from './ui/tooltip';
import { Plus, Copy, Trash2, ArrowDownAZ } from 'lucide-react'; import { Plus, Copy, Trash2, ArrowDownAZ, ChevronDown, ChevronUp, TrendingUp, TrendingDown } from 'lucide-react';
import type { DayGroup } from '../constants/defaults'; import type { DayGroup } from '../constants/defaults';
interface DayScheduleProps { interface DayScheduleProps {
@@ -43,6 +43,21 @@ const DaySchedule: React.FC<DayScheduleProps> = ({
}) => { }) => {
const canAddDay = days.length < 3; const canAddDay = days.length < 3;
// Track collapsed state for each day (by day ID)
const [collapsedDays, setCollapsedDays] = React.useState<Set<string>>(new Set());
const toggleDayCollapse = (dayId: string) => {
setCollapsedDays(prev => {
const newSet = new Set(prev);
if (newSet.has(dayId)) {
newSet.delete(dayId);
} else {
newSet.add(dayId);
}
return newSet;
});
};
// Check if doses are sorted chronologically // Check if doses are sorted chronologically
const isDaySorted = (day: DayGroup): boolean => { const isDaySorted = (day: DayGroup): boolean => {
for (let i = 1; i < day.doses.length; i++) { for (let i = 1; i < day.doses.length; i++) {
@@ -57,17 +72,94 @@ const DaySchedule: React.FC<DayScheduleProps> = ({
return ( return (
<div className="space-y-4"> <div className="space-y-4">
{days.map((day, dayIndex) => ( {days.map((day, dayIndex) => {
// Get template day for comparison
const templateDay = days.find(d => d.isTemplate);
// 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;
}
return (
<Card key={day.id}> <Card key={day.id}>
<CardHeader className="pb-3"> <CardHeader className="pb-3">
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<div className="flex items-center gap-2"> <div className="flex items-center gap-2 flex-wrap">
<Button
type="button"
size="sm"
variant="ghost"
className="h-6 w-6 p-0"
onClick={() => toggleDayCollapse(day.id)}
title={collapsedDays.has(day.id) ? t('expandDay') : t('collapseDay')}
>
{collapsedDays.has(day.id) ? (
<ChevronDown className="h-4 w-4" />
) : (
<ChevronUp className="h-4 w-4" />
)}
</Button>
<CardTitle className="text-lg"> <CardTitle className="text-lg">
{day.isTemplate ? t('regularPlan') : t('deviatingPlan')} {day.isTemplate ? t('regularPlan') : t('deviatingPlan')}
</CardTitle> </CardTitle>
<Badge variant="secondary" className="text-xs"> <Badge variant="secondary" className="text-xs">
{t('day')} {dayIndex + 1} {t('day')} {dayIndex + 1}
</Badge> </Badge>
{!day.isTemplate && doseCountDiff !== 0 ? (
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<Badge
variant="outline"
className={`text-xs ${doseCountDiff > 0 ? 'bg-blue-50' : 'bg-orange-50'}`}
>
{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')}
</Badge>
</TooltipTrigger>
<TooltipContent>
<p className="text-xs">
{doseCountDiff > 0 ? '+' : ''}{doseCountDiff} {Math.abs(doseCountDiff) === 1 ? t('dose') : t('doses')} {t('comparedToRegularPlan')}
</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
) : (
<Badge variant="outline" className="text-xs">
{day.doses.length} {day.doses.length === 1 ? t('dose') : t('doses')}
</Badge>
)}
{!day.isTemplate && Math.abs(totalMgDiff) > 0.1 ? (
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<Badge
variant="outline"
className={`text-xs ${totalMgDiff > 0 ? 'bg-blue-50' : 'bg-orange-50'}`}
>
{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
</Badge>
</TooltipTrigger>
<TooltipContent>
<p className="text-xs">
{totalMgDiff > 0 ? '+' : ''}{totalMgDiff.toFixed(1)} mg {t('comparedToRegularPlan')}
</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
) : (
<Badge variant="outline" className="text-xs">
{day.doses.reduce((sum, dose) => sum + (parseFloat(dose.ldx) || 0), 0).toFixed(1)} mg
</Badge>
)}
</div> </div>
<div className="flex gap-2"> <div className="flex gap-2">
{canAddDay && ( {canAddDay && (
@@ -94,6 +186,7 @@ const DaySchedule: React.FC<DayScheduleProps> = ({
</div> </div>
</div> </div>
</CardHeader> </CardHeader>
{!collapsedDays.has(day.id) && (
<CardContent className="space-y-3"> <CardContent className="space-y-3">
{/* Dose table header */} {/* Dose table header */}
<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">
@@ -186,8 +279,9 @@ const DaySchedule: React.FC<DayScheduleProps> = ({
</Button> </Button>
)} )}
</CardContent> </CardContent>
)}
</Card> </Card>
))} )})}
{/* Add day button */} {/* Add day button */}
{canAddDay && ( {canAddDay && (

View File

@@ -98,13 +98,18 @@ export const de = {
// Day-based schedule // Day-based schedule
regularPlan: "Regulärer Plan", regularPlan: "Regulärer Plan",
deviatingPlan: "Abweichung vom Plan", deviatingPlan: "Abweichung vom Plan",
regularPlanOverlay: "nach Plan", regularPlanOverlay: "Regulär",
dayNumber: "Tag {{number}}", dayNumber: "Tag {{number}}",
cloneDay: "Tag klonen", cloneDay: "Tag klonen",
addDay: "Tag hinzufügen", addDay: "Tag hinzufügen",
addDose: "Dosis hinzufügen", addDose: "Dosis hinzufügen",
removeDose: "Dosis entfernen", removeDose: "Dosis entfernen",
removeDay: "Tag entfernen", removeDay: "Tag entfernen",
collapseDay: "Tag einklappen",
expandDay: "Tag ausklappen",
dose: "Dosis",
doses: "Dosen",
comparedToRegularPlan: "verglichen mit regulärem Plan",
time: "Zeit", time: "Zeit",
ldx: "LDX", ldx: "LDX",
damph: "d-amph", damph: "d-amph",

View File

@@ -107,13 +107,18 @@ export const en = {
// Day-based schedule // Day-based schedule
regularPlan: "Regular Plan", regularPlan: "Regular Plan",
deviatingPlan: "Deviation from Plan", deviatingPlan: "Deviation from Plan",
regularPlanOverlay: "as planned", regularPlanOverlay: "Regular",
dayNumber: "Day {{number}}", dayNumber: "Day {{number}}",
cloneDay: "Clone day", cloneDay: "Clone day",
addDay: "Add day", addDay: "Add day",
addDose: "Add dose", addDose: "Add dose",
removeDose: "Remove dose", removeDose: "Remove dose",
removeDay: "Remove day", removeDay: "Remove day",
collapseDay: "Collapse day",
expandDay: "Expand day",
dose: "dose",
doses: "doses",
comparedToRegularPlan: "compared to regular plan",
time: "Time", time: "Time",
ldx: "LDX", ldx: "LDX",
damph: "d-amph", damph: "d-amph",