/** * GoingsOn - Task Board Module * View mode toggling (list vs board) and Kanban board rendering. * Loaded before tasks.js -- populates GoingsOn.taskBoard namespace. */ (function() { 'use strict'; const esc = GoingsOn.utils.escapeHtml; // ============ View Mode State ============ let viewMode = 'list'; // 'list' or 'board' /** * Get the current view mode. * @returns {string} 'list' or 'board' */ function getViewMode() { return viewMode; } /** * Switch between list and board view modes. * @param {string} mode - 'list' or 'board' */ function setViewMode(mode) { // Phase 7 Tier 3 #10 — Kanban has no touch drag-drop fallback yet // (Phase 6 #1). Force list mode on touch devices regardless of input. if (mode === 'board' && GoingsOn.touch?.isTouchDevice) { mode = 'list'; } viewMode = mode; // Toggle active class on buttons document.querySelectorAll('#task-view-toggle .view-toggle-btn').forEach(btn => { btn.classList.toggle('active', btn.dataset.mode === mode); }); const taskTable = document.getElementById('task-table'); const kanbanBoard = document.getElementById('task-kanban-board'); const statusFilter = document.getElementById('filter-status'); const pagination = document.getElementById('task-pagination'); if (mode === 'board') { taskTable.classList.add('hidden'); kanbanBoard.classList.remove('hidden'); if (statusFilter) statusFilter.style.display = 'none'; if (pagination) pagination.classList.add('hidden'); renderBoard(); } else { taskTable.classList.remove('hidden'); kanbanBoard.classList.add('hidden'); if (statusFilter) statusFilter.style.display = ''; GoingsOn.tasks.renderFilteredTasks(); } } /** * Fetch tasks and render the Kanban board with current filters applied. */ async function renderBoard() { const uiFilters = GoingsOn.tasksFilter.getFilters(); // Board fetches all statuses -- override status filter const backendFilters = { showSnoozed: uiFilters.showSnoozed, waitingOnly: uiFilters.waitingOnly, offset: 0, limit: 500, sortColumn: GoingsOn.tasksFilter.getSortColumn(), sortDirection: GoingsOn.tasksFilter.getSortDirection(), }; // Apply non-status filters if (uiFilters.projectId) backendFilters.projectId = uiFilters.projectId; if (uiFilters.milestoneId) backendFilters.milestoneId = uiFilters.milestoneId; if (uiFilters.priority) { const priorityMap = { H: 'High', M: 'Medium', L: 'Low' }; backendFilters.priority = priorityMap[uiFilters.priority] || uiFilters.priority; } try { const response = await GoingsOn.api.tasks.listFiltered(backendFilters); let displayTasks = response.tasks; displayTasks = displayTasks.map(t => ({ ...t, displayDescription: t.description })); GoingsOn.state.set('tasks', displayTasks); GoingsOn.tasksKanban.render(displayTasks); } catch (err) { const board = document.getElementById('task-kanban-board'); if (board) { board.innerHTML = `
Failed to load board: ${esc(err.message || String(err))}.
`; } } } // ============ Namespace ============ GoingsOn.taskBoard = { getViewMode, setViewMode, renderBoard, }; })();