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

  1. System Overview
  2. Architecture
  3. HTML Modal Structure
  4. School Calendar Data System
  5. Core Functions
  6. Modal Management
  7. API Integration
  8. Data Flow
  9. 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 number
  • endWeek: Ending calendar week number

Process:

  1. Reads sync options from checkboxes (formative/summative)
  2. Gets selected priority level (P0-P3, default P2)
  3. Iterates through week cards to build event list
  4. Creates consolidated events per week (one formative, one summative)
  5. 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:

  1. Shows confirmation dialog
  2. Builds list of event titles to delete (all priority variants)
  3. Attempts bulk DELETE to /api/calendar/delete_events
  4. Falls back to individual DELETE requests if bulk endpoint unavailable

Opening Modals

openSelectiveSyncModal(sprintKey, course, startWeek, endWeek)

Opens the selective sync modal with populated data.

Steps:

  1. Stores modal data in currentSyncModalData
  2. Sets header with sprint title and date range
  3. Generates week selection HTML via buildWeekSelectionHTML()
  4. Checks for existing calendar events
  5. Initializes checkbox handlers
  6. 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();
}

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 |

| 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

  1. Checkpoints are NOT synced - They are manually added by instructors via the calendar UI
  2. Duplicates are handled by the backend - The API matches on title and date
  3. Bulk delete fallback - If /delete_events returns 404, individual deletes are used
  4. Priority backwards compatibility - Old events without [P#] prefix are also deleted
  5. Break weeks show warnings but items can still be synced (useful for pre-break reminders)