import { useState, useMemo, useEffect, useRef } from 'react' import { useTripStore } from '../../store/tripStore' import { useCanDo } from '../../store/permissionsStore' import { useToast } from '../shared/Toast' import { useTranslation } from '../../i18n' import apiClient from '../../api/client' import { formatDate as fmtDate } from '../../utils/formatters' import type { TodoItem } from '../../types' import type { FilterType, Member } from './todoListModel' /** * Todo list logic — store actions, member load, the filter/selection/add-new * view state and the derived buckets (filtered list + counts) + handlers. * TodoListPanel stays a layout component that renders the sidebar, the rows * (TodoRow) and the detail/new panes from this state. */ export function useTodoList(tripId: number, items: TodoItem[], addItemSignal: number) { const { addTodoItem, updateTodoItem, deleteTodoItem, toggleTodoItem } = useTripStore() const trip = useTripStore((s) => s.trip) const can = useCanDo() const canEdit = can('packing_edit', trip) const toast = useToast() const { t, locale } = useTranslation() const formatDate = (d: string) => fmtDate(d, locale) || d const [isMobile, setIsMobile] = useState(() => window.innerWidth < 768) useEffect(() => { const mq = window.matchMedia('(max-width: 767px)') const handler = (e: MediaQueryListEvent) => setIsMobile(e.matches) mq.addEventListener('change', handler) return () => mq.removeEventListener('change', handler) }, []) const [filter, setFilter] = useState('all') const [selectedId, setSelectedId] = useState(null) const [isAddingNew, setIsAddingNew] = useState(false) const lastHandledAddSignal = useRef(addItemSignal) useEffect(() => { if (addItemSignal !== lastHandledAddSignal.current && addItemSignal > 0) { setSelectedId(null) setIsAddingNew(true) } lastHandledAddSignal.current = addItemSignal }, [addItemSignal]) const [sortByPrio, setSortByPrio] = useState(false) const [addingCategory, setAddingCategory] = useState(false) const [newCategoryName, setNewCategoryName] = useState('') const [members, setMembers] = useState([]) const [currentUserId, setCurrentUserId] = useState(null) useEffect(() => { apiClient.get(`/trips/${tripId}/members`).then(r => { const owner = r.data?.owner const mems = r.data?.members || [] const all = owner ? [owner, ...mems] : mems setMembers(all) setCurrentUserId(r.data?.current_user_id || null) }).catch(() => {}) }, [tripId]) const categories = useMemo(() => { const cats = new Set() items.forEach(i => { if (i.category) cats.add(i.category) }) return Array.from(cats).sort() }, [items]) const today = new Date().toISOString().split('T')[0] const filtered = useMemo(() => { let result: TodoItem[] if (filter === 'all') result = items.filter(i => !i.checked) else if (filter === 'done') result = items.filter(i => !!i.checked) else if (filter === 'my') result = items.filter(i => !i.checked && i.assigned_user_id === currentUserId) else if (filter === 'overdue') result = items.filter(i => !i.checked && i.due_date && i.due_date < today) else result = items.filter(i => i.category === filter) if (sortByPrio) result = [...result].sort((a, b) => { const ap = a.priority || 99 const bp = b.priority || 99 return ap - bp }) return result }, [items, filter, currentUserId, today, sortByPrio]) const selectedItem = items.find(i => i.id === selectedId) || null const totalCount = items.length const doneCount = items.filter(i => !!i.checked).length const overdueCount = items.filter(i => !i.checked && i.due_date && i.due_date < today).length const myCount = currentUserId ? items.filter(i => !i.checked && i.assigned_user_id === currentUserId).length : 0 const addCategory = () => { const name = newCategoryName.trim() if (!name || categories.includes(name)) { setAddingCategory(false); setNewCategoryName(''); return } addTodoItem(tripId, { name: t('todo.newItem'), category: name } as any) .then(() => { setAddingCategory(false); setNewCategoryName(''); setFilter(name) }) .catch(err => toast.error(err instanceof Error ? err.message : t('common.error'))) } // Get category count (non-done items) const catCount = (cat: string) => items.filter(i => i.category === cat && !i.checked).length return { canEdit, t, formatDate, toggleTodoItem, isMobile, filter, setFilter, selectedId, setSelectedId, isAddingNew, setIsAddingNew, sortByPrio, setSortByPrio, addingCategory, setAddingCategory, newCategoryName, setNewCategoryName, members, categories, today, filtered, selectedItem, totalCount, doneCount, overdueCount, myCount, addCategory, catCount, // exposed for completeness (DetailPane/NewTaskPane already get their own) updateTodoItem, deleteTodoItem, } }