/* Google Apps Script اربط هذا المشروع بملف Google Sheet الذي يحتوي على صفحة "المهام". ترتيب الأعمدة المعتمد: A = اليوم B = المهمة C = القسم D = الحالة إذا كان اسم الصفحة مختلفًا، غيّر SHEET_NAME فقط. */ const SHEET_NAME = "المهام"; function doGet() { return json_({ ok: true, message: "Tasks API is running" }); } function doPost(e) { try { const body = JSON.parse((e && e.postData && e.postData.contents) || "{}"); const action = String(body.action || "").trim(); if (action === "list") return listTasks_(); if (action === "add") return addTask_(body); if (action === "update") return updateTask_(body); if (action === "delete") return deleteTask_(body); return json_({ok:false, message:"طلب غير معروف"}); } catch (err) { return json_({ok:false, message: err.message || "حدث خطأ غير متوقع"}); } } function sheet_() { const ss = SpreadsheetApp.getActiveSpreadsheet(); const sh = ss.getSheetByName(SHEET_NAME); if (!sh) throw new Error('لم يتم العثور على صفحة باسم "' + SHEET_NAME + '"'); return sh; } function listTasks_() { const sh = sheet_(); const lastRow = sh.getLastRow(); if (lastRow < 2) return json_({ok:true, tasks:[]}); const values = sh.getRange(2, 1, lastRow - 1, 4).getValues(); const tz = Session.getScriptTimeZone() || "Asia/Riyadh"; const tasks = values .map((r, i) => ({ row: i + 2, date: normalizeDate_(r[0], tz), task: String(r[1] || "").trim(), department: String(r[2] || "").trim(), status: String(r[3] || "").trim() })) .filter(x => x.date || x.task || x.department || x.status) .sort((a,b) => { if (a.date === b.date) return b.row - a.row; return a.date < b.date ? 1 : -1; }); return json_({ok:true, tasks:tasks}); } function addTask_(body) { const date = String(body.date || "").trim(); const task = String(body.task || "").trim(); const department = String(body.department || "").trim(); const status = validStatus_(body.status); if (!/^\d{4}-\d{2}-\d{2}$/.test(date)) throw new Error("التاريخ غير صحيح"); if (!task) throw new Error("اكتب المهمة"); if (!department) throw new Error("اكتب القسم"); if (task.length > 1000) throw new Error("نص المهمة طويل جدًا"); if (department.length > 100) throw new Error("اسم القسم طويل جدًا"); const sh = sheet_(); const lock = LockService.getDocumentLock(); lock.waitLock(10000); try { const parts = date.split("-").map(Number); const dateObj = new Date(parts[0], parts[1] - 1, parts[2], 12, 0, 0); sh.appendRow([dateObj, task, department, status]); const row = sh.getLastRow(); sh.getRange(row, 1).setNumberFormat("yyyy-mm-dd"); SpreadsheetApp.flush(); } finally { lock.releaseLock(); } return json_({ok:true, message:"تم حفظ المهمة بنجاح"}); } function updateTask_(body) { const row = Number(body.row); const date = String(body.date || "").trim(); const task = String(body.task || "").trim(); const department = String(body.department || "").trim(); const status = validStatus_(body.status); if (!Number.isInteger(row) || row < 2) throw new Error("رقم السجل غير صحيح"); if (!/^\d{4}-\d{2}-\d{2}$/.test(date)) throw new Error("التاريخ غير صحيح"); if (!task) throw new Error("اكتب المهمة"); if (!department) throw new Error("اختر القسم"); const sh = sheet_(); if (row > sh.getLastRow()) throw new Error("المهمة لم تعد موجودة"); const parts = date.split("-").map(Number); const dateObj = new Date(parts[0], parts[1] - 1, parts[2], 12, 0, 0); const lock = LockService.getDocumentLock(); lock.waitLock(10000); try { sh.getRange(row, 1, 1, 4).setValues([[dateObj, task, department, status]]); sh.getRange(row, 1).setNumberFormat("yyyy-mm-dd"); SpreadsheetApp.flush(); } finally { lock.releaseLock(); } return json_({ok:true, message:"تم تعديل المهمة"}); } function deleteTask_(body) { const row = Number(body.row); if (!Number.isInteger(row) || row < 2) throw new Error("رقم السجل غير صحيح"); const sh = sheet_(); if (row > sh.getLastRow()) throw new Error("المهمة لم تعد موجودة"); const lock = LockService.getDocumentLock(); lock.waitLock(10000); try { sh.deleteRow(row); SpreadsheetApp.flush(); } finally { lock.releaseLock(); } return json_({ok:true, message:"تم حذف المهمة"}); } function validStatus_(value) { const status = String(value || "").trim(); const allowed = ["", "مخطط لها", "جاري العمل", "تم الإنجاز"]; if (!allowed.includes(status)) throw new Error("حالة المهمة غير صحيحة"); return status; } function normalizeDate_(value, tz) { if (value instanceof Date && !isNaN(value)) { return Utilities.formatDate(value, tz, "yyyy-MM-dd"); } const s = String(value || "").trim(); if (!s) return ""; // يدعم القيم النصية yyyy-mm-dd كما هي if (/^\d{4}-\d{2}-\d{2}$/.test(s)) return s; const d = new Date(s); if (!isNaN(d)) return Utilities.formatDate(d, tz, "yyyy-MM-dd"); return s; } function json_(obj) { return ContentService .createTextOutput(JSON.stringify(obj)) .setMimeType(ContentService.MimeType.JSON); }