You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Part of #1975 (course-home tab data → React Query), Phase 3 of the epic #1946.
Summary
Convert the dates tab from Redux thunks + model-store to React Query, and establish the per-tab conversion pattern for the rest of course-home: each tab becomes self-wrapping — it renders <TabPage> itself and owns its data-loading via query hooks (the shape CoursewareContainer already uses) — and its wrapper line is removed from index.jsx. TabContainer is not modified; it keeps serving the unconverted tabs + course-exit and is deleted later (Phase 4, with course-exit).
Dates is the pattern-setter because its existing DatesTab.test.jsx (365 lines) mocks at the axios layer and is already wired with QueryClientProvider + a /course/:courseId/dates router, so it survives the thunk→RQ conversion with its assertions intact and acts as the behavior-parity guard.
Tasks
New useCourseHomeMeta, useDatesTabData, useCourseHomeTab query hooks (+ src/course-home/data/queryKeys.ts).
DatesTab self-wraps <TabPage>; courseId via useParams; courseStatus derived from the queries.
Dates subtree (Timeline, Day, ShiftDatesAlert, UpgradeToShiftDatesAlert) switches courseId from useSelector(state.courseHome) to useParams.
Remove the dates wrapper from index.jsx; delete the now-dead fetchDatesTab thunk + re-export.
Transitional model-store bridges for courseHomeMeta and dates so the subtree keeps its useModel(...) reads unchanged (removed in Phase 5 / when TabPage reads meta from RQ).
Preserve DatesTab.test.jsx teeth (only drop the <TabContainer> wrapper from its harness); add apiHooks.test cases (status matrix + both bridges); update the one index.test route assertion; keep OutlineTab.test green.
Notes / decisions
Model-store stays a transitional read cache. This PR moves fetching to React Query; the courseHomeMeta/dates bridges keep the existing readers working. Removing model-store for course-home is Phase 5.
courseStatus derivation matches the thunk today: error → FAILED; fetching → LOADING; metadata !courseAccess.hasAccess → DENIED; else LOADED (getDatesTabData swallows 401/403, so access is authoritative from the metadata call).
403 failure detail: on a rare FAILED, TabPage shows the generic failure message rather than the 403 errorMessage/errorCode (access errors go through DENIED). Accepted for now.
Note
The plan below was generated by Claude (Claude Code) and reviewed before posting.
Claude Plan — dates tab conversion
Approach
One tab per PR. The tab component becomes self-wrapping (renders <TabPage> and owns its data via query hooks); TabContainer is untouched and only the converting tab's wrapper line leaves index.jsx. Stacked on the CTA-toast PR (#1982), which already took TabPage off Redux for toast.
Query hooks (src/course-home/data/apiHooks.ts; keys in a new queryKeys.ts)
DatesTab.test.jsx — drop the <TabContainer> wrapper only; keep all assertions incl. "handles shift due dates click" (invalidate-driven refetch clears the banner; toast via ToastProvider).
apiHooks.test.tsx — useDatesTabData/useCourseHomeMeta fetch, useCourseHomeTab status matrix, both bridges (addModel).
index.test.jsx — dates route now renders the Dates Tab mock (was Tab Container).
OutlineTab.test.jsx — green-check after the ShiftDatesAlert prop swap.
Verification
nvm use && npm run types && npm run lint && npm test (targeted first: DatesTab, apiHooks, OutlineTab, index, TabContainer), then full suite + npm run build. git grep fetchDatesTab → gone. Manual: timeline + suggested-schedule alerts render; "Shift due dates" refreshes the banner + toast; loading/denied/failed via TabPage; other tabs unaffected.
Part of #1975 (course-home tab data → React Query), Phase 3 of the epic #1946.
Summary
Convert the dates tab from Redux thunks +
model-storeto React Query, and establish the per-tab conversion pattern for the rest of course-home: each tab becomes self-wrapping — it renders<TabPage>itself and owns its data-loading via query hooks (the shapeCoursewareContaineralready uses) — and its wrapper line is removed fromindex.jsx.TabContaineris not modified; it keeps serving the unconverted tabs + course-exit and is deleted later (Phase 4, with course-exit).Dates is the pattern-setter because its existing
DatesTab.test.jsx(365 lines) mocks at the axios layer and is already wired withQueryClientProvider+ a/course/:courseId/datesrouter, so it survives the thunk→RQ conversion with its assertions intact and acts as the behavior-parity guard.Tasks
useCourseHomeMeta,useDatesTabData,useCourseHomeTabquery hooks (+src/course-home/data/queryKeys.ts).DatesTabself-wraps<TabPage>;courseIdviauseParams;courseStatusderived from the queries.Timeline,Day,ShiftDatesAlert,UpgradeToShiftDatesAlert) switchescourseIdfromuseSelector(state.courseHome)touseParams.ShiftDatesAlert'sfetch(thunk) prop → anonResetcallback: dates invalidates the RQ query; outline keeps dispatchingfetchOutlineTab(one-line call-site change, unchanged behavior).index.jsx; delete the now-deadfetchDatesTabthunk + re-export.model-storebridges forcourseHomeMetaanddatesso the subtree keeps itsuseModel(...)reads unchanged (removed in Phase 5 / whenTabPagereads meta from RQ).DatesTab.test.jsxteeth (only drop the<TabContainer>wrapper from its harness); addapiHooks.testcases (status matrix + both bridges); update the oneindex.testroute assertion; keepOutlineTab.testgreen.Notes / decisions
courseHomeMeta/datesbridges keep the existing readers working. Removing model-store for course-home is Phase 5.courseStatusderivation matches the thunk today: error → FAILED; fetching → LOADING; metadata!courseAccess.hasAccess→ DENIED; else LOADED (getDatesTabDataswallows 401/403, so access is authoritative from the metadata call).FAILED,TabPageshows the generic failure message rather than the 403errorMessage/errorCode(access errors go through DENIED). Accepted for now.Note
The plan below was generated by Claude (Claude Code) and reviewed before posting.
Claude Plan — dates tab conversion
Approach
One tab per PR. The tab component becomes self-wrapping (renders
<TabPage>and owns its data via query hooks);TabContaineris untouched and only the converting tab's wrapper line leavesindex.jsx. Stacked on the CTA-toast PR (#1982), which already tookTabPageoff Redux for toast.Query hooks (
src/course-home/data/apiHooks.ts; keys in a newqueryKeys.ts)Self-wrapping DatesTab
Files
src/course-home/data/queryKeys.ts—courseHomeQueryKeys(metadata,datesTab).src/course-home/data/apiHooks.ts—useCourseHomeMeta,useDatesTabData,useCourseHomeTab.src/course-home/dates-tab/DatesTab.jsx— self-wrap;useParams; query hooks;onResetinvalidate.src/course-home/dates-tab/timeline/Timeline.jsx,timeline/Day.jsx—courseIdviauseParams.src/course-home/suggested-schedule-messaging/ShiftDatesAlert.jsx—useParams;fetchprop →onReset; dropuseDispatch.src/course-home/suggested-schedule-messaging/UpgradeToShiftDatesAlert.jsx—courseIdviauseParams.src/course-home/outline-tab/OutlineTab.jsx—ShiftDatesAlertfetch→onReset(still dispatchesfetchOutlineTab).src/index.jsx— dates route →<DatesTab />; dropfetchDatesTabimport.src/course-home/data/thunks.js+index.js— removefetchDatesTab+ re-export.Tests
DatesTab.test.jsx— drop the<TabContainer>wrapper only; keep all assertions incl. "handles shift due dates click" (invalidate-driven refetch clears the banner; toast viaToastProvider).apiHooks.test.tsx—useDatesTabData/useCourseHomeMetafetch,useCourseHomeTabstatus matrix, both bridges (addModel).index.test.jsx— dates route now renders theDates Tabmock (wasTab Container).OutlineTab.test.jsx— green-check after theShiftDatesAlertprop swap.Verification
nvm use && npm run types && npm run lint && npm test(targeted first:DatesTab,apiHooks,OutlineTab,index,TabContainer), then full suite +npm run build.git grep fetchDatesTab→ gone. Manual: timeline + suggested-schedule alerts render; "Shift due dates" refreshes the banner + toast; loading/denied/failed viaTabPage; other tabs unaffected.