Skip to content

Fix: UI fixes#71

Merged
BeWelsh merged 11 commits into
mainfrom
fix/ui-fixes
Apr 23, 2026
Merged

Fix: UI fixes#71
BeWelsh merged 11 commits into
mainfrom
fix/ui-fixes

Conversation

@BeWelsh
Copy link
Copy Markdown
Collaborator

@BeWelsh BeWelsh commented Apr 23, 2026

Description

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?

  • General frontend UI fixes
  • Mostly focused on reducing api calls when necessary
  • Changed some backend code (sectionrich response and course model)

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:
    • *** Frontend tests


Notes & Nuances



Reviewer Notes

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

Summary by CodeRabbit

  • New Features

    • Added user authentication context system for improved stability across pages.
    • Introduced real-time upload progress tracking during schedule creation.
    • Added smooth step-entry animations to schedule creation workflow.
  • Improvements

    • Schedule creation modal now displays upload errors inline with better UX.
    • Course information now includes subject and code details.
    • Updated admin interface color scheme for Faculty page.
    • Refined default table sorting on Faculty page.
  • Bug Fixes

    • Database-level unique constraint prevents duplicate course entries.
    • Error messages truncated to prevent overflow on upload failures.

@BeWelsh BeWelsh requested review from Saisri24 and jobvengalil and removed request for Saisri24 April 23, 2026 23:35
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 23, 2026

Warning

Rate limit exceeded

@BeWelsh has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 42 minutes and 58 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 42 minutes and 58 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: fb3c121e-c971-4570-9282-4c23a51b7e10

📥 Commits

Reviewing files that changed from the base of the PR and between afa95bb and b4b9c40.

📒 Files selected for processing (4)
  • backend/app/routers/course.py
  • backend/tests/test_schedule.py
  • backend/tests/test_section_lock.py
  • backend/tests/test_section_service.py

Walkthrough

This PR introduces a user context system for managing authenticated user state across the frontend, adds database-level uniqueness constraints on Course records, extends course data in API responses with subject and code fields, and refactors multiple frontend components to consume user data from the centralized context instead of making individual API calls.

Changes

Cohort / File(s) Summary
Backend Course Constraints & Schema
backend/app/models/course.py, backend/app/schemas/section.py, backend/app/services/section.py, frontend/src/api/generated.ts
Added UniqueConstraint on subject+code to Course model; extended CourseInfo schema with subject and code fields; populated these fields in get_rich_sections service; updated TypeScript interface.
Backend Upload Error Limiting
backend/app/routers/upload.py
Limited errors message size in upload_faculty_preferences to show only first 5 skipped courses with ellipsis when truncated.
Frontend User Context
frontend/src/context/UserContext.tsx, frontend/src/App.tsx
Introduced UserProvider context component and useUser() hook to centralize authenticated user state; wrapped authenticated routes in UserProvider wrapper.
Frontend Components Adopting useUser()
frontend/src/components/Sidebar.tsx, frontend/src/components/SectionComments.tsx, frontend/src/pages/Courses.tsx, frontend/src/pages/Faculty.tsx, frontend/src/pages/Schedules.tsx
Migrated components from local user fetching via getMeApiUsersMeGet() to consuming me and meLoading from useUser() context hook.
Frontend Component Data Flow Refactoring
frontend/src/components/SectionMutationDrawer.tsx, frontend/src/components/ScheduleSectionRowView.tsx
Removed internal data fetching; SectionMutationDrawer now receives courses and scheduleSections from parent props; ScheduleSectionRowView refactored to use local catalog fetch and derive course metadata directly from section data.
Frontend Infrastructure & UI Enhancements
frontend/src/main.tsx, frontend/src/index.css, frontend/src/pages/ScheduleList.tsx
Refactored root render logic with conditional React.StrictMode wrapping; added step-enter CSS animation; enhanced ScheduleList with per-file upload progress tracking via axios onUploadProgress and styled progress cards with error display.
Frontend Test Updates
frontend/src/hooks/useScheduleWebSocket.test.ts, frontend/src/pages/Faculty.test.tsx, frontend/src/pages/ScheduleList.test.tsx, frontend/src/pages/Schedules.test.tsx
Updated test fixtures to include subject and code in mock course data; introduced mocks for useUser() context hook across multiple test suites; refactored user role simulation via context mocking instead of API mocking.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

  • Small db changes #43: Adds subject and code columns to Course model and populates them; this PR adds uniqueness constraint on the same fields.
  • Ssip 131: faculty browse page #47: Modifies SectionMutationDrawer props and data handling; aligns with this PR's refactor to pass courses/scheduleSections as props.
  • Frontend section changes #50: Extends SectionRichResponse and get_rich_sections service with additional course/section fields; shares the same code paths as this PR's updates.

