Skip to content

Rweg/google-apps-script-automation-templates

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

1 Commit
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Google Apps Script Automation Templates

Enterprise spreadsheet automation and web application framework

🎯 Overview

A collection of production-grade Google Apps Script templates demonstrating enterprise workflow automation, custom UI development, and Google Workspace integration. These templates showcase how to build powerful business applications using Google Sheets as a database with custom web interfaces.

Real-world pattern: Automated workflows processing dozens of requests daily, custom UIs for data entry, triggered email notifications, and real-time dashboard updates.

✨ Key Features

Custom User Interfaces

  • βœ… HtmlService Dialogs - Modal and modeless custom interfaces
  • βœ… Web Apps - Standalone web applications
  • βœ… Custom Sidebars - Integrated side panels
  • βœ… Custom Menus - Toolbar extensions

Automation

  • βœ… Time-based Triggers - Scheduled automation
  • βœ… Event-based Triggers - OnEdit, OnFormSubmit, etc.
  • βœ… Email Automation - Templated email sending
  • βœ… Data Processing - Automated data transformation

Data Management

  • βœ… CRUD Operations - Create, Read, Update, Delete records
  • βœ… Data Validation - Input validation and constraints
  • βœ… Search & Filter - Advanced data querying
  • βœ… Export Capabilities - Multiple format exports

Integration

  • βœ… Google Sheets API - Programmatic sheet manipulation
  • βœ… Gmail Integration - Email sending and templates
  • βœ… Calendar Integration - Event creation and management
  • βœ… External APIs - UrlFetchApp for REST APIs

πŸ› οΈ Tech Stack

  • Google Apps Script - Server-side JavaScript
  • HTML5 - Custom UI templates
  • CSS3 - Styling for web apps
  • JavaScript - Client-side interactivity
  • Google Services - Sheets, Gmail, Calendar, Drive

πŸ“¦ Installation

Prerequisites

  • Google Account
  • Google Sheets access
  • Basic JavaScript knowledge

Setup

Option 1: Standalone Script

  1. Go to script.google.com
  2. Create new project
  3. Copy template code
  4. Save and authorize

Option 2: Container-bound Script

  1. Open your Google Sheet
  2. Extensions β†’ Apps Script
  3. Copy template code
  4. Save and authorize

πŸš€ Templates Included

1. Custom Form Dialog Template

Interactive data entry form with validation

// Code.gs
function showCustomForm() {
  const html = HtmlService.createHtmlOutputFromFile('FormDialog')
    .setWidth(400)
    .setHeight(500)
    .setTitle('Data Entry Form');
  SpreadsheetApp.getUi().showModalDialog(html, 'Submit Request');
}

function submitFormData(formData) {
  const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Data');
  
  // Validate data
  if (!validateFormData(formData)) {
    return { success: false, error: 'Validation failed' };
  }
  
  // Append to sheet
  sheet.appendRow([
    new Date(),
    formData.name,
    formData.email,
    formData.category,
    formData.description,
    'Pending'
  ]);
  
  // Send confirmation email
  sendConfirmationEmail(formData.email, formData);
  
  return { success: true };
}

function validateFormData(data) {
  if (!data.name || data.name.trim() === '') return false;
  if (!data.email || !isValidEmail(data.email)) return false;
  if (!data.category) return false;
  return true;
}

