PlannerContext.jsx Hub
The brain of the entire application. ~1,600 lines of React state, computed values, side effects, and action handlers. Every UI component reads from it via usePlanner(). Understanding this file means understanding how the app works.
const {...} = usePlanner()Subsystems
State overview
Placement state
These are the core data structures that represent where courses live in the plan.
{ courseId → semId }. The primary placement map. "CS3500" → "fall2027". Every course-in-semester relationship is encoded here.{ workId → { semId, duration, company, companyDomain, subline } }. Co-op blocks. duration is 4 or 6 months. 6-month co-ops always span two semester rows. 4-month co-ops only span if placed on a summer semester. company is the display name; companyDomain is a bare domain (e.g. microsoft.com) used to fetch the company logo via Google's S2 favicon service. subline is the user-entered role/title. Any placed co-op grants the EX (Integration Experience) NUPath attribute — see getNuPathCoverage() in gradRequirements.{ intId → { semId, duration, company, companyDomain, subline } }. Internship blocks. duration is 2 or 4 months. 4-month internships span two rows only if placed on a summer semester. Internships do not grant EX NUPath — co-op only.Duration spanning — which semesters get occupied
Each block type occupies one or two consecutive semester rows. The grid below shows what happens when a block is dropped on each semester type. Shaded cells are the rows that become occupied.
| Block | Fall | Spring | sumA | sumB | Fall | Note |
|---|---|---|---|---|---|---|
| Co-op 4-mo on fall/spring | co-op | — | — | — | — | Single row |
| Co-op 4-mo on summer | — | — | start | cont. | — | Normalizes to sumA; spans sumA→sumB |
| Co-op 6-mo spring start | — | start | cont. | — | — | Spring → sumA |
| Co-op 6-mo sumB start | — | — | — | start | cont. | sumB → Fall |
| Internship 2-mo | — | — | intern | — | — | Summer only, single row |
| Internship 4-mo on fall/spring | intern | — | — | — | — | Single row |
| Internship 4-mo on summer | — | — | start | cont. | — | Normalizes to sumA; spans sumA→sumB |
Co-ops and internships are mutually exclusive per semester slot — a co-op blocks internship drops and vice versa. A course cannot be dropped into any semester occupied by either type.
{ semId → courseId[] }. User-defined ordering within a semester. When a user drags courses to reorder them, this array is updated. Courses not in the array appear at the end.{ courseId → number }. User-set credit hours for variable-credit courses (those with shMin ≠ shMax). Overrides the default course.credits.{ semId → number }. Extra credit hours added to a semester (AP credit, transfer credit applied to a specific term). Shown in the semester header SH count.[{ from, to }]. "ENGW3302 counts as ENGW3315." The effectiveCourseMap applies substitutions so grad requirements see the target course.Computed/derived state (useMemo)
These values are recomputed automatically when their dependencies change. Never set directly.
{ id, label, type, year }.{ semId → ordinal }. Used for "is course A before course B?" comparisons. Built by deriveSemMaps().placements but with substitution targets applied. Used as input to graduation requirement checking.courseId → { type: "order"|"unsatisfied", ... }. Computed by running evalPrereqTree() for every placed course. Used to show warning icons and draw red SVG lines.Key actions and setters
Placement mutations (all call pushUndo() first)
placements and semOrders.placements. Does not affect placedOut.semOrders[semId] by swapping positions.placements and adds to placedOut array.Undo / redo
undoStack before any mutation. Clears redoStack. The snapshot includes: placements, workPl, internPl, semOrders, shOverrides, bonusSH, placedOut, substitutions.Multi-plan management
plans index, saves to localStorage, then calls switchPlan(newId).plans index and deletes its localStorage key. If active plan was deleted, switches to the first remaining plan.plan-name.json.Side effects (useEffect)
| Dependency | Effect |
|---|---|
| Mount (once) | Load localStorage state, fetch courses |
selectedId, placements, scrollTick | Recompute SVG prerequisite line positions — see svg-lines/ |
| Any persisted state change | Debounced auto-save to localStorage (when persistEnabled) — see plans/ |
planEntSem/Year, planGradSem/Year | Rebuild SEMESTERS, optionally remap courses (stickyCourses) |
courses loaded | Build courseMap, compute initial violations |
| Window resize | Update isPhone, isMobile, uiScale |
| Keyboard events | Cmd+Z undo, Cmd+Y redo, Delete remove, Escape close panel |