Suggested reviewers

  • Saisri24

Poem

🐰✨ A context unfolds, no more API calls in the night—
UserProvider hops in, making auth a shared delight!
Constraints bind courses tight, uploads show their way,
Components dance lighter, context leads the day! 🎭🚀

🚥 Pre-merge checks | ✅ 2 | ❌ 3

❌ Failed checks (2 warnings, 1 inconclusive)

Check name Status Explanation Resolution
Description check ⚠️ Warning The PR description marks itself as complete but lacks critical details: testing steps are completely empty (1, 2, 3 remain unfilled), the 'What Changed' section is too vague, and unfinished frontend tests are explicitly flagged as deferred without clear explanation. Complete the testing steps section with specific test cases or manual validation steps, provide concrete details in 'What Changed' section, and clarify the scope and timeline for deferred frontend tests.
Docstring Coverage ⚠️ Warning Docstring coverage is 25.00% 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 'Fix: UI fixes' is vague and overly generic, using non-descriptive language that doesn't convey the specific changes or primary focus of the substantial changeset. Revise the title to be more specific and descriptive of the main changes, such as 'Refactor user authentication to context provider and reduce redundant API calls' or similar that captures the core work.
✅ Passed checks (2 passed)
Check name Status Explanation
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 unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/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: 3

🧹 Nitpick comments (6)
frontend/src/pages/Faculty.test.tsx (1)

27-29: Minor: getMeApiUsersMeGet mock entry is now dead.

With useUser mocked at the context boundary, Faculty.tsx no longer calls getMeApiUsersMeGet directly, so the getMeApiUsersMeGet: vi.fn().mockResolvedValue(mockAdmin) entry in mockApi (line 56) can be dropped. Non-blocking.

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

In `@frontend/src/pages/Faculty.test.tsx` around lines 27 - 29, The mock for
getMeApiUsersMeGet in mockApi is now unused because useUser is mocked at the
context boundary; remove the getMeApiUsersMeGet:
vi.fn().mockResolvedValue(mockAdmin) entry from the mockApi object (referencing
mockApi and getMeApiUsersMeGet) so the test fixture only includes active mocks
and avoids dead entries when Faculty.tsx relies on useUser.
frontend/src/context/UserContext.tsx (1)

38-40: Optional: split useUser out of the component module for React Fast Refresh.

Per the Lint check, co-locating a non-component export (useUser) with a component (UserProvider) disables Fast Refresh for this file. Low-impact, but trivially fixed by moving the hook (and the UserContext / UserContextValue type) into a sibling module like useUser.ts and keeping only UserProvider here.

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

In `@frontend/src/context/UserContext.tsx` around lines 38 - 40, The file
currently co-locates a non-component export with a component which disables
React Fast Refresh; extract the hook and its context/type into a sibling module
(e.g., create useUser.ts) by moving UserContext, UserContextValue and the
useUser function into that new file and export them from there, leaving only the
UserProvider component in this module; update any imports across the codebase to
import useUser, UserContext and/or UserContextValue from the new useUser.ts so
behavior and types remain unchanged.
frontend/src/pages/Schedules.test.tsx (1)

85-96: Stale getMeApiUsersMeGet mock — the component no longer calls it.

Since Schedules.tsx now reads identity exclusively from useUser() (mocked at line 36-38), the getMeApiUsersMeGet entry in this beforeEach never fires and can be dropped to avoid misleading future readers into thinking role flows through the API mock.

♻️ Drop the dead mock
       getFacultyFacultyGet: vi.fn().mockResolvedValue([
         { NUID: 100005 },
       ]),
-      getMeApiUsersMeGet: vi.fn().mockResolvedValue(viewerUser),
     } as unknown as ReturnType<typeof generated.getAutomatedCourseSchedulerAPI>);
