Calendar Sync System - Technical Documentation
- Calendar Sync System - Technical Documentation
- Table of Contents
- System Overview
- Architecture
- HTML Modal Structure
- School Calendar Data System
- Core Functions
- Modal Management
- API Integration
- Data Flow
- Error Handling
- Quick Actions Reference
- Priority System
- Global State Variables
- Initialization
- Function Reference Summary
- Usage Examples
- Notes for Developers
Calendar Sync System - Technical Documentation
This document provides comprehensive technical documentation for the Selective Sync/Remove Modal System used in the Sprint Calendar feature. The system allows users to selectively sync or remove sprint events (formative and summative assignments) to/from their personal calendar.
Table of Contents
- System Overview
- Architecture
- HTML Modal Structure
- School Calendar Data System
- Core Functions
- Modal Management
- API Integration
- Data Flow
- Error Handling
System Overview
The Calendar Sync System provides a user-friendly interface for managing sprint-to-calendar synchronization. It consists of two primary modals:
| Modal | Purpose | Key Features |
|---|---|---|
| Selective Sync Modal | Add sprint events to calendar | Priority selection, item filtering, duplicate detection |
| Selective Remove Modal | Remove sprint events from calendar | Bulk removal, category filtering |
Key Capabilities
- Granular Selection: Users can select individual items, categories (formative/summative), or entire weeks
- Priority System: Events can be tagged with priority levels (P0-P3)
- School Calendar Integration: Automatically handles holidays and break weeks
- Date Intelligence: Adjusts dates based on school calendar (e.g., Tuesday if Monday is a holiday)
Architecture
Component Diagram
┌─────────────────────────────────────────────────────────────────┐
│ User Interface │
├─────────────────────────────────────────────────────────────────┤
│ ┌──────────────────┐ ┌──────────────────┐ │
│ │ Sync Modal │ │ Remove Modal │ │
│ │ - Week List │ │ - Week List │ │
│ │ - Priority │ │ - Quick Actions │ │
│ │ - Quick Actions │ │ │ │
│ └────────┬─────────┘ └────────┬─────────┘ │
│ │ │ │
│ ▼ ▼ │
│ ┌─────────────────────────────────────────┐ │
│ │ Modal Management Layer │ │
│ │ - openSelectiveSyncModal() │ │
│ │ - openSelectiveRemoveModal() │ │
│ │ - initializeSyncModalCheckboxes() │ │
│ └────────────────────┬────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────┐ │
│ │ School Calendar Data System │ │
│ │ - SCHOOL_CALENDAR object │ │
│ │ - Date calculation functions │ │
│ └────────────────────┬────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────┐ │
│ │ API Integration │ │
│ │ - /api/calendar/add_event │ │
│ │ - /api/calendar/delete_events │ │
│ └─────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
HTML Modal Structure
Selective Sync Modal
The sync modal (#selective-sync-modal) contains the following key sections:
<div id="selective-sync-modal" class="calendar-action-modal">
<div class="calendar-action-modal-content">
<!-- Header: Title and close button -->
<div class="calendar-action-modal-header">...</div>
<!-- Body: Sprint info, warnings, quick actions, priority, week list -->
<div class="calendar-action-modal-body">
<!-- Sprint Information Display -->
<div class="sync-modal-sprint-info">...</div>
<!-- Dynamic Warnings Container -->
<div id="sync-warnings-container">...</div>
<!-- Quick Action Buttons -->
<div class="sync-quick-actions">
<!-- Select All, Deselect All, Formative Only, Summative Only -->
</div>
<!-- Priority Level Radio Buttons (P0-P3) -->
<div class="sync-modal-priority">...</div>
<!-- Dynamically Generated Week List -->
<div class="sync-week-list" id="sync-week-list">...</div>
<!-- Selection Summary Counter -->
<div class="sync-summary">...</div>
</div>
<!-- Footer: Cancel and Confirm buttons -->
<div class="calendar-action-modal-footer">...</div>
</div>
</div>
Key CSS Classes
| Class | Purpose |
|---|---|
.calendar-action-modal |
Full-screen overlay container |
.calendar-action-modal-content |
Centered modal box |
.sync-week-group |
Container for each week’s items |
.sync-item-checkbox |
Individual selectable item |
.priority-badge |
Colored priority indicator (p0, p1, p2, p3) |
School Calendar Data System
The system loads school calendar data from a JSON script tag embedded in the page. This data drives all date calculations.
Data Structure
SCHOOL_CALENDAR = {
schoolYear: "2025-2026",
firstDay: "2025-08-18",
lastDay: "2026-05-29",
weeks: {
"1": {
monday: "2025-08-18",
friday: "2025-08-22",
tuesday: null, // Used if Monday is a holiday
holidays: null, // Array of holiday names
holidayAdjustment: null, // "tuesday" if Monday is holiday
skipWeek: false, // true for break weeks
theme: null,
notes: null
},
// ... more weeks
}
}
Key Date Functions
| Function | Description | Returns |
|---|---|---|
getCalendarWeek(weekNum) |
Gets week data for a calendar week | Week object or null |
isSkipWeek(weekNum) |
Checks if week is a break/skip week | Boolean |
getReadingDate(weekNum) |
Gets Monday (or Tuesday if holiday) for formative materials | Date string or null |
getAssessmentDate(weekNum) |
Gets Friday date for summative assessments | Date string or null |
getCheckpointDate(weekNum) |
Gets Tuesday of next valid school week | Date string or null |
formatDateDisplay(dateStr) |
Formats date for full display | “Jan 23, 2026” |
formatDateShort(dateStr) |
Formats date for compact display | “Jan 23” |
Core Functions
Sprint Synchronization
syncSprintToCalendar(sprintKey, course, startWeek, endWeek)
Syncs all sprint events to the calendar (direct sync without modal).
Parameters:
sprintKey: Sprint identifier (e.g., “sprint-1”)course: Course name (e.g., “csp”, “csa”)startWeek: Starting calendar week numberendWeek: Ending calendar week number
Process:
- Reads sync options from checkboxes (formative/summative)
- Gets selected priority level (P0-P3, default P2)
- Iterates through week cards to build event list
- Creates consolidated events per week (one formative, one summative)
- Sends parallel POST requests to
/api/calendar/add_event
Event Title Format:
[P2] 📚 Week 5 Formative - CSP
[P2] 📝 Week 5 Summative - CSP
deleteSprintFromCalendar(sprintKey, course, startWeek, endWeek)
Removes all sprint events from the calendar (direct delete).
Parameters: Same as syncSprintToCalendar
Process:
- Shows confirmation dialog
- Builds list of event titles to delete (all priority variants)
- Attempts bulk DELETE to
/api/calendar/delete_events - Falls back to individual DELETE requests if bulk endpoint unavailable
Modal Management
Opening Modals
openSelectiveSyncModal(sprintKey, course, startWeek, endWeek)
Opens the selective sync modal with populated data.
Steps:
- Stores modal data in
currentSyncModalData - Sets header with sprint title and date range
- Generates week selection HTML via
buildWeekSelectionHTML() - Checks for existing calendar events
- Initializes checkbox handlers
- Displays modal
openSelectiveRemoveModal(sprintKey, course, startWeek, endWeek)
Opens the selective remove modal (similar to sync modal).
Week Selection HTML Generation
buildWeekSelectionHTML(sprintKey, course, startWeek, endWeek, modalType)
Generates the hierarchical checkbox structure for item selection.
Output Structure:
Week Group (with week checkbox)
├── Warning banners (skip week, holiday, past date)
├── Formative Category (with category checkbox)
│ ├── Item 1 (individual checkbox)
│ └── Item 2 (individual checkbox)
└── Summative Category (with category checkbox)
├── Item 1 (individual checkbox)
└── Item 2 (individual checkbox)
Warning Types:
| Type | Icon | Condition |
|——|——|———–|
| Skip Week | 🏖️ | calendarWeek.skipWeek === true |
| Holiday | ⚠️ | calendarWeek.holidays && !skipWeek |
| Past Date | 🕐 | readingDate < today |
Checkbox State Management
The system maintains three-level checkbox hierarchy:
Week Checkbox (select all in week)
└── Category Checkbox (select all formative/summative)
└── Item Checkbox (individual item)
updateCategoryCheckboxState(listEl, weekNum, type)
Updates category checkbox based on its child items.
updateWeekCheckboxState(listEl, weekNum)
Updates week checkbox based on all child items.
Both functions handle the indeterminate state when partial selection occurs.
API Integration
Endpoints Used
| Endpoint | Method | Purpose |
|---|---|---|
/api/calendar/add_event |
POST | Create calendar event |
/api/calendar/delete_event |
DELETE | Delete single event |
/api/calendar/delete_events |
DELETE | Bulk delete events |
Event Object Structure
{
title: "[P2] 📚 Week 5 Formative - CSP",
description: "Reading Materials:\n\n• Lesson Title\n https://...",
date: "2026-01-27",
period: "CSP",
priority: "P2"
}
Delete Request Structure
Single Delete:
{ title: "Event Title" }
Bulk Delete:
{ titles: ["Event 1", "Event 2", ...] }
Authentication
All API calls use credentials: 'include' to send session cookies for authentication.
Data Flow
Sync Flow Diagram
User clicks "Advanced Sync" button
│
▼
openSelectiveSyncModal()
│
├── Read sprint card data attributes
│ (data-sprint, data-course, data-start-week, data-end-week)
│
├── Build week selection HTML
│ └── parseWeekItems() for each week card
│ (reads data-lessons, data-assignments)
│
├── Check for existing events (async)
│
└── Display modal
User selects items and clicks "Sync"
│
▼
executeSelectiveSync()
│
├── getSelectedItems('sync')
│ └── Returns { weekNum: { formative: [], summative: [] } }
│
├── Build event objects for each selected category
│ └── Get dates from getReadingDate() / getAssessmentDate()
│
├── Send parallel POST requests to API
│
└── Show toast notification with results
Data Attributes on Week Cards
The system reads data from week card elements:
<div class="week-card"
data-week="5"
data-lessons="Lesson 1|||/path/to/lesson;;;Lesson 2|||/path"
data-assignments="Assignment 1|||/path;;;Assignment 2|||/path">
</div>
Delimiter Conventions:
|||separates title from URL within an item;;;separates multiple items
Error Handling
Status Messages
The system uses two feedback mechanisms:
1. Inline Status (showDateStatus)
Updates a status element within the dropdown:
function showDateStatus(el, message, type) {
// type: 'loading', 'success', 'error', 'warning'
el.className = 'sprint-date-status ' + type;
el.textContent = message;
// Auto-clear after 5 seconds (except loading)
if (type !== 'loading') {
setTimeout(() => { el.textContent = ''; }, 5000);
}
}
2. Toast Notifications (showToastNotification)
Displays an overlay notification visible regardless of dropdown state:
function showToastNotification(message, type) {
// Creates DOM element with class: calendar-toast-notification
// Auto-removes after 5 seconds
// Includes close button for manual dismissal
}
API Error Handling
Sync Errors:
successCount === 0: “✗ Failed to add events. Are you logged in?”successCount < total: “⚠ Added {n}/{total} events” (partial success)
Delete Errors:
- Falls back from bulk endpoint (404) to individual requests
- “⚠ No events found to remove” if deletedCount is 0
Calendar Data Loading
try {
const calendarEl = document.getElementById('school-calendar-json');
SCHOOL_CALENDAR = JSON.parse(calendarEl.textContent);
} catch (e) {
console.warn('Unable to parse SCHOOL_CALENDAR, falling back to empty object');
SCHOOL_CALENDAR = { weeks: {} };
}
Quick Actions Reference
Sync Modal Quick Actions
| Button | ID | Action |
|---|---|---|
| Select All | sync-select-all |
Checks all item checkboxes |
| Deselect All | sync-deselect-all |
Unchecks all checkboxes |
| Formative Only | sync-select-formative |
Selects only formative items |
| Summative Only | sync-select-summative |
Selects only summative items |
Implementation Pattern
document.getElementById('sync-select-formative')?.addEventListener('click', () => {
// Check all formative items
document.querySelectorAll('#sync-week-list .item-checkbox[data-type="formative"]')
.forEach(cb => cb.checked = true);
// Uncheck all summative items
document.querySelectorAll('#sync-week-list .item-checkbox[data-type="summative"]')
.forEach(cb => cb.checked = false);
// Update category checkbox states
document.querySelectorAll('#sync-week-list .category-select-all[data-type="formative"]')
.forEach(cb => { cb.checked = true; cb.indeterminate = false; });
document.querySelectorAll('#sync-week-list .category-select-all[data-type="summative"]')
.forEach(cb => { cb.checked = false; cb.indeterminate = false; });
// Update week checkbox states
document.querySelectorAll('#sync-week-list .week-select-all').forEach(cb => {
const weekNum = cb.dataset.week;
updateWeekCheckboxState(document.getElementById('sync-week-list'), weekNum);
});
// Refresh counter
updateSelectedCount('sync');
});
Priority System
Priority Levels
| Priority | Badge Class | Typical Use Case |
|---|---|---|
| P0 | .p0 |
Critical/urgent deadlines |
| P1 | .p1 |
High priority assignments |
| P2 | .p2 |
Normal priority (default) |
| P3 | .p3 |
Low priority/optional |
Priority in Event Titles
Priority is embedded in the event title for visibility:
[P0] 📚 Week 5 Formative - CSP
[P1] 📝 Week 5 Summative - CSA
Backwards Compatibility
When deleting events, the system checks for all priority variants plus legacy format:
const priorities = ['P0', 'P1', 'P2', 'P3'];
priorities.forEach(p => {
eventTitlesToDelete.push(`[${p}] 📚 Week ${weekNum} Formative - ${courseName}`);
});
// Also check old format without priority
eventTitlesToDelete.push(`📚 Week ${weekNum} Formative - ${courseName}`);
Global State Variables
// Current modal context (stores data between open and confirm)
let currentSyncModalData = null; // { sprintKey, course, startWeek, endWeek }
let currentRemoveModalData = null; // { sprintKey, course, startWeek, endWeek }
// School calendar data (loaded on page init)
let SCHOOL_CALENDAR = {}; // { schoolYear, firstDay, lastDay, weeks: {...} }
// Storage key for sprint dates (if custom dates are used)
const SPRINT_DATES_STORAGE_KEY = 'sprintDates';
Initialization
The system is initialized when the DOM is ready via initializeSprintDates():
async function initializeSprintDates() {
// 1. Populate sprint date previews
document.querySelectorAll('.sync-sprint-calendar-btn').forEach(btn => {
populateSprintDatePreview(...);
});
// 2. Setup dropdown toggle handlers
document.querySelectorAll('.sprint-btn.date-toggle').forEach(btn => {...});
// 3. Setup close dropdown handlers
document.querySelectorAll('.date-dropdown-close').forEach(btn => {...});
// 4. Close dropdowns on outside click
document.addEventListener('click', ...);
// 5. Direct sync button handlers
document.querySelectorAll('.sync-sprint-calendar-btn').forEach(btn => {...});
// 6. Advanced sync modal button handlers
document.querySelectorAll('.advanced-sync-btn').forEach(btn => {...});
// 7. Direct delete button handlers
document.querySelectorAll('.delete-sprint-calendar-btn').forEach(btn => {...});
// 8. Advanced remove modal button handlers
document.querySelectorAll('.advanced-remove-btn').forEach(btn => {...});
// 9. Initialize modal event handlers
initializeSelectiveSyncModals();
}
Modal Initialization
initializeSelectiveSyncModals() sets up:
- Close button handlers for both modals
- Confirm button handlers (executeSelectiveSync, executeSelectiveRemove)
- All quick action buttons for both modals
- Outside-click-to-close behavior
Function Reference Summary
Date Functions
| Function | Purpose |
|———-|———|
| getNextValidSchoolWeek(startWeekNum) | Find next non-skip week |
| isSchoolWeek(weekNum) | Check if week is a school day |
| getCalendarWeek(weekNum) | Get raw week data |
| isSkipWeek(weekNum) | Check if week is a break |
| getReadingDate(weekNum) | Get formative material date |
| getAssessmentDate(weekNum) | Get summative date |
| getCheckpointDate(weekNum) | Get checkpoint date (next Tuesday) |
| formatDateDisplay(dateStr) | Format: “Jan 23, 2026” |
| formatDateShort(dateStr) | Format: “Jan 23” |
| getSprintDateRange(startWeek, endWeek) | Get sprint start/end dates |
Sync Functions
| Function | Purpose |
|———-|———|
| syncSprintToCalendar(...) | Direct full sprint sync |
| deleteSprintFromCalendar(...) | Direct full sprint delete |
| executeSelectiveSync() | Modal-based selective sync |
| executeSelectiveRemove() | Modal-based selective delete |
Modal Functions
| Function | Purpose |
|———-|———|
| openSelectiveSyncModal(...) | Open sync modal |
| openSelectiveRemoveModal(...) | Open remove modal |
| closeSelectiveSyncModal() | Close sync modal |
| closeSelectiveRemoveModal() | Close remove modal |
| buildWeekSelectionHTML(...) | Generate checkbox HTML |
| parseWeekItems(weekCard) | Extract items from week card |
Checkbox Management
| Function | Purpose |
|———-|———|
| initializeSyncModalCheckboxes(modalType) | Setup checkbox handlers |
| updateCategoryCheckboxState(...) | Sync category with items |
| updateWeekCheckboxState(...) | Sync week with categories |
| updateSelectedCount(modalType) | Update counter display |
| getSelectedItems(modalType) | Get checked items |
UI Functions
| Function | Purpose |
|———-|———|
| showDateStatus(el, message, type) | Inline status message |
| showToastNotification(message, type) | Toast overlay |
| populateSprintDatePreview(...) | Show date range in dropdown |
| checkForExistingEvents(...) | Check for duplicates |
Usage Examples
Example: Syncing a Sprint
// Direct sync (all items, default priority)
await syncSprintToCalendar('sprint-1', 'csp', 1, 4);
// Advanced sync (selective, via modal)
openSelectiveSyncModal('sprint-1', 'csp', 1, 4);
// User selects items...
// User clicks "Sync Selected"
// executeSelectiveSync() is called automatically
Example: Removing Events
// Direct remove (all items)
await deleteSprintFromCalendar('sprint-1', 'csp', 1, 4);
// Advanced remove (selective, via modal)
openSelectiveRemoveModal('sprint-1', 'csp', 1, 4);
// User selects items...
// User clicks "Remove Selected"
// executeSelectiveRemove() is called automatically
Example: Getting School Calendar Dates
// Get Monday date for Week 5
const readingDate = getReadingDate(5); // "2026-02-03"
// Get Friday date for Week 5
const assessmentDate = getAssessmentDate(5); // "2026-02-07"
// Check if Week 10 is a break
if (isSkipWeek(10)) {
console.log('Week 10 is a break week');
}
Notes for Developers
- Checkpoints are NOT synced - They are manually added by instructors via the calendar UI
- Duplicates are handled by the backend - The API matches on title and date
- Bulk delete fallback - If
/delete_eventsreturns 404, individual deletes are used - Priority backwards compatibility - Old events without
[P#]prefix are also deleted - Break weeks show warnings but items can still be synced (useful for pre-break reminders)