/** * GoingsOn - Tasks Render Module * Task row rendering, badges, subtask modal rendering */ (function() { 'use strict'; const esc = GoingsOn.utils.escapeHtml; const escAttr = GoingsOn.utils.escapeAttr; // ============ Task Rendering Helpers ============ /** * Render subtask count and annotation badges for a task row. * @param {Object} task - Task object with subtaskCount, subtaskCompleted, annotations * @returns {string} HTML string of badge elements, or empty string if none */ function renderTaskBadges(task) { let badges = ''; const subtaskCount = task.subtaskCount; const completedSubtasks = task.subtaskCompleted; const annotationCount = task.annotations ? task.annotations.length : 0; if (subtaskCount > 0) { badges += `${completedSubtasks}/${subtaskCount}`; } if (annotationCount > 0) { badges += `Notes: ${annotationCount}`; } return badges ? `${badges}` : ''; } /** * Format a task's recurrence pattern for display. * Only shows recurrence when the task also has a due date. * @param {Object} task - Task object with recurrence and due fields * @returns {string} Recurrence label or '-' */ function formatRecurrence(task) { // Only show recurrence if task has both recurrence AND a due date if (task.recurrence && task.recurrence !== 'None' && task.due) { return task.recurrence; } return '-'; } /** * Render a single task row as a div (for virtual scrolling). * @param {Object} t - Task object * @param {number} index - Task index * @returns {string} HTML string */ /** * Format a minute count as a human-readable duration string. * @param {number} mins - Minutes to format * @returns {string} Formatted string (e.g., "1h 30m" or "45m") */ function formatMinutes(mins) { if (mins >= 60) return `${Math.floor(mins / 60)}h ${mins % 60}m`; return `${mins}m`; } /** * Render time tracking badge for a task (active timer or tracked/estimated). * @param {Object} t - Task object with timerActive, estimatedMinutes, actualMinutes * @returns {string} HTML string for the time badge, or empty string */ function renderTimeBadge(t) { if (t.timerActive) { return ''; } if (t.estimatedMinutes || t.actualMinutes) { const actual = t.actualMinutes || 0; const est = t.estimatedMinutes; const isOver = est && actual > est; const label = est ? `${formatMinutes(actual)} / ${formatMinutes(est)}` : formatMinutes(actual); return `${esc(label)}`; } return ''; } function renderTaskRow(t, index) { const progress = t.subtaskProgress ?? 0; const displayDesc = t.displayDescription || t.description; const isSelected = GoingsOn.tasks.selection.isSelected(t.id); const isStarted = t.status === 'Started'; return `
${isStarted ? `` : ''} ${esc(displayDesc)} ${renderTimeBadge(t)} ${renderTaskBadges(t)} ${t.contactName ? `${esc(t.contactName)}` : ''} ${t.isSnoozed ? `Snoozed` : ''}
${esc(t.projectName) || '-'}
${t.priority.charAt(0)}
${t.dueFormatted || '-'}
${formatRecurrence(t)}
${t.subtaskCount > 0 ? `
` : '-'}
`; } /** * Render and open the subtasks management modal for a task. * Shows progress bar, subtask list, add form, and link-task option. * @param {string} taskId - Parent task ID * @param {Array} subtasks - Array of subtask objects */ function renderSubtasksModal(taskId, subtasks) { const completedCount = subtasks.filter(s => s.isCompleted).length; const totalCount = subtasks.length; const progress = totalCount > 0 ? Math.round((completedCount / totalCount) * 100) : 0; const progressBar = totalCount > 0 ? `
Progress ${completedCount}/${totalCount} (${progress}%)
` : ''; const subtasksList = subtasks.length === 0 ? '

No subtasks yet. Add one below!

' : subtasks.map(s => { const isLinked = s.linkedTaskId || s.linked_task_id; const isCompleted = s.is_completed || s.isCompleted; const linkedTaskId = s.linkedTaskId || s.linked_task_id; if (isLinked) { // Linked task subtask - clicking navigates to the linked task return `
[Linked] ${esc(s.text)}
`; } else { // Regular text subtask return `
${esc(s.text)}
`; } }).join(''); const content = ` ${progressBar}
${subtasksList}
`; GoingsOn.ui.openModal('Manage Subtasks', content); } // ============ Populate GoingsOn.tasksRender Namespace ============ GoingsOn.tasksRender = { renderTaskBadges, renderTaskRow, renderSubtasksModal, formatRecurrence, }; })();