Building a Real-Time Task Notification System for maintenance.queenofsandiego.com
The maintenance tool for Queen of San Diego needed a critical capability: surfacing newly added tasks to the operations team in real-time. Previously, when Travis or other team members added tasks, there was no systematic way for Sergio and the team to discover them. This post covers the architecture, implementation patterns, and infrastructure decisions made to solve this problem.
The Problem: Task Discovery and Notification
The maintenance.queenofsandiego.com tool had evolved into a critical operations dashboard, but it lacked a notification system. When new tasks were logged, team members had no way to know they'd been added unless they actively checked the tool. This created operational inefficiency and potential gaps in maintenance scheduling.
The requirements were:
- Surface new tasks added to the maintenance system
- Notify Sergio and the operations team
- Make intelligent decisions about notification frequency based on task criticality
- Implement in staging environment first (jadasailing@gmail.com for testing)
- Support calendar integration for scheduling visibility
Architecture: Multi-Layer Notification System
The solution uses a three-tier architecture combining Google Apps Script, AWS Lambda, and email notifications:
- Client Layer: Modified staging-index.html at
/Users/cb/Documents/repos/sites/queenofsandiego.com/tools/maintenance/staging-index.html - Google Apps Script Layer: BookingAutomation.gs as the webhook receiver and MaintenanceCalendar.gs for calendar operations
- Persistence Layer: New MaintenancePersistence.gs for Lambda integration and notification logic
- AWS Lambda: Serverless function handling async notification routing based on task criticality
- Calendar Integration: Google Calendar "Jada Maintenance" for team visibility
Technical Implementation
1. Task Persistence and Lambda Integration
Created MaintenancePersistence.gs as a new module dedicated to handling task data persistence and Lambda integration:
// MaintenancePersistence.gs - New file created
function persistMaintenanceTask(taskData) {
// Validates task data structure
// Writes to Google Sheets backing store
// Triggers Lambda for async notification routing
const lambdaEndpoint = PropertiesService.getScriptProperties()
.getProperty('LAMBDA_MAINTENANCE_ENDPOINT');
const payload = {
taskId: taskData.id,
taskTitle: taskData.title,
criticality: taskData.criticality,
timestamp: new Date().toISOString(),
addedBy: taskData.addedBy
};
const options = {
method: 'post',
payload: JSON.stringify(payload),
headers: {
'Content-Type': 'application/json',
'X-API-Key': PropertiesService.getScriptProperties()
.getProperty('LAMBDA_API_KEY')
}
};
UrlFetchApp.fetch(lambdaEndpoint, options);
}
This approach decouples the web request handling from notification delivery. The Lambda function can make intelligent decisions about batching, frequency, and routing without blocking the user's interaction with the maintenance tool.
2. GAS Action Routing in BookingAutomation.gs
Extended the existing BookingAutomation.gs doPost handler to route log_maintenance actions:
// In BookingAutomation.gs doPost function
case 'log_maintenance':
const taskPayload = {
id: generateTaskId(),
title: e.parameter.task_title,
description: e.parameter.task_description,
criticality: e.parameter.criticality || 'medium',
addedBy: e.parameter.added_by,
timestamp: new Date()
};
persistMaintenanceTask(taskPayload);
return ContentService.createTextOutput(JSON.stringify({
success: true,
taskId: taskPayload.id
})).setMimeType(ContentService.MimeType.JSON);
This centralizes action routing, keeping the GAS codebase maintainable while new functionality can be added as additional cases.
3. Calendar Integration
Created MaintenanceCalendar.gs to manage Google Calendar events for maintenance tasks:
// MaintenanceCalendar.gs - New file created
function createMaintenanceCalendarEvent(taskData) {
const calendar = CalendarApp.getCalendarById('jada2026');
const event = calendar.createEvent(
taskData.title,
new Date(taskData.timestamp),
new Date(new Date(taskData.timestamp).getTime() + 30*60000), // 30 min default
{
description: `Criticality: ${taskData.criticality}\nAdded by: ${taskData.addedBy}`,
guests: 'sergio@operations.example.com'
}
);
return event.getId();
}
The calendar ID jada2026 (per the CB-ADMIN-26 reference in electrical documentation) creates a shared visibility layer where the entire team can see maintenance work on a calendar view, integrating with existing scheduling practices.
4. Frontend Modifications to staging-index.html
The staging HTML required UI changes to support task submission with criticality levels. Key modifications:
// HTML task submission form
<form id="taskForm">
<input type="text" id="taskTitle" placeholder="Task Title" required>
<textarea id="taskDescription" placeholder="Detailed description"></textarea>
<select id="criticality">
<option value="low">Low - Can wait</option>
<option value="medium" selected>Medium - This week</option>
<option value="high">High - Tomorrow</option>
<option value="critical">Critical - Immediate</option>
</select>
<button type="submit">Add Task</button>
</form>
// JavaScript submission handler
document.getElementById('taskForm').addEventListener('submit', async (e) => {
e.preventDefault();
const payload = {
action: 'log_maintenance',
task_title: document.getElementById('taskTitle').value,
task_description: document.getElementById('taskDescription').value,
criticality: document.getElementById('criticality').value,
added_by: getCurrentUser()
};
const response = await fetch('https://script.google.com/macros/d/[SCRIPT_ID]/usercontent', {
method: 'POST',
body: JSON.stringify(payload)
});
if (response.ok) {
displaySuccessMessage('Task added and notifications sent');
document.getElementById('taskForm').reset();
}
});
Notification Strategy: Industry Best Practices
Rather than notify on every single task, we implemented intelligent notification batching based on criticality:
- Critical tasks: Immediate notification to Sergio (within 2