Building Real-Time Task Notifications for maintenance.queenofsandiego.com: From Manual Tracking to Automated Alerting
What We Built
We implemented a multi-tier notification system for the JADA maintenance tracking tool that automatically alerts team members when new maintenance tasks are added. The system intelligently routes notifications based on task criticality—urgent tasks trigger immediate email alerts while routine tasks aggregate into end-of-day digests. This solves the critical UX problem where Travis adds tasks to the system but Sergio has no visibility into what's new.
Technical Architecture Overview
The solution spans four integration layers:
- Frontend persistence layer: Modified staging HTML at
/tools/maintenance/staging-index.htmlto POST new tasks to a GAS endpoint - GAS routing layer: Extended
BookingAutomation.gswith maintenance action handlers - Persistence & aggregation: New
MaintenancePersistence.gsfile managing task state and notification batching - Calendar integration: New
MaintenanceCalendar.gscreating calendar events alongside email notifications
Frontend Changes: Capturing New Tasks
The staging maintenance tool required modifications to detect when new tasks are added. The HTML form in /tools/maintenance/staging-index.html now includes a POST handler that sends task data to our GAS endpoint whenever the "Add Task" action completes:
// In staging-index.html init() function
const newTaskPayload = {
action: 'log_maintenance',
taskName: taskInput.value,
criticality: criticalitySelect.value,
assignee: assigneeSelect.value,
timestamp: new Date().toISOString()
};
fetch(GAS_ENDPOINT_URL, {
method: 'POST',
payload: JSON.stringify(newTaskPayload)
});
Why this approach: Rather than implementing a separate API for maintenance tasks, we leveraged the existing GAS endpoint that already handles booking automations. This reduces operational surface area and keeps all automation logic in one proven system.
GAS Route Handler: Extending BookingAutomation.gs
The BookingAutomation.gs doPost handler already had a pattern for routing different actions. We added a new conditional branch:
// In BookingAutomation.gs doPost function
if (e.parameter.action === 'log_maintenance') {
const maintenanceModule = MaintenancePersistence;
return maintenanceModule.handleNewTask(e.parameter);
}
This routing pattern ensures that maintenance events flow through our dedicated persistence module while maintaining the single-entry-point pattern already established in the codebase.
Persistence Layer: MaintenancePersistence.gs
We created a new GAS file at the project root: MaintenancePersistence.gs. This file implements:
- Task deduplication: Uses a key hash to prevent duplicate entries when form submissions retry
- Criticality-based batching: Immediate notifications for "critical" or "urgent" tasks; digest batching for "routine" tasks
- Time-windowed aggregation: Collects routine tasks for 24-hour digest cycles
- Persistence layer: Stores task state in Google Sheets (sheet named "MaintenanceTasks") to survive script executions
// Core function signature in MaintenancePersistence.gs
function handleNewTask(params) {
const sheet = getMaintenanceSheet();
const taskKey = generateTaskKey(params);
if (isTaskDuplicate(taskKey, sheet)) {
return { status: 'duplicate' };
}
const row = appendTaskRow(sheet, params, taskKey);
if (isCriticalTask(params.criticality)) {
NotificationManager.sendImmediateAlert(params);
} else {
NotificationManager.scheduleDigestBatch(params);
}
MaintenanceCalendar.createCalendarEvent(params);
return { status: 'success', taskId: row };
}
Why Sheets as persistence: Google Sheets provides built-in audit trails, is queryable via GAS without additional infrastructure, and integrates naturally with the Google Calendar events we're creating. For this operational tool, eventual consistency from Sheets is appropriate.
Notification Strategy: Data-Driven Decisions
Industry research on operational alerting (pulling from incident response best practices at companies like PagerDuty and Opsgenie) shows that notification velocity should match task severity:
- Critical/Urgent tasks: Immediate email notification (within seconds). These represent safety issues, broken systems, or time-sensitive repairs.
- Routine tasks: End-of-day digest emails at 5 PM Pacific. Teams performing high-performing operations batch routine notifications to prevent alert fatigue.
- Digest recipients: Testing sends to
jadasailing@gmail.comfrom the "JADA Operations" email alias (requires setup in Google Workspace)
The criticality parameter is captured from the frontend form's <select id="criticality"> element, with values: "routine", "important", "urgent", "critical".
Calendar Integration: MaintenanceCalendar.gs
Parallel to email notifications, we create calendar events in the "Jada Maintenance" calendar under the jadasailing@gmail.com account. This surfaces tasks in the team's existing workflow without requiring a separate task management tool:
// In MaintenanceCalendar.gs
function createCalendarEvent(params) {
const calendar = CalendarApp.getCalendarById('JADA_MAINTENANCE_CALENDAR_ID');
const eventTitle = `[${params.criticality.toUpperCase()}] ${params.taskName}`;
const eventDesc = `Assignee: ${params.assignee}\nLogged: ${params.timestamp}`;
const event = calendar.createEvent(eventTitle,
new Date(params.timestamp),
new Date(new Date(params.timestamp).getTime() + 3600000) // 1 hour duration
);
event.setDescription(eventDesc);
if (params.criticality === 'critical') {
event.setColor(CalendarApp.EventColor.RED);
}
}
Calendar setup requirement: The Google Calendar named "Jada Maintenance" must exist under jadasailing@gmail.com. If it doesn't exist, create it via Google Calendar UI and share with appropriate team members.
Staging vs. Production: Testing Strategy
The challenge with maintenance.queenofsandiego.com is that it's a single deployed tool. Our approach:
- HTML staging: Modifications live in
/tools/maintenance/staging-index.html(separate S3 key from production) - GAS code: All versions deploy to the same Apps Script project, but we add a `ENVIRONMENT` constant at the top of each GAS file
- Email routing: When
ENVIRONMENT === 'staging', all emails go to jadasailing@gmail.com. Production emails go to Sergio and Travis - Calendar events: