import { useState, useEffect, useCallback } from 'react'; import { useMutation, useQueryClient } from '@tanstack/react-query'; import { toast } from 'sonner'; import { format, parseISO, isPast, isToday } from 'date-fns'; import { X, Pencil, Trash2, Save, Bell, BellOff, Clock, Repeat, AlertCircle, AlignLeft, } from 'lucide-react'; import api, { getErrorMessage } from '@/lib/api'; import type { Reminder } from '@/types'; import { useConfirmAction } from '@/hooks/useConfirmAction'; import { formatUpdatedAt } from '@/components/shared/utils'; import CopyableField from '@/components/shared/CopyableField'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { DatePicker } from '@/components/ui/date-picker'; import { Textarea } from '@/components/ui/textarea'; import { Select } from '@/components/ui/select'; import { Label } from '@/components/ui/label'; // --- Types --- interface ReminderDetailPanelProps { reminder: Reminder | null; isCreating?: boolean; onClose: () => void; onSaved?: () => void; onDeleted?: () => void; } interface EditState { title: string; description: string; remind_at: string; recurrence_rule: string; } const recurrenceLabels: Record = { daily: 'Daily', weekly: 'Weekly', monthly: 'Monthly', }; const QUERY_KEYS = [['reminders'], ['dashboard'], ['upcoming']] as const; function buildEditState(reminder: Reminder): EditState { return { title: reminder.title, description: reminder.description || '', remind_at: reminder.remind_at ? reminder.remind_at.slice(0, 16) : '', recurrence_rule: reminder.recurrence_rule || '', }; } function buildCreateState(): EditState { return { title: '', description: '', remind_at: '', recurrence_rule: '', }; } // --- Component --- export default function ReminderDetailPanel({ reminder, isCreating = false, onClose, onSaved, onDeleted, }: ReminderDetailPanelProps) { const queryClient = useQueryClient(); const [isEditing, setIsEditing] = useState(isCreating); const [editState, setEditState] = useState(() => isCreating ? buildCreateState() : reminder ? buildEditState(reminder) : buildCreateState() ); // Reset state when reminder changes useEffect(() => { setIsEditing(false); if (reminder) setEditState(buildEditState(reminder)); }, [reminder?.id]); // Enter edit mode when creating useEffect(() => { if (isCreating) { setIsEditing(true); setEditState(buildCreateState()); } }, [isCreating]); const invalidateAll = useCallback(() => { QUERY_KEYS.forEach((key) => queryClient.invalidateQueries({ queryKey: [...key] })); }, [queryClient]); // --- Mutations --- const saveMutation = useMutation({ mutationFn: async (data: EditState) => { const payload = { title: data.title, description: data.description || null, remind_at: data.remind_at || null, recurrence_rule: data.recurrence_rule || null, }; if (reminder && !isCreating) { return api.put(`/reminders/${reminder.id}`, payload); } else { return api.post('/reminders', payload); } }, onSuccess: () => { invalidateAll(); toast.success(isCreating ? 'Reminder created' : 'Reminder updated'); if (isCreating) { onClose(); } else { setIsEditing(false); } onSaved?.(); }, onError: (error) => { toast.error(getErrorMessage(error, isCreating ? 'Failed to create reminder' : 'Failed to update reminder')); }, }); const dismissMutation = useMutation({ mutationFn: async () => { const { data } = await api.patch(`/reminders/${reminder!.id}/dismiss`); return data; }, onSuccess: () => { invalidateAll(); toast.success('Reminder dismissed'); }, onError: () => { toast.error('Failed to dismiss reminder'); }, }); const deleteMutation = useMutation({ mutationFn: async () => { await api.delete(`/reminders/${reminder!.id}`); }, onSuccess: () => { invalidateAll(); toast.success('Reminder deleted'); onClose(); onDeleted?.(); }, onError: (error) => { toast.error(getErrorMessage(error, 'Failed to delete reminder')); }, }); const executeDelete = useCallback(() => deleteMutation.mutate(), [deleteMutation]); const { confirming: confirmingDelete, handleClick: handleDeleteClick } = useConfirmAction(executeDelete); // --- Handlers --- const handleEditStart = () => { if (reminder) setEditState(buildEditState(reminder)); setIsEditing(true); }; const handleEditCancel = () => { setIsEditing(false); if (isCreating) { onClose(); } else if (reminder) { setEditState(buildEditState(reminder)); } }; const handleEditSave = () => { saveMutation.mutate(editState); }; const updateField = (key: K, value: EditState[K]) => { setEditState((s) => ({ ...s, [key]: value })); }; // Empty state if (!reminder && !isCreating) { return (

Select a reminder to view details

); } // View data const remindDate = reminder?.remind_at ? parseISO(reminder.remind_at) : null; const isOverdue = !reminder?.is_dismissed && remindDate && isPast(remindDate) && !isToday(remindDate); const isDueToday = remindDate ? isToday(remindDate) : false; return (
{/* Header */}
{isEditing && !isCreating ? ( updateField('title', e.target.value)} className="h-8 text-base font-semibold flex-1" placeholder="Reminder title" autoFocus /> ) : isCreating ? (

New Reminder

) : (

{reminder!.title}

)}
{(isEditing || isCreating) ? ( <> ) : ( <> {!reminder!.is_dismissed && ( )} {confirmingDelete ? ( ) : ( )} )}
{/* Body */}
{(isEditing || isCreating) ? ( /* Edit / Create mode */
{isCreating && (
updateField('title', e.target.value)} placeholder="Reminder title" required autoFocus />
)}