Цей день уже минув. Нові записи тут неможливі, але можна переглядати й відмічати виконані.
-
Розклад дня · 00:00 — 23:59
Без часу
Оберіть дату в календарі
Завдання
Прострочені, сьогоднішні та найближчі — в одному списку.
Матриця Ейзенхауера
Розподіліть завдання за важливістю та терміновістю.
Цілі
Розбивайте велику ціль на кроки з датами — кожен крок стає записом у розкладі.
Думки
Ідеї, нотатки, все, що крутиться в голові. Звільніть місце для важливого.
Думки поки зберігаються тільки на цьому пристрої. Щоб вони синхронізувались між телефоном і комп'ютером, онови скрипт Google (кроки 2–3, «Нова версія»).
Ctrl + Enter — зберегти
Аналітика
Налаштування
Тема
Світла, темна або як у системі телефона чи комп'ютера.
Синхронізація
Записи зберігаються у вашій Google Таблиці й однакові на телефоні та комп'ютері. Без інтернету все працює локально і відправляється, щойно з'явиться зв'язок. Стан: —
Встановити як застосунок
Android: відкрийте сайт у Chrome → ⋮ → «Установити застосунок». Комп'ютер: іконка «Установити» в адресному рядку Chrome або Edge.
Завдання
Нова ціль
Кроки цілі додаються як звичайні записи з датою.
→
Синхронізація через Google Sheets
Безкоштовно. Записи зберігаються у твоїй Google Таблиці і доступні з будь-якого пристрою. Без інтернету — працює локально.
Виділи весь старий код у редакторі (Ctrl+A), видали і встав цей:
const SPREADSHEET_ID = '1Y5YIuCSRnYJep9iWx6tpsWN3pzwBc4CoOWEL8awElKA';
const SHEET_NAME = 'Tasks';
const GOALS_SHEET_NAME = 'Цілі';
const NOTES_SHEET_NAME = 'Думки';
function getSheet() {
const ss = SpreadsheetApp.openById(SPREADSHEET_ID);
let sheet = ss.getSheetByName(SHEET_NAME);
if (!sheet) {
sheet = ss.insertSheet(SHEET_NAME);
const h = sheet.getRange(1,1,1,8);
h.setNumberFormat('@');
h.setValues([['id','date','timeStart','timeEnd','text','priority','done','goalId']]);
}
return sheet;
}
function getGoalsSheet() {
const ss = SpreadsheetApp.openById(SPREADSHEET_ID);
let sheet = ss.getSheetByName(GOALS_SHEET_NAME);
if (!sheet) {
sheet = ss.insertSheet(GOALS_SHEET_NAME);
const h = sheet.getRange(1,1,1,5);
h.setNumberFormat('@');
h.setValues([['id','title','start','end','desc']]);
}
return sheet;
}
function getNotesSheet() {
const ss = SpreadsheetApp.openById(SPREADSHEET_ID);
let sheet = ss.getSheetByName(NOTES_SHEET_NAME);
if (!sheet) {
sheet = ss.insertSheet(NOTES_SHEET_NAME);
const h = sheet.getRange(1,1,1,6);
h.setNumberFormat('@');
h.setValues([['id','title','text','pinned','created','updated']]);
}
return sheet;
}
function handle(e) {
try {
let payload = null;
if (e.parameter && e.parameter.payload) payload = e.parameter.payload;
else if (e.postData && e.postData.contents) payload = e.postData.contents;
if (!payload) return out({ok:false, error:'no payload'});
const data = JSON.parse(payload);
const action = data.action;
if (action === 'getAll') {
const sheet = getSheet();
const rows = sheet.getDataRange().getValues();
const tasks = rows.slice(1)
.filter(r => r[0] && String(r[0]).trim() !== '')
.map(r => ({
id: String(r[0]), date: String(r[1]),
timeStart: String(r[2]), timeEnd: String(r[3]),
text: String(r[4]), priority: String(r[5]),
done: String(r[6]) === 'true',
goalId: String(r[7]||'')
}));
return out({ok:true, tasks});
}
if (action === 'save') {
const sheet = getSheet();
const t = data.task;
const rows = sheet.getDataRange().getValues();
let found = -1;
for (let i = 1; i < rows.length; i++) {
if (String(rows[i][0]) === String(t.id)) { found = i+1; break; }
}
const row = [String(t.id),String(t.date),String(t.timeStart||''),
String(t.timeEnd||''),String(t.text),String(t.priority),String(t.done),String(t.goalId||'')];
if (found > 0) {
const r = sheet.getRange(found,1,1,8); r.setNumberFormat('@'); r.setValues([row]);
} else {
const r = sheet.getRange(sheet.getLastRow()+1,1,1,8); r.setNumberFormat('@'); r.setValues([row]);
}
return out({ok:true});
}
if (action === 'delete') {
const sheet = getSheet();
const rows = sheet.getDataRange().getValues();
for (let i = 1; i < rows.length; i++) {
if (String(rows[i][0]) === String(data.id)) { sheet.deleteRow(i+1); break; }
}
return out({ok:true});
}
if (action === 'getAllGoals') {
const sheet = getGoalsSheet();
const rows = sheet.getDataRange().getValues();
const goals = rows.slice(1)
.filter(r => r[0] && String(r[0]).trim() !== '')
.map(r => ({
id: String(r[0]), title: String(r[1]),
start: String(r[2]||''), end: String(r[3]||''),
desc: String(r[4]||'')
}));
return out({ok:true, goals});
}
if (action === 'saveGoal') {
const sheet = getGoalsSheet();
const g = data.goal;
const rows = sheet.getDataRange().getValues();
let found = -1;
for (let i = 1; i < rows.length; i++) {
if (String(rows[i][0]) === String(g.id)) { found = i+1; break; }
}
const row = [String(g.id),String(g.title),String(g.start||''),String(g.end||''),String(g.desc||'')];
if (found > 0) {
const r = sheet.getRange(found,1,1,5); r.setNumberFormat('@'); r.setValues([row]);
} else {
const r = sheet.getRange(sheet.getLastRow()+1,1,1,5); r.setNumberFormat('@'); r.setValues([row]);
}
return out({ok:true});
}
if (action === 'deleteGoal') {
const sheet = getGoalsSheet();
const rows = sheet.getDataRange().getValues();
for (let i = 1; i < rows.length; i++) {
if (String(rows[i][0]) === String(data.id)) { sheet.deleteRow(i+1); break; }
}
return out({ok:true});
}
if (action === 'getAllNotes') {
const sheet = getNotesSheet();
const rows = sheet.getDataRange().getValues();
const notes = rows.slice(1)
.filter(r => r[0] && String(r[0]).trim() !== '')
.map(r => ({
id: String(r[0]), title: String(r[1]||''), text: String(r[2]||''),
pinned: String(r[3]) === 'true',
created: String(r[4]||''), updated: String(r[5]||'')
}));
return out({ok:true, notes});
}
if (action === 'saveNote') {
const sheet = getNotesSheet();
const n = data.note;
const rows = sheet.getDataRange().getValues();
let found = -1;
for (let i = 1; i < rows.length; i++) {
if (String(rows[i][0]) === String(n.id)) { found = i+1; break; }
}
const row = [String(n.id),String(n.title||''),String(n.text||''),String(!!n.pinned),String(n.created||''),String(n.updated||'')];
const r = sheet.getRange(found > 0 ? found : sheet.getLastRow()+1,1,1,6);
r.setNumberFormat('@'); r.setValues([row]);
return out({ok:true});
}
if (action === 'deleteNote') {
const sheet = getNotesSheet();
const rows = sheet.getDataRange().getValues();
for (let i = 1; i < rows.length; i++) {
if (String(rows[i][0]) === String(data.id)) { sheet.deleteRow(i+1); break; }
}
return out({ok:true});
}
return out({ok:false, error:'unknown action'});
} catch(err) { return out({ok:false, error:err.toString()}); }
}
function out(obj) {
return ContentService.createTextOutput(JSON.stringify(obj)).setMimeType(ContentService.MimeType.JSON);
}
function doGet(e) { return handle(e); }
function doPost(e) { return handle(e); }
3Збережи і розгорни
1. Натисни Зберегти (або Ctrl+S) 2. Натисни «Розгорнути» → «Нове розгортання» 3. Тип: «Веб-застосунок» 4. «Виконувати від імені»: Мене 5. «Хто має доступ»: Усі 6. Натисни «Розгорнути» → дозволь доступ у спливаючому вікні 7. Скопіюй URL веб-застосунку
⚠ Якщо синхронізація вже була підключена раніше — не створюй нове розгортання, а онови наявне: «Розгорнути» → «Керувати розгортаннями» → ✎ біля активного розгортання → «Версія»: «Нова версія» → «Розгорнути». URL залишиться той самий.
4Встав URL нижче
URL починається з https://script.google.com/macros/s/...