Add collapsible-card-header component to consolidate and improve folding
This commit is contained in:
@@ -10,12 +10,13 @@
|
||||
|
||||
import React from 'react';
|
||||
import { Button } from './ui/button';
|
||||
import { Card, CardContent, CardHeader, CardTitle } from './ui/card';
|
||||
import { Card, CardContent } from './ui/card';
|
||||
import { Badge } from './ui/badge';
|
||||
import { FormTimeInput } from './ui/form-time-input';
|
||||
import { FormNumericInput } from './ui/form-numeric-input';
|
||||
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from './ui/tooltip';
|
||||
import { Plus, Copy, Trash2, ArrowDownAZ, ChevronDown, ChevronUp, TrendingUp, TrendingDown } from 'lucide-react';
|
||||
import CollapsibleCardHeader from './ui/collapsible-card-header';
|
||||
import { Plus, Copy, Trash2, ArrowDownAZ, TrendingUp, TrendingDown } from 'lucide-react';
|
||||
import type { DayGroup } from '../constants/defaults';
|
||||
|
||||
interface DayScheduleProps {
|
||||
@@ -46,6 +47,23 @@ const DaySchedule: React.FC<DayScheduleProps> = ({
|
||||
// Track collapsed state for each day (by day ID)
|
||||
const [collapsedDays, setCollapsedDays] = React.useState<Set<string>>(new Set());
|
||||
|
||||
// Load and persist collapsed days state
|
||||
React.useEffect(() => {
|
||||
const savedCollapsed = localStorage.getItem('dayScheduleCollapsedDays_v1');
|
||||
if (savedCollapsed) {
|
||||
try {
|
||||
const collapsedArray = JSON.parse(savedCollapsed);
|
||||
setCollapsedDays(new Set(collapsedArray));
|
||||
} catch (e) {
|
||||
console.warn('Failed to load collapsed days state:', e);
|
||||
}
|
||||
}
|
||||
}, []);
|
||||
|
||||
const saveCollapsedDays = (newCollapsedDays: Set<string>) => {
|
||||
localStorage.setItem('dayScheduleCollapsedDays_v1', JSON.stringify(Array.from(newCollapsedDays)));
|
||||
};
|
||||
|
||||
const toggleDayCollapse = (dayId: string) => {
|
||||
setCollapsedDays(prev => {
|
||||
const newSet = new Set(prev);
|
||||
@@ -54,6 +72,7 @@ const DaySchedule: React.FC<DayScheduleProps> = ({
|
||||
} else {
|
||||
newSet.add(dayId);
|
||||
}
|
||||
saveCollapsedDays(newSet);
|
||||
return newSet;
|
||||
});
|
||||
};
|
||||
@@ -89,89 +108,13 @@ const DaySchedule: React.FC<DayScheduleProps> = ({
|
||||
|
||||
return (
|
||||
<Card key={day.id}>
|
||||
<CardHeader className="pb-3">
|
||||
<div className="flex items-center justify-between">
|
||||
<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">
|
||||
{day.isTemplate ? t('regularPlan') : t('alternativePlan')}
|
||||
</CardTitle>
|
||||
<Badge variant="secondary" className="text-xs">
|
||||
{t('day')} {dayIndex + 1}
|
||||
</Badge>
|
||||
{!day.isTemplate && doseCountDiff !== 0 ? (
|
||||
<TooltipProvider>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<button
|
||||
type="button"
|
||||
className="inline-flex items-center cursor-help focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 rounded-md"
|
||||
>
|
||||
<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>
|
||||
</button>
|
||||
</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>
|
||||
<button
|
||||
type="button"
|
||||
className="inline-flex items-center cursor-help focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 rounded-md"
|
||||
>
|
||||
<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>
|
||||
</button>
|
||||
</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 className="flex gap-2">
|
||||
<CollapsibleCardHeader
|
||||
title={day.isTemplate ? t('regularPlan') : t('alternativePlan')}
|
||||
isCollapsed={collapsedDays.has(day.id)}
|
||||
onToggle={() => toggleDayCollapse(day.id)}
|
||||
toggleLabel={collapsedDays.has(day.id) ? t('expandDay') : t('collapseDay')}
|
||||
rightSection={
|
||||
<>
|
||||
{canAddDay && (
|
||||
<Button
|
||||
onClick={() => onAddDay(day.id)}
|
||||
@@ -193,9 +136,71 @@ const DaySchedule: React.FC<DayScheduleProps> = ({
|
||||
<Trash2 className="h-4 w-4" />
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</CardHeader>
|
||||
</>
|
||||
}
|
||||
>
|
||||
<Badge variant="secondary" className="text-xs">
|
||||
{t('day')} {dayIndex + 1}
|
||||
</Badge>
|
||||
{!day.isTemplate && doseCountDiff !== 0 ? (
|
||||
<TooltipProvider>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<button
|
||||
type="button"
|
||||
className="inline-flex items-center cursor-help focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 rounded-md"
|
||||
>
|
||||
<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>
|
||||
</button>
|
||||
</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>
|
||||
<button
|
||||
type="button"
|
||||
className="inline-flex items-center cursor-help focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 rounded-md"
|
||||
>
|
||||
<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>
|
||||
</button>
|
||||
</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>
|
||||
)}
|
||||
</CollapsibleCardHeader>
|
||||
{!collapsedDays.has(day.id) && (
|
||||
<CardContent className="space-y-3">
|
||||
{/* Dose table header */}
|
||||
|
||||
Reference in New Issue
Block a user