```html

Building a Real-Time Task Notification System for maintenance.queenofsandiego.com

What Was Done

We implemented a task notification pipeline for the JADA Sailing maintenance management tool that surfaces newly added tasks to the operations team in real-time. The system consists of four core components: a Lambda function for persistence and email orchestration, GAS (Google Apps Script) routing modifications, enhanced frontend validation, and a staging deployment with isolated email routing for testing.

The Problem We Solved

The maintenance tool at maintenance.queenofsandiego.com allows crew members to log tasks, but newly added tasks weren't surfacing to operations staff. Travis added tasks via SMS that went unnoticed. Additionally, there was no structured way to notify Sergio and the team about new maintenance items or to aggregate them intelligently based on task criticality. We needed a system that could:

  • Capture new task submissions from the frontend
  • Persist them reliably to a backend system
  • Route notifications based on task priority/criticality
  • Allow testing in staging without spamming production contacts
  • Eventually scale to digest-mode notifications for low-criticality tasks

Architecture Overview

The solution uses a three-tier notification architecture:

  • Frontend (HTML/JavaScript): Modified staging-index.html to capture task submission events and validate data client-side
  • GAS Layer: Updated BookingAutomation.gs to route maintenance actions to a dedicated handler in MaintenancePersistence.gs
  • Backend (Lambda): Serverless function for durability, email delivery orchestration, and future integration with calendar/CRM systems

Technical Implementation Details

GAS Modifications

We added a new file MaintenancePersistence.gs at /Users/cb/Documents/repos/sites/queenofsandiego.com/MaintenancePersistence.gs that acts as the handler for maintenance-related POST requests. This file contains:

function handleMaintenanceTask(payload) {
  // Validates task payload structure
  // Invokes Lambda for persistence and notification
  // Returns status to frontend
}

function notifyOperationsTeam(taskData, criticality) {
  // Routes to email or digest queue based on criticality
  // In staging: sends to jadasailing@gmail.com
  // In production: routes to team distribution list
}

We also modified BookingAutomation.gs to add routing for the log_maintenance action. The doPost handler now checks:

if (action === 'log_maintenance') {
  return MaintenancePersistence.handleMaintenanceTask(request.parameter);
}

This keeps concerns separated: booking automation handles calendar/charter logic, while maintenance persistence handles task logging and notifications.

Frontend Changes

The modified staging-index.html now includes:

  • Enhanced form validation that checks for required task fields (description, severity level, assigned crew)
  • Client-side criticality assessment based on keywords ("urgent", "safety", "electrical") and explicit severity selection
  • POST handler that sends task data to the GAS endpoint with action=log_maintenance
  • User feedback (success/error messages) after submission

The key change captures the criticality level as part of the submission payload, enabling intelligent routing downstream:

const criticality = determineCriticality(taskDescription, selectedSeverity);
const payload = {
  action: 'log_maintenance',
  task_description: taskDescription,
  assigned_to: crewMember,
  criticality: criticality,
  timestamp: new Date().toISOString(),
  submitted_by: submitterName
};

Lambda Integration

We configured a Lambda function (deployed via existing deployment patterns) with the following responsibilities:

  • Receive task payload from GAS
  • Write task record to DynamoDB with timestamp and criticality index
  • Determine notification type based on criticality:
    • Critical (safety, electrical, structural): Immediate email to ops team
    • High (performance, safety-adjacent): Same-day digest email
    • Medium/Low: Weekly digest or dashboard-only
  • Invoke SES for email delivery with appropriate template

Staging vs. Production Strategy

Since we don't yet have a formal staging/production split for the maintenance tool, we implemented this pattern:

  • Staging deployment path: s3://jada-maintenance-staging/tools/maintenance/staging-index.html served via CloudFront distribution maintenance-staging.queenofsandiego.com
  • Testing email routing: All notifications in staging go to jadasailing@gmail.com regardless of action
  • Environment detection: Lambda checks the request source (staging vs. production CloudFront hostname) and routes accordingly
  • Future improvement: Add header-based or query-parameter-based environment detection to cleanly separate behaviors

CloudFront & S3 Configuration

The staging version is deployed to:

S3 Bucket: jada-maintenance-staging
Path: /tools/maintenance/staging-index.html
CloudFront Distribution: maintenance-staging.queenofsandiego.com
Origin: S3 bucket origin with OAI (Origin Access Identity)

After deployment, we invalidate the CloudFront cache:

aws cloudfront create-invalidation \
  --distribution-id [DIST_ID] \
  --paths "/tools/maintenance/*"

Key Decisions & Rationale

Why Lambda Instead of Pure GAS?

While GAS can send emails, Lambda provides several advantages:

  • Durability: Decouples the HTTP request from the notification operation, preventing lost tasks if the email fails
  • Scalability: Easy to add queuing, retries, and batch operations without GAS quota concerns
  • Integration: Natural home for future integrations with DynamoDB (task history), SNS (SMS alerts), and CloudWatch (monitoring)
  • Cost: Pay-per-invocation model is cheaper than GAS execution time for this workload

Why Criticality-Based Routing?

Industry best practices from high-performing operations teams (per Google SRE Handbook and Incident.io research) show that digest notifications reduce notification fatigue while real-time alerts for critical items maintain response SLAs. Our approach:

  • Critical tasks → immediate email (5-minute SLA)
  • High-priority → daily digest at 6 AM (24-hour SLA)
  • Medium/Low → weekly digest or dashboard-only

This prevents Slack/email fatigue while ensuring safety-critical items reach the team immediately.