n / Code.gs
NMA2030's picture
Upload 4 files
52c791b verified
Raw History Blame Contribute Delete
5.58 kB
/*
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);
}