Skip to content

Display relevant weeks on exam timetable#4418

Merged
leslieyip02 merged 12 commits into
nusmodifications:masterfrom
not-a-scam:feat/full-week-exam
Jun 28, 2026
Merged

Display relevant weeks on exam timetable#4418
leslieyip02 merged 12 commits into
nusmodifications:masterfrom
not-a-scam:feat/full-week-exam

Conversation

@not-a-scam

Copy link
Copy Markdown
Contributor

Context

Initially just created to close #1717

Additionally updated exam timetable to only display the week when exams to be taken are happening. Previously, the first week will always be displayed even if there are no exams to be taken.
timetable with extra weeks

Implementation

Full Week Display

timetable with full week displayed
  • Check if there is a Saturday exam and display full week if there is
  • Change cell width to be equal across days with the addition of Sunday

Display relevant weeks

  1. Filter out examinable mods from visible mods
  2. Map visible and examinable mods to their exam dates
  3. Reduce by comparison to get first and last exam dates
  4. Normalise result to always be a Monday
  5. Calculate number of weeks for display
timetable with only relevant weeks

Other Information

First PR here! Feedback is appreciated!

  • Created it so that if there are no exams, the getExamCalendar would return the current date as the first date instead for simplicity, but not sure if there are better return value? Considered using the first day of exams from the academic calendar but I thought the additional call would be unnecessary since the value isn't used.

  • I think the addition of Sunday looks fine on mobile. Seemed to be a concern in Exams on Saturday should show full week #1717 but included a gif for reference

mobile view of timetable

@vercel

vercel Bot commented Jun 10, 2026

Copy link
Copy Markdown

@not-a-scam is attempting to deploy a commit to the modsbot's projects Team on Vercel.

A member of the Team first needs to authorize it.

@codecov

codecov Bot commented Jun 10, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 57.86%. Comparing base (988c6fd) to head (1eff3bf).
⚠️ Report is 247 commits behind head on master.

Additional details and impacted files
@@            Coverage Diff             @@
##           master    #4418      +/-   ##
==========================================
+ Coverage   54.52%   57.86%   +3.33%     
==========================================
  Files         274      316      +42     
  Lines        6076     7236    +1160     
  Branches     1455     1769     +314     
==========================================
+ Hits         3313     4187     +874     
- Misses       2763     3049     +286     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@vercel

vercel Bot commented Jun 28, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

2 Skipped Deployments
Project Deployment Actions Updated (UTC)
nusmods-export Ignored Ignored Preview Jun 28, 2026 4:52am
nusmods-website Ignored Ignored Preview Jun 28, 2026 4:52am

Request Review

@leslieyip02 leslieyip02 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM 👍

I've pushed some minor changes to use date-fns instead of custom logic, but other than that, the PR looks great!

Thanks for your contribution, and sorry for taking so long to approve this!

@leslieyip02

Copy link
Copy Markdown
Member

@greptileai

@greptile-apps

greptile-apps Bot commented Jun 28, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR refactors the exam timetable calendar to display only the weeks containing relevant exams (instead of always starting from the NUS academic calendar's fixed exam week), and expands the weekday view to Mon–Sun when a Saturday exam is detected. The dependency on NUSModerator.academicCalendar.getExamWeek is removed in favor of deriving the week range purely from the visible modules' exam dates.

  • Week range calculation: getExamCalendar now finds the earliest and latest exam dates among visible modules using reduce, snaps the start to the nearest Monday via date-fns startOfWeek, and computes weekCount with differenceInCalendarWeeks. Returns [new Date(), 0] (renders the "no exams" message) when no exam dates exist.
  • Full-week display: If any visible module has a Saturday exam (getDay() === 6), daysToDisplay is set to 7 (Mon–Sun via the newly exported DaysOfWeek constant); otherwise it stays 5 (Mon–Fri). The CSS column width is updated from 1/6 to 1/7 to match.
  • Timezone fix: ExamWeek.tsx date display changed from getUTCDate()/getUTCMonth() to getDate()/getMonth(), which is correct because toSingaporeTime deliberately shifts timestamps so that local-time methods return SGT values.

Confidence Score: 5/5

Safe to merge; the week-range calculation, Saturday detection, and timezone fix are all correct, and the new tests validate the key scenarios.

The new getExamCalendar logic correctly derives the first Monday and week count from visible exam dates using startOfWeek + differenceInCalendarWeeks, handles the zero-exam edge case, and is consistent with how modulesWithExamDate uses the same visible-module set. The switch from getUTCDate/getUTCMonth to getDate/getMonth in ExamWeek.tsx is correct because toSingaporeTime adjusts timestamps so local-time methods return SGT values, and the existing tests were updated to wrap firstDayOfExams with toSingaporeTime to remove timezone-sensitive raw Date literals.

The pre-existing test 'show month names only in the first cell and on first weekday of month' in ExamCalendar.test.tsx queries a selector that no longer matches any DOM nodes, so it never actually asserts anything — worth fixing alongside the rest of the test updates in this PR.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[ExamCalendar.render] --> B[getExamCalendar]
    B --> C[getVisibleModules]
    C --> D{examDates empty?}
    D -- Yes --> E[return new Date, weekCount=0]
    E --> F[Render 'no exams' message]
    D -- No --> G[reduce to firstExamDate & lastExamDate]
    G --> H[startOfWeek firstExamDate weekStartsOn: Monday]
    H --> I[differenceInCalendarWeeks + 1 = weekCount]
    I --> J[return firstDayOfExams, weekCount]
    J --> K[modulesWithExamDate]
    K --> L{any module getDay === 6? Saturday?}
    L -- Yes --> M[daysToDisplay = 7 Mon-Sun]
    L -- No --> N[daysToDisplay = 5 Mon-Fri]
    M --> O[Render table headers using DaysOfWeek]
    N --> O
    O --> P[For each week: render ExamWeek]
    P --> Q[addDays firstDayOfExams, offset for each column]
    Q --> R[getDate / getMonth local SGT-adjusted time]
    R --> S[Lookup modules by date key formatExamDate toISOString]
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
    A[ExamCalendar.render] --> B[getExamCalendar]
    B --> C[getVisibleModules]
    C --> D{examDates empty?}
    D -- Yes --> E[return new Date, weekCount=0]
    E --> F[Render 'no exams' message]
    D -- No --> G[reduce to firstExamDate & lastExamDate]
    G --> H[startOfWeek firstExamDate weekStartsOn: Monday]
    H --> I[differenceInCalendarWeeks + 1 = weekCount]
    I --> J[return firstDayOfExams, weekCount]
    J --> K[modulesWithExamDate]
    K --> L{any module getDay === 6? Saturday?}
    L -- Yes --> M[daysToDisplay = 7 Mon-Sun]
    L -- No --> N[daysToDisplay = 5 Mon-Fri]
    M --> O[Render table headers using DaysOfWeek]
    N --> O
    O --> P[For each week: render ExamWeek]
    P --> Q[addDays firstDayOfExams, offset for each column]
    Q --> R[getDate / getMonth local SGT-adjusted time]
    R --> S[Lookup modules by date key formatExamDate toISOString]
Loading

Reviews (2): Last reviewed commit: "fix: use toSingaporeTime in tests" | Re-trigger Greptile

Comment thread website/src/views/timetable/ExamWeek.test.tsx
@leslieyip02

Copy link
Copy Markdown
Member

@greptileai

@leslieyip02 leslieyip02 merged commit c8572e1 into nusmodifications:master Jun 28, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Exams on Saturday should show full week

2 participants