import { api, toast } from '/static/js/api.js'; import { subscribe, onPollFallback } from '/static/js/ws.js'; const errorBox = document.getElementById('error'); const esc = (s) => String(s ?? '').replace(/[&<>"']/g, (c) => ({ '&': '&', '<': '<', '>': '>', '"': '"', "'": ''', }[c])); // changes_requested renders in the In-progress column with a badge const colFor = (s) => (s === 'changes_requested' ? 'in_progress' : s); async function load() { try { const res = await api('GET', '/api/v1/my-tasks'); document.querySelectorAll('.kanban-col .stack').forEach((el) => el.replaceChildren()); res.tasks.forEach((t) => { const col = document.querySelector(`[data-col="${colFor(t.status)}"] .stack`); if (col) col.appendChild(card(t)); }); } catch (e) { errorBox.textContent = e.message; } } function card(t) { const el = document.createElement('div'); el.className = 'card'; el.style.padding = '12px'; const badges = []; if (t.status === 'changes_requested') badges.push('changes requested'); el.innerHTML = ` ${esc(t.title)} ${badges.join(' ')}
◈ ${t.bounty}
`; const actions = el.querySelector('[data-actions]'); const act = (label, path, primary) => { const b = document.createElement('button'); b.className = 'btn small' + (primary ? ' primary' : ''); b.textContent = label; b.addEventListener('click', async () => { try { await api('POST', `/api/v1/tasks/${t.id}/${path}`, {}); load(); } catch (e) { toast(e.message, 'err'); } }); actions.appendChild(b); }; if (t.status === 'assigned' || t.status === 'changes_requested') act('Start', 'start', true); if (t.status === 'in_progress') act('Submit for review', 'submit-review', true); if (['assigned', 'in_progress'].includes(t.status)) act('Abandon', 'abandon'); return el; } subscribe('board', () => setTimeout(load, 300)); onPollFallback(load); load();