🤖 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 85 - 96, The test mocks
returned by generated.getAutomatedCourseSchedulerAPI include getMeApiUsersMeGet
which is no longer used because Schedules.tsx reads identity from useUser()
(mocked earlier); remove the dead getMeApiUsersMeGet property from the mocked
object in the beforeEach to avoid confusion and keep the mock surface minimal
while leaving getScheduleSchedulesScheduleIdGet, getAllCampusesCampusesGet, and
getFacultyFacultyGet intact.
frontend/src/pages/Faculty.tsx (1)

244-247: Switching back to load via the header falls into asc, losing the new "load defaults to desc" intent.

With the default sort now load/desc, clicking Name and then clicking Load again will land on load/asc because handleSort hardcodes asc whenever the key changes. Consider picking a per-key default direction so load keeps defaulting to desc.

♻️ Proposed per-key default direction
 function handleSort(key: SortKey) {
   if (sortKey === key) setSortDir((d) => (d === 'asc' ? 'desc' : 'asc'));
-  else { setSortKey(key); setSortDir('asc'); }
+  else { setSortKey(key); setSortDir(key === 'load' ? 'desc' : 'asc'); }
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/pages/Faculty.tsx` around lines 244 - 247, The current
handleSort toggles direction only when the same key is clicked and always sets
'asc' when switching keys, which breaks the desired "load defaults to desc"
behavior; update handleSort (and introduce a small per-key default map or helper
like defaultSortDirFor) so that when setSortKey(key) is called you also
setSortDir(defaultSortDirFor(key)) instead of hardcoding 'asc', and keep the
existing toggle behavior when sortKey === key; reference the handleSort
function, SortKey type, and setSortDir/setSortKey to locate where to apply the
change.
frontend/src/components/SectionMutationDrawer.tsx (1)

521-529: Reset loadingData when campusName changes.

If the parent ever remounts this drawer with a different campusName, the effect refetches but loadingData stays false, so the body renders with stale/empty faculty (and no spinner) until the new request resolves. Today this is unlikely because callers don't swap campusName mid-lifetime, but the invariant is easy to make robust:

♻️ Proposed change
   useEffect(() => {
     const api = getAutomatedCourseSchedulerAPI();
+    setLoadingData(true);
     api.getFacultyFacultyGet(
       campusName ? { campus: campusName, active_only: true } : { active_only: true },
     ).then((result) => {
       setFaculty(result);
       setLoadingData(false);
     }).catch(() => setLoadingData(false));
   }, [campusName]);

Also, the .catch(() => setLoadingData(false)) silently swallows the failure — consider surfacing it via the existing error state so admins know instructors are missing and aren't tempted to save a section without its roster. Pre-existing behavior, so optional.

🤖 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 521 - 529,
The effect that fetches faculty in SectionMutationDrawer should reset loading
state and surface errors when campusName changes: inside the useEffect that
calls getAutomatedCourseSchedulerAPI().getFacultyFacultyGet, set loadingData to
true at the start (so the spinner shows on refetch) and in the promise catch
call setLoadingData(false) and setError with the caught error (or a descriptive
message) in addition to not swallowing it; ensure successful responses still
call setFaculty(result) and setLoadingData(false). Target the existing
useEffect, getAutomatedCourseSchedulerAPI/getFacultyFacultyGet, and the
setLoadingData/setFaculty/setError state setters.
frontend/src/components/ScheduleSectionRowView.tsx (1)

109-112: Catalog fetch has no error surfacing and no loading gate.

Two small concerns worth noting:

  1. .catch(() => {}) swallows failures silently. If /courses ever fails, admins who open the edit/create drawer will see an empty "Course" select with no hint why.
  2. There's no gating on catalogCourses readiness before opening the drawer — if an admin clicks a row very quickly after mount, they can reach the drawer before catalogCourses resolves. In practice the race window is tiny, but consider either surfacing a load error or disabling the Add/Edit affordances until the catalog is present.

Non-blocking for this PR, but logging the error (or a toast) would help triage.

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

In `@frontend/src/components/ScheduleSectionRowView.tsx` around lines 109 - 112,
The fetch in the useEffect that calls
getAutomatedCourseSchedulerAPI().getCoursesCoursesGet() swallows errors and has
no loading gate; update the hook to track loading and error state (e.g.,
catalogLoading, catalogError) and call setCatalogCourses on success, log or
surface the caught error (via processLogger/toast) inside the .catch so failures
aren't silent, and ensure any UI that opens the Add/Edit drawer checks
catalogLoading or catalogError (or disables the Add/Edit affordance) until
catalogCourses is populated so the drawer cannot open before the catalog is
ready; reference the existing useEffect, getAutomatedCourseSchedulerAPI,
api.getCoursesCoursesGet, setCatalogCourses, and catalogCourses to locate where
to add these states and checks.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@backend/app/models/course.py`:
- Line 19: The POST /courses endpoint currently only catches ValueError and will
return 500 on duplicate (subject, code); update the create_course function in
backend/app/routers/course.py to also catch sqlalchemy.exc.IntegrityError (or
SQLAlchemyError) from course_service.create_course, call db.rollback() in that
except block, and raise HTTPException(status_code=400, detail="A course with
this subject and code already exists") so duplicate key violations return a
friendly 400; keep the existing ValueError handling intact.

In `@frontend/src/context/UserContext.tsx`:
- Around line 13-36: The effect in UserProvider leaves meLoading true when
isAuthenticated is false and doesn't clear me/meError on logout; modify the
useEffect watching isAuthenticated in UserProvider so that when isAuthenticated
is false it immediately sets setMeLoading(false) and also resets setMe(null) and
setMeError(null), and when true proceeds to call
getAutomatedCourseSchedulerAPI().getMeApiUsersMeGet() as before
(clearing/setLoading appropriately in the promise handlers); optionally consider
using Auth0's isLoading if you need to distinguish transient auth hydration from
a definite anonymous state.

In `@frontend/src/pages/ScheduleList.tsx`:
- Line 421: The footer "Done" button bypasses the modal close guard: align its
behavior with the existing canClose logic (where canClose = step !== 'generate'
|| generateDone) by disabling and preventing its onClick when canClose is false;
update the Footer Done button (the element using the Done label and its
onClick/onClose handler) to check canClose (or the equivalent expression using
step/generateDone) before calling the close handler so the footer cannot close
the modal while step === 'generate' && !generateDone (i.e., mirror the
backdrop/header X guard).

---

Nitpick comments:
In `@frontend/src/components/ScheduleSectionRowView.tsx`:
- Around line 109-112: The fetch in the useEffect that calls
getAutomatedCourseSchedulerAPI().getCoursesCoursesGet() swallows errors and has
no loading gate; update the hook to track loading and error state (e.g.,
catalogLoading, catalogError) and call setCatalogCourses on success, log or
surface the caught error (via processLogger/toast) inside the .catch so failures
aren't silent, and ensure any UI that opens the Add/Edit drawer checks
catalogLoading or catalogError (or disables the Add/Edit affordance) until
catalogCourses is populated so the drawer cannot open before the catalog is
ready; reference the existing useEffect, getAutomatedCourseSchedulerAPI,
api.getCoursesCoursesGet, setCatalogCourses, and catalogCourses to locate where
to add these states and checks.

In `@frontend/src/components/SectionMutationDrawer.tsx`:
- Around line 521-529: The effect that fetches faculty in SectionMutationDrawer
should reset loading state and surface errors when campusName changes: inside
the useEffect that calls getAutomatedCourseSchedulerAPI().getFacultyFacultyGet,
set loadingData to true at the start (so the spinner shows on refetch) and in
the promise catch call setLoadingData(false) and setError with the caught error
(or a descriptive message) in addition to not swallowing it; ensure successful
responses still call setFaculty(result) and setLoadingData(false). Target the
existing useEffect, getAutomatedCourseSchedulerAPI/getFacultyFacultyGet, and the
setLoadingData/setFaculty/setError state setters.

In `@frontend/src/context/UserContext.tsx`:
- Around line 38-40: The file currently co-locates a non-component export with a
component which disables React Fast Refresh; extract the hook and its
context/type into a sibling module (e.g., create useUser.ts) by moving
UserContext, UserContextValue and the useUser function into that new file and
export them from there, leaving only the UserProvider component in this module;
update any imports across the codebase to import useUser, UserContext and/or
UserContextValue from the new useUser.ts so behavior and types remain unchanged.

In `@frontend/src/pages/Faculty.test.tsx`:
- Around line 27-29: The mock for getMeApiUsersMeGet in mockApi is now unused
because useUser is mocked at the context boundary; remove the
getMeApiUsersMeGet: vi.fn().mockResolvedValue(mockAdmin) entry from the mockApi
object (referencing mockApi and getMeApiUsersMeGet) so the test fixture only
includes active mocks and avoids dead entries when Faculty.tsx relies on
useUser.

In `@frontend/src/pages/Faculty.tsx`:
- Around line 244-247: The current handleSort toggles direction only when the
same key is clicked and always sets 'asc' when switching keys, which breaks the
desired "load defaults to desc" behavior; update handleSort (and introduce a
small per-key default map or helper like defaultSortDirFor) so that when
setSortKey(key) is called you also setSortDir(defaultSortDirFor(key)) instead of
hardcoding 'asc', and keep the existing toggle behavior when sortKey === key;
reference the handleSort function, SortKey type, and setSortDir/setSortKey to
locate where to apply the change.

In `@frontend/src/pages/Schedules.test.tsx`:
- Around line 85-96: The test mocks returned by
generated.getAutomatedCourseSchedulerAPI include getMeApiUsersMeGet which is no
longer used because Schedules.tsx reads identity from useUser() (mocked
earlier); remove the dead getMeApiUsersMeGet property from the mocked object in
the beforeEach to avoid confusion and keep the mock surface minimal while
leaving getScheduleSchedulesScheduleIdGet, getAllCampusesCampusesGet, and
getFacultyFacultyGet intact.
🪄 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: 8f9b442a-5c89-4bcb-9ce4-a11e22eb4433

📥 Commits

Reviewing files that changed from the base of the PR and between 779230e and afa95bb.

📒 Files selected for processing (22)
  • backend/app/models/course.py
  • backend/app/routers/upload.py
  • backend/app/schemas/section.py
  • backend/app/services/section.py
  • frontend/openapi.json
  • frontend/src/App.tsx
  • frontend/src/api/generated.ts
  • frontend/src/components/ScheduleSectionRowView.tsx
  • frontend/src/components/SectionComments.tsx
  • frontend/src/components/SectionMutationDrawer.tsx
  • frontend/src/components/Sidebar.tsx
  • frontend/src/context/UserContext.tsx
  • frontend/src/hooks/useScheduleWebSocket.test.ts
  • frontend/src/index.css
  • frontend/src/main.tsx
  • frontend/src/pages/Courses.tsx
  • frontend/src/pages/Faculty.test.tsx
  • frontend/src/pages/Faculty.tsx
  • frontend/src/pages/ScheduleList.test.tsx
  • frontend/src/pages/ScheduleList.tsx
  • frontend/src/pages/Schedules.test.tsx
  • frontend/src/pages/Schedules.tsx

Comment thread backend/app/models/course.py
Comment on lines +13 to +36
export function UserProvider({ children }: { children: React.ReactNode }) {
const { isAuthenticated } = useAuth0();
const [me, setMe] = useState<UserResponse | null>(null);
const [meError, setMeError] = useState<string | null>(null);
const [meLoading, setMeLoading] = useState(true);

useEffect(() => {
if (!isAuthenticated) return;
getAutomatedCourseSchedulerAPI()
.getMeApiUsersMeGet()
.then(setMe)
.catch((err: unknown) => {
const status = (err as { response?: { status?: number } })?.response?.status;
setMeError(
status === 403
? 'Your Auth0 account is not linked to a DB user yet. Ask an admin to invite you or run bootstrap_admin.py.'
: 'Could not load your user profile.',
);
})
.finally(() => setMeLoading(false));
}, [isAuthenticated]);

return <UserContext.Provider value={{ me, meError, meLoading }}>{children}</UserContext.Provider>;
}
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 | 🟠 Major

meLoading is never cleared when the user is unauthenticated, and state isn't reset on logout.

Two related issues in the provider:

  1. When isAuthenticated is false on mount, the effect early-returns before setMeLoading(false) runs, so meLoading stays true forever. Any consumer that gates rendering on meLoading (rather than the me != null || meError != null pattern used in Schedules.tsx) will hang on a loading state during the pre-auth phase or for anonymous views.
  2. When isAuthenticated flips true → false (e.g., Auth0 logout inside the same SPA lifetime), me and meError are never reset, so stale user data leaks into subsequent sessions until a full page reload.
🛠️ Proposed fix
   useEffect(() => {
-    if (!isAuthenticated) return;
+    if (!isAuthenticated) {
+      setMe(null);
+      setMeError(null);
+      setMeLoading(false);
+      return;
+    }
+    setMeLoading(true);
     getAutomatedCourseSchedulerAPI()
       .getMeApiUsersMeGet()
       .then(setMe)
       .catch((err: unknown) => {
         const status = (err as { response?: { status?: number } })?.response?.status;
         setMeError(
           status === 403
             ? 'Your Auth0 account is not linked to a DB user yet. Ask an admin to invite you or run bootstrap_admin.py.'
             : 'Could not load your user profile.',
         );
       })
       .finally(() => setMeLoading(false));
   }, [isAuthenticated]);

Also worth considering that Auth0's isAuthenticated is itself false during the initial silent-auth hydration; treating that transient state as "unauthenticated, stop loading" is usually the right behavior, but if any consumer needs to distinguish "auth still loading" vs "definitely anonymous", you may want to pull in Auth0's own isLoading here.

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

In `@frontend/src/context/UserContext.tsx` around lines 13 - 36, The effect in
UserProvider leaves meLoading true when isAuthenticated is false and doesn't
clear me/meError on logout; modify the useEffect watching isAuthenticated in
UserProvider so that when isAuthenticated is false it immediately sets
setMeLoading(false) and also resets setMe(null) and setMeError(null), and when
true proceeds to call getAutomatedCourseSchedulerAPI().getMeApiUsersMeGet() as
before (clearing/setLoading appropriately in the promise handlers); optionally
consider using Auth0's isLoading if you need to distinguish transient auth
hydration from a definite anonymous state.

}
}

