let notes = []; let goals = []; let editingNoteId = null; let filterTimer = null; let currentUser = null; let savedGoalId = null; async function loadNotes() { const goalId = document.getElementById("filter-goal").value; const search = document.getElementById("filter-search").value.trim(); let params = new URLSearchParams(); if (goalId) params.set("goal_id", goalId); if (search) params.set("search", search); try { notes = await get(`/api/notes?${params.toString()}`); renderNotes(); } catch (error) { console.error("Failed to load notes:", error); } } async function loadGoals() { try { goals = await get("/api/goals"); currentUser = await get("/api/auth/me"); savedGoalId = currentUser.selected_goal_id; populateGoalSelectors(); } catch (error) { console.error("Failed to load goals:", error); } } function populateGoalSelectors() { const filterSelect = document.getElementById("filter-goal"); const modalSelect = document.getElementById("note-goal"); const activated = goals.filter(g => g.activated); const options = activated.map(g => `` ).join(""); const savedGoalExists = savedGoalId && activated.some(g => g.id === savedGoalId); filterSelect.innerHTML = `` + options; if (savedGoalExists) { filterSelect.value = savedGoalId; } modalSelect.innerHTML = `` + options; } function renderNotes() { const container = document.getElementById("notes-list"); if (notes.length === 0) { container.innerHTML = `

No notes yet

Create your first note to get started!

`; return; } container.innerHTML = notes.map(note => { const snippet = (note.content || "").replace(/[#*`\[\]()>|~_-]/g, "").substring(0, 120); const time = formatTime(note.updated_at); let link = ""; if (note.task_title) { link = `${escapeHtml(note.goal_title)} / ${escapeHtml(note.task_title)}`; } else if (note.goal_title) { link = `${escapeHtml(note.goal_title)}`; } return `
${escapeHtml(note.title)}
${time} ${link}
${escapeHtml(snippet)}
`; }).join(""); } async function openNote(noteId) { editingNoteId = noteId; const note = notes.find(n => n.id === noteId); if (!note) return; document.getElementById("note-modal-title").textContent = "Edit Note"; document.getElementById("note-id").value = note.id; document.getElementById("note-title").value = note.title; document.getElementById("note-content").value = note.content || ""; document.getElementById("note-goal").value = note.goal_id || ""; document.getElementById("note-error").textContent = ""; await populateTasks(note.goal_id); document.getElementById("note-task").value = note.task_id || ""; document.getElementById("delete-note-btn").style.display = "inline-block"; updatePreview(); document.getElementById("note-modal").classList.add("active"); } async function openNewNote() { editingNoteId = null; document.getElementById("note-modal-title").textContent = "New Note"; document.getElementById("note-id").value = ""; document.getElementById("note-title").value = ""; document.getElementById("note-content").value = ""; document.getElementById("note-goal").value = savedGoalId || ""; document.getElementById("note-error").textContent = ""; document.getElementById("delete-note-btn").style.display = "none"; updatePreview(); document.getElementById("note-modal").classList.add("active"); if (savedGoalId) { await populateTasks(savedGoalId); const goal = goals.find(g => g.id === savedGoalId); if (goal && goal.selected_task_id) { document.getElementById("note-task").value = goal.selected_task_id; } } else { document.getElementById("note-task").innerHTML = ''; } } function closeNoteModal() { document.getElementById("note-modal").classList.remove("active"); editingNoteId = null; } async function populateTasks(goalId) { const select = document.getElementById("note-task"); select.innerHTML = ''; if (!goalId) return; try { const tasks = await get(`/api/tasks?goal_id=${goalId}`); select.innerHTML += tasks.map(t => `` ).join(""); } catch (error) { console.error("Failed to load tasks:", error); } } function updatePreview() { const content = document.getElementById("note-content").value; const preview = document.getElementById("note-preview"); try { preview.innerHTML = marked.parse(content || ""); } catch (e) { preview.innerHTML = escapeHtml(content || ""); } } async function handleNoteSubmit(event) { event.preventDefault(); const error = document.getElementById("note-error"); error.textContent = ""; const title = document.getElementById("note-title").value.trim(); if (!title) { error.textContent = "Title is required"; return; } const goalId = parseInt(document.getElementById("note-goal").value) || null; const taskId = parseInt(document.getElementById("note-task").value) || null; const content = document.getElementById("note-content").value; try { if (editingNoteId) { await put(`/api/notes/${editingNoteId}`, { title, content }); } else { await post("/api/notes", { goal_id: goalId, task_id: taskId, title, content }); } closeNoteModal(); await loadNotes(); } catch (err) { error.textContent = err.message; } } async function deleteNote() { if (!editingNoteId) return; if (!confirm("Delete this note?")) return; try { await del(`/api/notes/${editingNoteId}`); closeNoteModal(); await loadNotes(); } catch (error) { console.error("Failed to delete note:", error); } } function formatTime(isoString) { if (!isoString) return ""; const date = new Date(isoString); return date.toLocaleString(); } function escapeHtml(text) { const div = document.createElement('div'); div.textContent = text; return div.innerHTML; } document.addEventListener("DOMContentLoaded", () => { document.getElementById("create-note-btn").addEventListener("click", openNewNote); document.getElementById("note-modal-close").addEventListener("click", closeNoteModal); document.getElementById("note-modal-cancel").addEventListener("click", closeNoteModal); document.getElementById("note-form").addEventListener("submit", handleNoteSubmit); document.getElementById("delete-note-btn").addEventListener("click", deleteNote); document.getElementById("note-content").addEventListener("input", updatePreview); document.getElementById("note-goal").addEventListener("change", async (e) => { const goalId = parseInt(e.target.value) || null; savedGoalId = goalId; await patch("/api/user/selected-goal", { goal_id: goalId }); populateTasks(goalId); }); document.getElementById("filter-goal").addEventListener("change", () => { loadNotes(); }); document.getElementById("filter-search").addEventListener("input", () => { clearTimeout(filterTimer); filterTimer = setTimeout(loadNotes, 300); }); loadGoals(); loadNotes(); });