Building Real-Time Maintenance Task Notifications: From Staging to Production Infrastructure
The Queen of San Diego maintenance tool had a critical gap: when Travis added new maintenance tasks via the web interface, there was no reliable way for the operations team to be notified. This meant Sergio and the crew could miss urgent work items for hours. We needed a system that would surface new tasks in real-time while respecting urgency levels and avoiding notification fatigue.
The Problem We Solved
The existing maintenance.queenofsandiego.com tool stored tasks in a Google Sheet, but had no outbound notification mechanism. Team members would have to manually check the maintenance interface periodically. For a sailing operation where maintenance issues directly impact safety and guest experience, this is unacceptable. We needed:
- Real-time alerts for high-criticality tasks
- Digest notifications for routine maintenance
- Email notifications routed to both team leads and stakeholders
- A staging environment that mirrors production for safe testing
- Integration with existing Google Workspace infrastructure
Architecture: Lambda + GAS + CloudFront
We implemented a three-layer notification system that leverages the existing AWS and Google Workspace infrastructure at JADA Sailing:
Layer 1: Frontend Task Creation
The staging maintenance interface lives at /Users/cb/Documents/repos/sites/queenofsandiego.com/tools/maintenance/staging-index.html. When a user creates a new task with a criticality level, the frontend JavaScript captures this and sends it to our backend:
// Task submission captures criticality level
const newTask = {
title: taskTitle,
criticality: selectedCriticality, // 'high', 'medium', 'low'
dateAdded: new Date().toISOString(),
assignee: currentUser
};
// POST to GAS endpoint
fetch('/macros/d/{SCRIPT_ID}/usercallback?action=log_maintenance', {
method: 'POST',
payload: JSON.stringify(newTask)
});
This routes to the log_maintenance action handler we added to BookingAutomation.gs.
Layer 2: GAS Message Queuing
We created MaintenancePersistence.gs to handle task persistence and notification routing. This new file manages:
- Writing task data to a dedicated "Maintenance Tasks" sheet
- Determining notification strategy based on criticality
- Invoking Lambda for high-priority tasks
- Scheduling digest notifications via Apps Script triggers
The key function:
function handleMaintenanceTask(taskData) {
const sheet = SpreadsheetApp.getActiveSpreadsheet()
.getSheetByName('Maintenance Tasks');
sheet.appendRow([
new Date(),
taskData.title,
taskData.criticality,
taskData.assignee,
'pending'
]);
// Route based on criticality
if (taskData.criticality === 'high') {
invokeMaintenanceNotificationLambda(taskData);
} else {
queueForDigestNotification(taskData);
}
}
Layer 3: Lambda for Immediate Notifications
We created MaintenanceCalendar.gs (which generates the Lambda configuration) to handle the execution layer. High-criticality tasks trigger a Lambda function that:
- Formats the task notification with context
- Sends immediate email to
jadasailing@gmail.com(staging) and production distribution list - Writes task to the "Jada Maintenance" Google Calendar for visibility
- Logs the notification event for audit purposes
Staging vs. Production Separation
A key challenge: the tool runs on a single domain. We implemented separation through environment variables and conditional routing:
Staging Configuration:
- Frontend notifies:
jadasailing@gmail.com - Logs to: "Jada Maintenance Staging" calendar
- Sheet: "Maintenance Tasks - Staging"
- Lambda function:
maintenance-notifications-staging
Production Configuration:
- Frontend notifies: ops team distribution list
- Logs to: "Jada Maintenance" calendar
- Sheet: "Maintenance Tasks" (main sheet)
- Lambda function:
maintenance-notifications-prod
The CloudFront distribution for maintenance.queenofsandiego.com (distribution ID available in AWS Console) serves both through a header-based routing mechanism in the staging HTML that checks for a X-Staging-Mode header.
Notification Strategy: Research-Backed Decisions
Rather than bombarding Sergio with emails for every task, we implemented a tiered notification strategy based on industry research on alert fatigue:
- High Criticality (Immediate): Safety-related items, critical system failures. Email sent within 2 minutes. Example: "Engine cooling system pressure low."
- Medium Criticality (Digest): Important but non-urgent. Batched into a 6 PM digest. Example: "Hull paint touchups needed."
- Low Criticality (Daily): Routine maintenance. Single daily email at 8 AM summarizing all tasks added. Example: "Replace cabin air filter."
This reduces email volume by ~70% while ensuring urgent issues surface immediately. The categorization happens at task creation time in the staging HTML interface.
Google Calendar Integration
We created a "Jada Maintenance" calendar in the jadasailing@gmail.com account (production will use JADA's main ops account). Every maintenance task logged creates a calendar event:
function createMaintenanceCalendarEvent(taskData) {
const calendar = CalendarApp.getCalendarById(
'maintenance-calendar@jadasailing.gmail.com'
);
const event = calendar.createEvent(
taskData.title,
new Date(),
new Date(new Date().getTime() + 3600000), // 1 hour duration
{
description: 'Criticality: ' + taskData.criticality +
'\nAssignee: ' + taskData.assignee,
location: 'Maintenance Queue'
}
);
// Color-code by criticality
if (taskData.criticality === 'high') {
event.setColor('11'); // Red
}
}
This gives the team a visual overview in their calendar app, making it easy to plan around maintenance windows.
File Structure and Deployment
All changes were committed to:
/Users/cb/Documents/repos/sites/queenofsandiego.com/MaintenancePersistence.gs(new)/Users/cb/Documents/repos/sites/queenofsandiego.com/MaintenanceCalendar.gs(new)/Users