Yuyao Huang a8fe6ed7b3 Migrate from TinyDB to SQLite
- 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
2026-05-08 16:18:26 +08:00

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();
});