Building a Task Notification Pipeline for maintenance.queenofsandiego.com: Real-Time Crew Alerts via Email and Calendar Integration
What Was Done
We implemented an end-to-end notification system for the maintenance tool that alerts crew members (specifically Sergio) when new maintenance tasks are added. The system combines Google Apps Script (GAS) handlers, Lambda persistence, email notifications, and Google Calendar integration to ensure critical maintenance work doesn't slip through the cracks. This work was completed in the staging environment at maintenance.queenofsandiego.com with testing directed to jadasailing@gmail.com.
The Problem We Solved
Travis was adding new tasks to the maintenance tool, but there was no mechanism to notify the team. Visibility into new work items was poor, and there was no audit trail. We needed a solution that:
- Surfaces new tasks in real-time
- Notifies Sergio and other crew leads immediately
- Supports variable notification cadence based on task criticality
- Integrates with existing calendar systems (JADA Maintenance calendar)
- Maintains separation between staging and production environments
Technical Architecture
Component 1: GAS Maintenance Route Handler
We added a new route handler in /Users/cb/Documents/repos/sites/queenofsandiego.com/BookingAutomation.gs that intercepts maintenance-related POST requests:
if (action === 'log_maintenance') {
const result = LogMaintenance.handleLogMaintenance(requestData);
return ContentService
.createTextOutput(JSON.stringify(result))
.setMimeType(ContentService.MimeType.JSON);
}
This routing pattern follows our existing booking automation conventions, making it maintainable and consistent with the codebase.
Component 2: MaintenancePersistence.gs – The Persistence Layer
Created a new file at /Users/cb/Documents/repos/sites/queenofsandiego.com/MaintenancePersistence.gs that handles:
- Task validation and storage: Receives task data from the maintenance UI, validates required fields (title, category, priority level)
- Duplicate detection: Checks against recent task history to prevent duplicate notifications
- Lambda invocation: Triggers the notification Lambda asynchronously so the UI doesn't wait for email/calendar operations
- Audit logging: Writes to a Google Sheet for historical tracking (accessible at the JADA Internal drive)
The key function signature:
function handleLogMaintenance(taskData) {
// Validate: title, category, priority (HIGH, MEDIUM, LOW)
// Store in Sheet
// Invoke Lambda for notifications
// Return confirmation with task ID
}
Component 3: MaintenanceCalendar.gs – Calendar Integration
Created /Users/cb/Documents/repos/sites/queenofsandiego.com/MaintenanceCalendar.gs to manage calendar events. This file:
- Integrates with the JADA Maintenance calendar (created under
jadasailing@gmail.com) - Creates calendar events for all tasks, with event details including criticality
- Uses recurring event patterns for recurring maintenance (e.g., weekly electrical checks)
- Tags events with the task ID for bidirectional linking with the maintenance tool
We created the calendar via the Google Calendar UI and confirmed it exists in the JADA account. All subsequent events are created via GAS CalendarApp.
Component 4: Staging HTML Modifications
Modified /Users/cb/Documents/repos/sites/queenofsandiego.com/tools/maintenance/staging-index.html to add:
- Task submission handler: When users add a task, the form POSTs to the GAS endpoint with action
log_maintenance - Real-time UI feedback: Toast notifications showing "Task added - notifications sent"
- Task history sidebar: Shows recently added tasks with timestamps and who added them
- Criticality badges: Visual indicators (color-coded) for HIGH/MEDIUM/LOW priority
The key form submission:
document.getElementById('addTaskForm').addEventListener('submit', async (e) => {
e.preventDefault();
const taskData = {
action: 'log_maintenance',
title: document.getElementById('taskTitle').value,
category: document.getElementById('taskCategory').value,
priority: document.getElementById('taskPriority').value,
assignedTo: document.getElementById('taskAssigned').value,
notes: document.getElementById('taskNotes').value,
timestamp: new Date().toISOString()
};
const response = await fetch(GAS_ENDPOINT_URL, {
method: 'POST',
payload: JSON.stringify(taskData)
});
// Handle response
});
Infrastructure: Lambda and Email Pipeline
Lambda Function Deployment
We deployed a Lambda function (exact name: maintenance-task-notifier) that receives events from the GAS persistence layer. The function:
- Determines notification cadence: HIGH priority tasks trigger immediate email; MEDIUM tasks are collected for hourly digest; LOW tasks go to daily digest
- Sends email via SES: Using the
noreply@queenofsandiego.comalias (or appropriate alias for task notifications) - Calls Calendar API: Creates corresponding events in the JADA Maintenance calendar
- Writes to DynamoDB: Stores task metadata for dashboards and reporting
The Lambda IAM role uses the existing pattern from tips-box Lambda deployments with added permissions for:
ses:SendEmailandses:SendRawEmaildynamodb:PutItemanddynamodb:Query- Google Calendar API credentials (via Secrets Manager)
Email Template Strategy
HIGH priority emails go immediately with subject line: 🚨 [URGENT] New Maintenance Task: {title}
MEDIUM priority tasks are digested hourly: 📋 Maintenance Update - Hourly Digest
LOW priority tasks are in daily digest: 📅 Daily Maintenance Summary
Staging vs. Production Separation
Since both staging and production use the same CloudFront distribution pointing to the S3 maintenance bucket, we implemented separation at the application layer:
- Environment detection: The staging HTML checks hostname; if
staging-is in the path, it setsENVIRONMENT='staging' - Email routing in Lambda: When environment is staging, all emails go to
jadasailing@gmail.com(test address) instead of Sergio's production address