51 lines
3.7 KiB
JavaScript
51 lines
3.7 KiB
JavaScript
/* Shared record writes for forms and the Life OS terminal. */
|
|
(() => {
|
|
const board = () => {
|
|
const current = sys().tasksBoard;
|
|
return current && Array.isArray(current.goals) && Array.isArray(current.tasks) ? current : { goals: [], tasks: [] };
|
|
};
|
|
const validDate = value => !value || (/^\d{4}-\d{2}-\d{2}$/.test(value) && !Number.isNaN(Date.parse(`${value}T12:00:00`)) && new Date(`${value}T12:00:00`).getFullYear() === Number(value.slice(0, 4)) && new Date(`${value}T12:00:00`).getMonth() + 1 === Number(value.slice(5, 7)) && new Date(`${value}T12:00:00`).getDate() === Number(value.slice(8)));
|
|
const required = (value, max) => {
|
|
const result = String(value || '').trim();
|
|
if (!result || result.length > max) throw new Error('invalidTitle');
|
|
return result;
|
|
};
|
|
const persistBoard = data => { const state = sys(); state.tasksBoard = data; save('lifeos_system', state); renderAll(); };
|
|
window.LifeOSActions = {
|
|
board,
|
|
createGoal({ title, description = '', targetDate = '', showOnDesktop = false }) {
|
|
title = required(title, 100);
|
|
if (!validDate(targetDate)) throw new Error('invalidDate');
|
|
const goal = { id: `goal_${crypto.randomUUID()}`, title, description: String(description).trim().slice(0, 240), targetDate, showOnDesktop: Boolean(targetDate && showOnDesktop), done: false, createdAt: new Date().toISOString() };
|
|
const data = board(); data.goals.unshift(goal); persistBoard(data); return goal;
|
|
},
|
|
createTask({ title, dueDate = '', goalId = '', priority = 'normal' }) {
|
|
title = required(title, 140);
|
|
if (!validDate(dueDate)) throw new Error('invalidDate');
|
|
if (!['normal', 'high', 'low'].includes(priority)) throw new Error('invalidPriority');
|
|
const data = board();
|
|
if (goalId && !data.goals.some(goal => goal.id === goalId)) throw new Error('notFound');
|
|
const task = { id: `task_${crypto.randomUUID()}`, title, dueDate, goalId, priority, done: false, createdAt: new Date().toISOString() };
|
|
data.tasks.push(task); persistBoard(data); return task;
|
|
},
|
|
setTaskDone(id, done) {
|
|
const data = board(), task = data.tasks.find(item => item.id === id);
|
|
if (!task) throw new Error('notFound');
|
|
task.done = done; task.completedAt = done ? new Date().toISOString() : null;
|
|
persistBoard(data); return task;
|
|
},
|
|
createMemory({ date = day(), title, text = '', mood = 'cloudy', energy = 3, tags = [], location = '', taskId = '', replace = false }) {
|
|
title = String(title || '').trim();
|
|
if (title.length > 120) throw new Error('invalidTitle');
|
|
if (!validDate(date) || !date) throw new Error('invalidDate');
|
|
if (!['clear', 'light', 'cloudy', 'rain', 'storm'].includes(mood) || !Number.isInteger(Number(energy)) || Number(energy) < 1 || Number(energy) > 5) throw new Error('invalidValue');
|
|
if (taskId && !board().tasks.some(task => task.id === taskId)) throw new Error('notFound');
|
|
const all = mems(), old = all.find(item => item.date === date);
|
|
if (old && !replace) throw new Error('memoryExists');
|
|
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 };
|
|
save('lifeos_memories', [...all.filter(item => item.date !== date), memory].sort((a, b) => b.date.localeCompare(a.date))); renderAll(); return memory;
|
|
}
|
|
};
|
|
})();
|