document.addEventListener("DOMContentLoaded", () => { console.log("OrgMyLife frontend initialized."); // Fetch Dashboard Data fetch("/api/dashboard") .then(res => res.json()) .then(data => { console.log("Dashboard Data:", data); // Populate Digest document.getElementById("digest-content").textContent = data.digest; // Populate Suggestions const suggestionsContainer = document.getElementById("suggestions-container"); if (data.suggestions && data.suggestions.length > 0) { suggestionsContainer.innerHTML = data.suggestions.map(s => { const severityClass = s.severity === 'high' ? 'suggestion-high' : s.severity === 'medium' ? 'suggestion-medium' : 'suggestion-low'; return `
${data.health === 'growing' ? '๐ Backlog growing' : data.health === 'shrinking' ? '๐ Backlog shrinking' : 'โก๏ธ Backlog stable'}
Net change: ${data.net_change > 0 ? '+' : ''}${data.net_change} tasks
${data.events_attended} calendar events this week
`; document.getElementById("retro-priorities").innerHTML = Object.entries(data.priority_breakdown) .map(([label, count]) => `Loading calls...
"; fetch("/api/calls") .then(res => res.json()) .then(data => { if (!data.calls || data.calls.length === 0) { callsList.innerHTML = "No pending calls.
"; return; } callsList.innerHTML = data.calls.map(c => { const phoneDisplay = c.phone_number ? `${c.phone_number}` : 'No number'; const priorityLabels = { 1: 'Very High', 2: 'High', 3: 'Medium', 4: 'Low' }; return `Error loading calls.
"; }); } // --- Sort Buttons --- document.querySelectorAll(".sort-btn").forEach(btn => { btn.addEventListener("click", () => { document.querySelectorAll(".sort-btn").forEach(b => b.classList.remove("active")); btn.classList.add("active"); currentSort = btn.dataset.sort; loadAllTasks(); }); }); // --- Filter Dropdowns --- const filterOrigin = document.getElementById("filter-origin"); const filterAgent = document.getElementById("filter-agent"); const filterLabel = document.getElementById("filter-label"); filterOrigin?.addEventListener("change", () => loadAllTasks()); filterAgent?.addEventListener("change", () => loadAllTasks()); filterLabel?.addEventListener("change", () => loadAllTasks()); // --- Edit Form State --- const addTaskForm = document.getElementById("add-task-form"); const formHeading = document.getElementById("form-heading"); const formSubmitBtn = document.getElementById("form-submit-btn"); const formCancelBtn = document.getElementById("form-cancel-btn"); const taskEditId = document.getElementById("task-edit-id"); const taskTitleInput = document.getElementById("task-title"); const taskDescInput = document.getElementById("task-desc"); const taskPrioritySelect = document.getElementById("task-priority"); const taskDueInput = document.getElementById("task-due"); const taskLabelsInput = document.getElementById("task-labels"); function enterEditMode(task) { taskEditId.value = task.id; taskTitleInput.value = task.title; taskDescInput.value = task.description || ""; taskPrioritySelect.value = task.priority_value; taskDueInput.value = task.due_date || ""; if (taskLabelsInput) taskLabelsInput.value = task.labels || ""; formHeading.textContent = "Edit Task"; formSubmitBtn.textContent = "Save"; formCancelBtn.style.display = "inline-block"; taskTitleInput.focus(); } function exitEditMode() { taskEditId.value = ""; addTaskForm.reset(); formHeading.textContent = "Add New Task"; formSubmitBtn.textContent = "Add"; formCancelBtn.style.display = "none"; } formCancelBtn?.addEventListener("click", exitEditMode); // --- All Tasks Logic --- let currentSort = ""; function loadAllTasks() { const params = new URLSearchParams(); if (currentSort) params.set("sort", currentSort); const originVal = document.getElementById("filter-origin")?.value; const agentVal = document.getElementById("filter-agent")?.value; const labelVal = document.getElementById("filter-label")?.value; if (originVal) params.set("filter_origin", originVal); if (agentVal) params.set("filter_agent", agentVal); if (labelVal) params.set("filter_label", labelVal); const url = `/api/tasks${params.toString() ? '?' + params.toString() : ''}`; fetch(url) .then(res => res.json()) .then(data => { const allTasksList = document.getElementById("all-tasks-list"); if (data.tasks.length === 0) { allTasksList.innerHTML = "${t.description}
` : ''}No tasks
'; }); // Move handlers document.querySelectorAll(".kanban-move").forEach(btn => { btn.addEventListener("click", () => { const taskId = btn.dataset.id; const dir = btn.dataset.dir; const card = btn.closest(".kanban-card"); const column = card.closest(".kanban-column"); const currentStatus = column.dataset.status; const order = ["backlog", "ready", "in_progress", "blocked", "review", "done"]; const idx = order.indexOf(currentStatus); const newStatus = dir === "right" ? order[idx + 1] : order[idx - 1]; if (!newStatus) return; fetch(`/api/projects/${taskId}`, { method: "PUT", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ status: newStatus }) }) .then(res => { if (!res.ok) { console.error("Failed to move project task:", res.status, res.statusText); return res.json().then(d => { throw new Error(d.detail || "Move failed"); }); } return res.json(); }) .then(() => loadProjects()) .catch(err => { console.error("Error moving project task:", err); alert("Failed to move task: " + err.message); }); }); }); // Delete handlers document.querySelectorAll(".btn-delete-project").forEach(btn => { btn.addEventListener("click", () => { if (!confirm("Delete this project task?")) return; fetch(`/api/projects/${btn.dataset.id}`, { method: "DELETE" }) .then(res => res.json()) .then(() => loadProjects()) .catch(err => console.error("Error deleting project task:", err)); }); }); }) .catch(err => console.error("Error loading projects:", err)); } // Add project task form const addProjectForm = document.getElementById("add-project-task-form"); addProjectForm?.addEventListener("submit", (e) => { e.preventDefault(); const title = document.getElementById("pt-title").value; const repo = document.getElementById("pt-repo").value; fetch("/api/projects/create", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ title, repo }) }) .then(res => res.json()) .then(data => { if (data.status === "success") { addProjectForm.reset(); loadProjects(); } }) .catch(err => console.error("Error adding project task:", err)); }); // --- Add Call Form --- const addCallForm = document.getElementById("add-call-form"); addCallForm?.addEventListener("submit", (e) => { e.preventDefault(); const person = document.getElementById("call-person").value; const reason = document.getElementById("call-reason").value; const phone_number = document.getElementById("call-phone").value || null; const priority = parseInt(document.getElementById("call-priority").value, 10); fetch("/api/calls", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ person, reason, phone_number, priority }) }) .then(res => { if (!res.ok) throw new Error("Failed to add call"); return res.json(); }) .then(() => { addCallForm.reset(); loadCalls(); }) .catch(err => { console.error("Error adding call:", err); alert("Failed to add call."); }); }); // --- Conflict Resolution Modal --- function showConflictModal(taskId) { // Remove any existing modal document.querySelectorAll(".conflict-modal-overlay").forEach(m => m.remove()); fetch("/api/tasks/conflicts") .then(res => res.json()) .then(data => { const conflict = data.conflicts.find(c => c.id == taskId); if (!conflict) { alert("No conflict data found for this task."); return; } const remote = conflict.conflict_data || {}; const overlay = document.createElement("div"); overlay.className = "conflict-modal-overlay"; overlay.innerHTML = `This task was modified both locally and remotely. Choose which version to keep.