feat(terminal): add guided goals, tasks, and confirmed deletion
This commit is contained in:
1 parent
8ffeb2897f
commit
78c1ffe1fd
7 files changed
+175
-10
No files matched your search
@@ -34,6 +34,27 @@
|
|||||||
task.done = done; task.completedAt = done ? new Date().toISOString() : null;
|
task.done = done; task.completedAt = done ? new Date().toISOString() : null;
|
||||||
persistBoard(data); return task;
|
persistBoard(data); return task;
|
||||||
},
|
},
|
||||||
|
setGoalDone(id, done) {
|
||||||
|
const data = board(), goal = data.goals.find(item => item.id === id);
|
||||||
|
if (!goal) throw new Error('notFound');
|
||||||
|
goal.done = done; goal.completedAt = done ? new Date().toISOString() : null;
|
||||||
|
persistBoard(data); return goal;
|
||||||
|
},
|
||||||
|
deleteTask(id) {
|
||||||
|
const data = board(), task = data.tasks.find(item => item.id === id);
|
||||||
|
if (!task) throw new Error('notFound');
|
||||||
|
data.tasks = data.tasks.filter(item => item.id !== id);
|
||||||
|
const all = mems();
|
||||||
|
if (all.some(item => item.taskId === id)) save('lifeos_memories', all.map(item => item.taskId === id ? { ...item, taskId: '', updatedAt: new Date().toISOString() } : item));
|
||||||
|
persistBoard(data); return task;
|
||||||
|
},
|
||||||
|
deleteGoal(id) {
|
||||||
|
const data = board(), goal = data.goals.find(item => item.id === id);
|
||||||
|
if (!goal) throw new Error('notFound');
|
||||||
|
data.goals = data.goals.filter(item => item.id !== id);
|
||||||
|
data.tasks.forEach(task => { if (task.goalId === id) task.goalId = ''; });
|
||||||
|
persistBoard(data); return goal;
|
||||||
|
},
|
||||||
createMemory({ date = day(), title, text = '', mood = 'cloudy', energy = 3, tags = [], location = '', taskId = '', replace = false }) {
|
createMemory({ date = day(), title, text = '', mood = 'cloudy', energy = 3, tags = [], location = '', taskId = '', replace = false }) {
|
||||||
title = String(title || '').trim();
|
title = String(title || '').trim();
|
||||||
if (title.length > 120) throw new Error('invalidTitle');
|
if (title.length > 120) throw new Error('invalidTitle');
|
||||||
@@ -45,6 +66,13 @@
|
|||||||
const film = filmFor(mood, Number(energy)), now = new Date().toISOString();
|
const film = filmFor(mood, Number(energy)), now = new Date().toISOString();
|
||||||
const memory = { ...old, id: old?.id || `memory_${crypto.randomUUID()}`, date, title, text: String(text).trim(), mood, energy: Number(energy), tags: [...new Set(tags.map(tag => String(tag).trim()).filter(Boolean))].slice(0, 12), location: String(location).trim().slice(0, 100), taskId, filmTitle: film.filmTitle, filmSubtitle: film.filmSubtitle, filmLine: film.filmLine, createdAt: old?.createdAt || now, updatedAt: now };
|
const memory = { ...old, id: old?.id || `memory_${crypto.randomUUID()}`, date, title, text: String(text).trim(), mood, energy: Number(energy), tags: [...new Set(tags.map(tag => String(tag).trim()).filter(Boolean))].slice(0, 12), location: String(location).trim().slice(0, 100), taskId, filmTitle: film.filmTitle, filmSubtitle: film.filmSubtitle, filmLine: film.filmLine, createdAt: old?.createdAt || now, updatedAt: now };
|
||||||
save('lifeos_memories', [...all.filter(item => item.date !== date), memory].sort((a, b) => b.date.localeCompare(a.date))); renderAll(); return memory;
|
save('lifeos_memories', [...all.filter(item => item.date !== date), memory].sort((a, b) => b.date.localeCompare(a.date))); renderAll(); return memory;
|
||||||
|
},
|
||||||
|
deleteMemory(id) {
|
||||||
|
const all = mems(), memory = all.find(item => item.id === id);
|
||||||
|
if (!memory) throw new Error('notFound');
|
||||||
|
save('lifeos_memories', all.filter(item => item.id !== id));
|
||||||
|
save('lifeos_museum', museum().filter(item => item.memoryId !== id));
|
||||||
|
renderAll(); return memory;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
})();
|
})();
|
||||||
@@ -60,7 +60,7 @@
|
|||||||
window.saveMemoryEdit = (event,id) => { event.preventDefault(); const all=mems(), memory=all.find(item=>item.id===id); if(!memory)return; memory.title=document.querySelector('#memory-edit-title').value.trim(); memory.text=document.querySelector('#memory-edit-note').value.trim(); memory.tags=tagList(document.querySelector('#memory-tags').value); memory.location=document.querySelector('#memory-place').value.trim(); memory.taskId=document.querySelector('#memory-task').value||''; memory.updatedAt=new Date().toISOString(); save('lifeos_memories',all); closeModal(); renderAll(); detail(id); };
|
window.saveMemoryEdit = (event,id) => { event.preventDefault(); const all=mems(), memory=all.find(item=>item.id===id); if(!memory)return; memory.title=document.querySelector('#memory-edit-title').value.trim(); memory.text=document.querySelector('#memory-edit-note').value.trim(); memory.tags=tagList(document.querySelector('#memory-tags').value); memory.location=document.querySelector('#memory-place').value.trim(); memory.taskId=document.querySelector('#memory-task').value||''; memory.updatedAt=new Date().toISOString(); save('lifeos_memories',all); closeModal(); renderAll(); detail(id); };
|
||||||
window.preserve = id => { const all = museum(), alreadyPreserved = all.some(item => item.memoryId === id); if (alreadyPreserved) { save('lifeos_museum', all.filter(item => item.memoryId !== id)); toast(t('timeline.unpreserved')); } else { save('lifeos_museum', [...all, { id: `artifact_${crypto.randomUUID()}`, memoryId: id, preservedAt: new Date().toISOString() }]); toast(t('timeline.added')); } closeModal(); renderAll(); };
|
window.preserve = id => { const all = museum(), alreadyPreserved = all.some(item => item.memoryId === id); if (alreadyPreserved) { save('lifeos_museum', all.filter(item => item.memoryId !== id)); toast(t('timeline.unpreserved')); } else { save('lifeos_museum', [...all, { id: `artifact_${crypto.randomUUID()}`, memoryId: id, preservedAt: new Date().toISOString() }]); toast(t('timeline.added')); } closeModal(); renderAll(); };
|
||||||
window.confirmDelete = id => { rootModal().innerHTML = `<div class="modal"><div class="modalbox"><div class="eyebrow">${t('timeline.confirmDelete')}</div><h2>${t('timeline.remove')}</h2><p class="sub">${t('timeline.irreversible')}</p><div class="actions"><button class="danger" onclick="deleteMemory('${id}')">${t('timeline.deleteMemory')}</button><button class="secondary" onclick="modal.innerHTML=''">${t('common.cancel')}</button></div></div></div>`; };
|
window.confirmDelete = id => { rootModal().innerHTML = `<div class="modal"><div class="modalbox"><div class="eyebrow">${t('timeline.confirmDelete')}</div><h2>${t('timeline.remove')}</h2><p class="sub">${t('timeline.irreversible')}</p><div class="actions"><button class="danger" onclick="deleteMemory('${id}')">${t('timeline.deleteMemory')}</button><button class="secondary" onclick="modal.innerHTML=''">${t('common.cancel')}</button></div></div></div>`; };
|
||||||
window.deleteMemory = id => { save('lifeos_memories', mems().filter(item => item.id !== id)); save('lifeos_museum', museum().filter(item => item.memoryId !== id)); rootModal().innerHTML = ''; renderAll(); toast(t('timeline.removed')); };
|
window.deleteMemory = id => { window.LifeOSActions.deleteMemory(id); rootModal().innerHTML = ''; toast(t('timeline.removed')); };
|
||||||
let insightView = 'week', insightOffset = 0;
|
let insightView = 'week', insightOffset = 0;
|
||||||
const reportDateKey = value => `${value.getFullYear()}-${String(value.getMonth() + 1).padStart(2, '0')}-${String(value.getDate()).padStart(2, '0')}`;
|
const reportDateKey = value => `${value.getFullYear()}-${String(value.getMonth() + 1).padStart(2, '0')}-${String(value.getDate()).padStart(2, '0')}`;
|
||||||
const reportDayNumber = value => Date.UTC(value.getFullYear(), value.getMonth(), value.getDate()) / 864e5;
|
const reportDayNumber = value => Date.UTC(value.getFullYear(), value.getMonth(), value.getDate()) / 864e5;
|
||||||
|
|||||||
+18
-1
@@ -564,7 +564,7 @@
|
|||||||
"invalidDate": "Use a real date in YYYY-MM-DD format, today, or tomorrow.",
|
"invalidDate": "Use a real date in YYYY-MM-DD format, today, or tomorrow.",
|
||||||
"invalidPriority": "Priority must be high, normal, or low.",
|
"invalidPriority": "Priority must be high, normal, or low.",
|
||||||
"invalidValue": "Weather or energy value is invalid.",
|
"invalidValue": "Weather or energy value is invalid.",
|
||||||
"notFound": "The referenced task or goal was not found.",
|
"notFound": "The referenced record was not found.",
|
||||||
"ambiguous": "More than one item has this title. Use its ID instead.",
|
"ambiguous": "More than one item has this title. Use its ID instead.",
|
||||||
"memoryExists": "A memory already exists for that date. Edit it in Today or Timeline.",
|
"memoryExists": "A memory already exists for that date. Edit it in Today or Timeline.",
|
||||||
"invalidOption": "Unknown option or missing option value. Type help.",
|
"invalidOption": "Unknown option or missing option value. Type help.",
|
||||||
@@ -584,6 +584,23 @@
|
|||||||
"location": "Place or scene? [{value}]",
|
"location": "Place or scene? [{value}]",
|
||||||
"taskId": "Related task? [{value}] — enter a list number, title, or ID\n{tasks}",
|
"taskId": "Related task? [{value}] — enter a list number, title, or ID\n{tasks}",
|
||||||
"controls": "Enter keeps the displayed value; back/cancel controls the flow. Use - to clear an optional field."
|
"controls": "Enter keeps the displayed value; back/cancel controls the flow. Use - to clear an optional field."
|
||||||
|
},
|
||||||
|
"goalCompleted": "Goal completed: {title}",
|
||||||
|
"goalReopened": "Goal reopened: {title}",
|
||||||
|
"confirmAnswer": "Type yes to confirm or no to cancel.",
|
||||||
|
"flow": {
|
||||||
|
"title": "Title? (required; current: {value})",
|
||||||
|
"description": "Description? [{value}] (optional)",
|
||||||
|
"targetDate": "Target date? [{value}] — YYYY-MM-DD, today, or tomorrow",
|
||||||
|
"showOnDesktop": "Show countdown on desktop? [{value}] — yes/no",
|
||||||
|
"dueDate": "Due date? [{value}] — YYYY-MM-DD, today, or tomorrow",
|
||||||
|
"goalId": "Link to a goal? [{value}] — number, title, or ID\n{items}",
|
||||||
|
"priority": "Priority? [{value}] — high / normal / low",
|
||||||
|
"pick": "Choose a {kind} by number, title, date, or ID:",
|
||||||
|
"confirmDelete": "Delete {kind}: {title}? Type yes to delete or no to cancel.",
|
||||||
|
"confirmCreate": "Create {kind}: {title}? Type yes to save or no to cancel.",
|
||||||
|
"cancelled": "Operation cancelled.",
|
||||||
|
"deleted": "Deleted {kind}: {title}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Loaded 3 of 7 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user