diff --git a/web/templates/courses/detail.html b/web/templates/courses/detail.html index 7869d110d..5c0b64734 100644 --- a/web/templates/courses/detail.html +++ b/web/templates/courses/detail.html @@ -455,7 +455,7 @@

Session Calendar

+ class="bg-white dark:bg-gray-800 rounded-lg border dark:border-gray-700 overflow-visible">
Sun
Mon
@@ -470,13 +470,30 @@

Session Calendar

{% for day in week %}
{% if day.date %} -
- +
+ {{ day.date|date:"j" }} {% if day.has_session %}
+ {% endif %}
{% endif %} @@ -1517,18 +1534,46 @@

Course Reviews

const grid = document.getElementById('calendar-grid'); let html = ''; + function escapeHtml(text) { + const div = document.createElement('div'); + div.appendChild(document.createTextNode(text)); + return div.innerHTML; + } + data.calendar_weeks.forEach(week => { week.forEach(day => { - const today = day.is_today ? 'bg-gray-100 dark:bg-gray-700' : ''; + const todayCls = day.is_today ? 'bg-gray-100 dark:bg-gray-700' : ''; + let tooltipHtml = ''; + if (day.has_session && day.sessions && day.sessions.length > 0) { + const tooltipId = `tooltip-${day.date}`; + const items = day.sessions.map((s, i) => { + const sep = i > 0 ? 'mt-1.5 pt-1.5 border-t border-gray-600' : ''; + return `
+
${escapeHtml(s.title)}
+
+ ${escapeHtml(s.start_time)} - ${escapeHtml(s.end_time)} +
+
`; + }).join(''); + tooltipHtml = ` + `; + } + const ariaAttr = day.has_session && day.date ? `aria-describedby="tooltip-${day.date}"` : ''; html += ` -
+
${day.date ? ` -
- +
+ ${day.date.split('-')[2].replace(/^0/, '')} ${day.has_session ? `
+ ${tooltipHtml} ` : ''}
` : ''} diff --git a/web/tests/test_views.py b/web/tests/test_views.py index a5ff705f3..76757df6e 100644 --- a/web/tests/test_views.py +++ b/web/tests/test_views.py @@ -561,7 +561,7 @@ def test_enrolled_student_functionality(self): self.assertContains(response, "Completed") def test_calendar_display(self): - """Test that the session calendar is correctly displayed""" + """Test that the session calendar is correctly displayed with session details for tooltips""" # Request the calendar for the month containing the session session_month = self.future_session.start_time.month session_year = self.future_session.start_time.year @@ -581,6 +581,14 @@ def test_calendar_display(self): for day in week: if day["date"] and day["date"] == session_date: self.assertTrue(day["has_session"]) + # Verify session details are included for tooltip + self.assertIn("sessions", day) + self.assertGreater(len(day["sessions"]), 0) + session_info = day["sessions"][0] + self.assertIn("title", session_info) + self.assertIn("start_time", session_info) + self.assertIn("end_time", session_info) + self.assertEqual(session_info["title"], self.future_session.title) session_day_found = True break if session_day_found: @@ -588,6 +596,38 @@ def test_calendar_display(self): self.assertTrue(session_day_found, "Session date not found in calendar") + def test_calendar_ajax_endpoint(self): + """Test that the AJAX calendar endpoint returns session details for tooltips""" + session_month = self.future_session.start_time.month + session_year = self.future_session.start_time.year + url = reverse("course_calendar", args=[self.course.slug]) + response = self.client.get(f"{url}?year={session_year}&month={session_month}") + self.assertEqual(response.status_code, 200) + + data = response.json() + self.assertIn("calendar_weeks", data) + self.assertIn("current_month", data) + + # Find the session day in the JSON response + session_date = self.future_session.start_time.date() + session_day_found = False + for week in data["calendar_weeks"]: + for day in week: + if day["date"] and day["date"] == session_date.isoformat(): + self.assertTrue(day["has_session"]) + self.assertIn("sessions", day) + self.assertGreater(len(day["sessions"]), 0) + session_info = day["sessions"][0] + self.assertEqual(session_info["title"], self.future_session.title) + self.assertIn("start_time", session_info) + self.assertIn("end_time", session_info) + session_day_found = True + break + if session_day_found: + break + + self.assertTrue(session_day_found, "Session date not found in AJAX calendar response") + def test_session_completion_form(self): """Test session completion form for enrolled students""" self.client.login(username="student", password="testpass123") diff --git a/web/views.py b/web/views.py index 8dd972d98..8cbc9cbee 100644 --- a/web/views.py +++ b/web/views.py @@ -827,12 +827,22 @@ def course_detail(request, slug): # Get the calendar for current month cal = calendar.monthcalendar(current_month.year, current_month.month) - # Get all session dates for this course in current month - session_dates = set( - session.start_time.date() - for session in sessions - if session.start_time.year == current_month.year and session.start_time.month == current_month.month - ) + # Get all sessions for this course in current month, grouped by date + month_sessions = [ + s for s in sessions if s.start_time.year == current_month.year and s.start_time.month == current_month.month + ] + session_map = {} + for s in month_sessions: + d = s.start_time.date() + if d not in session_map: + session_map[d] = [] + session_map[d].append( + { + "title": s.title, + "start_time": s.start_time.strftime("%I:%M %p"), + "end_time": s.end_time.strftime("%I:%M %p"), + } + ) # Prepare calendar weeks data calendar_weeks = [] @@ -840,10 +850,27 @@ def course_detail(request, slug): calendar_week = [] for day in week: if day == 0: - calendar_week.append({"date": None, "in_month": False, "has_session": False}) + calendar_week.append( + { + "date": None, + "in_month": False, + "has_session": False, + "is_today": False, + "sessions": [], + } + ) else: date = current_month.replace(day=day) - calendar_week.append({"date": date, "in_month": True, "has_session": date in session_dates}) + day_sessions = session_map.get(date, []) + calendar_week.append( + { + "date": date, + "in_month": True, + "has_session": bool(day_sessions), + "is_today": date == today, + "sessions": day_sessions, + } + ) calendar_weeks.append(calendar_week) # Check if the current user has already reviewed this course @@ -3359,7 +3386,7 @@ def get_course_calendar(request, slug): calendar_week = [] for day in week: if day == 0: - calendar_week.append({"date": None, "has_session": False, "is_today": False}) + calendar_week.append({"date": None, "has_session": False, "is_today": False, "sessions": []}) else: date = timezone.datetime(year, month, day).date() sessions_on_day = [s for s in month_sessions if s.start_time.date() == date] @@ -3368,6 +3395,14 @@ def get_course_calendar(request, slug): "date": date.isoformat() if date else None, "has_session": bool(sessions_on_day), "is_today": date == today, + "sessions": [ + { + "title": s.title, + "start_time": s.start_time.strftime("%I:%M %p"), + "end_time": s.end_time.strftime("%I:%M %p"), + } + for s in sessions_on_day + ], } ) calendar_weeks.append(calendar_week)