120 lines
4.1 KiB
TypeScript
120 lines
4.1 KiB
TypeScript
/**
|
|
* Deviation List Component
|
|
*
|
|
* Tracks and manages deviations from the planned medication schedule.
|
|
* Supports adding, editing, and deleting deviations with automatic or
|
|
* manual timestamps. Each deviation can be marked as planned or actual.
|
|
*
|
|
* @author Andreas Weyer
|
|
* @license MIT
|
|
*/
|
|
|
|
import React from 'react';
|
|
import { FormTimeInput } from './ui/form-time-input';
|
|
import { FormNumericInput } from './ui/form-numeric-input';
|
|
import { Card, CardContent, CardHeader, CardTitle } from './ui/card';
|
|
import { Button } from './ui/button';
|
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from './ui/select';
|
|
import { Switch } from './ui/switch';
|
|
import { Label } from './ui/label';
|
|
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from './ui/tooltip';
|
|
import { X } from 'lucide-react';
|
|
|
|
const DeviationList = ({
|
|
deviations,
|
|
doseIncrement,
|
|
simulationDays,
|
|
onAddDeviation,
|
|
onRemoveDeviation,
|
|
onDeviationChange,
|
|
t
|
|
}: any) => {
|
|
return (
|
|
<Card className="bg-amber-50/50 border-amber-200">
|
|
<CardHeader>
|
|
<CardTitle>{t.deviationsFromPlan}</CardTitle>
|
|
</CardHeader>
|
|
<CardContent className="space-y-2">
|
|
{deviations.map((dev: any, index: number) => (
|
|
<div key={index} className="relative flex items-start gap-3 p-3 bg-card rounded-lg border flex-wrap">
|
|
<div className="flex items-center gap-3 flex-1 flex-wrap">
|
|
<Select
|
|
value={String(dev.dayOffset || 0)}
|
|
onValueChange={val => onDeviationChange(index, 'dayOffset', parseInt(val, 10))}
|
|
>
|
|
<SelectTrigger className="w-28">
|
|
<SelectValue />
|
|
</SelectTrigger>
|
|
<SelectContent>
|
|
{[...Array(parseInt(simulationDays, 10) || 1).keys()].map(day => (
|
|
<SelectItem key={day} value={String(day)}>
|
|
{t.day} {day + 1}
|
|
</SelectItem>
|
|
))}
|
|
</SelectContent>
|
|
</Select>
|
|
|
|
<FormTimeInput
|
|
value={dev.time}
|
|
onChange={newTime => onDeviationChange(index, 'time', newTime)}
|
|
required={true}
|
|
errorMessage={t.timeRequired || 'Time is required'}
|
|
/>
|
|
|
|
<FormNumericInput
|
|
value={dev.dose}
|
|
onChange={newDose => onDeviationChange(index, 'dose', newDose)}
|
|
increment={doseIncrement}
|
|
min={0}
|
|
unit={t.mg}
|
|
required={true}
|
|
errorMessage={t.doseRequired || 'Dose is required'}
|
|
/>
|
|
|
|
<TooltipProvider>
|
|
<Tooltip>
|
|
<TooltipTrigger asChild>
|
|
<div className="flex items-center gap-2">
|
|
<Switch
|
|
id={`add_dose_${index}`}
|
|
checked={dev.isAdditional}
|
|
onCheckedChange={checked => onDeviationChange(index, 'isAdditional', checked)}
|
|
/>
|
|
<Label htmlFor={`add_dose_${index}`} className="text-xs whitespace-nowrap">
|
|
{t.additional}
|
|
</Label>
|
|
</div>
|
|
</TooltipTrigger>
|
|
<TooltipContent>
|
|
<p>{t.additionalTooltip}</p>
|
|
</TooltipContent>
|
|
</Tooltip>
|
|
</TooltipProvider>
|
|
</div>
|
|
|
|
<Button
|
|
type="button"
|
|
variant="outline"
|
|
size="icon"
|
|
onClick={() => onRemoveDeviation(index)}
|
|
className="absolute top-3 right-3 text-destructive hover:bg-destructive hover:text-destructive-foreground border-destructive/30"
|
|
>
|
|
<X className="h-4 w-4" />
|
|
</Button>
|
|
</div>
|
|
))}
|
|
|
|
<Button
|
|
type="button"
|
|
onClick={onAddDeviation}
|
|
className="w-full bg-amber-500 hover:bg-amber-600 text-white"
|
|
>
|
|
{t.addDeviation}
|
|
</Button>
|
|
</CardContent>
|
|
</Card>
|
|
);
|
|
};
|
|
|
|
export default DeviationList;
|