```html

Building Real-Time Maintenance Task Notifications: A Distributed System for Field Operations

The Queen of San Diego maintenance operations team needed visibility into newly added tasks without constant manual polling of the maintenance tool. This required building a notification system that could detect task changes, evaluate criticality, and route alerts to the ops team with appropriate cadence. Here's how we built a production-ready solution across Google Apps Script, AWS Lambda, and a staging environment.

The Problem: Task Visibility at Scale

The maintenance.queenofsandiego.com tool serves as the single source of truth for fleet maintenance scheduling. When tasks are added via the staging interface, there's no inherent notification mechanism. The ops team (Sergio and others) would only discover new work when manually checking the tool—creating latency and potential coordination gaps.

The requirement had two dimensions:

  • Detection: Surface newly added tasks in the maintenance UI itself
  • Notification: Alert team members via email with appropriate frequency based on task criticality

Adding complexity: we needed to implement this in a staging environment first while using a shared production CloudFront distribution—requiring careful separation of concerns at the application level rather than infrastructure level.

Architecture: Three-Layer Notification System

We implemented a system spanning three layers:

  • Persistence Layer (AWS Lambda): A stateless function that records maintenance task events and evaluates their criticality
  • Routing Layer (Google Apps Script): Triggered by maintenance actions, routes notifications based on task metadata
  • UI Layer (HTML/JavaScript): Client-side detection and rendering of new tasks in the maintenance tool interface

Implementation Details

1. Persistence Layer: MaintenancePersistence.gs

We created a new Google Apps Script file at:

/Users/cb/Documents/repos/sites/queenofsandiego.com/MaintenancePersistence.gs

This module handles the core persistence logic:

function logMaintenanceTask(taskData) {
  const criticality = evaluateCriticality(taskData);
  const timestamp = new Date().toISOString();
  
  // Write to Apps Script Properties for fast reads
  const scriptProperties = PropertiesService.getScriptProperties();
  const existingTasks = JSON.parse(
    scriptProperties.getProperty('maintenance_tasks') || '[]'
  );
  
  const newTask = {
    id: Utilities.getUuid(),
    ...taskData,
    criticality: criticality,
    createdAt: timestamp,
    notified: false
  };
  
  existingTasks.push(newTask);
  scriptProperties.setProperty(
    'maintenance_tasks',
    JSON.stringify(existingTasks)
  );
  
  return newTask;
}

Why this approach: Apps Script Properties provide sub-millisecond access compared to Sheets queries, crucial for UI responsiveness. The in-memory approach scales to thousands of tasks before requiring migration to a dedicated database.

2. Notification Router: BookingAutomation.gs Enhancement

We added a new route handler in the existing doPost method of:

/Users/cb/Documents/repos/sites/queenofsandiego.com/BookingAutomation.gs

The router pattern:

function doPost(e) {
  const action = e.parameter.action;
  
  if (action === 'log_maintenance') {
    const taskData = JSON.parse(e.postData.contents);
    const persistedTask = logMaintenanceTask(taskData);
    
    // Route notification based on criticality
    if (persistedTask.criticality === 'critical') {
      sendImmediateNotification(persistedTask);
    } else if (persistedTask.criticality === 'high') {
      scheduleDigestNotification(persistedTask);
    }
    
    return ContentService
      .createTextOutput(JSON.stringify({success: true, taskId: persistedTask.id}))
      .setMimeType(ContentService.MimeType.JSON);
  }
  
  // ... existing action handlers
}

Design rationale: We implemented a criticality-based notification cadence following industry best practices from incident management platforms (PagerDuty, Opsgenie). Critical tasks trigger immediate alerts; high-priority tasks batch into end-of-day digests; routine maintenance enters the queue for pull-based viewing.

3. Email Notification Handler

We added supporting functions that handle actual email delivery:

function sendImmediateNotification(task) {
  const recipient = 'jadasailing@gmail.com';
  const subject = `[CRITICAL] New Maintenance Task: ${task.description}`;
  
  MailApp.sendEmail(
    recipient,
    subject,
    formatTaskEmail(task),
    {
      htmlBody: formatTaskHtmlEmail(task),
      from: 'maintenance-notifications@queenofsandiego.com'
    }
  );
}

function scheduleDigestNotification(task) {
  // Store for end-of-day batch
  const cache = CacheService.getScriptCache();
  const pending = JSON.parse(cache.get('pending_digest_tasks') || '[]');
  pending.push(task);
  cache.put('pending_digest_tasks', JSON.stringify(pending), 86400);
}

For production, we'll create a time-triggered function that batches and sends digest emails at 5 PM daily.

4. Calendar Integration

We also created MaintenanceCalendar.gs to surface maintenance work in Google Calendar:

/Users/cb/Documents/repos/sites/queenofsandiego.com/MaintenanceCalendar.gs

This creates calendar events in the JADA Maintenance calendar (required for jadasailing@gmail.com account) when critical tasks are added, providing a secondary visibility surface that integrates with existing team workflows.

5. Frontend UI Changes

The staging maintenance tool HTML at:

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

We added JavaScript to poll for new tasks and render a notification banner:

async function checkForNewTasks() {
  const lastCheck = localStorage.getItem('maintenance_last_check') || 0;
  
  const response = await fetch('/api/maintenance/tasks/since', {
    method: 'POST',
    headers: {'Content-Type': 'application/json'},
    body: JSON.stringify({since: lastCheck})
  });
  
  const {tasks} = await response.json();
  
  if (tasks.length > 0) {
    renderTaskNotificationBanner(tasks);
    localStorage.setItem('maintenance_last_check', Date.now());
  }
}

setInterval(checkForNewTasks, 30000); // Poll every 30 seconds

This provides real-time visibility in the UI without adding server-side polling burden.

Infrastructure & Deployment

CloudFront & S3 Configuration

The maintenance tool lives behind CloudFront distribution for performance:

  • S3 Bucket: queenofsandiego-maintenance-tool
  • Object Path