Skip to content

Fix/final UI fixes#73

Merged
BeWelsh merged 2 commits into
mainfrom
fix/final-ui-fixes
Apr 24, 2026
Merged

Fix/final UI fixes#73
BeWelsh merged 2 commits into
mainfrom
fix/final-ui-fixes

Conversation

@BeWelsh
Copy link
Copy Markdown
Collaborator

@BeWelsh BeWelsh commented Apr 24, 2026

Description

Time block creation/selection and semester name display

Type of Change

  • Bug fix (non-breaking change that fixes an issue)
  • New feature (non-breaking change that adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Refactoring (code improvement without changing functionality)
  • Documentation update
  • Configuration/infrastructure change
  • Performance improvement
  • Test coverage improvement

What Changed?

  • Fixed timeselect component to create new timeblocks and display all time blocks (with standard filter)
  • Switched all components in FE that were displaying semester ID to semester name

Testing & Validation

How this was tested

Screenshots/Recordings

Unfinished Work & Known Issues

  • None, this PR is complete and production-ready
  • The following items are intentionally deferred:



Notes & Nuances



Reviewer Notes

  • Areas needing extra attention: ...
  • Questions for reviewers: ...

Summary by CodeRabbit

Release Notes

  • New Features

    • Time selector now preserves partial hour/minute selections as drafts
    • Added "Standard only" toggle to filter time block options
    • Semester identifiers throughout the app now display as human-readable labels (e.g., "Fall 2025") instead of numeric IDs
  • Tests

    • Extended test mocks to support semester-related endpoints

@BeWelsh BeWelsh requested a review from Saisri24 April 24, 2026 01:18
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 24, 2026

Walkthrough

Multiple pages (Faculty, ScheduleList, Schedules) are updated to load semester metadata on mount and display human-readable season/year labels instead of numeric semester IDs. SectionMutationDrawer's TimeSelect component now maintains local draft state for hour/minute selections and adds a "Standard only" toggle for filtering time block options.

Changes

Cohort / File(s) Summary
Semester Label Loading & Display
frontend/src/pages/Faculty.tsx, frontend/src/pages/ScheduleList.tsx, frontend/src/pages/Schedules.tsx
Each page now loads semester metadata on mount, creates a memoized lookup map from semester_id to ${season} ${year} labels, and updates UI elements to display human-readable semester labels instead of numeric IDs.
TimeSelect Draft State & Time Block Filtering
frontend/src/components/SectionMutationDrawer.tsx
TimeSelect component maintains local draft state for hour/minute selections, only calling onChange when both are set, and re-syncs when parent value changes. Time block options now support a "Standard only" toggle that filters the dropdown while keeping the current selection visible; UI header adjusted with new checkbox.
Test Mocks for Semester Endpoints
frontend/src/pages/Faculty.test.tsx, frontend/src/pages/Schedules.test.tsx
Test setup extended with mocked semester-related API methods (getAllSemestersSemestersGet and getSemesterSemestersSemesterIdGet) to support semester-dependent test logic.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested reviewers

  • Saisri24
  • jobvengalil

Poem

🐰 A rabbit hops through semester labels bright,
Draft states persist where hour meets night,
TimeSelect whispers, "Wait for two,"
Then calls onChange—oh, so true!
Standards filter, selections stay,
Human-readable labels save the day! 🐾

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 44.44% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title is too vague and generic. 'Fix/final UI fixes' uses non-descriptive language that doesn't convey what specific changes were made in the changeset. Use a more specific title that highlights the main change, such as 'Display semester names instead of IDs and fix time block selection UI' or similar.
✅ Passed checks (3 passed)
Check name Status Explanation
Description check ✅ Passed The description includes the main change summary but has incomplete testing validation steps (1., 2., 3. are empty) and unfilled placeholder sections for notes and reviewer guidance.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/final-ui-fixes

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
frontend/src/pages/Schedules.tsx (1)

61-80: ⚠️ Potential issue | 🟡 Minor

Stale semesterLabel / race on scheduleId change.

Two related concerns with the new semester fetch:

  1. No reset on scheduleId changeschedule, campusName, and now semesterLabel are never cleared when scheduleId changes. While the new schedule is loading, the header will keep rendering the previous schedule's semester label (and campus name). The {schedule && semesterLabel && …} guard doesn't help because both values remain truthy from the prior schedule.
  2. Unguarded async chain — if the user navigates between schedules quickly, the inner getSemesterSemestersSemesterIdGet from an earlier run can resolve after the newer run and overwrite semesterLabel with the previous semester. Same class of race applies to setSchedule/setCampusName, but semesterLabel is new here.

A cancellation flag in the effect addresses both:

🛠 Proposed fix
   useEffect(() => {
+    let cancelled = false;
+    setSchedule(null);
+    setSemesterLabel(null);
+    setCampusName(null);
     const api = getAutomatedCourseSchedulerAPI();
     api.getScheduleSchedulesScheduleIdGet(scheduleId)
       .then((s) => {
+        if (cancelled) return;
         setSchedule(s);
         api.getSemesterSemestersSemesterIdGet(s.semester_id)
-          .then((sem) => setSemesterLabel(`${sem.season} ${sem.year}`))
+          .then((sem) => { if (!cancelled) setSemesterLabel(`${sem.season} ${sem.year}`); })
           .catch(() => {});
         // Resolve campus name for drawer filtering
         return api.getAllCampusesCampusesGet().then((campuses) => {
+          if (cancelled) return;
           const match = campuses.find((c) => c.campus_id === s.campus);
           if (match) setCampusName(match.name);
         });
       })
       .catch(() => {});
+    return () => { cancelled = true; };
   }, [scheduleId]);

Also applies to: 135-137

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/pages/Schedules.tsx` around lines 61 - 80, When scheduleId
changes you must clear previous state and guard async callbacks to avoid races:
inside the useEffect that calls
getAutomatedCourseSchedulerAPI()/getScheduleSchedulesScheduleIdGet(scheduleId)
reset setSchedule(null), setCampusName(null), and setSemesterLabel(null)
immediately, then use a local cancellation flag (e.g., let cancelled = false;
and a cleanup that sets it true) before running each async .then/.catch chain;
after each awaited API response check if cancelled is false before calling
setSchedule, setCampusName, or setSemesterLabel (references: useEffect,
scheduleId, getAutomatedCourseSchedulerAPI, getScheduleSchedulesScheduleIdGet,
getSemesterSemestersSemesterIdGet, getAllCampusesCampusesGet, setSchedule,
setCampusName, setSemesterLabel).
🧹 Nitpick comments (3)
frontend/src/pages/Schedules.test.tsx (1)

89-91: Consider adding coverage for the new semester label render.

The mock is wired correctly, but none of the existing assertions actually exercise the new semesterLabel <p> added in Schedules.tsx (line 136). Because schedule.name is also 'Fall 2025' in the mock, findByRole('heading', { name: 'Fall 2025' }) would pass even if the semester label weren't rendered. An explicit assertion (e.g. targeting the sub-paragraph with a different season/year than the schedule name) would guard the new behavior.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/pages/Schedules.test.tsx` around lines 89 - 91, Add an explicit
assertion in the Schedules.test.tsx to ensure the new semester label paragraph
renders: after the existing mock for getSemesterSemestersSemesterIdGet and
rendering of the Schedules component, query for the semester label element (the
<p> rendered from semesterLabel in Schedules.tsx) and assert its text content
matches the semester mock (e.g., "Fall 2025") separately from the heading
assertion; this can be done by using getByText or findByText targeting the
paragraph string or by querying a role/aria-label if you add one to the
paragraph, referencing the getSemesterSemestersSemesterIdGet mock and the
semesterLabel output to locate the code to assert.
frontend/src/components/SectionMutationDrawer.tsx (2)

120-135: JSDoc block is misplaced — it describes TimeSelect, not parseTimeValue.

The doc comment at lines 120–125 (“Compound AM/PM time selector…”) sits directly above parseTimeValue due to the refactor, so tooling/hover docs will attach it to the wrong symbol. Move it back above TimeSelect and add a brief one-liner for parseTimeValue.

♻️ Proposed fix
-/**
- * Compound AM/PM time selector — three linked selects for hour, minute, period.
- * Reports the chosen time as a "HH:MM" 24-hour string via `onChange`, but only
- * once hour and minute are both set. Local state retains partial selections so
- * each dropdown "sticks" as the user works through them.
- */
+/** Parse a "HH:MM" 24-hour string into the {hour, minute, period} draft shape. */
 function parseTimeValue(value: string): { hour: string; minute: string; period: 'AM' | 'PM' } {
   ...
 }
 
+/**
+ * Compound AM/PM time selector — three linked selects for hour, minute, period.
+ * Reports the chosen time as a "HH:MM" 24-hour string via `onChange`, but only
+ * once hour and minute are both set. Local state retains partial selections so
+ * each dropdown "sticks" as the user works through them.
+ */
 function TimeSelect({ value, onChange }: { value: string; onChange: (v: string) => void }) {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/components/SectionMutationDrawer.tsx` around lines 120 - 135,
The JSDoc describing the compound AM/PM selector is currently above
parseTimeValue; move that multi-line comment so it sits immediately above the
TimeSelect component (symbol TimeSelect) and add a short one-line JSDoc summary
above parseTimeValue (symbol parseTimeValue) such as "Parse a 'HH:MM' 24-hour
string into hour, minute, and AM/PM period." Ensure the new placement preserves
existing wording for TimeSelect and the one-liner succinctly describes
parseTimeValue's return shape.

670-712: Filter logic looks correct; minor DRY opportunity.

The showOnlyStandard branch correctly preserves (a) the currently-selected block and (b) every split-block half (block_group != null), so toggling the checkbox can't hide the user's selection or orphan a split pair. Sorting by start_time before the partner lookup is also the right order so the earlier half of a pair drives the label.

Nit: the inline STANDARD_SEQUENCES.some(...) at lines 678–680 duplicates the exact predicate already encapsulated in findSequenceName. You could reuse it:

♻️ Proposed fix
-          if (tb.block_group != null) return true;
-          return STANDARD_SEQUENCES.some(
-            (s) => s.days === tb.days && s.start === tb.start_time && s.end === tb.end_time,
-          );
+          if (tb.block_group != null) return true;
+          return findSequenceName(tb.days, tb.start_time, tb.end_time) !== null;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/components/SectionMutationDrawer.tsx` around lines 670 - 712,
The filter duplicates the sequence-matching predicate; in the useMemo for
timeBlockOptions (the showOnlyStandard branch) replace the inline
STANDARD_SEQUENCES.some(...) test with a call to the existing sequence helper
(e.g., use findSequenceName or the function that returns the sequence name) and
check its truthiness so you reuse that logic and avoid duplication while keeping
the existing behavior of preserving the selected block and block_group items.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@frontend/src/components/SectionMutationDrawer.tsx`:
- Around line 145-148: The update function currently only calls onChange when
both next.hour and next.minute are truthy, so clearing either select leaves the
parent with the previous complete time; modify update (in
SectionMutationDrawer.tsx) to call onChange with an empty string (or null if
your app prefers) whenever the draft transitions from a complete state to an
incomplete state, and only call onChange(to24Hour(...)) when both next.hour and
next.minute are present; use the existing setDraft, onChange, and to24Hour
identifiers to implement this guard/emit-empty behavior so the parent reflects
cleared selections immediately.

---

Outside diff comments:
In `@frontend/src/pages/Schedules.tsx`:
- Around line 61-80: When scheduleId changes you must clear previous state and
guard async callbacks to avoid races: inside the useEffect that calls
getAutomatedCourseSchedulerAPI()/getScheduleSchedulesScheduleIdGet(scheduleId)
reset setSchedule(null), setCampusName(null), and setSemesterLabel(null)
immediately, then use a local cancellation flag (e.g., let cancelled = false;
and a cleanup that sets it true) before running each async .then/.catch chain;
after each awaited API response check if cancelled is false before calling
setSchedule, setCampusName, or setSemesterLabel (references: useEffect,
scheduleId, getAutomatedCourseSchedulerAPI, getScheduleSchedulesScheduleIdGet,
getSemesterSemestersSemesterIdGet, getAllCampusesCampusesGet, setSchedule,
setCampusName, setSemesterLabel).

---

Nitpick comments:
In `@frontend/src/components/SectionMutationDrawer.tsx`:
- Around line 120-135: The JSDoc describing the compound AM/PM selector is
currently above parseTimeValue; move that multi-line comment so it sits
immediately above the TimeSelect component (symbol TimeSelect) and add a short
one-line JSDoc summary above parseTimeValue (symbol parseTimeValue) such as
"Parse a 'HH:MM' 24-hour string into hour, minute, and AM/PM period." Ensure the
new placement preserves existing wording for TimeSelect and the one-liner
succinctly describes parseTimeValue's return shape.
- Around line 670-712: The filter duplicates the sequence-matching predicate; in
the useMemo for timeBlockOptions (the showOnlyStandard branch) replace the
inline STANDARD_SEQUENCES.some(...) test with a call to the existing sequence
helper (e.g., use findSequenceName or the function that returns the sequence
name) and check its truthiness so you reuse that logic and avoid duplication
while keeping the existing behavior of preserving the selected block and
block_group items.

In `@frontend/src/pages/Schedules.test.tsx`:
- Around line 89-91: Add an explicit assertion in the Schedules.test.tsx to
ensure the new semester label paragraph renders: after the existing mock for
getSemesterSemestersSemesterIdGet and rendering of the Schedules component,
query for the semester label element (the <p> rendered from semesterLabel in
Schedules.tsx) and assert its text content matches the semester mock (e.g.,
"Fall 2025") separately from the heading assertion; this can be done by using
getByText or findByText targeting the paragraph string or by querying a
role/aria-label if you add one to the paragraph, referencing the
getSemesterSemestersSemesterIdGet mock and the semesterLabel output to locate
the code to assert.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: fe5b574a-c3b3-4efc-9af5-640380510558

📥 Commits

Reviewing files that changed from the base of the PR and between d67eda6 and 607c388.

📒 Files selected for processing (6)
  • frontend/src/components/SectionMutationDrawer.tsx
  • frontend/src/pages/Faculty.test.tsx
  • frontend/src/pages/Faculty.tsx
  • frontend/src/pages/ScheduleList.tsx
  • frontend/src/pages/Schedules.test.tsx
  • frontend/src/pages/Schedules.tsx

Comment on lines +145 to 148
function update(next: { hour: string; minute: string; period: 'AM' | 'PM' }) {
setDraft(next);
if (next.hour && next.minute) onChange(to24Hour(next.hour, next.minute, next.period));
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Clearing a completed selection leaves stale time in parent.

update only calls onChange when both next.hour and next.minute are truthy. If a user has already committed a valid time (e.g. 08:30) and then reopens the Hour or Minute select and picks the placeholder (""), the draft clears visually but the parent state retains the old 08:30. Submitting the form then persists a value the user believes they cleared.

Consider emitting an empty string to the parent when the draft transitions from complete → incomplete, or simply guarding the complete-case:

🛡️ Suggested handling
   function update(next: { hour: string; minute: string; period: 'AM' | 'PM' }) {
     setDraft(next);
-    if (next.hour && next.minute) onChange(to24Hour(next.hour, next.minute, next.period));
+    if (next.hour && next.minute) {
+      onChange(to24Hour(next.hour, next.minute, next.period));
+    } else if (value) {
+      // User cleared a previously-complete selection — reflect that upstream.
+      onChange('');
+    }
   }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/components/SectionMutationDrawer.tsx` around lines 145 - 148,
The update function currently only calls onChange when both next.hour and
next.minute are truthy, so clearing either select leaves the parent with the
previous complete time; modify update (in SectionMutationDrawer.tsx) to call
onChange with an empty string (or null if your app prefers) whenever the draft
transitions from a complete state to an incomplete state, and only call
onChange(to24Hour(...)) when both next.hour and next.minute are present; use the
existing setDraft, onChange, and to24Hour identifiers to implement this
guard/emit-empty behavior so the parent reflects cleared selections immediately.

@BeWelsh BeWelsh merged commit 1eec757 into main Apr 24, 2026
2 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.

2 participants