Reformat detail panel view modes to 2-column grid layout
Match TaskDetailPanel gold standard: short fields use grid grid-cols-2 with icon+label headers, full-width fields (description) below the grid. All grid slots render with "—" fallback to keep alignment consistent. - Todo: Priority, Category, Due Date, Recurrence in grid - Reminder: Status, Recurrence, Remind At, Snoozed Until in grid - Calendar: Calendar, Starred, Start, End, Location, Recurrence in grid - Task: Add "Updated X ago" footer (was missing) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
2e2466bfa6
commit
87a7a4ae32
@ -807,79 +807,95 @@ export default function EventDetailPanel({
|
|||||||
) : (
|
) : (
|
||||||
/* View mode */
|
/* View mode */
|
||||||
<>
|
<>
|
||||||
{/* Calendar */}
|
{/* 2-column grid: Calendar, Starred, Start, End, Location, Recurrence */}
|
||||||
<div className="space-y-0.5">
|
<div className="grid grid-cols-2 gap-3">
|
||||||
<p className="text-[11px] uppercase tracking-wider text-muted-foreground">Calendar</p>
|
{/* Calendar */}
|
||||||
<div className="flex items-center gap-2">
|
<div className="space-y-1">
|
||||||
<div
|
<div className="flex items-center gap-1.5 text-[11px] text-muted-foreground uppercase tracking-wider">
|
||||||
className="w-2 h-2 rounded-full shrink-0"
|
<Calendar className="h-3 w-3" />
|
||||||
style={{ backgroundColor: event?.calendar_color || 'hsl(var(--accent-color))' }}
|
Calendar
|
||||||
/>
|
</div>
|
||||||
<span className="text-sm">{event?.calendar_name}</span>
|
<div className="flex items-center gap-2">
|
||||||
|
<div
|
||||||
|
className="w-2 h-2 rounded-full shrink-0"
|
||||||
|
style={{ backgroundColor: event?.calendar_color || 'hsl(var(--accent-color))' }}
|
||||||
|
/>
|
||||||
|
<span className="text-sm">{event?.calendar_name}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Starred */}
|
||||||
|
<div className="space-y-1">
|
||||||
|
<div className="flex items-center gap-1.5 text-[11px] text-muted-foreground uppercase tracking-wider">
|
||||||
|
<Star className="h-3 w-3" />
|
||||||
|
Starred
|
||||||
|
</div>
|
||||||
|
{event?.is_starred ? (
|
||||||
|
<p className="text-sm text-amber-200/90">Starred</p>
|
||||||
|
) : (
|
||||||
|
<p className="text-sm text-muted-foreground">—</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Start */}
|
||||||
|
<div className="space-y-1">
|
||||||
|
<div className="flex items-center gap-1.5 text-[11px] text-muted-foreground uppercase tracking-wider">
|
||||||
|
<Clock className="h-3 w-3" />
|
||||||
|
Start
|
||||||
|
</div>
|
||||||
|
<CopyableField value={startStr} icon={Clock} label="Start time" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* End */}
|
||||||
|
<div className="space-y-1">
|
||||||
|
<div className="flex items-center gap-1.5 text-[11px] text-muted-foreground uppercase tracking-wider">
|
||||||
|
<Clock className="h-3 w-3" />
|
||||||
|
End
|
||||||
|
</div>
|
||||||
|
{endStr ? (
|
||||||
|
<CopyableField value={endStr} icon={Clock} label="End time" />
|
||||||
|
) : (
|
||||||
|
<p className="text-sm text-muted-foreground">—</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Location */}
|
||||||
|
<div className="space-y-1">
|
||||||
|
<div className="flex items-center gap-1.5 text-[11px] text-muted-foreground uppercase tracking-wider">
|
||||||
|
<MapPin className="h-3 w-3" />
|
||||||
|
Location
|
||||||
|
</div>
|
||||||
|
{locationName ? (
|
||||||
|
<CopyableField value={locationName} icon={MapPin} label="Location" />
|
||||||
|
) : (
|
||||||
|
<p className="text-sm text-muted-foreground">—</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Recurrence */}
|
||||||
|
<div className="space-y-1">
|
||||||
|
<div className="flex items-center gap-1.5 text-[11px] text-muted-foreground uppercase tracking-wider">
|
||||||
|
<Repeat className="h-3 w-3" />
|
||||||
|
Recurrence
|
||||||
|
</div>
|
||||||
|
{isRecurring && event?.recurrence_rule ? (
|
||||||
|
<p className="text-sm">{formatRecurrenceRule(event.recurrence_rule)}</p>
|
||||||
|
) : isRecurring ? (
|
||||||
|
<p className="text-sm">Recurring event</p>
|
||||||
|
) : (
|
||||||
|
<p className="text-sm text-muted-foreground">—</p>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Start */}
|
{/* Description — full width */}
|
||||||
<div className="space-y-0.5">
|
|
||||||
<p className="text-[11px] uppercase tracking-wider text-muted-foreground">Start</p>
|
|
||||||
<CopyableField value={startStr} icon={Clock} label="Start time" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* End */}
|
|
||||||
{endStr && (
|
|
||||||
<div className="space-y-0.5">
|
|
||||||
<p className="text-[11px] uppercase tracking-wider text-muted-foreground">End</p>
|
|
||||||
<CopyableField value={endStr} icon={Clock} label="End time" />
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Location */}
|
|
||||||
{locationName && (
|
|
||||||
<div className="space-y-0.5">
|
|
||||||
<p className="text-[11px] uppercase tracking-wider text-muted-foreground">Location</p>
|
|
||||||
<CopyableField value={locationName} icon={MapPin} label="Location" />
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Description */}
|
|
||||||
{event?.description && (
|
{event?.description && (
|
||||||
<div className="space-y-0.5">
|
<div className="space-y-1">
|
||||||
<p className="text-[11px] uppercase tracking-wider text-muted-foreground">Description</p>
|
<div className="flex items-center gap-1.5 text-[11px] text-muted-foreground uppercase tracking-wider">
|
||||||
<div className="flex items-start gap-2">
|
<AlignLeft className="h-3 w-3" />
|
||||||
<AlignLeft className="h-3.5 w-3.5 text-muted-foreground shrink-0 mt-0.5" />
|
Description
|
||||||
<p className="text-sm whitespace-pre-wrap">{event.description}</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Starred */}
|
|
||||||
{event?.is_starred && (
|
|
||||||
<div className="space-y-0.5">
|
|
||||||
<div className="flex items-center gap-2">
|
|
||||||
<Star className="h-3.5 w-3.5 text-amber-400 fill-amber-400 shrink-0" />
|
|
||||||
<span className="text-sm text-amber-200/90">Starred event</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Recurrence */}
|
|
||||||
{isRecurring && event?.recurrence_rule && (
|
|
||||||
<div className="space-y-0.5">
|
|
||||||
<p className="text-[11px] uppercase tracking-wider text-muted-foreground">Recurrence</p>
|
|
||||||
<div className="flex items-center gap-2">
|
|
||||||
<Repeat className="h-3.5 w-3.5 text-muted-foreground shrink-0" />
|
|
||||||
<span className="text-sm">{formatRecurrenceRule(event.recurrence_rule)}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{isRecurring && !event?.recurrence_rule && (
|
|
||||||
<div className="space-y-0.5">
|
|
||||||
<p className="text-[11px] uppercase tracking-wider text-muted-foreground">Recurrence</p>
|
|
||||||
<div className="flex items-center gap-2">
|
|
||||||
<Repeat className="h-3.5 w-3.5 text-muted-foreground shrink-0" />
|
|
||||||
<span className="text-sm">Recurring event</span>
|
|
||||||
</div>
|
</div>
|
||||||
|
<p className="text-sm whitespace-pre-wrap">{event.description}</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|||||||
@ -7,6 +7,7 @@ import {
|
|||||||
Calendar, User, Flag, Activity, Send, X, Save,
|
Calendar, User, Flag, Activity, Send, X, Save,
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
import api, { getErrorMessage } from '@/lib/api';
|
import api, { getErrorMessage } from '@/lib/api';
|
||||||
|
import { formatUpdatedAt } from '@/components/shared/utils';
|
||||||
import type { ProjectTask, TaskComment, Person } from '@/types';
|
import type { ProjectTask, TaskComment, Person } from '@/types';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { Badge } from '@/components/ui/badge';
|
import { Badge } from '@/components/ui/badge';
|
||||||
@ -558,6 +559,15 @@ export default function TaskDetailPanel({
|
|||||||
<Send className="h-4 w-4" />
|
<Send className="h-4 w-4" />
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Updated at footer */}
|
||||||
|
{task.updated_at && (
|
||||||
|
<div className="pt-2 border-t border-border">
|
||||||
|
<span className="text-[11px] text-muted-foreground">
|
||||||
|
{formatUpdatedAt(task.updated_at)}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -3,7 +3,7 @@ import { useMutation, useQueryClient } from '@tanstack/react-query';
|
|||||||
import { toast } from 'sonner';
|
import { toast } from 'sonner';
|
||||||
import { format, parseISO, isPast, isToday } from 'date-fns';
|
import { format, parseISO, isPast, isToday } from 'date-fns';
|
||||||
import {
|
import {
|
||||||
X, Pencil, Trash2, Save, Bell, BellOff, Clock, Repeat, AlertCircle,
|
X, Pencil, Trash2, Save, Bell, BellOff, Clock, Repeat, AlertCircle, AlignLeft,
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
import api, { getErrorMessage } from '@/lib/api';
|
import api, { getErrorMessage } from '@/lib/api';
|
||||||
import type { Reminder } from '@/types';
|
import type { Reminder } from '@/types';
|
||||||
@ -377,76 +377,82 @@ export default function ReminderDetailPanel({
|
|||||||
) : (
|
) : (
|
||||||
/* View mode */
|
/* View mode */
|
||||||
<>
|
<>
|
||||||
{/* Status */}
|
{/* 2-column grid: Status, Recurrence, Remind At, Snoozed Until */}
|
||||||
<div className="space-y-0.5">
|
<div className="grid grid-cols-2 gap-3">
|
||||||
<p className="text-[11px] uppercase tracking-wider text-muted-foreground">Status</p>
|
{/* Status */}
|
||||||
<div className="flex items-center gap-2">
|
<div className="space-y-1">
|
||||||
|
<div className="flex items-center gap-1.5 text-[11px] text-muted-foreground uppercase tracking-wider">
|
||||||
|
{reminder!.is_dismissed ? (
|
||||||
|
<BellOff className="h-3 w-3" />
|
||||||
|
) : isOverdue ? (
|
||||||
|
<AlertCircle className="h-3 w-3" />
|
||||||
|
) : (
|
||||||
|
<Bell className="h-3 w-3" />
|
||||||
|
)}
|
||||||
|
Status
|
||||||
|
</div>
|
||||||
{reminder!.is_dismissed ? (
|
{reminder!.is_dismissed ? (
|
||||||
<>
|
<p className="text-sm text-muted-foreground">Dismissed</p>
|
||||||
<BellOff className="h-3.5 w-3.5 text-muted-foreground shrink-0" />
|
|
||||||
<span className="text-sm text-muted-foreground">Dismissed</span>
|
|
||||||
</>
|
|
||||||
) : isOverdue ? (
|
) : isOverdue ? (
|
||||||
<>
|
<p className="text-sm text-red-400">Overdue</p>
|
||||||
<AlertCircle className="h-3.5 w-3.5 text-red-400 shrink-0" />
|
|
||||||
<span className="text-sm text-red-400">Overdue</span>
|
|
||||||
</>
|
|
||||||
) : isDueToday ? (
|
) : isDueToday ? (
|
||||||
<>
|
<p className="text-sm text-yellow-400">Due today</p>
|
||||||
<Bell className="h-3.5 w-3.5 text-yellow-400 shrink-0" />
|
|
||||||
<span className="text-sm text-yellow-400">Due today</span>
|
|
||||||
</>
|
|
||||||
) : (
|
) : (
|
||||||
<>
|
<p className="text-sm text-orange-400">Active</p>
|
||||||
<Bell className="h-3.5 w-3.5 text-orange-400 shrink-0" />
|
)}
|
||||||
<span className="text-sm text-orange-400">Active</span>
|
</div>
|
||||||
</>
|
|
||||||
|
{/* Recurrence */}
|
||||||
|
<div className="space-y-1">
|
||||||
|
<div className="flex items-center gap-1.5 text-[11px] text-muted-foreground uppercase tracking-wider">
|
||||||
|
<Repeat className="h-3 w-3" />
|
||||||
|
Recurrence
|
||||||
|
</div>
|
||||||
|
{reminder!.recurrence_rule ? (
|
||||||
|
<p className="text-sm">{recurrenceLabels[reminder!.recurrence_rule] || reminder!.recurrence_rule}</p>
|
||||||
|
) : (
|
||||||
|
<p className="text-sm text-muted-foreground">—</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Remind At */}
|
||||||
|
<div className="space-y-1">
|
||||||
|
<div className="flex items-center gap-1.5 text-[11px] text-muted-foreground uppercase tracking-wider">
|
||||||
|
<Clock className="h-3 w-3" />
|
||||||
|
Remind At
|
||||||
|
</div>
|
||||||
|
{remindDate ? (
|
||||||
|
<CopyableField
|
||||||
|
value={format(remindDate, 'EEEE, MMMM d, yyyy · h:mm a')}
|
||||||
|
icon={Clock}
|
||||||
|
label="Remind at"
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<p className="text-sm text-muted-foreground">—</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Snoozed Until */}
|
||||||
|
<div className="space-y-1">
|
||||||
|
<div className="flex items-center gap-1.5 text-[11px] text-muted-foreground uppercase tracking-wider">
|
||||||
|
<Clock className="h-3 w-3" />
|
||||||
|
Snoozed Until
|
||||||
|
</div>
|
||||||
|
{reminder!.snoozed_until ? (
|
||||||
|
<p className="text-sm">{format(parseISO(reminder!.snoozed_until), 'MMM d, h:mm a')}</p>
|
||||||
|
) : (
|
||||||
|
<p className="text-sm text-muted-foreground">—</p>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Remind At */}
|
{/* Description — full width */}
|
||||||
{remindDate && (
|
|
||||||
<div className="space-y-0.5">
|
|
||||||
<p className="text-[11px] uppercase tracking-wider text-muted-foreground">Remind At</p>
|
|
||||||
<CopyableField
|
|
||||||
value={format(remindDate, 'EEEE, MMMM d, yyyy · h:mm a')}
|
|
||||||
icon={Clock}
|
|
||||||
label="Remind at"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Snoozed */}
|
|
||||||
{reminder!.snoozed_until && (
|
|
||||||
<div className="space-y-0.5">
|
|
||||||
<p className="text-[11px] uppercase tracking-wider text-muted-foreground">Snoozed Until</p>
|
|
||||||
<div className="flex items-center gap-2">
|
|
||||||
<Clock className="h-3.5 w-3.5 text-muted-foreground shrink-0" />
|
|
||||||
<span className="text-sm">
|
|
||||||
{format(parseISO(reminder!.snoozed_until), 'MMM d, h:mm a')}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Recurrence */}
|
|
||||||
{reminder!.recurrence_rule && (
|
|
||||||
<div className="space-y-0.5">
|
|
||||||
<p className="text-[11px] uppercase tracking-wider text-muted-foreground">Recurrence</p>
|
|
||||||
<div className="flex items-center gap-2">
|
|
||||||
<Repeat className="h-3.5 w-3.5 text-muted-foreground shrink-0" />
|
|
||||||
<span className="text-sm">
|
|
||||||
{recurrenceLabels[reminder!.recurrence_rule] || reminder!.recurrence_rule}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Description */}
|
|
||||||
{reminder!.description && (
|
{reminder!.description && (
|
||||||
<div className="space-y-0.5">
|
<div className="space-y-1">
|
||||||
<p className="text-[11px] uppercase tracking-wider text-muted-foreground">Description</p>
|
<div className="flex items-center gap-1.5 text-[11px] text-muted-foreground uppercase tracking-wider">
|
||||||
|
<AlignLeft className="h-3 w-3" />
|
||||||
|
Description
|
||||||
|
</div>
|
||||||
<p className="text-sm whitespace-pre-wrap text-muted-foreground leading-relaxed">
|
<p className="text-sm whitespace-pre-wrap text-muted-foreground leading-relaxed">
|
||||||
{reminder!.description}
|
{reminder!.description}
|
||||||
</p>
|
</p>
|
||||||
|
|||||||
@ -3,7 +3,7 @@ import { useMutation, useQueryClient } from '@tanstack/react-query';
|
|||||||
import { toast } from 'sonner';
|
import { toast } from 'sonner';
|
||||||
import { format, parseISO, isToday } from 'date-fns';
|
import { format, parseISO, isToday } from 'date-fns';
|
||||||
import {
|
import {
|
||||||
X, Pencil, Trash2, Save, Clock, Calendar, Flag, Tag, Repeat, CheckSquare, AlertCircle,
|
X, Pencil, Trash2, Save, Clock, Calendar, Flag, Tag, Repeat, CheckSquare, AlertCircle, AlignLeft,
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
import api, { getErrorMessage } from '@/lib/api';
|
import api, { getErrorMessage } from '@/lib/api';
|
||||||
import type { Todo } from '@/types';
|
import type { Todo } from '@/types';
|
||||||
@ -433,95 +433,104 @@ export default function TodoDetailPanel({
|
|||||||
) : (
|
) : (
|
||||||
/* View mode */
|
/* View mode */
|
||||||
<>
|
<>
|
||||||
{/* Priority */}
|
{/* 2-column grid: Priority, Category, Due Date, Recurrence */}
|
||||||
<div className="space-y-0.5">
|
<div className="grid grid-cols-2 gap-3">
|
||||||
<p className="text-[11px] uppercase tracking-wider text-muted-foreground">Priority</p>
|
{/* Priority */}
|
||||||
<div className="flex items-center gap-2">
|
<div className="space-y-1">
|
||||||
<Flag className="h-3.5 w-3.5 text-muted-foreground shrink-0" />
|
<div className="flex items-center gap-1.5 text-[11px] text-muted-foreground uppercase tracking-wider">
|
||||||
|
<Flag className="h-3 w-3" />
|
||||||
|
Priority
|
||||||
|
</div>
|
||||||
<Badge className={`text-[9px] px-1.5 py-0.5 rounded-full ${priorityColors[todo!.priority] ?? ''}`}>
|
<Badge className={`text-[9px] px-1.5 py-0.5 rounded-full ${priorityColors[todo!.priority] ?? ''}`}>
|
||||||
{todo!.priority}
|
{todo!.priority}
|
||||||
</Badge>
|
</Badge>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Category */}
|
{/* Category */}
|
||||||
{todo!.category && (
|
<div className="space-y-1">
|
||||||
<div className="space-y-0.5">
|
<div className="flex items-center gap-1.5 text-[11px] text-muted-foreground uppercase tracking-wider">
|
||||||
<p className="text-[11px] uppercase tracking-wider text-muted-foreground">Category</p>
|
<Tag className="h-3 w-3" />
|
||||||
<div className="flex items-center gap-2">
|
Category
|
||||||
<Tag className="h-3.5 w-3.5 text-muted-foreground shrink-0" />
|
</div>
|
||||||
|
{todo!.category ? (
|
||||||
<Badge className="text-[9px] px-1.5 py-0.5 bg-blue-500/15 text-blue-400">
|
<Badge className="text-[9px] px-1.5 py-0.5 bg-blue-500/15 text-blue-400">
|
||||||
{todo!.category}
|
{todo!.category}
|
||||||
</Badge>
|
</Badge>
|
||||||
|
) : (
|
||||||
|
<p className="text-sm text-muted-foreground">—</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Due Date */}
|
||||||
|
<div className="space-y-1">
|
||||||
|
<div className="flex items-center gap-1.5 text-[11px] text-muted-foreground uppercase tracking-wider">
|
||||||
|
{isOverdue ? <AlertCircle className="h-3 w-3" /> : <Calendar className="h-3 w-3" />}
|
||||||
|
Due Date
|
||||||
</div>
|
</div>
|
||||||
|
{dueDate ? (
|
||||||
|
<CopyableField
|
||||||
|
value={`${isOverdue ? 'Overdue · ' : isDueToday ? 'Today · ' : ''}${format(dueDate, 'EEEE, MMMM d, yyyy')}${todo!.due_time ? ` at ${todo!.due_time.slice(0, 5)}` : ''}`}
|
||||||
|
icon={isOverdue ? AlertCircle : Calendar}
|
||||||
|
label="Due date"
|
||||||
|
/>
|
||||||
|
) : todo!.due_time ? (
|
||||||
|
<CopyableField value={todo!.due_time.slice(0, 5)} icon={Clock} label="Due time" />
|
||||||
|
) : (
|
||||||
|
<p className="text-sm text-muted-foreground">—</p>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Due Date */}
|
{/* Recurrence */}
|
||||||
{dueDate && (
|
<div className="space-y-1">
|
||||||
<div className="space-y-0.5">
|
<div className="flex items-center gap-1.5 text-[11px] text-muted-foreground uppercase tracking-wider">
|
||||||
<p className="text-[11px] uppercase tracking-wider text-muted-foreground">Due Date</p>
|
<Repeat className="h-3 w-3" />
|
||||||
<CopyableField
|
Recurrence
|
||||||
value={`${isOverdue ? 'Overdue · ' : isDueToday ? 'Today · ' : ''}${format(dueDate, 'EEEE, MMMM d, yyyy')}${todo!.due_time ? ` at ${todo!.due_time.slice(0, 5)}` : ''}`}
|
|
||||||
icon={isOverdue ? AlertCircle : Calendar}
|
|
||||||
label="Due date"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Due Time (if no date but has time) */}
|
|
||||||
{!dueDate && todo!.due_time && (
|
|
||||||
<div className="space-y-0.5">
|
|
||||||
<p className="text-[11px] uppercase tracking-wider text-muted-foreground">Due Time</p>
|
|
||||||
<CopyableField value={todo!.due_time.slice(0, 5)} icon={Clock} label="Due time" />
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Recurrence */}
|
|
||||||
{todo!.recurrence_rule && (
|
|
||||||
<div className="space-y-0.5">
|
|
||||||
<p className="text-[11px] uppercase tracking-wider text-muted-foreground">Recurrence</p>
|
|
||||||
<div className="flex items-center gap-2">
|
|
||||||
<Repeat className="h-3.5 w-3.5 text-muted-foreground shrink-0" />
|
|
||||||
<span className="text-sm">{recurrenceLabels[todo!.recurrence_rule] || todo!.recurrence_rule}</span>
|
|
||||||
</div>
|
</div>
|
||||||
|
{todo!.recurrence_rule ? (
|
||||||
|
<p className="text-sm">{recurrenceLabels[todo!.recurrence_rule] || todo!.recurrence_rule}</p>
|
||||||
|
) : (
|
||||||
|
<p className="text-sm text-muted-foreground">—</p>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
</div>
|
||||||
|
|
||||||
{/* Description */}
|
{/* Completion status — full width */}
|
||||||
{todo!.description && (
|
{todo!.completed && todo!.completed_at && (
|
||||||
<div className="space-y-0.5">
|
<div className="space-y-1">
|
||||||
<p className="text-[11px] uppercase tracking-wider text-muted-foreground">Description</p>
|
<div className="flex items-center gap-1.5 text-[11px] text-muted-foreground uppercase tracking-wider">
|
||||||
<p className="text-sm whitespace-pre-wrap text-muted-foreground leading-relaxed">
|
<CheckSquare className="h-3 w-3" />
|
||||||
{todo!.description}
|
Completed
|
||||||
|
</div>
|
||||||
|
<p className="text-sm text-green-400">
|
||||||
|
{format(parseISO(todo!.completed_at), 'MMM d, yyyy · h:mm a')}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Completion status */}
|
{/* Reset info — full width */}
|
||||||
{todo!.completed && todo!.completed_at && (
|
{todo!.completed && todo!.recurrence_rule && todo!.reset_at && (
|
||||||
<div className="space-y-0.5">
|
<div className="space-y-1">
|
||||||
<p className="text-[11px] uppercase tracking-wider text-muted-foreground">Completed</p>
|
<div className="flex items-center gap-1.5 text-[11px] text-muted-foreground uppercase tracking-wider">
|
||||||
<div className="flex items-center gap-2">
|
<Repeat className="h-3 w-3" />
|
||||||
<CheckSquare className="h-3.5 w-3.5 text-green-400 shrink-0" />
|
Resets
|
||||||
<span className="text-sm text-green-400">
|
|
||||||
{format(parseISO(todo!.completed_at), 'MMM d, yyyy · h:mm a')}
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
|
<p className="text-sm text-purple-400">
|
||||||
|
{format(parseISO(todo!.reset_at), 'EEE, MMM d')}
|
||||||
|
{todo!.next_due_date && ` · Next due ${format(parseISO(todo!.next_due_date), 'MMM d')}`}
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Reset info for recurring */}
|
{/* Description — full width */}
|
||||||
{todo!.completed && todo!.recurrence_rule && todo!.reset_at && (
|
{todo!.description && (
|
||||||
<div className="space-y-0.5">
|
<div className="space-y-1">
|
||||||
<p className="text-[11px] uppercase tracking-wider text-muted-foreground">Resets</p>
|
<div className="flex items-center gap-1.5 text-[11px] text-muted-foreground uppercase tracking-wider">
|
||||||
<div className="flex items-center gap-2">
|
<AlignLeft className="h-3 w-3" />
|
||||||
<Repeat className="h-3.5 w-3.5 text-purple-400 shrink-0" />
|
Description
|
||||||
<span className="text-sm text-purple-400">
|
|
||||||
{format(parseISO(todo!.reset_at), 'EEE, MMM d')}
|
|
||||||
{todo!.next_due_date && ` · Next due ${format(parseISO(todo!.next_due_date), 'MMM d')}`}
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
|
<p className="text-sm whitespace-pre-wrap text-muted-foreground leading-relaxed">
|
||||||
|
{todo!.description}
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user