Download Code.gs from NMA2030/n: direct link, hf CLI and curl.
- Browser
- Download file 5.58 kB
-
https://huggingface.co/spaces/NMA2030/n/resolve/main/Code.gs
- Command line
-
hf download hf://spaces/NMA2030/n/Code.gs
-
curl -L -o Code.gs https://huggingface.co/spaces/NMA2030/n/resolve/main/Code.gs
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); | |
| } | |