function isValidEmail(email) {
  const regex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
  return regex.test(email);
}
<!-- FormDialog.html -->
<!DOCTYPE html>
<html>
  <head>
    <base target="_top">
    <style>
      body {
        font-family: 'Google Sans', Arial, sans-serif;
        padding: 20px;
        background-color: #f5f5f5;
      }
      .form-group {
        margin-bottom: 15px;
      }
      label {
        display: block;
        margin-bottom: 5px;
        font-weight: 500;
        color: #333;
      }
      input, select, textarea {
        width: 100%;
        padding: 8px;
        border: 1px solid #ddd;
        border-radius: 4px;
        box-sizing: border-box;
      }
      textarea {
        resize: vertical;
        min-height: 100px;
      }
      .btn-submit {
        background-color: #1a73e8;
        color: white;
        padding: 10px 20px;
        border: none;
        border-radius: 4px;
        cursor: pointer;
        width: 100%;
        font-size: 14px;
      }
      .btn-submit:hover {
        background-color: #1557b0;
      }
      .error {
        color: #d93025;
        font-size: 12px;
        margin-top: 5px;
      }
      .success {
        color: #1e8e3e;
        font-size: 14px;
        padding: 10px;
        background-color: #e6f4ea;
        border-radius: 4px;
        margin-bottom: 15px;
      }
    </style>
  </head>
  <body>
    <div id="message"></div>
    <form id="dataForm">
      <div class="form-group">
        <label for="name">Name *</label>
        <input type="text" id="name" name="name" required>
      </div>
      
      <div class="form-group">
        <label for="email">Email *</label>
        <input type="email" id="email" name="email" required>
      </div>
      
      <div class="form-group">
        <label for="category">Category *</label>
        <select id="category" name="category" required>
          <option value="">Select...</option>
          <option value="Type A">Type A</option>
          <option value="Type B">Type B</option>
          <option value="Type C">Type C</option>
        </select>
      </div>
      
      <div class="form-group">
        <label for="description">Description *</label>
        <textarea id="description" name="description" required></textarea>
      </div>
      
      <button type="submit" class="btn-submit">Submit</button>
    </form>
    
    <script>
      document.getElementById('dataForm').addEventListener('submit', function(e) {
        e.preventDefault();
        
        const formData = {
          name: document.getElementById('name').value,
          email: document.getElementById('email').value,
          category: document.getElementById('category').value,
          description: document.getElementById('description').value
        };
        
        // Show loading state
        const submitBtn = document.querySelector('.btn-submit');
        submitBtn.textContent = 'Submitting...';
        submitBtn.disabled = true;
        
        // Submit to server
        google.script.run
          .withSuccessHandler(onSuccess)
          .withFailureHandler(onFailure)
          .submitFormData(formData);
      });
      
      function onSuccess(response) {
        if (response.success) {
          document.getElementById('message').innerHTML = 
            '<div class="success">βœ“ Submitted successfully!</div>';
          document.getElementById('dataForm').reset();
        } else {
          document.getElementById('message').innerHTML = 
            '<div class="error">Error: ' + response.error + '</div>';
        }
        resetButton();
      }
      
      function onFailure(error) {
        document.getElementById('message').innerHTML = 
          '<div class="error">Error: ' + error.message + '</div>';
        resetButton();
      }
      
      function resetButton() {
        const submitBtn = document.querySelector('.btn-submit');
        submitBtn.textContent = 'Submit';
        submitBtn.disabled = false;
      }
    </script>
  </body>
</html>

2. Automated Email Notification Template

// Code.gs
function sendConfirmationEmail(recipientEmail, data) {
  const subject = `Confirmation: ${data.category} Request`;
  
  const htmlBody = `
    <div style="font-family: Arial, sans-serif; max-width: 600px;">
      <h2 style="color: #1a73e8;">Request Confirmation</h2>
      <p>Hello ${data.name},</p>
      <p>We've received your request with the following details:</p>
      <div style="background-color: #f5f5f5; padding: 15px; border-radius: 5px; margin: 20px 0;">
        <p><strong>Category:</strong> ${data.category}</p>
        <p><strong>Description:</strong> ${data.description}</p>
        <p><strong>Status:</strong> Pending Review</p>
        <p><strong>Submitted:</strong> ${new Date().toLocaleString()}</p>
      </div>
      <p>We'll review your request and get back to you soon.</p>
      <p style="color: #666; font-size: 12px; margin-top: 30px;">
        This is an automated message. Please do not reply to this email.
      </p>
    </div>
  `;
  
  GmailApp.sendEmail(recipientEmail, subject, '', {
    htmlBody: htmlBody,
    name: 'Automated System'
  });
}

function sendBulkNotifications() {
  const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Data');
  const data = sheet.getDataRange().getValues();
  const headers = data[0];
  
  // Find columns
  const emailCol = headers.indexOf('Email');
  const statusCol = headers.indexOf('Status');
  const notifiedCol = headers.indexOf('Notified');
  
  // Process each row
  for (let i = 1; i < data.length; i++) {
    const row = data[i];
    
    // Skip if already notified or not relevant status
    if (row[notifiedCol] === 'Yes' || row[statusCol] !== 'Completed') {
      continue;
    }
    
    // Send notification
    try {
      const email = row[emailCol];
      sendStatusUpdateEmail(email, row, headers);
      
      // Mark as notified
      sheet.getRange(i + 1, notifiedCol + 1).setValue('Yes');
      
      // Add delay to avoid rate limiting
      Utilities.sleep(1000);
    } catch (error) {
      Logger.log(`Error sending to row ${i}: ${error}`);
    }
  }
}

3. Data Dashboard with Charts

// Code.gs
function createDashboard() {
  const html = HtmlService.createHtmlOutputFromFile('Dashboard')
    .setWidth(800)
    .setHeight(600)
    .setTitle('Analytics Dashboard');
  SpreadsheetApp.getUi().showModalDialog(html, 'Dashboard');
}

function getDashboardData() {
  const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Data');
  const data = sheet.getDataRange().getValues();
  const headers = data[0];
  
  // Calculate metrics
  const metrics = {
    total: data.length - 1,
    byCategory: {},
    byStatus: {},
    recentItems: []
  };
  
  for (let i = 1; i < data.length; i++) {
    const row = data[i];
    const category = row[headers.indexOf('Category')];
    const status = row[headers.indexOf('Status')];
    
    // Count by category
    metrics.byCategory[category] = (metrics.byCategory[category] || 0) + 1;
    
    // Count by status
    metrics.byStatus[status] = (metrics.byStatus[status] || 0) + 1;
    
    // Recent items (last 10)
    if (i <= 10) {
      metrics.recentItems.push({
        date: row[0],
        name: row[1],
        category: category,
        status: status
      });
    }
  }
  
  return metrics;
}

