Update day-schedule add folding and summary badges with trend indicator
This commit is contained in:
@@ -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 && (
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
Reference in New Issue
Block a user