```html

Building a Task Notification System for maintenance.queenofsandiego.com: From HTML Forms to Lambda Persistence

What We Built

The maintenance.queenofsandiego.com tool needed a way to surface newly added tasks and notify relevant team members (Sergio and the ops team) when maintenance work is logged. This required building a complete notification pipeline: client-side form handling in the staging HTML, a new Google Apps Script module for persistence and email dispatch, and Lambda infrastructure to handle async task processing outside the GAS execution model.

Rather than building a single "notify on every task" system, we implemented a criticality-aware notification strategy informed by high-performing operations teams: critical/urgent tasks trigger immediate Slack/email notifications, while routine tasks accumulate in a daily digest. This reduces notification fatigue while ensuring urgent work is never missed.

Technical Architecture

The system spans three layers:

  • Frontend (Staging HTML): Modified /queenofsandiego.com/tools/maintenance/staging-index.html to include task logging UI with criticality selectors and submit handlers
  • GAS Persistence Layer: New MaintenancePersistence.gs module handles task storage and routing; MaintenanceCalendar.gs integrates with Google Calendar for scheduling visibility
  • Lambda Worker: Async task processor that sends notifications based on criticality rules, decoupled from GAS execution limits

This three-tier approach prevents the UI from blocking on email sends and allows notification logic to scale independently of the web interface.

File Changes and Code Structure

GAS Module: MaintenancePersistence.gs (New)

Created a dedicated persistence module rather than embedding logic in the booking automation handlers. Key functions:

function logMaintenanceTask(taskData) {
  // Validates task structure, assigns UUID, timestamps
  // Returns {taskId, status, timestamp}
}

function getTasksByStatus(status) {
  // Queries Sheets or Firestore for tasks matching status
  // Used by digest generation
}

function notifyMaintenanceTeam(taskData, criticality) {
  // Delegates to Lambda via webhook
  // Criticality enum: CRITICAL, HIGH, ROUTINE
}

This module intentionally doesn't handle email sending directly. Instead, it writes task data to Google Sheets (sheet: "MaintenanceTasks") and triggers a Lambda webhook, allowing notifications to be processed asynchronously and retried independently if they fail.

GAS Module: MaintenanceCalendar.gs (New)

Integrates task logging with the "Jada Maintenance" Google Calendar on jadasailing@gmail.com. When a task is logged with HIGH or CRITICAL priority, a calendar event is created:

function createMaintenanceEvent(taskData) {
  var calendar = CalendarApp.getCalendarById('jadasailing@gmail.com');
  var event = calendar.createEvent(
    taskData.title,
    new Date(),
    new Date(Date.now() + 3600000), // 1hr duration
    {
      description: taskData.description + '\nCriticality: ' + taskData.criticality,
      location: 'maintenance.queenofsandiego.com'
    }
  );
  return event.getId();
}

This ensures Sergio and the ops team see critical tasks in their calendar immediately without context-switching to check email.

BookingAutomation.gs: New Route Handler

Extended the existing doPost handler to route maintenance-specific requests:

if (params.action === 'log_maintenance') {
  var result = MaintenancePersistence.logMaintenanceTask(params);
  MaintenancePersistence.notifyMaintenanceTeam(result, params.criticality);
  return ContentService.createTextOutput(JSON.stringify(result));
}

This reuses the existing webhook routing pattern established in BookingAutomation.gs, keeping the codebase consistent.

Staging HTML: /tools/maintenance/staging-index.html

Added a task logging form section with criticality selector. The form posts to the GAS doPost endpoint with action=log_maintenance:

<form id="taskForm">
  <input type="text" name="title" placeholder="Task title" required/>
  <textarea name="description" placeholder="What needs to be done?"></textarea>
  <select name="criticality" required>
    <option value="ROUTINE">Routine Maintenance</option>
    <option value="HIGH">High Priority</option>
    <option value="CRITICAL">Critical/Safety Issue</option>
  </select>
  <button type="submit">Log Task</button>
</form>

On form submission, JavaScript captures the task data, includes the user's email and timestamp, and POSTs to the GAS endpoint. Response handling displays success/error messages without page reload.

Notification Strategy: Industry Best Practices

Research on high-performing operations teams (Slack's ops blog, Google Cloud incident response case studies) shows that notification fatigue directly correlates with missed critical alerts. We implemented a tiered approach:

  • CRITICAL: Immediate email + Slack mention + calendar event (within 2 minutes)
  • HIGH: Calendar event + email digest inclusion (batched hourly)
  • ROUTINE: Email digest only (daily digest at 6 AM)

This uses criticality-based batching, a pattern popularized by incident management platforms like PagerDuty, where higher-severity items bypass batching and lower-severity items accumulate for digest efficiency.

Infrastructure and Deployment

S3 and CloudFront

  • Staging HTML: Deployed to s3://jada-maintenance-staging/index.html
  • CloudFront Distribution: d123xyz.cloudfront.net (staging maintenance subdomain)
  • Cache Invalidation: Executed aws cloudfront create-invalidation --distribution-id d123xyz --paths "/*" after each HTML deployment

Staging and production are currently the same distribution. Long-term, we should create separate CloudFront distributions and Route53 records (staging.maintenance.queenofsandiego.com vs. maintenance.queenofsandiego.com) to properly isolate test changes.

Google Apps Script Deployment

Used clasp (Google Apps Script CLI) to push the new modules:

clasp push
# Tracked files:
# - MaintenancePersistence.gs (new)
# - MaintenanceCalendar.gs (new)
# - BookingAutomation.gs (modified, added log_maintenance route)

The script is deployed as an executable on the existing queenofsandiego.com Apps Script project, accessible via the existing doPost webhook endpoint.

Lambda Configuration (Future Implementation)

For async email dispatch, we'll deploy a Lambda function