4. Automated Triggers Template

// Code.gs
function setupTriggers() {
  // Remove existing triggers
  const triggers = ScriptApp.getProjectTriggers();
  triggers.forEach(trigger => ScriptApp.deleteTrigger(trigger));
  
  // Daily report at 9 AM
  ScriptApp.newTrigger('generateDailyReport')
    .timeBased()
    .atHour(9)
    .everyDays(1)
    .create();
  
  // Weekly summary on Monday at 10 AM
  ScriptApp.newTrigger('generateWeeklyReport')
    .timeBased()
    .onWeekDay(ScriptApp.WeekDay.MONDAY)
    .atHour(10)
    .create();
  
  // OnEdit trigger
  ScriptApp.newTrigger('onEditHandler')
    .forSpreadsheet(SpreadsheetApp.getActive())
    .onEdit()
    .create();
}

function onEditHandler(e) {
  const sheet = e.source.getActiveSheet();
  const range = e.range;
  
  // Example: Auto-timestamp on status change
  if (sheet.getName() === 'Data' && range.getColumn() === 6) { // Status column
    const timestampCol = 7; // Adjacent column
    sheet.getRange(range.getRow(), timestampCol)
      .setValue(new Date())
      .setNumberFormat('yyyy-mm-dd hh:mm:ss');
    
    // Send notification if status is "Completed"
    if (range.getValue() === 'Completed') {
      const row = sheet.getRange(range.getRow(), 1, 1, sheet.getLastColumn()).getValues()[0];
      const email = row[2]; // Email column
      sendStatusUpdateEmail(email, 'Your request has been completed!');
    }
  }
}

5. API Integration Template

// Code.gs
function fetchExternalData() {
  const apiUrl = 'https://api.example.com/data';
  const apiKey = PropertiesService.getScriptProperties().getProperty('API_KEY');
  
  const options = {
    method: 'get',
    headers: {
      'Authorization': `Bearer ${apiKey}`,
      'Content-Type': 'application/json'
    },
    muteHttpExceptions: true
  };
  
  try {
    const response = UrlFetchApp.fetch(apiUrl, options);
    const responseCode = response.getResponseCode();
    
    if (responseCode === 200) {
      const data = JSON.parse(response.getContentText());
      processExternalData(data);
      return { success: true, data: data };
    } else {
      throw new Error(`API returned status: ${responseCode}`);
    }
  } catch (error) {
    Logger.log(`Error fetching data: ${error}`);
    return { success: false, error: error.message };
  }
}

function processExternalData(data) {
  const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('API Data');
  sheet.clear();
  
  // Write headers
  const headers = Object.keys(data[0]);
  sheet.getRange(1, 1, 1, headers.length).setValues([headers]);
  
  // Write data
  const rows = data.map(item => headers.map(header => item[header]));
  sheet.getRange(2, 1, rows.length, headers.length).setValues(rows);
}

πŸ—οΈ Architecture

Google Sheets (Database)
         ↓
Apps Script (Backend)
    β”œβ”€β”€ Custom Functions
    β”œβ”€β”€ Event Handlers
    β”œβ”€β”€ API Clients
    └── Automation Logic
         ↓
HtmlService (Frontend)
    β”œβ”€β”€ Custom Dialogs
    β”œβ”€β”€ Web Apps
    └── Sidebars
         ↓
External Integrations
    β”œβ”€β”€ Gmail
    β”œβ”€β”€ Calendar
    β”œβ”€β”€ Drive
    └── External APIs

πŸ“‹ Use Cases

  • Workflow Automation - Request tracking and approval workflows
  • Data Entry Forms - Custom interfaces for structured data collection
  • Automated Reporting - Scheduled report generation and distribution
  • Email Campaigns - Bulk email sending with templates
  • Dashboard Creation - Real-time analytics and visualization
  • API Integration - Connect external services to Sheets

πŸ”’ Security Best Practices

Storing Secrets

// Use Script Properties for sensitive data
function setApiKey() {
  PropertiesService.getScriptProperties().setProperty('API_KEY', 'your_key_here');
}

function getApiKey() {
  return PropertiesService.getScriptProperties().getProperty('API_KEY');
}

Authorization

// Check user permissions
function isAuthorizedUser() {
  const authorizedEmails = ['[email protected]', '[email protected]'];
  const userEmail = Session.getActiveUser().getEmail();
  return authorizedEmails.includes(userEmail);
}

πŸ“„ License

MIT License

πŸ’‘ About

These templates demonstrate:

  • Enterprise workflow automation with Google Workspace
  • Custom UI development with HtmlService
  • Event-driven programming with triggers
  • API integration patterns
  • Production-grade error handling

Author: Toussaint Rwego
GitHub: @Rweg

About

Enterprise spreadsheet automation and web app framework

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors