Logs agent activities to the unified central sheet and their personal sheet.
*/
function logAction(action, aux, status, timerData, reason, shiftTimes, phoneNumber) {
var lock = LockService.getScriptLock();
try {
lock.waitLock(LOCK_TIMEOUT_MS);
var ss = SpreadsheetApp.openById(SPREADSHEET_ID);
var userEmail = Session.getActiveUser().getEmail();
var sheetName = userEmail.split('@')[0].replace(/[^a-zA-Z0-9]/g, "");
var agentName = sheetName.split('').map(word => word.charAt(0).toUpperCase() + word.slice(1)).join(' ');
var now = new Date();
// Adjust timestamp for historical sync or completion parameters
if (shiftTimes && shiftTimes.end && (action === "End Shift" || action === "Force Sync" || action === "Manual Sync" || action.includes("Sync"))) {
try {
var parts = shiftTimes.end.split(" ");
var timeParts = parts[0].split(":");
var tempDate = new Date();
var hrs = parseInt(timeParts[0], 10);
var mins = parseInt(timeParts[1], 10);
var secs = parseInt(timeParts[2], 10);
if (parts[1]) {
var ampm = parts[1].toUpperCase();
if (ampm === "PM" && hrs < 12) hrs += 12;
if (ampm === "AM" && hrs === 12) hrs = 0;
}
tempDate.setHours(hrs, mins, secs);
now = tempDate;
} catch(e) {
now = new Date(); // Fallback
}
}
var timeNote = calculateShiftStatus(action, now);
var userAgent = HtmlService.getUserAgent();
var deviceInfo = parseDevice(userAgent);
var liveTeam = (shiftTimes && shiftTimes.team) ? shiftTimes.team : DEFAULT_TEAM;
var finalDevice = (action === "Start Shift" && reason && reason.includes("CPU:")) ? deviceInfo + " | " + reason : deviceInfo;
var finalReason = (action === "Start Shift" && reason && reason.includes("CPU:")) ? "System Check Passed" : (reason || "");
var cleanPhone = phoneNumber ? phoneNumber.trim() : "";
var rowData = [now, agentName, action, finalDevice, userEmail, aux, status, timeNote];
if ((action === "End Shift" || action === "Force Sync" || action === "Manual Sync" || action.includes("Sync")) && timerData) {
rowData.push(timerData["1"], timerData["2"], timerData["3"], timerData["4"], timerData["5"], timerData["6"]);
} else {
rowData.push("", "", "", "", "", "");
}
rowData.push(finalReason);
if (shiftTimes) {
rowData.push(shiftTimes.start || "", shiftTimes.end || "", liveTeam);
} else {
rowData.push("", "", liveTeam);
}
rowData.push(cleanPhone);
var mainSheet = ss.getSheetByName(ATTENDANCE_SHEET_NAME) || ss.getSheets()[0];
mainSheet.appendRow(rowData);
var agentSheet = ss.getSheetByName(sheetName);
if (!agentSheet) {
agentSheet = ss.insertSheet(sheetName);
var headers = ["Timestamp", "Agent Name", "Action", "Device", "User Email", "AUX", "Status", "Time Notes", "Auto-in", "ACW", "Outgoing", "Break", "Meeting", "Misc", "Reason/Note", "Start Shift", "End Shift", "Team", "Customer Phone"];
agentSheet.appendRow(headers);
agentSheet.getRange(1, 1, 1, headers.length).setFontWeight("bold").setBackground("#2C3B79").setFontColor("white").setHorizontalAlignment("center");
agentSheet.setFrozenRows(1);
}
agentSheet.appendRow(rowData);
// Synchronize Cache with System Properties
var cache = PropertiesService.getScriptProperties();
var agentState = {
name: agentName,
action: action,
status: status,
timestamp: now.getTime(),
note: finalReason,
email: userEmail,
device: finalDevice,
team: liveTeam,
breakdown: timerData ? JSON.stringify(timerData) : "{}",
phoneNumber: cleanPhone
};
cache.setProperty("live_agent_" + userEmail.toLowerCase(), JSON.stringify(agentState));
return { success: true, note: timeNote };
} catch(e) {
return { success: false, error: e.toString() };
} finally {
lock.releaseLock();
}
}
/**
*/
// Global Spreadsheet Identifier
const SPREADSHEET_ID = "1ApKbwjdJPyjK-t3EZrd8XGhzs52aYevQNfAo8L3b5kU";
const ATTENDANCE_SHEET_NAME = "Attendance Digital";
const DEFAULT_TEAM = "Community Operations";
const LOCK_TIMEOUT_MS = 10000;
/**
*/
function doGet(e) {
var page = e && e.parameter ? e.parameter.page : null;
// 1. Supervisor/Admin Dashboard (?page=admin)
if (page === 'admin') {
return HtmlService.createHtmlOutputFromFile('Admin')
.setTitle('TRU - Supervisor Dashboard')
.setXFrameOptionsMode(HtmlService.XFrameOptionsMode.ALLOWALL);
}
// 2. Main Live Hall Dashboard (?page=wallboard)
if (page === 'wallboard') {
return HtmlService.createHtmlOutputFromFile('Wallboard')
.setTitle('TRU - Live TV Wallboard')
.setXFrameOptionsMode(HtmlService.XFrameOptionsMode.ALLOWALL);
}
// 3. IT Helpdesk Panel (?page=it)
if (page === 'it') {
return HtmlService.createHtmlOutputFromFile('ITPanel')
.setTitle('TRU - IT Support HelpDesk')
.setXFrameOptionsMode(HtmlService.XFrameOptionsMode.ALLOWALL);
}
// 4. Shift Management & Scheduling Platform
if (page === 'rota' || page === 'shifts' || page === 'ShiftManager' || page === 'TRU Shift Management Platform') {
return HtmlService.createHtmlOutputFromFile('ShiftManager')
.setTitle('TRU - Shift Management & Scheduling')
.setXFrameOptionsMode(HtmlService.XFrameOptionsMode.ALLOWALL);
}
// 5. Standard Agent Portal (Default view)
var template = HtmlService.createTemplateFromFile('Index');
var phoneNumber = (e && e.parameter && e.parameter.phoneNumber) ? e.parameter.phoneNumber : "";
template.phoneNumber = phoneNumber;
return template.evaluate()
.setTitle('TRU - WorkForce Management')
.setXFrameOptionsMode(HtmlService.XFrameOptionsMode.ALLOWALL);
}
/**
*/
function getUserEmail() {
return Session.getActiveUser().getEmail();
}
/**
*/
function getUserFirstName() {
var email = Session.getActiveUser().getEmail();
var namePart = email.split('@')[0];
var firstName = namePart.split('.')[0];
return firstName.charAt(0).toUpperCase() + firstName.slice(1);
}
/**
*/
function pingServer() {
return "pong";
}
/**
Counts total calls received today in the general queue.
@return {number} Calculated call count.
*/
function getTotalCallsToday() {
try {
var ss = SpreadsheetApp.openById(SPREADSHEET_ID);
var mainSheet = ss.getSheetByName(ATTENDANCE_SHEET_NAME) || ss.getSheets()[0];
var lastRow = mainSheet.getLastRow();
if (lastRow <= 1) return 0;
var data = mainSheet.getRange(2, 1, lastRow - 1, 19).getValues();
var count = 0;
var todayStr = new Date().toDateString();
for (var i = 0; i < data.length; i++) {
var rowDateStr = new Date(data[i][0]).toDateString();
if (rowDateStr === todayStr && data[i][18]) {
count++;
}
}
return count;
} catch(e) {
console.error("Error calculating total calls today: " + e.toString());
return 0;
}
}
/**
Logs agent activities to the unified central sheet and their personal sheet.
*/
function logAction(action, aux, status, timerData, reason, shiftTimes, phoneNumber) {
var lock = LockService.getScriptLock();
try {
lock.waitLock(LOCK_TIMEOUT_MS);
var ss = SpreadsheetApp.openById(SPREADSHEET_ID);
var userEmail = Session.getActiveUser().getEmail();
var sheetName = userEmail.split('@')[0].replace(/[^a-zA-Z0-9]/g, "");
var agentName = sheetName.split('').map(word => word.charAt(0).toUpperCase() + word.slice(1)).join(' ');
var now = new Date();
// Adjust timestamp for historical sync or completion parameters
if (shiftTimes && shiftTimes.end && (action === "End Shift" || action === "Force Sync" || action === "Manual Sync" || action.includes("Sync"))) {
try {
var parts = shiftTimes.end.split(" ");
var timeParts = parts[0].split(":");
var tempDate = new Date();
var hrs = parseInt(timeParts[0], 10);
var mins = parseInt(timeParts[1], 10);
var secs = parseInt(timeParts[2], 10);
} catch(e) {
now = new Date(); // Fallback
}
}
var timeNote = calculateShiftStatus(action, now);
var userAgent = HtmlService.getUserAgent();
var deviceInfo = parseDevice(userAgent);
var liveTeam = (shiftTimes && shiftTimes.team) ? shiftTimes.team : DEFAULT_TEAM;
var finalDevice = (action === "Start Shift" && reason && reason.includes("CPU:")) ? deviceInfo + " | " + reason : deviceInfo;
var finalReason = (action === "Start Shift" && reason && reason.includes("CPU:")) ? "System Check Passed" : (reason || "");
var cleanPhone = phoneNumber ? phoneNumber.trim() : "";
var rowData = [now, agentName, action, finalDevice, userEmail, aux, status, timeNote];
if ((action === "End Shift" || action === "Force Sync" || action === "Manual Sync" || action.includes("Sync")) && timerData) {
rowData.push(timerData["1"], timerData["2"], timerData["3"], timerData["4"], timerData["5"], timerData["6"]);
} else {
rowData.push("", "", "", "", "", "");
}
rowData.push(finalReason);
if (shiftTimes) {
rowData.push(shiftTimes.start || "", shiftTimes.end || "", liveTeam);
} else {
rowData.push("", "", liveTeam);
}
rowData.push(cleanPhone);
var mainSheet = ss.getSheetByName(ATTENDANCE_SHEET_NAME) || ss.getSheets()[0];
mainSheet.appendRow(rowData);
var agentSheet = ss.getSheetByName(sheetName);
if (!agentSheet) {
agentSheet = ss.insertSheet(sheetName);
var headers = ["Timestamp", "Agent Name", "Action", "Device", "User Email", "AUX", "Status", "Time Notes", "Auto-in", "ACW", "Outgoing", "Break", "Meeting", "Misc", "Reason/Note", "Start Shift", "End Shift", "Team", "Customer Phone"];
agentSheet.appendRow(headers);
agentSheet.getRange(1, 1, 1, headers.length).setFontWeight("bold").setBackground("#2C3B79").setFontColor("white").setHorizontalAlignment("center");
agentSheet.setFrozenRows(1);
}
agentSheet.appendRow(rowData);
// Synchronize Cache with System Properties
var cache = PropertiesService.getScriptProperties();
var agentState = {
name: agentName,
action: action,
status: status,
timestamp: now.getTime(),
note: finalReason,
email: userEmail,
device: finalDevice,
team: liveTeam,
breakdown: timerData ? JSON.stringify(timerData) : "{}",
phoneNumber: cleanPhone
};
cache.setProperty("live_agent_" + userEmail.toLowerCase(), JSON.stringify(agentState));
return { success: true, note: timeNote };
} catch(e) {
return { success: false, error: e.toString() };
} finally {
lock.releaseLock();
}
}
/**
Retrieves the state of all agents currently monitored in the system.
*/
function getAllAgentsStatus() {
try {
var cache = PropertiesService.getScriptProperties();
var keys = cache.getKeys();
var list = [];
var now = new Date().getTime();
var limit12Hours = 12 * 60 * 60 * 1000;
for (var i = 0; i < keys.length; i++) {
if (keys[i].indexOf("live_agent_") === 0) {
var val = cache.getProperty(keys[i]);
if (val) {
var parsed = JSON.parse(val);
}
}
if (list.length > 0) return list;
// Fallback directly to spreadsheet layer
var ss = SpreadsheetApp.openById(SPREADSHEET_ID);
var mainSheet = ss.getSheetByName(ATTENDANCE_SHEET_NAME) || ss.getSheets()[0];
var lastRow = mainSheet.getLastRow();
if (lastRow <= 1) return [];
var data = mainSheet.getRange(2, 1, lastRow - 1, 19).getValues();
var agents = {};
for (var j = 0; j < data.length; j++) {
var email = data[j][4];
if (email) {
agents[email.toLowerCase()] = {
name: data[j][1],
action: data[j][2],
status: data[j][6],
timestamp: new Date(data[j][0]).getTime(),
note: data[j][14] || "",
email: email,
device: data[j][3],
team: data[j][17] || DEFAULT_TEAM,
breakdown: "{}",
phoneNumber: data[j][18] || ""
};
}
}
return Object.values(agents);
} catch (e) {
return [];
}
}
/**
Daily Cache Cleanup Utility
Removes logged inactive/out-of-shift agents older than 16 hours.
*/
function cleanupStaleAgents() {
try {
var cache = PropertiesService.getScriptProperties();
var keys = cache.getKeys();
var now = new Date().getTime();
var limit16Hours = 16 * 60 * 60 * 1000;
var count = 0;
for (var i = 0; i < keys.length; i++) {
if (keys[i].indexOf("live_agent_") === 0) {
var val = cache.getProperty(keys[i]);
if (val) {
var parsed = JSON.parse(val);
if (now - parsed.timestamp > limit16Hours || parsed.action === "End Shift" || parsed.status === "Idle") {
cache.deleteProperty(keys[i]);
count++;
}
}
}
}
console.log("Cleanup finished. Removed " + count + " stale agent properties.");
return "Cleared " + count + " agents.";
} catch(e) {
return "Error: " + e.toString();
}
}
/**
*/
function parseDevice(ua) {
var os = "Unknown OS", model = "Unknown Device";
if (ua.indexOf("Android") > -1) {
os = "Android";
var match = ua.match(/Android\s.[^;]+;\s([^;)]+)/);
model = match ? match[1].trim() : "Generic Device";
} else if (ua.indexOf("iPhone") > -1) {
os = "iOS";
model = "iPhone";
} else if (ua.indexOf("Windows") > -1) {
os = "Windows";
model = "PC";
}
return os + " | " + model;
}
/**
*/
function calculateShiftStatus(action, now) {
var hours = now.getHours(), minutes = now.getMinutes(), seconds = now.getSeconds();
var actualTotalSeconds = (hours * 3600) + (minutes * 60) + seconds;
if (action === "Start Shift") {
var targetSec;
if (actualTotalSeconds <= (11 * 3600)) targetSec = 10 * 3600;
else if (actualTotalSeconds <= (13.5 * 3600)) targetSec = 12 * 3600;
else targetSec = 15 * 3600;
return formatDifference(actualTotalSeconds, targetSec, "On Time", "Early", "Late");
} else if (action === "End Shift") {
var targetSec;
if (actualTotalSeconds <= (19 * 3600)) targetSec = 18 * 3600;
else if (actualTotalSeconds <= (21.5 * 3600)) targetSec = 20 * 3600;
else targetSec = 23 * 3600;
return formatDifference(actualTotalSeconds, targetSec, "On Time Logout", "Early Logout", "Over Time");
}
return "";
}
/**
*/
function formatDifference(actual, target, onTimeLabel, earlyLabel, lateLabel) {
if (Math.abs(actual - target) < 60) return onTimeLabel;
var diff = Math.abs(actual - target);
var h = Math.floor(diff / 3600), m = Math.floor((diff % 3600) / 60), s = diff % 60;
var timeStr = [h, m, s].map(v => v < 10 ? "0" + v : v).join(":");
return (actual < target) ? earlyLabel + " " + timeStr : lateLabel + " " + timeStr;
}
/**
Administrator approval process for pending agent requests.
*/
function adminGrantPermission(agentEmail, reqType) {
reqType = reqType || "Permission";
var lock = LockService.getScriptLock();
try {
lock.waitLock(LOCK_TIMEOUT_MS);
var ss = SpreadsheetApp.openById(SPREADSHEET_ID);
var sheetName = agentEmail.split('@')[0].replace(/[^a-zA-Z0-9]/g, "");
var agentName = sheetName.split('').map(word => word.charAt(0).toUpperCase() + word.slice(1)).join(' ');
var now = new Date();
var displayAction = reqType.replace(/request/gi, "Approved").replace(/leave/gi, "Leave Approved");
if (displayAction.indexOf("Approved") === -1) {
displayAction += " Approved";
}
var rowData = [now, agentName, displayAction, "System | Remote", agentEmail, "Approved", "Available", "Supervisor Approved " + reqType, "", "", "", "", "", "", "Remote Permission Granted", "", "", "", ""];
var mainSheet = ss.getSheetByName(ATTENDANCE_SHEET_NAME) || ss.getSheets()[0];
mainSheet.appendRow(rowData);
var agentSheet = ss.getSheetByName(sheetName);
if (agentSheet) agentSheet.appendRow(rowData);
var cache = PropertiesService.getScriptProperties();
var liveVal = cache.getProperty("live_agent_" + agentEmail.toLowerCase());
var parsed = liveVal ? JSON.parse(liveVal) : { name: agentName, email: agentEmail };
parsed.action = displayAction;
parsed.status = "Available";
parsed.timestamp = now.getTime();
cache.setProperty("live_agent_" + agentEmail.toLowerCase(), JSON.stringify(parsed));
return { success: true };
} catch(e) {
return { success: false, error: e.toString() };
} finally {
lock.releaseLock();
}
}
/**
Administrator rejection process for pending agent requests.
*/
function adminRejectPermission(agentEmail, reqType) {
reqType = reqType || "Permission";
var lock = LockService.getScriptLock();
try {
lock.waitLock(LOCK_TIMEOUT_MS);
var ss = SpreadsheetApp.openById(SPREADSHEET_ID);
var sheetName = agentEmail.split('@')[0].replace(/[^a-zA-Z0-9]/g, "");
var agentName = sheetName.split('').map(word => word.charAt(0).toUpperCase() + word.slice(1)).join(' ');
var now = new Date();
var displayAction = reqType.replace(/request/gi, "Rejected").replace(/leave/gi, "Leave Rejected");
if (displayAction.indexOf("Rejected") === -1) {
displayAction += " Rejected";
}
var rowData = [now, agentName, displayAction, "System | Remote", agentEmail, "Rejected", "Available", "Supervisor Rejected " + reqType, "", "", "", "", "", "", "Remote Permission Rejected", "", "", "", ""];
var mainSheet = ss.getSheetByName(ATTENDANCE_SHEET_NAME) || ss.getSheets()[0];
mainSheet.appendRow(rowData);
var agentSheet = ss.getSheetByName(sheetName);
if (agentSheet) agentSheet.appendRow(rowData);
var cache = PropertiesService.getScriptProperties();
var liveVal = cache.getProperty("live_agent_" + agentEmail.toLowerCase());
var parsed = liveVal ? JSON.parse(liveVal) : { name: agentName, email: agentEmail };
parsed.action = displayAction;
parsed.status = "Available";
parsed.timestamp = now.getTime();
cache.setProperty("live_agent_" + agentEmail.toLowerCase(), JSON.stringify(parsed));
return { success: true };
} catch(e) {
return { success: false, error: e.toString() };
} finally {
lock.releaseLock();
}
}
/**
Manual state override or system broadcast logging.
*/
function logActionManual(targetEmail, action, note) {
var lock = LockService.getScriptLock();
try {
lock.waitLock(LOCK_TIMEOUT_MS);
var ss = SpreadsheetApp.openById(SPREADSHEET_ID);
var now = new Date();
var rowData = [now, "SYSTEM", action, "Admin Control", targetEmail, "", "Note", "", "", "", "", "", "", "", note, "", "", "", ""];
var mainSheet = ss.getSheetByName(ATTENDANCE_SHEET_NAME) || ss.getSheets()[0];
mainSheet.appendRow(rowData);
var cache = PropertiesService.getScriptProperties();
var timestamp = now.getTime();
if (action === "Broadcast") {
var broadcastObj = {
type: "popup",
targets: [targetEmail],
msg: note,
time: timestamp,
duration: 60
};
cache.setProperty("latest_broadcast_data", JSON.stringify(broadcastObj));
} else {
var liveVal = cache.getProperty("live_agent_" + targetEmail.toLowerCase());
var parsed = liveVal ? JSON.parse(liveVal) : { name: "Agent", email: targetEmail };
parsed.action = action;
parsed.note = note;
parsed.timestamp = timestamp;
cache.setProperty("live_agent_" + targetEmail.toLowerCase(), JSON.stringify(parsed));
}
return { success: true };
} catch(e) {
return { success: false, error: e.toString() };
} finally {
lock.releaseLock();
}
}
/**
Dispatches a broadcast popup or audio notification to target agents.
*/
function sendBroadcast(type, targets, message, duration) {
var lock = LockService.getScriptLock();
try {
lock.waitLock(LOCK_TIMEOUT_MS);
var ss = SpreadsheetApp.openById(SPREADSHEET_ID);
var now = new Date();
var timestamp = now.getTime();
var targetsStr = targets.join(", ");
var rowData = [now, "SYSTEM", "Broadcast (" + type + ")", "Admin Control", targetsStr, "", "Note", "", "", "", "", "", "", "", message, "", "", "", ""];
var mainSheet = ss.getSheetByName(ATTENDANCE_SHEET_NAME) || ss.getSheets()[0];
mainSheet.appendRow(rowData);
var cache = PropertiesService.getScriptProperties();
var broadcastObj = {
type: type,
targets: targets,
msg: message,
time: timestamp,
duration: parseInt(duration, 10) || 60
};
cache.setProperty("latest_broadcast_data", JSON.stringify(broadcastObj));
return { success: true };
} catch(e) {
return { success: false, error: e.toString() };
} finally {
lock.releaseLock();
}
}
/**
Superiors force-control action over an agent.
*/
function remoteAction(agentEmail, actionType) {
var lock = LockService.getScriptLock();
try {
lock.waitLock(LOCK_TIMEOUT_MS);
var ss = SpreadsheetApp.openById(SPREADSHEET_ID);
var sheetName = agentEmail.split('@')[0].replace(/[^a-zA-Z0-9]/g, "");
var agentName = sheetName.split('').map(word => word.charAt(0).toUpperCase() + word.slice(1)).join(' ');
var now = new Date();
var status = (actionType === "Force Logout") ? "Away" : "Available";
var rowData = [now, agentName, "Admin " + actionType, "System | Remote", agentEmail, "Remote Control", status, "Action by Supervisor", "", "", "", "", "", "", "Remote " + actionType, "", "", "", ""];
var mainSheet = ss.getSheetByName(ATTENDANCE_SHEET_NAME) || ss.getSheets()[0];
mainSheet.appendRow(rowData);
var agentSheet = ss.getSheetByName(sheetName);
if (agentSheet) agentSheet.appendRow(rowData);
var cache = PropertiesService.getScriptProperties();
var liveVal = cache.getProperty("live_agent_" + agentEmail.toLowerCase());
var parsed = liveVal ? JSON.parse(liveVal) : { name: agentName, email: agentEmail };
parsed.action = (actionType === "Force Logout") ? "Admin Force Logout" : "Admin Reset to Auto-in";
parsed.status = status;
parsed.timestamp = now.getTime();
parsed.phoneNumber = "";
cache.setProperty("live_agent_" + agentEmail.toLowerCase(), JSON.stringify(parsed));
return { success: true };
} catch(e) {
return { success: false, error: e.toString() };
} finally {
lock.releaseLock();
}
}
/**
*/
function saveBreakLimitToProperties(teamKey, value) {
try {
var cache = PropertiesService.getScriptProperties();
cache.setProperty("MaxBreakLimit_" + teamKey.toUpperCase(), value);
return "Success";
} catch(e) {
return "Error: " + e.toString();
}
}
/**
Pulls active system broadcasts matching the current agent's email/team.
*/
function getLatestBroadcastForUser(userEmail, userTeam) {
try {
var cache = PropertiesService.getScriptProperties();
var raw = cache.getProperty("latest_broadcast_data");
if (!raw) return null;
var data = JSON.parse(raw);
var userEmailClean = userEmail ? userEmail.toLowerCase() : "";
var userTeamClean = userTeam ? userTeam.toLowerCase() : "";
var isTargeted = false;
for (var i = 0; i < data.targets.length; i++) {
var tgt = data.targets[i].toLowerCase();
if (tgt === "all" || tgt === "team:" + userTeamClean || tgt === "user:" + userEmailClean || tgt === userEmailClean) {
isTargeted = true;
break;
}
}
return isTargeted ? data : null;
} catch (e) {
return null;
}
}
/**
Uploads medical leave documentation directly to Google Drive.
*/
function uploadMedicalDoc(base64Data, fileName, agentEmail, dateStr) {
var lock = LockService.getScriptLock();
try {
lock.waitLock(LOCK_TIMEOUT_MS);
var folderName = "TRU_Workforce_Medical_Proofs";
var folders = DriveApp.getFoldersByName(folderName);
var folder = folders.hasNext() ? folders.next() : DriveApp.createFolder(folderName);
var contentType = base64Data.substring(base64Data.indexOf(":") + 1, base64Data.indexOf(";"));
var base64Clean = base64Data.substring(base64Data.indexOf(",") + 1);
var rawBlob = Utilities.newBlob(Utilities.base64Decode(base64Clean), contentType, fileName);
var file = folder.createFile(rawBlob);
file.setSharing(DriveApp.Access.ANYONE_WITH_LINK, DriveApp.Permission.VIEW);
return { success: true, url: file.getUrl() };
} catch(e) {
return { success: false, error: e.toString() };
} finally {
lock.releaseLock();
}
}
/**
*/
function saveWeeklySchedules(scheduleDb) {
var lock = LockService.getScriptLock();
try {
lock.waitLock(LOCK_TIMEOUT_MS);
var cache = PropertiesService.getScriptProperties();
cache.setProperty("TRU_ShiftManager_Weekly_DB", JSON.stringify(scheduleDb));
return { success: true };
} catch(e) {
return { success: false, error: e.toString() };
} finally {
lock.releaseLock();
}
}
/**
*/
function getWeeklySchedules() {
try {
var cache = PropertiesService.getScriptProperties();
var raw = cache.getProperty("TRU_ShiftManager_Weekly_DB");
return raw ? JSON.parse(raw) : null;
} catch(e) {
return null;
}
}