From 48fe86273a66e09671b5dcd8d726662d6671d266 Mon Sep 17 00:00:00 2001 From: vansh-09 Date: Sun, 24 May 2026 15:22:17 +0530 Subject: [PATCH] bug: btn fix in navbar for navigation (#684) --- js/app.js | 1205 ++++++++++++++++++++++++++++++----------------------- 1 file changed, 680 insertions(+), 525 deletions(-) diff --git a/js/app.js b/js/app.js index 787d08a..99fdef5 100644 --- a/js/app.js +++ b/js/app.js @@ -1,13 +1,22 @@ -import { store } from './store.js'; -import { extractTasksFromText } from './utils/api.js'; -import { initGlobalErrorBoundary } from './utils/errorBoundary.js'; -import { analyzeWorkload } from './utils/scheduler.js'; -import { Toast } from './utils/toast.js'; +import { store } from "./store.js"; +import { extractTasksFromText } from "./utils/api.js"; +import { initGlobalErrorBoundary } from "./utils/errorBoundary.js"; +import { analyzeWorkload } from "./utils/scheduler.js"; +import { Toast } from "./utils/toast.js"; initGlobalErrorBoundary(); function getLabelColor(labelStr) { - const colors = ['#ef4444', '#f59e0b', '#3b82f6', '#8b5cf6', '#10b981', '#ec4899', '#14b8a6', '#f97316']; + const colors = [ + "#ef4444", + "#f59e0b", + "#3b82f6", + "#8b5cf6", + "#10b981", + "#ec4899", + "#14b8a6", + "#f97316", + ]; let hash = 0; for (let i = 0; i < labelStr.length; i++) { hash = labelStr.charCodeAt(i) + ((hash << 5) - hash); @@ -22,11 +31,11 @@ function extractLabels(title) { while ((match = labelRegex.exec(title)) !== null) { labels.push(match[1]); } - const cleanTitle = title.replace(labelRegex, '').trim(); + const cleanTitle = title.replace(labelRegex, "").trim(); return { cleanTitle, labels }; } -let activeLabelFilter = ''; +let activeLabelFilter = ""; function generateSummary(tasks, subjects) { const now = new Date(); @@ -37,8 +46,8 @@ function generateSummary(tasks, subjects) { let weekCount = 0; let subjectCount = {}; - tasks.forEach(t => { - if (t.archived || t.status === 'Done' || !t.due_at) return; + tasks.forEach((t) => { + if (t.archived || t.status === "Done" || !t.due_at) return; const d = new Date(t.due_at); @@ -52,16 +61,16 @@ function generateSummary(tasks, subjects) { weekCount++; } - const sub = subjects.find(s => s.id === t.subject_id); - const name = sub ? sub.name : 'General'; + const sub = subjects.find((s) => s.id === t.subject_id); + const name = sub ? sub.name : "General"; subjectCount[name] = (subjectCount[name] || 0) + 1; }); const topSubject = Object.keys(subjectCount).length ? Object.keys(subjectCount).reduce((a, b) => - subjectCount[a] > subjectCount[b] ? a : b + subjectCount[a] > subjectCount[b] ? a : b, ) - : 'no specific subject'; + : "no specific subject"; return ` 📅 Daily
@@ -76,114 +85,125 @@ function generateSummary(tasks, subjects) { let currentMonthDate = new Date(); let selectedDate = null; -let currentView = 'calendar'; // 'calendar', 'all-tasks', 'archived' - -const tasksSection = document.getElementById('tasks-section'); -const focusSection = document.getElementById('focus-section'); -const extractPreview = document.getElementById('extract-preview'); -const pasteInput = document.getElementById('paste-input'); -const extractBtn = document.getElementById('extract-btn'); -const clearBtn = document.getElementById('clear-btn'); -const addItemsBtn = document.getElementById('add-btn'); -const downloadBtn = document.getElementById('download-btn'); -const calendarDownloadBtn = document.getElementById('calendar-download-btn'); -const newTaskBtn = document.getElementById('add-task-btn'); -const labelFilterSelect = document.getElementById('label-filter'); +let currentView = "calendar"; // 'calendar', 'all-tasks', 'archived' + +const tasksSection = document.getElementById("tasks-section"); +const focusSection = document.getElementById("focus-section"); +const extractPreview = document.getElementById("extract-preview"); +const pasteInput = document.getElementById("paste-input"); +const extractBtn = document.getElementById("extract-btn"); +const clearBtn = document.getElementById("clear-btn"); +const addItemsBtn = document.getElementById("add-btn"); +const downloadBtn = document.getElementById("download-btn"); +const calendarDownloadBtn = document.getElementById("calendar-download-btn"); +const newTaskBtn = document.getElementById("add-task-btn"); +const labelFilterSelect = document.getElementById("label-filter"); if (labelFilterSelect) { - labelFilterSelect.addEventListener('change', (e) => { + labelFilterSelect.addEventListener("change", (e) => { activeLabelFilter = e.target.value; renderTasks(); }); } const SUBJECT_COLORS = [ - 'var(--color-text-info)', - 'var(--color-text-success)', - 'var(--color-text-purple)', - 'var(--color-text-warning)', - 'var(--color-text-danger)', - 'var(--color-text-secondary)', + "var(--color-text-info)", + "var(--color-text-success)", + "var(--color-text-purple)", + "var(--color-text-warning)", + "var(--color-text-danger)", + "var(--color-text-secondary)", ]; let selectedNewSubjectColor = SUBJECT_COLORS[0]; function escapeHtml(s) { return String(s) - .replace(/&/g, '&') - .replace(//g, '>') - .replace(/"/g, '"'); + .replace(/&/g, "&") + .replace(//g, ">") + .replace(/"/g, """); } -const newSubjectModal = document.getElementById('new-subject-modal'); -const newSubjectName = document.getElementById('new-subject-name'); -const newSubjectColorsEl = document.getElementById('new-subject-colors'); -const newSubjectCancel = document.getElementById('new-subject-cancel'); -const newSubjectSave = document.getElementById('new-subject-save'); -const addSubjectBtn = document.getElementById('add-subject-btn'); +const newSubjectModal = document.getElementById("new-subject-modal"); +const newSubjectName = document.getElementById("new-subject-name"); +const newSubjectColorsEl = document.getElementById("new-subject-colors"); +const newSubjectCancel = document.getElementById("new-subject-cancel"); +const newSubjectSave = document.getElementById("new-subject-save"); +const addSubjectBtn = document.getElementById("add-subject-btn"); function syncNewSubjectColorSwatches() { if (!newSubjectColorsEl) return; - newSubjectColorsEl.querySelectorAll('.subject-color-swatch').forEach(btn => { - const on = btn.dataset.color === selectedNewSubjectColor; - btn.classList.toggle('subject-color-swatch--selected', on); - btn.setAttribute('aria-pressed', on ? 'true' : 'false'); - }); + newSubjectColorsEl + .querySelectorAll(".subject-color-swatch") + .forEach((btn) => { + const on = btn.dataset.color === selectedNewSubjectColor; + btn.classList.toggle("subject-color-swatch--selected", on); + btn.setAttribute("aria-pressed", on ? "true" : "false"); + }); } function openNewSubjectModal() { if (!newSubjectModal || !newSubjectName) return; - newSubjectName.value = ''; + newSubjectName.value = ""; selectedNewSubjectColor = SUBJECT_COLORS[0]; syncNewSubjectColorSwatches(); - newSubjectModal.style.display = 'flex'; + newSubjectModal.style.display = "flex"; newSubjectName.focus(); } function renderSidebarSubjects() { - const listEl = document.getElementById('subjects-sidebar-list'); + const listEl = document.getElementById("subjects-sidebar-list"); if (!listEl) return; const subjects = store.subjects; const tasks = store.tasks; const countBySubject = {}; - subjects.forEach(s => { + subjects.forEach((s) => { countBySubject[s.id] = 0; }); - tasks.forEach(t => { - if (t.archived || !t.subject_id || countBySubject[t.subject_id] === undefined) return; + tasks.forEach((t) => { + if ( + t.archived || + !t.subject_id || + countBySubject[t.subject_id] === undefined + ) + return; countBySubject[t.subject_id]++; }); - listEl.innerHTML = subjects.map(s => { - const n = countBySubject[s.id] ?? 0; - const safeColor = s.color ? escapeHtml(s.color) : 'var(--color-text-info)'; - return ` `; - - const completeBtn = activeFocusTask.querySelector('.complete-focus-task-btn'); + + const completeBtn = activeFocusTask.querySelector( + ".complete-focus-task-btn", + ); if (completeBtn) { - completeBtn.addEventListener('click', () => { + completeBtn.addEventListener("click", () => { store.toggleTaskStatus(activeT.id); activeFocusTaskId = null; renderFocusTasks(); }); } - - const clearBtn = activeFocusTask.querySelector('.clear-focus-task-btn'); + + const clearBtn = activeFocusTask.querySelector(".clear-focus-task-btn"); if (clearBtn) { - clearBtn.addEventListener('click', () => { + clearBtn.addEventListener("click", () => { activeFocusTaskId = null; renderFocusTasks(); }); } } else { activeFocusTaskId = null; - activeFocusTask.innerHTML = '
No task selected. Choose one below.
'; + activeFocusTask.innerHTML = + '
No task selected. Choose one below.
'; } } else { - activeFocusTask.innerHTML = '
No task selected. Choose one below.
'; + activeFocusTask.innerHTML = + '
No task selected. Choose one below.
'; } } function formatDate(dateStr) { - if (!dateStr) return 'No Date'; + if (!dateStr) return "No Date"; const d = new Date(dateStr); - return d.toLocaleDateString('en-US', { month: 'short', day: 'numeric', hour: '2-digit', minute:'2-digit' }); + return d.toLocaleDateString("en-US", { + month: "short", + day: "numeric", + hour: "2-digit", + minute: "2-digit", + }); } async function downloadData() { - try { - const response = await fetch('/api/download'); - - if (!response.ok) { - throw new Error('Failed to download data'); - } + try { + const response = await fetch("/api/download"); - const blob = await response.blob(); - const url = URL.createObjectURL(blob); - const a = document.createElement('a'); - a.href = url; - a.download = 'study_data.csv'; - document.body.appendChild(a); - a.click(); - document.body.removeChild(a); - setTimeout(() => URL.revokeObjectURL(url), 100); - - } catch (error) { - console.error(error); - Toast.show('Failed to download data', 'error'); + if (!response.ok) { + throw new Error("Failed to download data"); } + + const blob = await response.blob(); + const url = URL.createObjectURL(blob); + const a = document.createElement("a"); + a.href = url; + a.download = "study_data.csv"; + document.body.appendChild(a); + a.click(); + document.body.removeChild(a); + setTimeout(() => URL.revokeObjectURL(url), 100); + } catch (error) { + console.error(error); + Toast.show("Failed to download data", "error"); + } } async function downloadCalendar() { - try { - const response = await fetch('/api/download/calendar'); + try { + const response = await fetch("/api/download/calendar"); - if (!response.ok) { - throw new Error('Failed to export calendar'); - } - - const blob = await response.blob(); - const url = URL.createObjectURL(blob); - const a = document.createElement('a'); - a.href = url; - a.download = 'studyplan_calendar.ics'; - document.body.appendChild(a); - a.click(); - document.body.removeChild(a); - setTimeout(() => URL.revokeObjectURL(url), 100); - - } catch (error) { - console.error(error); - alert('Failed to export calendar'); + if (!response.ok) { + throw new Error("Failed to export calendar"); } + + const blob = await response.blob(); + const url = URL.createObjectURL(blob); + const a = document.createElement("a"); + a.href = url; + a.download = "studyplan_calendar.ics"; + document.body.appendChild(a); + a.click(); + document.body.removeChild(a); + setTimeout(() => URL.revokeObjectURL(url), 100); + } catch (error) { + console.error(error); + alert("Failed to export calendar"); + } } function renderTasks() { const tasks = store.tasks; const subjects = store.subjects; - + if (subjects.length === 0) return; // Wait for subjects to load - + // Filter based on archived status - const activeTasks = tasks.filter(t => !t.archived); - const archivedTasks = tasks.filter(t => t.archived); - + const activeTasks = tasks.filter((t) => !t.archived); + const archivedTasks = tasks.filter((t) => t.archived); + // Update badges - const allTasksBadge = document.querySelector('#all-tasks-btn .badge'); + const allTasksBadge = document.querySelector("#all-tasks-btn .badge"); if (allTasksBadge) { allTasksBadge.textContent = activeTasks.length; } - const archivedBadge = document.querySelector('#archived-tasks-btn .badge'); + const archivedBadge = document.querySelector("#archived-tasks-btn .badge"); if (archivedBadge) { archivedBadge.textContent = archivedTasks.length; } - - const displayTasksRaw = currentView === 'archived' ? archivedTasks : activeTasks; + + const displayTasksRaw = + currentView === "archived" ? archivedTasks : activeTasks; const displayTasks = activeLabelFilter - ? displayTasksRaw.filter(t => t.labels && t.labels.includes(activeLabelFilter)) + ? displayTasksRaw.filter( + (t) => t.labels && t.labels.includes(activeLabelFilter), + ) : displayTasksRaw; // Extract unique labels to populate the filter dropdown if (labelFilterSelect) { const uniqueLabels = new Set(); - store.tasks.forEach(t => { + store.tasks.forEach((t) => { if (t.labels && Array.isArray(t.labels)) { - t.labels.forEach(l => uniqueLabels.add(l)); + t.labels.forEach((l) => uniqueLabels.add(l)); } }); - + // Store current selection to restore it const currentSel = labelFilterSelect.value; let optionsHtml = ''; - Array.from(uniqueLabels).sort().forEach(lbl => { - optionsHtml += ``; - }); + Array.from(uniqueLabels) + .sort() + .forEach((lbl) => { + optionsHtml += ``; + }); labelFilterSelect.innerHTML = optionsHtml; } - const sorted = [...displayTasks].sort((a,b) => new Date(a.due_at) - new Date(b.due_at)); - - const now = new Date(); - + const sorted = [...displayTasks].sort( + (a, b) => new Date(a.due_at) - new Date(b.due_at), + ); + + const now = new Date(); + const dueSoon = []; const thisWeek = []; const completed = []; const pending = []; - - if (currentView === 'calendar' && selectedDate) { - sorted.forEach(t => { + + if (currentView === "calendar" && selectedDate) { + sorted.forEach((t) => { const d = new Date(t.due_at); - if (d.getDate() === selectedDate.getDate() && d.getMonth() === selectedDate.getMonth() && d.getFullYear() === selectedDate.getFullYear()) { - if (t.status === 'Done') completed.push(t); + if ( + d.getDate() === selectedDate.getDate() && + d.getMonth() === selectedDate.getMonth() && + d.getFullYear() === selectedDate.getFullYear() + ) { + if (t.status === "Done") completed.push(t); else { dueSoon.push(t); pending.push(t); @@ -502,8 +546,8 @@ function renderTasks() { } }); } else { - sorted.forEach(t => { - if (t.status === 'Done') { + sorted.forEach((t) => { + if (t.status === "Done") { completed.push(t); return; } @@ -514,46 +558,50 @@ function renderTasks() { else thisWeek.push(t); }); } - + const renderGroup = (title, items, titleColor, showConflict = false) => { - if (items.length === 0) return ''; + if (items.length === 0) return ""; let html = `
${title}
`; - + if (showConflict) { const workloadSuggestions = analyzeWorkload(items); - workloadSuggestions.forEach(workload => { + workloadSuggestions.forEach((workload) => { html += `
âš  Heavy workload detected on ${workload.date}
Workload Score: ${workload.score}
-
    ${workload.suggestions.map(s => `
  • ${s}
  • `).join('')}
+
    ${workload.suggestions.map((s) => `
  • ${s}
  • `).join("")}
`; }); } - - - items.forEach(t => { - const sub = subjects.find(s => s.id === t.subject_id) || subjects[0]; - const isDone = t.status === 'Done'; - const isHighPriority = t.priority === 'high'; + + items.forEach((t) => { + const sub = subjects.find((s) => s.id === t.subject_id) || subjects[0]; + const isDone = t.status === "Done"; + const isHighPriority = t.priority === "high"; const isOverdue = !isDone && t.due_at && new Date(t.due_at) < now; - const isUrgent = isHighPriority && title === 'âš  Due soon'; - - let pillClass = ''; - if(sub.short_code === 'CS') pillClass = 'pill-blue'; - else if(sub.short_code === 'Maths') pillClass = 'pill-green'; - else if(sub.short_code === 'English') pillClass = 'pill-purple'; - else pillClass = 'pill-amber'; - + const isUrgent = isHighPriority && title === "âš  Due soon"; + + let pillClass = ""; + if (sub.short_code === "CS") pillClass = "pill-blue"; + else if (sub.short_code === "Maths") pillClass = "pill-green"; + else if (sub.short_code === "English") pillClass = "pill-purple"; + else pillClass = "pill-amber"; + if (t._isEditing) { - let subjectOptions = subjects.map(s => - `` - ).join(''); - - const localDate = t.due_at ? new Date(t.due_at).toISOString().substring(0, 16) : ''; - + let subjectOptions = subjects + .map( + (s) => + ``, + ) + .join(""); + + const localDate = t.due_at + ? new Date(t.due_at).toISOString().substring(0, 16) + : ""; + html += `
@@ -562,18 +610,18 @@ function renderTasks() { - + - +
@@ -583,7 +631,7 @@ function renderTasks() {
`; } else { - const actionButtons = !t.archived + const actionButtons = !t.archived ? ` ` @@ -591,18 +639,23 @@ function renderTasks() { `; - let labelsHtml = ''; + let labelsHtml = ""; if (t.labels && Array.isArray(t.labels)) { - labelsHtml = t.labels.map(l => `${l}`).join(' '); + labelsHtml = t.labels + .map( + (l) => + `${l}`, + ) + .join(" "); } html += ` -
-
+
+
${t.title}
- ${isDone ? 'Done' : 'Due ' + formatDate(t.due_at)} + ${isDone ? "Done" : "Due " + formatDate(t.due_at)} ${sub.short_code} ${labelsHtml}
@@ -617,16 +670,20 @@ function renderTasks() { html += `
`; return html; }; - - if (currentView === 'calendar' && selectedDate) { - const selStr = selectedDate.toLocaleDateString('en-US', {month:'short', day:'numeric'}); + + if (currentView === "calendar" && selectedDate) { + const selStr = selectedDate.toLocaleDateString("en-US", { + month: "short", + day: "numeric", + }); const actionBar = `
- - + +
`; - const emptyState = dueSoon.length === 0 && completed.length === 0 - ? `
+ const emptyState = + dueSoon.length === 0 && completed.length === 0 + ? `
📅
No tasks for today
Your schedule is looking clear! Use this time to rest or start planning ahead.
@@ -635,227 +692,279 @@ function renderTasks() { Add your first task
` - : ''; + : ""; - tasksSection.innerHTML = actionBar + - renderGroup(`Tasks for ${selStr}`, dueSoon, 'var(--color-text-primary)') + - renderGroup('Completed', completed, 'var(--color-text-tertiary)') + - emptyState; + tasksSection.innerHTML = + actionBar + + renderGroup(`Tasks for ${selStr}`, dueSoon, "var(--color-text-primary)") + + renderGroup("Completed", completed, "var(--color-text-tertiary)") + + emptyState; } else { - const actionBar = currentView === 'archived' ? '' : `
- + const actionBar = + currentView === "archived" + ? "" + : `
+
`; - const titlePrefix = currentView === 'archived' ? 'Archived: ' : ''; - const emptyStateTitle = currentView === 'archived' ? 'No archived tasks' : 'Start your journey'; - const emptyStateText = currentView === 'archived' - ? 'Your archive is empty. Completed tasks you archive will appear here.' - : 'No tasks yet! Start planning your study schedule and stay on top of your goals.'; - const emptyStateIcon = currentView === 'archived' ? '📦' : '✨'; - - const emptyState = dueSoon.length === 0 && thisWeek.length === 0 && completed.length === 0 - ? `
+ const titlePrefix = currentView === "archived" ? "Archived: " : ""; + const emptyStateTitle = + currentView === "archived" ? "No archived tasks" : "Start your journey"; + const emptyStateText = + currentView === "archived" + ? "Your archive is empty. Completed tasks you archive will appear here." + : "No tasks yet! Start planning your study schedule and stay on top of your goals."; + const emptyStateIcon = currentView === "archived" ? "📦" : "✨"; + + const emptyState = + dueSoon.length === 0 && thisWeek.length === 0 && completed.length === 0 + ? `
${emptyStateIcon}
${emptyStateTitle}
${emptyStateText}
- ${currentView !== 'archived' ? ` + ${ + currentView !== "archived" + ? ` ` : ''} + ` + : "" + }
` - : ''; - - tasksSection.innerHTML = actionBar + - renderGroup(titlePrefix + 'âš  Due soon', dueSoon, 'var(--color-text-danger)', true) - + renderGroup(titlePrefix + 'This week', thisWeek, 'var(--color-text-secondary)', true) + - renderGroup(titlePrefix + 'Completed', completed, 'var(--color-text-tertiary)') + - emptyState; + : ""; + + tasksSection.innerHTML = + actionBar + + renderGroup( + titlePrefix + "âš  Due soon", + dueSoon, + "var(--color-text-danger)", + true, + ) + + renderGroup( + titlePrefix + "This week", + thisWeek, + "var(--color-text-secondary)", + true, + ) + + renderGroup( + titlePrefix + "Completed", + completed, + "var(--color-text-tertiary)", + ) + + emptyState; } // Bind CTA button in empty state - const emptyStateAddBtn = document.getElementById('empty-state-add-btn'); + const emptyStateAddBtn = document.getElementById("empty-state-add-btn"); if (emptyStateAddBtn) { - emptyStateAddBtn.addEventListener('click', () => { - document.getElementById('add-task-btn')?.click(); + emptyStateAddBtn.addEventListener("click", () => { + document.getElementById("add-task-btn")?.click(); }); } - - document.querySelectorAll('.task-item').forEach(el => { - el.addEventListener('click', (e) => { - if (e.target.closest('.task-actions') || e.target.closest('.task-check')) return; - + + document.querySelectorAll(".task-item").forEach((el) => { + el.addEventListener("click", (e) => { + if (e.target.closest(".task-actions") || e.target.closest(".task-check")) + return; + const taskId = el.dataset.id; - const task = store.tasks.find(t => String(t.id) === String(taskId)); + const task = store.tasks.find((t) => String(t.id) === String(taskId)); if (task && task._isEditing) return; - + store.toggleTaskStatus(taskId); }); }); - document.querySelectorAll('.edit-task-btn').forEach(el => { - el.addEventListener('click', (e) => { + document.querySelectorAll(".edit-task-btn").forEach((el) => { + el.addEventListener("click", (e) => { e.stopPropagation(); store.setTaskEditing(el.dataset.id, true); }); }); - document.querySelectorAll('.cancel-board-edit-btn').forEach(el => { - el.addEventListener('click', (e) => { + document.querySelectorAll(".cancel-board-edit-btn").forEach((el) => { + el.addEventListener("click", (e) => { e.stopPropagation(); store.setTaskEditing(el.dataset.id, false); }); }); - document.querySelectorAll('.save-board-edit-btn').forEach(el => { - el.addEventListener('click', (e) => { + document.querySelectorAll(".save-board-edit-btn").forEach((el) => { + el.addEventListener("click", (e) => { e.stopPropagation(); const taskId = el.dataset.id; - const itemEl = el.closest('.task-item'); - - const rawTitle = itemEl.querySelector('.board-edit-title').value; - const subject_id = itemEl.querySelector('.board-edit-subject').value; - let dateVal = itemEl.querySelector('.board-edit-date').value; - const notes = itemEl.querySelector('.board-edit-notes').value; - const priority = itemEl.querySelector('.board-edit-priority').value; - + const itemEl = el.closest(".task-item"); + + const rawTitle = itemEl.querySelector(".board-edit-title").value; + const subject_id = itemEl.querySelector(".board-edit-subject").value; + let dateVal = itemEl.querySelector(".board-edit-date").value; + const notes = itemEl.querySelector(".board-edit-notes").value; + const priority = itemEl.querySelector(".board-edit-priority").value; + const { cleanTitle, labels } = extractLabels(rawTitle); - + store.updateTask(taskId, { title: cleanTitle || rawTitle, subject_id, - due_at: dateVal ? new Date(dateVal).toISOString() : '', + due_at: dateVal ? new Date(dateVal).toISOString() : "", notes, priority, - labels + labels, }); }); }); - document.querySelectorAll('.task-check').forEach(el => { - el.addEventListener('click', (e) => { + document.querySelectorAll(".task-check").forEach((el) => { + el.addEventListener("click", (e) => { e.stopPropagation(); - const taskId = el.closest('.task-item').dataset.id; + const taskId = el.closest(".task-item").dataset.id; store.toggleTaskStatus(taskId); }); }); - document.querySelectorAll('.archive-task-btn').forEach(el => { - el.addEventListener('click', (e) => { + document.querySelectorAll(".archive-task-btn").forEach((el) => { + el.addEventListener("click", (e) => { e.stopPropagation(); store.archiveTask(el.dataset.id); }); }); - document.querySelectorAll('.restore-task-btn').forEach(el => { - el.addEventListener('click', (e) => { + document.querySelectorAll(".restore-task-btn").forEach((el) => { + el.addEventListener("click", (e) => { e.stopPropagation(); store.restoreTask(el.dataset.id); }); }); - document.querySelectorAll('.delete-task-btn').forEach(el => { - el.addEventListener('click', (e) => { + document.querySelectorAll(".delete-task-btn").forEach((el) => { + el.addEventListener("click", (e) => { e.stopPropagation(); store.deleteTask(el.dataset.id); }); }); - const markAllPendingBtn = document.getElementById('mark-all-pending-btn'); + const markAllPendingBtn = document.getElementById("mark-all-pending-btn"); if (markAllPendingBtn) { - markAllPendingBtn.addEventListener('click', (e) => { + markAllPendingBtn.addEventListener("click", (e) => { e.stopPropagation(); store.markAllPendingCompleted(); }); } - const markDayCompleteBtn = document.getElementById('mark-day-complete-btn'); + const markDayCompleteBtn = document.getElementById("mark-day-complete-btn"); if (markDayCompleteBtn) { - markDayCompleteBtn.addEventListener('click', (e) => { + markDayCompleteBtn.addEventListener("click", (e) => { e.stopPropagation(); store.markPendingTasksForDateCompleted(selectedDate); }); } } - -const summaryBox = document.getElementById('summary-box'); +const summaryBox = document.getElementById("summary-box"); if (summaryBox) { summaryBox.innerHTML = generateSummary(store.tasks, store.subjects); } function renderCalendar() { - const calTitle = document.getElementById('cal-month-title'); - const calGrid = document.getElementById('cal-grid'); + const calTitle = document.getElementById("cal-month-title"); + const calGrid = document.getElementById("cal-grid"); if (!calGrid) return; - + const year = currentMonthDate.getFullYear(); const month = currentMonthDate.getMonth(); - - const monthNames = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"]; + + const monthNames = [ + "January", + "February", + "March", + "April", + "May", + "June", + "July", + "August", + "September", + "October", + "November", + "December", + ]; calTitle.textContent = `${monthNames[month]} ${year}`; - - const topbarTitle = document.querySelector('.topbar-title'); - if(topbarTitle) topbarTitle.textContent = `${monthNames[month]} ${year}`; + + const topbarTitle = document.querySelector(".topbar-title"); + if (topbarTitle) topbarTitle.textContent = `${monthNames[month]} ${year}`; const firstDay = new Date(year, month, 1).getDay(); const daysInMonth = new Date(year, month + 1, 0).getDate(); const prevMonthDays = new Date(year, month, 0).getDate(); - + const today = new Date(); - + let html = `
Su
Mo
Tu
We
Th
Fr
Sa
`; - + for (let i = 0; i < firstDay; i++) { html += `
${prevMonthDays - firstDay + i + 1}
`; } - + for (let i = 1; i <= daysInMonth; i++) { - const isToday = i === today.getDate() && month === today.getMonth() && year === today.getFullYear(); - const isSelected = selectedDate && i === selectedDate.getDate() && month === selectedDate.getMonth() && year === selectedDate.getFullYear(); - + const isToday = + i === today.getDate() && + month === today.getMonth() && + year === today.getFullYear(); + const isSelected = + selectedDate && + i === selectedDate.getDate() && + month === selectedDate.getMonth() && + year === selectedDate.getFullYear(); + // Find tasks for this day - const dayTasks = store.tasks.filter(t => { + const dayTasks = store.tasks.filter((t) => { if (t.archived) return false; - if (t.status === 'Done') return false; + if (t.status === "Done") return false; if (!t.due_at) return false; const d = new Date(t.due_at); - return d.getDate() === i && d.getMonth() === month && d.getFullYear() === year; + return ( + d.getDate() === i && d.getMonth() === month && d.getFullYear() === year + ); }); - let indicatorHtml = ''; + let indicatorHtml = ""; if (dayTasks.length > 0) { indicatorHtml = `
`; dayTasks.forEach((t, idx) => { - if (idx > 2) return; - const sub = store.subjects.find(s => s.id === t.subject_id) || store.subjects[0]; - indicatorHtml += `
`; + if (idx > 2) return; + const sub = + store.subjects.find((s) => s.id === t.subject_id) || + store.subjects[0]; + indicatorHtml += `
`; }); indicatorHtml += `
`; } - const extraStyle = isSelected ? `border: 1.5px solid var(--color-text-primary);` : ''; + const extraStyle = isSelected + ? `border: 1.5px solid var(--color-text-primary);` + : ""; - html += `
+ html += `
${i} ${indicatorHtml}
`; } - + const totalCells = firstDay + daysInMonth; const nextDays = (7 - (totalCells % 7)) % 7; for (let i = 1; i <= nextDays; i++) { html += `
${i}
`; } - + calGrid.innerHTML = html; // Bind day clicks - document.querySelectorAll('.interactive-day').forEach(el => { - el.addEventListener('click', (e) => { - const d = parseInt(e.currentTarget.getAttribute('data-day')); + document.querySelectorAll(".interactive-day").forEach((el) => { + el.addEventListener("click", (e) => { + const d = parseInt(e.currentTarget.getAttribute("data-day")); const clickedDate = new Date(year, month, d); - + if (selectedDate && clickedDate.getTime() === selectedDate.getTime()) { selectedDate = null; } else { @@ -870,29 +979,37 @@ function renderCalendar() { function renderExtraction() { const pasteItems = store.currentPaste; if (!pasteItems || pasteItems.length === 0) { - extractPreview.innerHTML = ''; + extractPreview.innerHTML = ""; addItemsBtn.disabled = true; - addItemsBtn.textContent = 'Add items to planner'; + addItemsBtn.textContent = "Add items to planner"; return; } - + addItemsBtn.disabled = false; addItemsBtn.textContent = `Add ${pasteItems.length} items to planner`; - + let html = `
Extracted — ${pasteItems.length} items
`; pasteItems.forEach((item, index) => { // try to match subject name - const sub = store.subjects.find(s => s.name.toLowerCase().includes((item.subject_name || '').toLowerCase())) || store.subjects[3]; + const sub = + store.subjects.find((s) => + s.name.toLowerCase().includes((item.subject_name || "").toLowerCase()), + ) || store.subjects[3]; // Attach subject id to item so Add will work item.subject_id = sub.id; - + if (item._isEditing) { - let subjectOptions = store.subjects.map(s => - `` - ).join(''); - - const localDate = item.due_at ? new Date(item.due_at).toISOString().substring(0, 16) : ''; - + let subjectOptions = store.subjects + .map( + (s) => + ``, + ) + .join(""); + + const localDate = item.due_at + ? new Date(item.due_at).toISOString().substring(0, 16) + : ""; + html += `
@@ -907,7 +1024,7 @@ function renderExtraction() { - +
@@ -919,48 +1036,48 @@ function renderExtraction() {
${sub.name}
${item.title}
-
${item.icon || '📅'} ${formatDate(item.due_at)}
-
📎 ${item.notes || 'No notes attached'}
-
+
${item.icon || "📅"} ${formatDate(item.due_at)}
+
📎 ${item.notes || "No notes attached"}
+
${item.confidence_score}% confidence Edit
`; } }); - + extractPreview.innerHTML = html; - + setTimeout(() => { - document.querySelectorAll('.conf-fill').forEach(el => { - el.style.width = el.getAttribute('data-width') + '%'; + document.querySelectorAll(".conf-fill").forEach((el) => { + el.style.width = el.getAttribute("data-width") + "%"; }); }, 100); - - document.querySelectorAll('.conf-edit').forEach(btn => { - btn.addEventListener('click', (e) => { - const idx = e.target.getAttribute('data-index'); + + document.querySelectorAll(".conf-edit").forEach((btn) => { + btn.addEventListener("click", (e) => { + const idx = e.target.getAttribute("data-index"); store.updateExtractedItem(idx, { _isEditing: true }); }); }); - document.querySelectorAll('.save-edit-btn').forEach(btn => { - btn.addEventListener('click', (e) => { - const idx = e.target.getAttribute('data-index'); - const card = e.target.closest('.extract-card'); - const subjectId = card.querySelector('.edit-subject-input').value; - const title = card.querySelector('.edit-title-input').value; - let dateVal = card.querySelector('.edit-date-input').value; - const notes = card.querySelector('.edit-notes-input').value; - - const newSubject = store.subjects.find(s => s.id === subjectId); - + document.querySelectorAll(".save-edit-btn").forEach((btn) => { + btn.addEventListener("click", (e) => { + const idx = e.target.getAttribute("data-index"); + const card = e.target.closest(".extract-card"); + const subjectId = card.querySelector(".edit-subject-input").value; + const title = card.querySelector(".edit-title-input").value; + let dateVal = card.querySelector(".edit-date-input").value; + const notes = card.querySelector(".edit-notes-input").value; + + const newSubject = store.subjects.find((s) => s.id === subjectId); + store.updateExtractedItem(idx, { subject_id: subjectId, - subject_name: newSubject ? newSubject.name : 'General', + subject_name: newSubject ? newSubject.name : "General", title: title, - due_at: dateVal ? new Date(dateVal).toISOString() : '', + due_at: dateVal ? new Date(dateVal).toISOString() : "", notes: notes, - _isEditing: false + _isEditing: false, }); }); }); @@ -972,15 +1089,15 @@ store.subscribe(renderCalendar); store.subscribe(renderFocusTasks); store.subscribe(renderSidebarSubjects); -document.addEventListener('DOMContentLoaded', () => { +document.addEventListener("DOMContentLoaded", () => { if (newSubjectColorsEl) { - SUBJECT_COLORS.forEach(c => { - const btn = document.createElement('button'); - btn.type = 'button'; - btn.className = 'subject-color-swatch'; + SUBJECT_COLORS.forEach((c) => { + const btn = document.createElement("button"); + btn.type = "button"; + btn.className = "subject-color-swatch"; btn.dataset.color = c; btn.style.background = c; - btn.addEventListener('click', () => { + btn.addEventListener("click", () => { selectedNewSubjectColor = c; syncNewSubjectColorSwatches(); }); @@ -990,9 +1107,9 @@ document.addEventListener('DOMContentLoaded', () => { } if (addSubjectBtn) { - addSubjectBtn.addEventListener('click', () => openNewSubjectModal()); - addSubjectBtn.addEventListener('keydown', (e) => { - if (e.key === 'Enter' || e.key === ' ') { + addSubjectBtn.addEventListener("click", () => openNewSubjectModal()); + addSubjectBtn.addEventListener("keydown", (e) => { + if (e.key === "Enter" || e.key === " ") { e.preventDefault(); openNewSubjectModal(); } @@ -1000,27 +1117,30 @@ document.addEventListener('DOMContentLoaded', () => { } if (newSubjectCancel) { - newSubjectCancel.addEventListener('click', () => { - if (newSubjectModal) newSubjectModal.style.display = 'none'; + newSubjectCancel.addEventListener("click", () => { + if (newSubjectModal) newSubjectModal.style.display = "none"; }); } if (newSubjectModal) { - newSubjectModal.addEventListener('click', (e) => { - if (e.target === newSubjectModal) newSubjectModal.style.display = 'none'; + newSubjectModal.addEventListener("click", (e) => { + if (e.target === newSubjectModal) newSubjectModal.style.display = "none"; }); } if (newSubjectSave) { - newSubjectSave.addEventListener('click', async () => { - const ok = await store.addSubject({ name: newSubjectName.value, color: selectedNewSubjectColor }); - if (ok && newSubjectModal) newSubjectModal.style.display = 'none'; + newSubjectSave.addEventListener("click", async () => { + const ok = await store.addSubject({ + name: newSubjectName.value, + color: selectedNewSubjectColor, + }); + if (ok && newSubjectModal) newSubjectModal.style.display = "none"; }); } if (newSubjectName) { - newSubjectName.addEventListener('keydown', (e) => { - if (e.key === 'Enter') { + newSubjectName.addEventListener("keydown", (e) => { + if (e.key === "Enter") { e.preventDefault(); newSubjectSave?.click(); } @@ -1028,192 +1148,227 @@ document.addEventListener('DOMContentLoaded', () => { } store.fetchInitialData(); - - const calendarBtn = document.getElementById('calendar-btn'); - const allTasksBtn = document.getElementById('all-tasks-btn'); - const archivedTasksBtn = document.getElementById('archived-tasks-btn'); - const focusModeBtn = document.getElementById('focus-mode-btn'); + + const calendarBtn = document.getElementById("calendar-btn"); + const allTasksBtn = document.getElementById("all-tasks-btn"); + const archivedTasksBtn = document.getElementById("archived-tasks-btn"); + const focusModeBtn = document.getElementById("focus-mode-btn"); + + // Wire top header nav links (Dashboard / Tasks / Calendar) to the same view handlers + const headerNavLinks = document.querySelectorAll(".header-nav a"); + if (headerNavLinks && headerNavLinks.length) { + headerNavLinks.forEach((a) => { + a.addEventListener("click", (e) => { + e.preventDefault(); + const txt = (a.textContent || "").trim().toLowerCase(); + if (txt === "dashboard") { + currentView = "calendar"; + document.querySelector(".cal-section")?.classList.remove("hidden"); + document.getElementById("tasks-section")?.classList.remove("hidden"); + document.getElementById("focus-section")?.classList.add("hidden"); + updateSidebarActive("calendar-btn"); + renderTasks(); + } else if (txt === "tasks") { + currentView = "all-tasks"; + document.querySelector(".cal-section")?.classList.add("hidden"); + document.getElementById("tasks-section")?.classList.remove("hidden"); + document.getElementById("focus-section")?.classList.add("hidden"); + updateSidebarActive("all-tasks-btn"); + renderTasks(); + } else if (txt === "calendar") { + currentView = "calendar"; + document.querySelector(".cal-section")?.classList.remove("hidden"); + document.getElementById("tasks-section")?.classList.remove("hidden"); + document.getElementById("focus-section")?.classList.add("hidden"); + updateSidebarActive("calendar-btn"); + renderTasks(); + } + }); + }); + } function updateSidebarActive(id) { - document.querySelectorAll('.sidebar .nav-item').forEach(el => el.classList.remove('active')); - document.getElementById(id).classList.add('active'); + document + .querySelectorAll(".sidebar .nav-item") + .forEach((el) => el.classList.remove("active")); + document.getElementById(id).classList.add("active"); } - calendarBtn.addEventListener('click', () => { - currentView = 'calendar'; - document.querySelector('.cal-section').classList.remove('hidden'); - document.getElementById('tasks-section').classList.remove('hidden'); - document.getElementById('focus-section').classList.add('hidden'); - updateSidebarActive('calendar-btn'); + calendarBtn.addEventListener("click", () => { + currentView = "calendar"; + document.querySelector(".cal-section").classList.remove("hidden"); + document.getElementById("tasks-section").classList.remove("hidden"); + document.getElementById("focus-section").classList.add("hidden"); + updateSidebarActive("calendar-btn"); renderTasks(); }); - allTasksBtn.addEventListener('click', () => { - currentView = 'all-tasks'; - document.querySelector('.cal-section').classList.add('hidden'); - document.getElementById('tasks-section').classList.remove('hidden'); - document.getElementById('focus-section').classList.add('hidden'); - updateSidebarActive('all-tasks-btn'); + allTasksBtn.addEventListener("click", () => { + currentView = "all-tasks"; + document.querySelector(".cal-section").classList.add("hidden"); + document.getElementById("tasks-section").classList.remove("hidden"); + document.getElementById("focus-section").classList.add("hidden"); + updateSidebarActive("all-tasks-btn"); renderTasks(); }); - archivedTasksBtn.addEventListener('click', () => { - currentView = 'archived'; - document.querySelector('.cal-section').classList.add('hidden'); - document.getElementById('tasks-section').classList.remove('hidden'); - document.getElementById('focus-section').classList.add('hidden'); - updateSidebarActive('archived-tasks-btn'); + archivedTasksBtn.addEventListener("click", () => { + currentView = "archived"; + document.querySelector(".cal-section").classList.add("hidden"); + document.getElementById("tasks-section").classList.remove("hidden"); + document.getElementById("focus-section").classList.add("hidden"); + updateSidebarActive("archived-tasks-btn"); renderTasks(); }); - if(focusModeBtn) { - focusModeBtn.addEventListener('click', () => { - currentView = 'focus'; - document.querySelector('.cal-section').classList.add('hidden'); - document.getElementById('tasks-section').classList.add('hidden'); - document.getElementById('focus-section').classList.remove('hidden'); - updateSidebarActive('focus-mode-btn'); + if (focusModeBtn) { + focusModeBtn.addEventListener("click", () => { + currentView = "focus"; + document.querySelector(".cal-section").classList.add("hidden"); + document.getElementById("tasks-section").classList.add("hidden"); + document.getElementById("focus-section").classList.remove("hidden"); + updateSidebarActive("focus-mode-btn"); renderFocusTasks(); }); } - document.getElementById('cal-prev').addEventListener('click', () => { + document.getElementById("cal-prev").addEventListener("click", () => { currentMonthDate.setMonth(currentMonthDate.getMonth() - 1); renderCalendar(); }); - document.getElementById('cal-next').addEventListener('click', () => { + document.getElementById("cal-next").addEventListener("click", () => { currentMonthDate.setMonth(currentMonthDate.getMonth() + 1); renderCalendar(); }); + //NEw Task addition event listeners + newTaskBtn.addEventListener("click", () => { + if (!store.subjects || store.subjects.length === 0) { + Toast.show( + "Subjects are still loading. Please try again in a moment.", + "warning", + ); + return; + } -//NEw Task addition event listeners -newTaskBtn.addEventListener('click', () => { - - if (!store.subjects || store.subjects.length === 0) { - Toast.show('Subjects are still loading. Please try again in a moment.', 'warning'); - return; - } - - newTaskSubject.innerHTML = store.subjects - .map(s => ``) - .join(''); - - - if (selectedDate) { - const d = new Date(selectedDate); - d.setHours(18, 0, 0, 0); - newTaskDate.value = d.toISOString().substring(0, 16); - } else { - newTaskDate.value = ''; - } + newTaskSubject.innerHTML = store.subjects + .map((s) => ``) + .join(""); - newTaskTitle.value = ''; - newTaskNotes.value = ''; + if (selectedDate) { + const d = new Date(selectedDate); + d.setHours(18, 0, 0, 0); + newTaskDate.value = d.toISOString().substring(0, 16); + } else { + newTaskDate.value = ""; + } - newTaskModal.style.display = 'flex'; -}); + newTaskTitle.value = ""; + newTaskNotes.value = ""; -newTaskCancel.addEventListener('click', () => { - newTaskModal.style.display = 'none'; -}); + newTaskModal.style.display = "flex"; + }); -newTaskModal.addEventListener('click', (e) => { - if (e.target === newTaskModal) { - newTaskModal.style.display = 'none'; - } -}); + newTaskCancel.addEventListener("click", () => { + newTaskModal.style.display = "none"; + }); -newTaskSave.addEventListener('click', async () => { - const rawTitle = newTaskTitle.value.trim(); - const subject_id = newTaskSubject.value; - const notes = newTaskNotes.value.trim(); - const dateVal = newTaskDate.value; + newTaskModal.addEventListener("click", (e) => { + if (e.target === newTaskModal) { + newTaskModal.style.display = "none"; + } + }); - if (!rawTitle) { - alert('Please enter a task name'); - return; - } + newTaskSave.addEventListener("click", async () => { + const rawTitle = newTaskTitle.value.trim(); + const subject_id = newTaskSubject.value; + const notes = newTaskNotes.value.trim(); + const dateVal = newTaskDate.value; - if (!dateVal) { - alert('Please enter a deadline'); - return; -} + if (!rawTitle) { + alert("Please enter a task name"); + return; + } -if (!subject_id) { - alert('Please select a subject'); - return; -} - const { cleanTitle, labels } = extractLabels(rawTitle); - const due_at = dateVal ? new Date(dateVal).toISOString() : ''; - - const newTask = { - title: cleanTitle || rawTitle, - subject_id, - due_at, - notes, - priority: 'medium', - status: 'Not Started', - archived: 0, - labels - }; + if (!dateVal) { + alert("Please enter a deadline"); + return; + } - await store.addTasks([newTask]); - newTaskModal.style.display = 'none'; -}); + if (!subject_id) { + alert("Please select a subject"); + return; + } + const { cleanTitle, labels } = extractLabels(rawTitle); + const due_at = dateVal ? new Date(dateVal).toISOString() : ""; + + const newTask = { + title: cleanTitle || rawTitle, + subject_id, + due_at, + notes, + priority: "medium", + status: "Not Started", + archived: 0, + labels, + }; + + await store.addTasks([newTask]); + newTaskModal.style.display = "none"; + }); -addItemsBtn.addEventListener('click', () => { - if (store.currentPaste) { - const pasteWithLabels = store.currentPaste.map(t => { - const { cleanTitle, labels } = extractLabels(t.title); - return { ...t, title: cleanTitle || t.title, labels }; - }); - store.addTasks(pasteWithLabels); - store.clearExtracted(); - pasteInput.value = ''; - } -}); + addItemsBtn.addEventListener("click", () => { + if (store.currentPaste) { + const pasteWithLabels = store.currentPaste.map((t) => { + const { cleanTitle, labels } = extractLabels(t.title); + return { ...t, title: cleanTitle || t.title, labels }; + }); + store.addTasks(pasteWithLabels); + store.clearExtracted(); + pasteInput.value = ""; + } + }); }); // Ensures the button is hidden on initial page load if the textarea is empty if (pasteInput.value.trim() === "") { - clearBtn.style.display = 'none'; + clearBtn.style.display = "none"; } -extractBtn.addEventListener('click', async () => { +extractBtn.addEventListener("click", async () => { const text = pasteInput.value; if (!text.trim()) return; - + extractBtn.innerHTML = ''; extractBtn.disabled = true; - + const items = await extractTasksFromText(text); - - extractBtn.innerHTML = 'Extract with AI →'; + + extractBtn.innerHTML = "Extract with AI →"; extractBtn.disabled = false; - + store.setExtracted(items); }); // Wipes the text, clears the store, hides the button, and refocuses the cursor -clearBtn.addEventListener('click', () => { - pasteInput.value = ''; - store.clearExtracted(); - clearBtn.style.display = 'none'; // Hides the clear button instantly - pasteInput.focus(); // Puts the typing cursor back in the box +clearBtn.addEventListener("click", () => { + pasteInput.value = ""; + store.clearExtracted(); + clearBtn.style.display = "none"; // Hides the clear button instantly + pasteInput.focus(); // Puts the typing cursor back in the box }); // Listens to typing/pasting to show or hide the button dynamically -pasteInput.addEventListener('input', () => { - if (pasteInput.value.trim().length > 0) { - clearBtn.style.display = 'block'; - } else { - clearBtn.style.display = 'none'; - } +pasteInput.addEventListener("input", () => { + if (pasteInput.value.trim().length > 0) { + clearBtn.style.display = "block"; + } else { + clearBtn.style.display = "none"; + } }); -downloadBtn.addEventListener('click', () => { +downloadBtn.addEventListener("click", () => { downloadData(); }); @@ -1228,10 +1383,10 @@ const quotes = [ "Time is not refundable, use it with intention.", "Sometimes, getting it done is better than perfect.", "Believe you can and you're halfway there.", - "Arise, awake, and stop not till the goal is reached." + "Arise, awake, and stop not till the goal is reached.", ]; -const quoteEl = document.getElementById('motivational-quotes'); +const quoteEl = document.getElementById("motivational-quotes"); if (quoteEl) { const today = new Date(); const seed = today.toDateString(); @@ -1242,6 +1397,6 @@ if (quoteEl) { const index = Math.abs(hash % quotes.length); quoteEl.textContent = `${quotes[index]}`; } -calendarDownloadBtn.addEventListener('click', () => { +calendarDownloadBtn.addEventListener("click", () => { downloadCalendar(); });