const canClose = step !== 'generate' || generateDone;
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

Footer "Done" button bypasses the canClose guard.

canClose disables the backdrop (Line 427) and header X (Lines 438-440) whenever step === 'generate' && !generateDone, but the footer "Done" button at Lines 674-681 has no such guard. Net effect:

  • Before the user clicks "Run Algorithm" (generating=false, generateDone=false): X and backdrop are disabled, but "Done" is clickable → the user can close from the footer while both other close paths are locked.
  • Mid-generation (generating=true, generateDone=false): same mismatch — user can abandon the modal via "Done" but not via X/backdrop.

Pick one semantics. If the intent is "don't let the user close until generation finishes," gate "Done" too; if the intent is "only block accidental close during the actual network call," narrow canClose.

🔧 Option 1 — tighten footer to match guard
           {step === 'generate' && (
             <button
-              onClick={onClose}
-              className="px-4 py-2 text-sm font-medium text-white bg-burgundy-600 rounded-lg hover:bg-burgundy-700 transition-colors"
+              onClick={canClose ? onClose : undefined}
+              disabled={!canClose}
+              className="px-4 py-2 text-sm font-medium text-white bg-burgundy-600 rounded-lg hover:bg-burgundy-700 disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
             >
               Done
             </button>
           )}
🔧 Option 2 — loosen guard to only block during the in-flight call
-  const canClose = step !== 'generate' || generateDone;
+  const canClose = step !== 'generate' || generateDone || !generating;

Also applies to: 674-681

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

In `@frontend/src/pages/ScheduleList.tsx` at line 421, The footer "Done" button
bypasses the modal close guard: align its behavior with the existing canClose
logic (where canClose = step !== 'generate' || generateDone) by disabling and
preventing its onClick when canClose is false; update the Footer Done button
(the element using the Done label and its onClick/onClose handler) to check
canClose (or the equivalent expression using step/generateDone) before calling
the close handler so the footer cannot close the modal while step === 'generate'
&& !generateDone (i.e., mirror the backdrop/header X guard).

Copy link
Copy Markdown
Collaborator

@jobvengalil jobvengalil left a comment

Choose a reason for hiding this comment

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

Took a quick look, lgtm, maybe worth seeing if the code rabbit comments are valid

@BeWelsh BeWelsh merged commit 6b76ed9 into main Apr 23, 2026
3 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