Skip to main content

max / goingson

3.7 KB · 106 lines History Blame Raw
1 /**
2 * GoingsOn - Task Board Module
3 * View mode toggling (list vs board) and Kanban board rendering.
4 * Loaded before tasks.js -- populates GoingsOn.taskBoard namespace.
5 */
6
7 (function() {
8 'use strict';
9 const esc = GoingsOn.utils.escapeHtml;
10
11 // ============ View Mode State ============
12 let viewMode = 'list'; // 'list' or 'board'
13
14 /**
15 * Get the current view mode.
16 * @returns {string} 'list' or 'board'
17 */
18 function getViewMode() {
19 return viewMode;
20 }
21
22 /**
23 * Switch between list and board view modes.
24 * @param {string} mode - 'list' or 'board'
25 */
26 function setViewMode(mode) {
27 // Phase 7 Tier 3 #10 — Kanban has no touch drag-drop fallback yet
28 // (Phase 6 #1). Force list mode on touch devices regardless of input.
29 if (mode === 'board' && GoingsOn.touch?.isTouchDevice) {
30 mode = 'list';
31 }
32 viewMode = mode;
33
34 // Toggle active class on buttons
35 document.querySelectorAll('#task-view-toggle .view-toggle-btn').forEach(btn => {
36 btn.classList.toggle('active', btn.dataset.mode === mode);
37 });
38
39 const taskTable = document.getElementById('task-table');
40 const kanbanBoard = document.getElementById('task-kanban-board');
41 const statusFilter = document.getElementById('filter-status');
42 const pagination = document.getElementById('task-pagination');
43
44 if (mode === 'board') {
45 taskTable.classList.add('hidden');
46 kanbanBoard.classList.remove('hidden');
47 if (statusFilter) statusFilter.style.display = 'none';
48 if (pagination) pagination.classList.add('hidden');
49 renderBoard();
50 } else {
51 taskTable.classList.remove('hidden');
52 kanbanBoard.classList.add('hidden');
53 if (statusFilter) statusFilter.style.display = '';
54 GoingsOn.tasks.renderFilteredTasks();
55 }
56 }
57
58 /**
59 * Fetch tasks and render the Kanban board with current filters applied.
60 */
61 async function renderBoard() {
62 const uiFilters = GoingsOn.tasksFilter.getFilters();
63
64 // Board fetches all statuses -- override status filter
65 const backendFilters = {
66 showSnoozed: uiFilters.showSnoozed,
67 waitingOnly: uiFilters.waitingOnly,
68 offset: 0,
69 limit: 500,
70 sortColumn: GoingsOn.tasksFilter.getSortColumn(),
71 sortDirection: GoingsOn.tasksFilter.getSortDirection(),
72 };
73
74 // Apply non-status filters
75 if (uiFilters.projectId) backendFilters.projectId = uiFilters.projectId;
76 if (uiFilters.milestoneId) backendFilters.milestoneId = uiFilters.milestoneId;
77 if (uiFilters.priority) {
78 const priorityMap = { H: 'High', M: 'Medium', L: 'Low' };
79 backendFilters.priority = priorityMap[uiFilters.priority] || uiFilters.priority;
80 }
81
82 try {
83 const response = await GoingsOn.api.tasks.listFiltered(backendFilters);
84 let displayTasks = response.tasks;
85 displayTasks = displayTasks.map(t => ({ ...t, displayDescription: t.description }));
86
87 GoingsOn.state.set('tasks', displayTasks);
88 GoingsOn.tasksKanban.render(displayTasks);
89 } catch (err) {
90 const board = document.getElementById('task-kanban-board');
91 if (board) {
92 board.innerHTML = `<div class="loading loading--error">Failed to load board: ${esc(err.message || String(err))}. <button class="btn-link" onclick="GoingsOn.taskBoard.renderBoard()">Try again</button></div>`;
93 }
94 }
95 }
96
97 // ============ Namespace ============
98
99 GoingsOn.taskBoard = {
100 getViewMode,
101 setViewMode,
102 renderBoard,
103 };
104
105 })();
106