import { useState, useEffect, useMemo } from 'react'; import { useToast } from '../common/ToastContainer'; import { usePlannables } from '../../hooks/usePlannables'; import PlannableItem from './PlannableItem'; import PlannableForm from './PlannableForm'; import ConfirmDialog from '../common/ConfirmDialog'; import './PlannablesList.css'; const PlannablesList = ({ tripId }) => { const { showSuccess, showError } = useToast(); const { fetchBothData, createPlannable, updatePlannable, deletePlannable, loading: apiLoading } = usePlannables(); const [plannables, setPlannables] = useState([]); const [calendarSlots, setCalendarSlots] = useState([]); const [loading, setLoading] = useState(true); const [showForm, setShowForm] = useState(false); const [editingItem, setEditingItem] = useState(null); const [error, setError] = useState(null); const [confirmDialog, setConfirmDialog] = useState({ isOpen: false, title: '', message: '', onConfirm: null }); useEffect(() => { const loadData = async () => { try { setLoading(true); const { plannables, calendarSlots, errors } = await fetchBothData(tripId); console.log('PlannablesList: Received data:', { plannablesCount: plannables.length, calendarSlotsCount: calendarSlots.length, firstFewSlots: calendarSlots.slice(0, 3).map(s => ({ id: s.id, name: s.name, date: s.slot_date })) }); setPlannables(plannables); // Safeguard: limit calendar slots to prevent performance issues const limitedCalendarSlots = calendarSlots.slice(0, 365); // Max 1 year of slots setCalendarSlots(limitedCalendarSlots); if (errors.plannables) { console.error('Failed to fetch plannables:', errors.plannables); showError('Failed to load plannable items'); } if (errors.calendarSlots) { console.error('Failed to fetch calendar slots:', errors.calendarSlots); showError('Failed to load calendar slots'); } } catch (err) { setError('Failed to load data'); console.error(err); } finally { setLoading(false); } }; loadData(); }, [tripId, fetchBothData, showError]); const handleAddItem = () => { setEditingItem(null); setShowForm(true); }; const handleEditItem = (item) => { setEditingItem(item); setShowForm(true); }; const handleDeleteItem = (itemId) => { const item = plannables.find(p => p.id === itemId); setConfirmDialog({ isOpen: true, title: 'Delete Item', message: `Are you sure you want to delete "${item?.name}"? This action cannot be undone.`, onConfirm: () => performDelete(itemId) }); }; const performDelete = async (itemId) => { try { await deletePlannable(itemId); setPlannables(plannables.filter(item => item.id !== itemId)); showSuccess('Item deleted successfully'); } catch (err) { console.error('Error deleting item:', err); showError('Failed to delete item. Please try again.'); } finally { setConfirmDialog({ isOpen: false, title: '', message: '', onConfirm: null }); } }; const handleFormSubmit = async (formData) => { try { const isEditing = !!editingItem; let savedItem; if (isEditing) { savedItem = await updatePlannable(editingItem.id, formData); setPlannables(plannables.map(item => item.id === editingItem.id ? savedItem : item )); showSuccess('Item updated successfully'); } else { savedItem = await createPlannable(tripId, formData); setPlannables([...plannables, savedItem]); showSuccess('Item added successfully'); } setShowForm(false); setEditingItem(null); } catch (err) { console.error('Error saving item:', err); showError('Failed to save item. Please try again.'); } }; const handleFormCancel = () => { setShowForm(false); setEditingItem(null); }; // Memoize expensive grouping computation to prevent recalculation on every render const { unplannedItems, plannedItemsBySlot } = useMemo(() => { const unplanned = plannables.filter(item => !item.calendar_slot_id); const planned = {}; plannables.forEach(item => { if (item.calendar_slot_id) { if (!planned[item.calendar_slot_id]) { planned[item.calendar_slot_id] = []; } planned[item.calendar_slot_id].push(item); } }); return { unplannedItems: unplanned, plannedItemsBySlot: planned }; }, [plannables]); if (loading) { return (

Loading items...

); } return (

Itinerary Items

{error && (
{error}
)}
{/* Unplanned Items Section */}

📋 Unplanned Items {unplannedItems.length > 0 && ( {unplannedItems.length} )}

{unplannedItems.length === 0 ? (

No unplanned items

) : ( unplannedItems.map(item => ( )) )}
{/* Calendar Slots Sections */} {calendarSlots.map(slot => { const items = plannedItemsBySlot[slot.id] || []; const slotDate = new Date(slot.slot_date); const dayName = slotDate.toLocaleDateString('en-US', { weekday: 'long' }); const dateStr = slotDate.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); return (

📅 {slot.name} {dayName}, {dateStr} {items.length > 0 && ( {items.length} )}

{items.length === 0 ? (

No items planned for this day

) : ( items.map(item => ( )) )}
); })}
{showForm && ( )} setConfirmDialog({ isOpen: false, title: '', message: '', onConfirm: null })} />
); }; export default PlannablesList;