- Replace TinyDB (JSON file) with sqlite3 for data persistence - Add schema.py: table creation + data migration from db.json - Rewrite database.py: all CRUD operations use sqlite3 directly - All data retains original IDs via migration script - Remove tinydb dependency from pyproject.toml
153 lines
4.5 KiB
JavaScript
153 lines
4.5 KiB
JavaScript
let goals = [];
|
|
let deleteGoalId = null;
|
|
|
|
async function loadGoals() {
|
|
try {
|
|
goals = await get("/api/goals");
|
|
renderGoals();
|
|
} catch (error) {
|
|
console.error("Failed to load goals:", error);
|
|
}
|
|
}
|
|
|
|
function renderGoals() {
|
|
const container = document.getElementById("goals-list");
|
|
|
|
if (goals.length === 0) {
|
|
container.innerHTML = `
|
|
<div class="empty-state">
|
|
<h3>No goals yet</h3>
|
|
<p>Create your first goal to get started!</p>
|
|
</div>
|
|
`;
|
|
return;
|
|
}
|
|
|
|
container.innerHTML = goals.map(goal => `
|
|
<div class="goal-card" data-goal-id="${goal.id}">
|
|
<div class="goal-info">
|
|
<div class="goal-title">${escapeHtml(goal.title)}</div>
|
|
<div class="goal-status ${goal.activated ? 'active' : 'inactive'}">
|
|
${goal.activated ? 'Active' : 'Inactive'}
|
|
</div>
|
|
</div>
|
|
<div class="goal-actions">
|
|
<button class="toggle-btn ${goal.activated ? 'deactivate' : 'activate'}"
|
|
onclick="toggleGoal(${goal.id})">
|
|
${goal.activated ? 'Deactivate' : 'Activate'}
|
|
</button>
|
|
<button class="edit-btn" onclick="editGoal(${goal.id})">Edit</button>
|
|
<button class="delete-btn" onclick="confirmDelete(${goal.id})">Delete</button>
|
|
</div>
|
|
</div>
|
|
`).join("");
|
|
}
|
|
|
|
function escapeHtml(text) {
|
|
const div = document.createElement('div');
|
|
div.textContent = text;
|
|
return div.innerHTML;
|
|
}
|
|
|
|
function openModal(goal = null) {
|
|
const modal = document.getElementById("goal-modal");
|
|
const modalTitle = document.getElementById("modal-title");
|
|
const goalId = document.getElementById("goal-id");
|
|
const goalTitle = document.getElementById("goal-title");
|
|
const error = document.getElementById("goal-error");
|
|
|
|
error.textContent = "";
|
|
|
|
if (goal) {
|
|
modalTitle.textContent = "Edit Goal";
|
|
goalId.value = goal.id;
|
|
goalTitle.value = goal.title;
|
|
} else {
|
|
modalTitle.textContent = "Create Goal";
|
|
goalId.value = "";
|
|
goalTitle.value = "";
|
|
}
|
|
|
|
modal.classList.add("active");
|
|
}
|
|
|
|
function closeModal() {
|
|
document.getElementById("goal-modal").classList.remove("active");
|
|
}
|
|
|
|
async function handleGoalSubmit(event) {
|
|
event.preventDefault();
|
|
const error = document.getElementById("goal-error");
|
|
error.textContent = "";
|
|
|
|
const goalId = document.getElementById("goal-id").value;
|
|
const title = document.getElementById("goal-title").value.trim();
|
|
|
|
if (!title) {
|
|
error.textContent = "Title is required";
|
|
return;
|
|
}
|
|
|
|
try {
|
|
if (goalId) {
|
|
await put(`/api/goals/${goalId}`, { title });
|
|
} else {
|
|
await post("/api/goals", { title });
|
|
}
|
|
closeModal();
|
|
await loadGoals();
|
|
} catch (err) {
|
|
error.textContent = err.message;
|
|
}
|
|
}
|
|
|
|
async function toggleGoal(goalId) {
|
|
try {
|
|
await patch(`/api/goals/${goalId}/toggle`, {});
|
|
await loadGoals();
|
|
} catch (error) {
|
|
console.error("Failed to toggle goal:", error);
|
|
}
|
|
}
|
|
|
|
function editGoal(goalId) {
|
|
const goal = goals.find(g => g.id === goalId);
|
|
if (goal) {
|
|
openModal(goal);
|
|
}
|
|
}
|
|
|
|
function confirmDelete(goalId) {
|
|
deleteGoalId = goalId;
|
|
document.getElementById("delete-confirm-modal").classList.add("active");
|
|
}
|
|
|
|
function closeDeleteModal() {
|
|
document.getElementById("delete-confirm-modal").classList.remove("active");
|
|
deleteGoalId = null;
|
|
}
|
|
|
|
async function handleDelete() {
|
|
if (!deleteGoalId) return;
|
|
|
|
try {
|
|
await del(`/api/goals/${deleteGoalId}`);
|
|
closeDeleteModal();
|
|
await loadGoals();
|
|
} catch (error) {
|
|
console.error("Failed to delete goal:", error);
|
|
}
|
|
}
|
|
|
|
document.addEventListener("DOMContentLoaded", () => {
|
|
document.getElementById("create-goal-btn").addEventListener("click", () => openModal());
|
|
document.getElementById("modal-close").addEventListener("click", closeModal);
|
|
document.getElementById("modal-cancel").addEventListener("click", closeModal);
|
|
document.getElementById("goal-form").addEventListener("submit", handleGoalSubmit);
|
|
document.getElementById("delete-modal-close").addEventListener("click", closeDeleteModal);
|
|
document.getElementById("cancel-delete").addEventListener("click", closeDeleteModal);
|
|
document.getElementById("confirm-delete").addEventListener("click", handleDelete);
|
|
|
|
loadGoals();
|
|
});
|