Building Real-Time Task Notification for Maintenance Operations: Lambda + Google Apps Script + CloudFront Architecture

What Was Done

We implemented a comprehensive task notification system for maintenance.queenofsandiego.com that surfaces newly-added maintenance tasks to operations staff with intelligent notification pacing based on task criticality. The system spans three layers: a serverless Lambda function for persistence, Google Apps Script handlers for email dispatch, and frontend modifications to the staging maintenance tool UI.

The Problem Statement

The maintenance tool at /Users/cb/Documents/repos/sites/queenofsandiego.com/tools/maintenance/staging-index.html had no visibility mechanism when new tasks were added by crew members like Travis. Without this, operations staff (Sergio, etc.) couldn't respond to emerging maintenance needs in a timely manner. We needed:

  • Real-time surface of new tasks in the UI
  • Intelligent notification delivery to operations email
  • Separation between staging (test) and production notification flows
  • Task criticality as a signal for notification frequency
  • A Google Calendar integration ("Jada Maintenance") for persistence visibility

Architecture Overview

The solution follows a serverless-first pattern with three primary components:

  • Frontend (staging HTML): Client-side task submission + local UI updates
  • Backend Persistence (Lambda): Receives task submissions, stores state, triggers notifications
  • Notification Handler (Google Apps Script): Evaluates criticality, determines notification cadence, sends email digests

Technical Implementation

1. Lambda Persistence Layer

Created a new Lambda function to handle maintenance task submissions. This function:

  • Receives POST requests from the staging maintenance tool frontend
  • Validates task payload (task name, description, criticality level)
  • Stores tasks in DynamoDB with timestamp and submitter metadata
  • Invokes the GAS notification handler asynchronously

The Lambda follows the existing deployment pattern established in the codebase (referenced from tips-box Lambda configuration). The IAM role includes permissions for DynamoDB PutItem and SNS Publish to trigger the GAS handler.

2. Google Apps Script Notification Handler

Created /Users/cb/Documents/repos/sites/queenofsandiego.com/MaintenancePersistence.gs with the following responsibilities:

// Receives task events from Lambda
function handleMaintenanceTaskNotification(taskEvent) {
  const criticality = taskEvent.criticality; // 'low', 'medium', 'high', 'critical'
  const shouldNotifyImmediately = criticality === 'critical' || criticality === 'high';
  
  if (shouldNotifyImmediately) {
    sendImmediateNotification(taskEvent);
  } else {
    queueForDailyDigest(taskEvent);
  }
}

This function integrates with BookingAutomation.gs via the existing doPost routing mechanism. We added a new action handler:

// In BookingAutomation.gs doPost handler
if (action === 'log_maintenance_task') {
  MaintenancePersistence.handleMaintenanceTaskNotification(parsedPayload);
  return ContentService.createTextOutput(JSON.stringify({status: 'queued'}));
}

For testing/staging, notifications are sent to jadasailing@gmail.com. The system evaluates:

  • Critical/High Priority: Immediate email notification
  • Medium Priority: Batched in end-of-day digest (sent at 18:00 UTC)
  • Low Priority: Included in next-day consolidated report

This follows industry best practices used by high-performing ops teams (Datadog, PagerDuty, etc.) where notification fatigue is minimized through intelligent batching while critical issues maintain alert velocity.

3. Google Calendar Integration

Created /Users/cb/Documents/repos/sites/queenofsandiego.com/MaintenanceCalendar.gs to:

  • Create a "Jada Maintenance" calendar in the jadasailing@gmail.com account (if not present)
  • Add maintenance tasks as calendar events with color coding by criticality
  • Enable at-a-glance visibility and calendar-based workflow integration

Tasks are created as all-day events with description payloads containing task metadata. This provides a secondary persistence layer and integrates with existing calendar-based operations workflows.

4. Staging Frontend Changes

Modified /Users/cb/Documents/repos/sites/queenofsandiego.com/tools/maintenance/staging-index.html to:

  • Add a "New Task" form with fields: Task Name, Description, Criticality (radio buttons: Low/Medium/High/Critical)
  • Implement client-side validation and optimistic UI updates
  • Submit tasks to the Lambda backend via fetch API
  • Display submission status feedback to users

Key JavaScript addition to staging HTML:

async function submitMaintenanceTask(formData) {
  const payload = {
    action: 'log_maintenance_task',
    taskName: formData.get('taskName'),
    description: formData.get('description'),
    criticality: formData.get('criticality'),
    submittedBy: formData.get('submittedBy'),
    timestamp: new Date().toISOString()
  };
  
  try {
    const response = await fetch('/api/maintenance/task', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(payload)
    });
    
    if (response.ok) {
      updateLocalTaskList(payload);
      showSuccessMessage('Task submitted successfully');
    }
  } catch (error) {
    console.error('Task submission failed:', error);
    showErrorMessage('Failed to submit task');
  }
}

Infrastructure Changes

S3 Bucket Structure

Staging HTML deployed to: s3://jada-maintenance-staging/tools/maintenance/staging-index.html

Lambda function stores tasks with DynamoDB table: jada-maintenance-tasks-staging (for staging environment)

CloudFront Distribution

Invalidated the CloudFront distribution cache for maintenance.queenofsandiego.com after HTML deployment to ensure staging changes propagate immediately:

aws cloudfront create-invalidation \
  --distribution-id [DIST_ID] \
  --paths "/tools/maintenance/staging-index.html"

GAS Deployment

Used clasp to deploy new GAS files to the existing Apps Script project:

cd /Users/cb/Documents/repos/sites/queenofsandiego.com
clasp push

This added MaintenancePersistence.gs and