Building Real-Time Task Notifications for the Maintenance Management System
The maintenance.queenofsandiego.com tool needed a critical feature: surface newly added tasks and notify stakeholders in real-time. Previously, when Travis or other team members added maintenance tasks, there was no mechanism to alert Sergio or other crew members. This post details the infrastructure and code changes required to implement task notification with criticality-aware delivery patterns.
Problem Statement
The maintenance tool lacked visibility into task additions. Without explicit notifications, new tasks could be missed, creating operational gaps. The challenge was implementing notifications that:
- Surface new tasks to all relevant stakeholders
- Use task criticality to determine notification frequency (immediate for critical tasks, digest for routine items)
- Support staging/production separation for the web-based tool
- Integrate with existing Google Workspace infrastructure
- Follow industry best practices for notification systems in high-performing teams
Architecture Overview
The solution spans multiple infrastructure components:
- Frontend: Modified
staging-index.htmlwith task submission interceptors - Routing: New maintenance action handlers in
BookingAutomation.gs - Persistence: New
MaintenancePersistence.gsfor task storage and lookup - Notifications: Lambda function for email delivery with criticality routing
- Calendar Integration: New
MaintenanceCalendar.gsfor Google Calendar sync - Distribution: CloudFront caching layer for
maintenance.queenofsandiego.com
Technical Implementation Details
Frontend Task Submission
Modified /Users/cb/Documents/repos/sites/queenofsandiego.com/tools/maintenance/staging-index.html to intercept task creation events. When a task form is submitted, JavaScript now captures:
- Task title and description
- Assigned crew member
- Criticality level (Critical / High / Medium / Low)
- Timestamp of task creation
The form submission triggers a POST to the GAS endpoint with action log_maintenance, passing task metadata for processing.
GAS Routing and Persistence
Created MaintenancePersistence.gs in the Apps Script project to handle task storage:
function storeMaintenance(taskData) {
// Store in Google Sheets as single source of truth
// Format: timestamp, title, assignee, criticality, status
const sheet = getMaintenanceSheet();
sheet.appendRow([
new Date(),
taskData.title,
taskData.assignee,
taskData.criticality,
'pending'
]);
return {success: true, taskId: generateTaskId()};
}
Updated BookingAutomation.gs doPost handler to route maintenance actions:
if (params.action === 'log_maintenance') {
const result = MaintenancePersistence.storeMaintenance(params.taskData);
triggerNotificationWorkflow(result.taskId, params.taskData);
}
Created MaintenanceCalendar.gs to sync tasks with Google Calendar for visibility across the Jada Sailing workspace. The calendar "Jada Maintenance" stores all maintenance events linked to their criticality.
Criticality-Aware Notification Logic
Research from high-performing operations teams (specifically studies on incident response and on-call rotations) shows that notification fatigue degrades team responsiveness. The solution implements tiered delivery:
- Critical: Immediate email to Sergio + calendar alert. These are safety or revenue-blocking issues.
- High: Immediate email notification. Same-day action required.
- Medium: Daily digest email (accumulated tasks sent once at end-of-business). Batching reduces context switching.
- Low: Weekly digest. Routine maintenance items reviewed in planning context.
This approach is backed by Atlassian's research on notification interruption costs and Google's SRE practices around alert fatigue.
Email Notification System
Implemented a Lambda function (deployed in AWS) that receives task notifications from the GAS workflow:
// Lambda receives event with taskData and criticality
// Routes to appropriate email queue and delivery schedule
exports.handler = async (event) => {
const criticality = event.taskData.criticality;
const recipient = process.env.MAINTENANCE_EMAIL; // jadasailing@gmail.com for staging
if (['Critical', 'High'].includes(criticality)) {
await sendImmediate(recipient, event.taskData);
} else {
await queueForDigest(recipient, event.taskData, criticality);
}
return {statusCode: 200};
};
The Lambda function uses SNS for immediate notifications and DynamoDB for digest queue storage. Digest emails are triggered by CloudWatch Events (daily at 5 PM Pacific for medium priority, weekly Monday 9 AM for low priority).
Staging/Production Separation
For now, staging and production use the same maintenance.queenofsandiego.com domain served by CloudFront distribution E2XXXXXXXXXXX. Separation achieved through:
- Environment variables in Lambda:
MAINTENANCE_EMAILset tojadasailing@gmail.comfor staging testing - HTML form includes hidden field:
<input type="hidden" name="environment" value="staging"> - GAS routing checks environment flag and logs to separate sheets before production deployment
- CloudFront cache invalidation used after each staging deployment to bypass caching
A future enhancement will use separate S3 origins or a Lambda@Edge function to cleanly separate staging and production at the infrastructure level.
Infrastructure Changes
S3 Buckets:
maintenance.queenofsandiego.comS3 bucket: stores liveindex.htmland assets- Staging version: uploaded to same bucket with path prefix or separate bucket (TBD)
CloudFront Distribution:
- Distribution ID:
E2XXXXXXXXXXX - Origin: S3 bucket for maintenance tool
- Cache invalidation command:
aws cloudfront create-invalidation --distribution-id E2XXXXXXXXXXX --paths "/*"
Google Workspace:
- Calendar: Created "Jada Maintenance" calendar in jadasailing@gmail.com account
- Shared with crew members for visibility into scheduled maintenance windows
AWS Lambda:
- IAM Role:
lambda-maintenance-notifications-rolewith permissions for SNS Publish and DynamoDB PutItem - Runtime: Node.js 18