// ── Global tasks view ── import { $, esc, api } from './util.js'; import { state } from './state.js'; const overlay = $('tasks'); const btn = $('tasks-btn'); const closeBtn = $('tasks-close'); const statusFilter = $('tasks-status-filter'); const bucketFilter = $('tasks-bucket-filter'); const list = $('tasks-list'); const summary = $('tasks-summary'); const countBadge = $('tasks-open-count'); function collectTasks() { const all = []; for (const card of state.cards) { const re = /- \[([ xX])\][ \t]*([^\n\r]*)/g; let m, idx = 0; while ((m = re.exec(card.body || '')) !== null) { all.push({ cardId: card.id, cardTitle: card.title || 'untitled', cardColor: card.color || '#94a3b8', bucketId: card.bucket_id, bucketName: card.bucket_name || '', done: m[1].toLowerCase() === 'x', text: (m[2] || '').trim() || '(empty task)', todoIndex: idx++ }); } } return all; } function filter(tasks) { const s = statusFilter?.value || 'open', b = bucketFilter?.value || ''; return tasks.filter(t => { if (s === 'open' && t.done) return false; if (s === 'done' && !t.done) return false; if (b === 'none') return !t.bucketId; if (b && t.bucketId != b) return false; return true; }); } export function refreshTasksView() { if (!countBadge) return; const tasks = collectTasks(); const open = tasks.filter(t => !t.done).length; countBadge.textContent = String(open); if (!overlay?.classList.contains('open') || !list) return; const filtered = filter(tasks); if (summary) summary.textContent = `open: ${open} | done: ${tasks.length - open} | showing: ${filtered.length}`; if (!filtered.length) { list.innerHTML = `

no tasks in this filter

`; return; } list.innerHTML = filtered.map(t => `
${esc(t.text)}
`).join(''); list.querySelectorAll('[data-task-toggle]').forEach(b => b.addEventListener('click', async e => { e.preventDefault(); e.stopPropagation(); const [cardId, idx] = b.dataset.taskToggle.split(':'); await toggleTask(cardId, Number(idx)); })); list.querySelectorAll('[data-task-open]').forEach(b => b.addEventListener('click', e => { e.preventDefault(); e.stopPropagation(); closeTasks(); window._openDetail(b.dataset.taskOpen); })); } async function toggleTask(cardId, todoIdx) { const card = state.cards.find(c => c.id == cardId); if (!card) return; let idx = 0, changed = false; const next = (card.body || '').replace(/- \[([ xX])\]/g, (m, s) => { if (idx++ !== todoIdx) return m; changed = true; return `- [${s.toLowerCase() === 'x' ? ' ' : 'x'}]`; }); if (!changed) return; const updated = await api(`api/cards/${cardId}`, { method: 'PATCH', body: { body: next } }); if (updated?.id) { const i = state.cards.findIndex(c => c.id == updated.id); if (i !== -1) state.cards[i] = updated; } else card.body = next; if (state.viewingCard?.id == cardId) { const latest = state.cards.find(c => c.id == cardId); if (latest) { state.viewingCard = latest; /* detail re-render handled by board */ } } window._renderBoard(); } export function openTasks() { if (!overlay) return; window._closeSearch?.(); overlay.classList.add('open'); refreshTasksView(); } export function closeTasks() { overlay?.classList.remove('open'); } btn?.addEventListener('click', openTasks); closeBtn?.addEventListener('click', closeTasks); overlay?.addEventListener('click', e => { if (e.target === overlay) closeTasks(); }); statusFilter?.addEventListener('change', refreshTasksView); bucketFilter?.addEventListener('change', refreshTasksView);