diff --git a/app.js b/app.js
index 40aad86..4d480b2 100644
--- a/app.js
+++ b/app.js
@@ -1,8 +1,11 @@
// --- Basic Todo Model ---
const STORAGE_KEY = 'todos-v1';
const CATEGORY_KEY = 'todo-categories';
-let todos = [];
-let categories = [];
+window.todos = window.todos || [];
+window.categories = window.categories || [];
+// For backward compatibility, assign to local variables as references
+let todos = window.todos;
+let categories = window.categories;
function loadTodos() {
todos = JSON.parse(localStorage.getItem(STORAGE_KEY) || '[]');
@@ -217,15 +220,53 @@ function renderTodos() {
th.addEventListener('click', () => sortTodos(th.dataset.col));
});
// Add modal editing listeners
- document.querySelectorAll('.todos-table td[data-editable="true"]').forEach(td => {
+ // Attach popup modal to ID cell only
+ document.querySelectorAll('.todo-id-cell').forEach(td => {
+ console.log('[TodoApp] Attaching click handler to ID cell:', td);
td.addEventListener('click', function(e) {
+ const todoId = +td.dataset.id;
const rowIdx = +td.dataset.row;
- const key = td.dataset.key;
- if (window.showEditTodoModal) {
- window.showEditTodoModal(rowIdx, key);
+ console.log('[TodoApp] ID cell clicked. todoId:', todoId, 'rowIdx:', rowIdx);
+ console.log('[TodoApp] window.showEditTodoModal available:', typeof window.showEditTodoModal);
+ console.log('[TodoApp] window.showEditTodoModal value:', window.showEditTodoModal);
+
+ // Find the todo by ID instead of row index
+ const todoIndex = todos.findIndex(t => t.id === todoId);
+ console.log('[TodoApp] Found todo at index:', todoIndex);
+
+ if (todoIndex === -1) {
+ console.error('[TodoApp] Todo not found with ID:', todoId);
+ alert('Error: Todo not found. Please refresh the page.');
+ return;
}
+
+ // Force set the function if needed
+ if (typeof showEditTodoModal === 'function' && typeof window.showEditTodoModal !== 'function') {
+ console.log('[TodoApp] Setting window.showEditTodoModal from local showEditTodoModal');
+ window.showEditTodoModal = showEditTodoModal;
+ }
+
+ if (typeof window.showEditTodoModal === 'function') {
+ try {
+ console.log('[TodoApp] Calling window.showEditTodoModal with todoIndex:', todoIndex);
+ window.showEditTodoModal(todoIndex, 'id');
+ console.log('[TodoApp] Editing via popup modal.');
+ } catch (err) {
+ console.warn('[TodoApp] Popup modal error:', err);
+ console.error(err);
+ }
+ } else {
+ console.error('[TodoApp] showEditTodoModal is not a function:', window.showEditTodoModal);
+ alert('Error: Edit modal function not available. Please refresh the page.');
+ }
+ e.stopPropagation();
});
});
+ // Add inline editing to all other editable cells
+ document.querySelectorAll('.todos-table td[data-editable="true"]').forEach(td => {
+ if (td.classList.contains('todo-id-cell')) return; // skip ID, handled above
+ td.addEventListener('click', handleCellEdit);
+ });
}
function renderEditableCell(todo, key, rowIdx) {
@@ -243,7 +284,8 @@ function renderEditableCell(todo, key, rowIdx) {
if (key === 'coms' && value) value = `Link`;
if (key === 'link' && value) value = `Link`;
if (key === 'status') return `
${todo.status || ''}
`;
- if (nonEditable.includes(key)) return `
${todo[key]}
`;
+ if (key === 'id') return `
${todo[key]}
`;
+if (nonEditable.includes(key)) return `
${todo[key]}
`;
return `
${value}
`;
}
diff --git a/modal.js b/modal.js
index 5b0e8b0..38d668d 100644
--- a/modal.js
+++ b/modal.js
@@ -2,18 +2,103 @@
// Create and show a modal with the Add/Edit Todo form, focusing the relevant field
function showEditTodoModal(rowIdx, key) {
- // Find the todo
- const todo = window.todos[rowIdx];
- if (!todo) return;
+ console.log('[Modal] Opening modal for rowIdx:', rowIdx, 'key:', key);
+ console.log('[Modal] Initial window.todos:', window.todos ? window.todos.length : 'null/undefined');
+ console.log('[Modal] Initial todos:', todos ? todos.length : 'null/undefined');
+
+ // Ensure todos are loaded from localStorage if arrays are empty
+ if (!window.todos || window.todos.length === 0 || !todos || todos.length === 0) {
+ console.log('[Modal] Loading todos from localStorage at modal open');
+ const storedTodos = localStorage.getItem('todos-v1');
+ if (storedTodos) {
+ try {
+ window.todos = JSON.parse(storedTodos);
+ todos = window.todos; // Update local reference as well
+ console.log('[Modal] Successfully loaded todos from localStorage:', window.todos.length);
+ } catch (e) {
+ console.error('[Modal] Error parsing todos from localStorage:', e);
+ }
+ } else {
+ console.error('[Modal] No todos found in localStorage under key "todos-v1"');
+ }
+ }
+
+ // If called with an index, get the ID from that todo
+ let todoId = null;
+ if (typeof rowIdx === 'number' || !isNaN(Number(rowIdx))) {
+ // First look it up by index if number is within range
+ const numericIdx = Number(rowIdx);
+ if (window.todos && numericIdx >= 0 && numericIdx < window.todos.length) {
+ todoId = window.todos[numericIdx].id;
+ console.log('[Modal] Found todo ID by index in window.todos:', todoId);
+ }
+ // If index doesn't work, use the number directly as ID
+ else {
+ todoId = numericIdx;
+ console.log('[Modal] Using rowIdx directly as todoId:', todoId);
+ }
+ } else if (rowIdx && typeof rowIdx === 'object' && rowIdx.id) {
+ // Handle case where entire todo object is passed
+ todoId = rowIdx.id;
+ console.log('[Modal] Using object.id as todoId:', todoId);
+ } else if (rowIdx) {
+ // Any other non-null value, try to use directly
+ todoId = rowIdx;
+ console.log('[Modal] Using rowIdx as todoId:', todoId);
+ }
- // Build modal overlay
+ // Remove any existing modal
+ document.querySelectorAll('.modal').forEach(m => m.remove());
+
+ // Always find the todo by ID
+ let todo = null;
+ if (todoId !== null) {
+ console.log('[Modal] Looking for todo with ID:', todoId, 'Type:', typeof todoId);
+ // Debug all todos in the array to see what IDs we have
+ if (window.todos && window.todos.length > 0) {
+ window.todos.forEach((t, idx) => {
+ if (t) console.log(`[Modal] Todo ${idx}: ID=${t.id} (${typeof t.id})`);
+ });
+ }
+
+ // More flexible ID comparison logic
+ const findTodoById = (arr, id) => {
+ if (!arr || !Array.isArray(arr)) return null;
+ return arr.find(t => {
+ if (!t) return false;
+ const idStr = String(id).trim();
+ const tIdStr = String(t.id).trim();
+ const idNum = Number(id);
+ const tIdNum = Number(t.id);
+
+ return t.id === id || // Direct comparison
+ tIdStr === idStr || // String comparison
+ (!isNaN(idNum) && !isNaN(tIdNum) && idNum === tIdNum); // Number comparison
+ });
+ };
+
+ todo = findTodoById(window.todos, todoId);
+ if (todo) console.log('[Modal] Found todo by ID in window.todos:', todo);
+
+ if (!todo && todos !== window.todos) {
+ todo = findTodoById(todos, todoId);
+ if (todo) console.log('[Modal] Found todo by ID in local todos:', todo);
+ }
+ }
+
+ if (!todo && todoId !== null) {
+ console.error('[Modal] Todo not found for ID:', todoId);
+ alert('Error: Could not find the todo to edit. Please refresh the page and try again.');
+ return;
+ }
+
+ // Build modal overlay (same structure as password modal)
let modal = document.createElement('div');
modal.className = 'modal';
modal.style.zIndex = 2000;
modal.innerHTML = `
-
-
-
Edit Todo
+
+
Edit Todo
+
`;
- // Close modal on overlay click or close button
+ // Insert modal into the DOM before attaching event listeners
+ document.body.appendChild(modal);
+ document.body.style.overflow = 'hidden';
+
+ // Close modal on overlay click or close/cancel button
function closeModal() {
modal.remove();
document.body.style.overflow = '';
}
- modal.querySelector('.modal-close-btn').onclick = closeModal;
modal.onclick = (e) => { if (e.target === modal) closeModal(); };
+ modal.querySelector('#modal-close-btn').onclick = closeModal;
// Save changes
- modal.querySelector('#modal-todo-form').onsubmit = function(evt) {
+ const modalForm = modal.querySelector('#modal-todo-form');
+ modalForm.onsubmit = function(evt) {
+ console.log('[Modal] Save button clicked');
evt.preventDefault();
+
+ // Get form data and create an updated todo object
const formData = new FormData(this);
const updated = {};
- for (let [k, v] of formData.entries()) updated[k] = v;
+ for (let [k, v] of formData.entries()) {
+ if (k !== 'id') updated[k] = v; // Never overwrite the ID
+ }
+
// Type conversions
updated.urgent = parseInt(updated.urgent) || 1;
updated.importance = parseInt(updated.importance) || 1;
updated.prio = updated.urgent * updated.importance;
updated.timeEstimation = parseInt(updated.timeEstimation) || 0;
updated.actualTime = parseInt(updated.actualTime) || 0;
- window.todos[rowIdx] = { ...window.todos[rowIdx], ...updated };
- window.localStorage.setItem('todos-v1', JSON.stringify(window.todos));
+
+ // Get the original ID from the todo we found at modal open time
+ let todoId = todo.id;
+ console.log('[Modal] Updating todo with ID:', todoId);
+
+ // Create a new complete todo object with the updates
+ const updatedTodo = { ...todo, ...updated, id: todoId };
+
+ // DIRECT APPROACH: Simply replace the entire todos array
+ try {
+ // Get the todos directly from localStorage
+ let allTodos = [];
+ try {
+ const stored = localStorage.getItem('todos-v1');
+ if (stored) {
+ allTodos = JSON.parse(stored);
+ console.log('[Modal] Successfully loaded', allTodos.length, 'todos from localStorage');
+ }
+ } catch (err) {
+ console.error('[Modal] Error loading todos from localStorage:', err);
+ }
+
+ // If we couldn't load from localStorage, use window.todos
+ if (!allTodos || !allTodos.length) {
+ allTodos = Array.isArray(window.todos) ? [...window.todos] : [];
+ console.log('[Modal] Using window.todos instead, length:', allTodos.length);
+ }
+
+ // Find the todo by ID
+ let found = false;
+ for (let i = 0; i < allTodos.length; i++) {
+ if (allTodos[i] && String(allTodos[i].id) === String(todoId)) {
+ // Replace the todo with our updated version
+ allTodos[i] = updatedTodo;
+ found = true;
+ console.log('[Modal] Updated todo at index', i);
+ break;
+ }
+ }
+
+ if (!found) {
+ console.warn('[Modal] Todo with ID', todoId, 'not found in array, adding it');
+ allTodos.push(updatedTodo);
+ }
+
+ // Save the updated todos back to localStorage
+ localStorage.setItem('todos-v1', JSON.stringify(allTodos));
+ console.log('[Modal] Saved todos to localStorage');
+
+ // Update both global and local todos arrays
+ window.todos = allTodos;
+ todos = allTodos;
+
+ // Update the UI
+ if (typeof renderTodos === 'function') {
+ renderTodos();
+ console.log('[Modal] UI updated with new todos');
+ }
+
+ // Close the modal
+ closeModal();
+ return;
+ } catch (err) {
+ console.error('[Modal] Error saving todo:', err);
+ const errMsg = modal.querySelector('#modal-error-msg');
+ if (errMsg) {
+ errMsg.textContent = 'Error saving todo: ' + err.message;
+ errMsg.style.display = 'block';
+ }
+ }
closeModal();
- window.renderTodos && window.renderTodos();
};
+ // Debug: log if handler is attached
+ console.log('[Modal] onsubmit handler attached to modal-todo-form');
+
// Insert and focus correct field
document.body.appendChild(modal);
document.body.style.overflow = 'hidden';
@@ -127,7 +297,11 @@ function showEditTodoModal(rowIdx, key) {
}, 80);
}
-// Attach to window for use in app.js
+// Immediately attach to window for use in app.js
+window.showEditTodoModal = showEditTodoModal;
+
+// Also attach on DOMContentLoaded for safety
document.addEventListener('DOMContentLoaded', () => {
window.showEditTodoModal = showEditTodoModal;
+ console.log('[Modal] showEditTodoModal attached to window object');
});