// ==== Code.gs ====
// Your Spreadsheet ID (optional if needed for external usage)
const SPREADSHEET_ID = '16vxtYxLb9052SGX6ZB5ySqh89MuheyGD-wWsS5l0r14';
/**
* Add custom menu when spreadsheet is opened
*/
function onOpen() {
SpreadsheetApp.getUi()
.createMenu('Form Manager')
.addItem('Open Form Manager Sidebar', 'openSidebar')
.addToUi();
}
/**
* Handle POST requests (Form submissions)
*/
function doPost(e) {
try {
setupCORS();
if (!e || !e.postData || !e.postData.contents) {
return createResponse({ success: false, error: 'No data received' }, 400);
}
let data;
try {
data = JSON.parse(e.postData.contents);
} catch (parseError) {
data = parseFormData(e.postData.contents);
}
const validation = validateFormData(data);
if (!validation.isValid) {
return createResponse({ success: false, error: validation.message }, 400);
}
const sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
prepareHeaders(sheet);
addFormDataToSheet(sheet, data);
return createResponse({
success: true,
message: 'Form submitted successfully!',
timestamp: new Date().toISOString(),
submittedData: data
});
} catch (error) {
console.error('Error in doPost:', error);
return createResponse({ success: false, error: 'Server error: ' + error.toString() }, 500);
}
}
/**
* Handle GET requests (basic info endpoint)
*/
function doGet(e) {
setupCORS();
return createResponse({
success: true,
message: 'Form submission endpoint is active',
endpoints: {
POST: 'Submit form data',
GET: 'Get endpoint information'
},
instructions: 'Send POST request with form data to submit to Google Sheets'
});
}
/**
* Setup CORS headers
*/
function setupCORS() {
const headers = {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type'
};
Object.keys(headers).forEach(key => {
ContentService.createTextOutput('').setMimeType(ContentService.MimeType.JSON)
});
}
/**
* Utility: Create JSON response
*/
function createResponse(data, statusCode = 200) {
return ContentService.createTextOutput(JSON.stringify(data))
.setMimeType(ContentService.MimeType.JSON)
.setStatusCode(statusCode);
}
/**
* Parse URL-encoded form data if JSON fails
*/
function parseFormData(contents) {
const data = {};
contents.split('&').forEach(pair => {
const [key, value] = pair.split('=');
if (key && value) data[decodeURIComponent(key)] = decodeURIComponent(value);
});
return data;
}
/**
* Validate required form fields
*/
function validateFormData(data) {
const requiredFields = ['fullName', 'dob'];
for (const field of requiredFields) {
if (!data[field] || data[field].trim() === '') {
return { isValid: false, message: `Required field '${field}' is missing` };
}
}
return { isValid: true };
}
/**
* Add headers to a new sheet
*/
function prepareHeaders(sheet) {
if (sheet.getLastRow() === 0) {
const headers = [
'Timestamp', 'Full Name', 'Date of Birth', 'Name Signature',
'Work Party', 'Degree', 'Campus University', 'Graduate Year',
'Majesty 1', 'Majesty 2', 'User Contact'
];
sheet.getRange(1, 1, 1, headers.length).setValues([headers]);
sheet.getRange(1, 1, 1, headers.length)
.setBackground('#4F200D')
.setFontColor('#FFFFFF')
.setFontWeight('bold');
}
}
/**
* Add a form submission row
*/
function addFormDataToSheet(sheet, data) {
const rowData = [
new Date(),
data.fullName || '',
data.dob || '',
data.nameSignature || '',
data.workParty || '',
data.degree || '',
data.campusUniversity || '',
data.gradYear || '',
data.majesty1 || '',
data.majesty2 || '',
data.contact || ''
];
sheet.appendRow(rowData);
sheet.autoResizeColumns(1, rowData.length);
}
/**
* Create a new sheet
*/
function createNewSheet(sheetName) {
const spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
let sheet = spreadsheet.getSheetByName(sheetName);
if (!sheet) {
sheet = spreadsheet.insertSheet(sheetName);
}
prepareHeaders(sheet);
return { success: true, message: `Sheet "${sheetName}" created successfully`, sheetName: sheet.getName() };
}
/**
* Open sidebar with HTML file
*/
function openSidebar() {
const html = HtmlService.createHtmlOutputFromFile('Sidebar')
.setTitle('Form Manager')
.setWidth(300);
SpreadsheetApp.getUi().showSidebar(html);
}
/**
* Get all sheet names
*/
function getSheetNames() {
return SpreadsheetApp.getActiveSpreadsheet().getSheets().map(s => s.getName());
}
/**
* Set active sheet by name
*/
function setActiveSheet(sheetName) {
const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(sheetName);
if (sheet) {
sheet.activate();
return { success: true, message: `Active sheet set to: ${sheetName}` };
} else {
return { success: false, error: `Sheet "${sheetName}" not found` };
}
}
/**
* Get the Web App URL
*/
function getWebAppUrl() {
return ScriptApp.getService().getUrl();
}
/**
* Test submission (adds dummy row)
*/
function testFormSubmission() {
const testData = {
fullName: 'Test User',
dob: '01/01/1990',
nameSignature: 'Test Signature',
workParty: 'party1',
degree: 'Bachelor of Science',
campusUniversity: 'Test University',
gradYear: '2000',
majesty1: 'Test Majesty 1',
majesty2: 'Test Majesty 2',
contact: 'test@example.com'
};
return addFormDataToSheet(SpreadsheetApp.getActiveSpreadsheet().getActiveSheet(), testData);
}
<!DOCTYPE html>
<html>
<head>
<base target="_top">
<style>
/* ====== Global Styles ====== */
body {
font-family: Arial, sans-serif;
margin: 20px;
color: #333;
background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
}
.container {
max-width: 100%;
}
h1 {
font-size: 18px;
margin-bottom: 20px;
color: #4F200D;
text-align: center;
padding-bottom: 10px;
border-bottom: 2px solid #FF9A00;
}
.section {
margin-bottom: 20px;
padding: 15px;
background: rgba(255, 255, 255, 0.9);
border-radius: 10px;
border-left: 4px solid #FF9A00;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
}
label {
display: block;
margin-bottom: 5px;
font-weight: bold;
font-size: 14px;
color: #4F200D;
}
select, input {
width: 100%;
padding: 10px;
border: 2px solid #e0e0e0;
border-radius: 5px;
margin-bottom: 10px;
box-sizing: border-box;
font-size: 14px;
transition: border-color 0.3s;
}
select:focus, input:focus {
border-color: #FF9A00;
outline: none;
box-shadow: 0 0 0 2px rgba(255, 154, 0, 0.2);
}
button {
background: linear-gradient(to right, #FF9A00, #FFD93D);
color: #4F200D;
border: none;
padding: 12px 15px;
border-radius: 5px;
cursor: pointer;
font-size: 14px;
font-weight: bold;
width: 100%;
margin-top: 10px;
transition: all 0.3s;
box-shadow: 0 2px 5px rgba(255, 154, 0, 0.3);
}
button:hover {
transform: translateY(-2px);
box-shadow: 0 4px 8px rgba(255, 154, 0, 0.4);
}
button:active {
transform: translateY(0);
}
.status {
margin-top: 15px;
padding: 10px;
border-radius: 5px;
font-size: 12px;
display: none;
text-align: center;
}
.success {
background: #d4edda;
color: #155724;
border: 1px solid #c3e6cb;
}
.error {
background: #f8d7da;
color: #721c24;
border: 1px solid #f5c6cb;
}
.info {
background: #cce7ff;
color: #004085;
border: 1px solid #b3d7ff;
display: block;
}
.url-box {
background: #f8f9fa;
padding: 10px;
border-radius: 5px;
border: 1px solid #dee2e6;
word-break: break-all;
font-size: 11px;
margin-bottom: 10px;
}
.copy-btn {
background: #6c757d;
margin-top: 5px;
}
.copy-btn:hover {
background: #545b62;
}
.info-text {
font-size: 12px;
margin-bottom: 10px;
}
</style>
</head>
<body>
<div class="container">
<h1>Form Manager</h1>
<div class="section">
<h3>Current Sheets</h3>
<label for="sheetSelect">Select Active Sheet:</label>
<select id="sheetSelect">
<option value="">Loading sheets...</option>
</select>
<button onclick="setActiveSheet()">Set as Active Sheet</button>
</div>
<div class="section">
<h3>Create New Sheet</h3>
<label for="newSheetName">New Sheet Name:</label>
<input type="text" id="newSheetName" placeholder="e.g., Contact Form, Signups">
<button onclick="createNewSheet()">Create New Sheet</button>
</div>
<div class="section">
<h3>Webhook URL for Your Form</h3>
<p class="info-text">Copy this URL and update the WEB_APP_URL in your signup form:</p>
<div class="url-box info" id="webhookUrl">Loading webhook URL...</div>
<button onclick="copyWebhookUrl()" class="copy-btn">Copy Webhook URL</button>
</div>
<div class="section">
<h3>Test Submission</h3>
<button onclick="testSubmission()">Test Form Submission</button>
</div>
<div id="statusMessage" class="status"></div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
loadSheets();
loadWebhookUrl();
});
function loadSheets() {
google.script.run
.withSuccessHandler(function(sheets) {
const select = document.getElementById('sheetSelect');
select.innerHTML = '';
sheets.forEach(sheet => {
const option = document.createElement('option');
option.value = sheet;
option.textContent = sheet;
select.appendChild(option);
});
})
.withFailureHandler(showError)
.getSheetNames();
}
function loadWebhookUrl() {
google.script.run
.withSuccessHandler(function(url) {
const webhookDiv = document.getElementById('webhookUrl');
webhookDiv.textContent = url;
})
.withFailureHandler(function() {
document.getElementById('webhookUrl').textContent = 'Error loading URL. Please deploy the script first.';
})
.getWebAppUrl();
}
function setActiveSheet() {
const sheetSelect = document.getElementById('sheetSelect');
const sheetName = sheetSelect.value;
if (!sheetName) { showError('Please select a sheet first'); return; }
google.script.run
.withSuccessHandler(result => showMessage(result.message, 'success'))
.withFailureHandler(showError)
.setActiveSheet(sheetName);
}
function createNewSheet() {
const sheetName = document.getElementById('newSheetName').value;
if (!sheetName) { showError('Please enter a sheet name'); return; }
google.script.run
.withSuccessHandler(result => {
showMessage(result.message, 'success');
document.getElementById('newSheetName').value = '';
loadSheets();
})
.withFailureHandler(showError)
.createNewSheet(sheetName);
}
function copyWebhookUrl() {
const webhookUrl = document.getElementById('webhookUrl').textContent;
navigator.clipboard.writeText(webhookUrl)
.then(() => showMessage('Webhook URL copied to clipboard!', 'success'))
.catch(() => showError('Failed to copy URL. Please copy manually.'));
}
function testSubmission() {
google.script.run
.withSuccessHandler(() => showMessage('Test submission completed successfully!', 'success'))
.withFailureHandler(showError)
.testFormSubmission();
}
function showMessage(message, type) {
const statusDiv = document.getElementById('statusMessage');
statusDiv.textContent = message;
statusDiv.className = 'status ' + type;
statusDiv.style.display = 'block';
setTimeout(() => statusDiv.style.display = 'none', 5000);
}
function showError(error) {
showMessage(error, 'error');
}
</script>
</body>
</html>