nu-map

A zero-backend, single-page React degree planner for Northeastern University. Every piece of logic runs in the browser — no server, no database, no auth. Plans live in localStorage and export as JSON.

Type
Root — entire application
Entry
src/main.jsxsrc/App.jsx
Deploy
GitHub Pages + Netlify (static SPA)
Stack
React 18, Vite 5, vanilla CSS vars

Architecture overview

The app uses a strict 4-layer hexagonal architecture. Data flows upward only — core never imports UI, and data adapters never import context. PlannerContext is the hub: it calls core logic and data adapters, then exposes state to all UI components via usePlanner().

graph TB subgraph UI["🖼 UI Layer (src/ui/)"] H["Header"] --- BP["BankPanel"] BP --- GP["GradPanel"] GP --- SR["SemRow"] SR --- CC["CourseCard"] CC --- IP["InfoPanel"] end subgraph CTX["⚙️ Context Layer (src/context/)"] PC["PlannerContext\n★ hub of everything"] TC["ThemeContext"] end subgraph CORE["⚡ Core Logic (src/core/)"] CM["courseModel"] --- GR["gradRequirements"] GR --- PE["prereqEval"] PE --- SG["semGrid"] SG --- PM["planModel"] end subgraph DATA["🗂 Data Layer (src/data/)"] CL["courseLoader"] --- ML["majorLoader"] ML --- PS["persistence"] end UI -->|"usePlanner()"| CTX CTX -->|"pure fn calls"| CORE CTX -->|"I/O"| DATA DATA -->|"fetch"| FS[("public/\nall-courses.json")] DATA -->|"fetch"| GN[("graduatenu/\nfork")] DATA -->|"read/write"| LS[("localStorage")] style UI fill:#ddf4ff,stroke:#0969da style CTX fill:#fbefff,stroke:#8250df style CORE fill:#dafbe1,stroke:#1a7f37 style DATA fill:#fff8c5,stroke:#9a6700
Full dependency graph. Arrows show which layer calls/imports which.

Source tree

Navigate any node below to read its full documentation — methods, state, connections to siblings and parents.

Key design decisions

No backend, ever

Zero hosting cost, no migrations, no auth. Plans persist in localStorage and export as JSON files. Course data is scraped and bundled at build time.

React Context over Redux

All state in PlannerContext (~1,600 lines). No action/reducer boilerplate. Any component reads or mutates any state with one usePlanner() call.

CSS variables for theming

ThemeContext injects ~100 CSS custom properties onto <html>. All components use var(--token) inline. No CSS-in-JS library.

Fixed layout + CSS scale

Components are written at desktop scale. On small screens, App.jsx applies transform: scale(uiScale) — one layout for all viewports.

Data sources

SourceFormatWhen loadedSize
public/all-courses.jsonJSON arrayApp startup (primary)6.5 MB, 4,800+ courses
husker.vercel.app/courses/allJSON arrayApp startup fallback (if local JSON fails)Same data via SearchNEU API
tableau.northeastern.eduPlaywright DOM scrapeNUPath Update tool (manual)~4,800 course NUPath records
graduatenu/ forkMajor2 JSONOn major selection1,471 files
localStorageJSON stringsApp startup + every mutation~50 KB per plan

Quick start

git clone https://github.com/nayugu/nu-map
cd nu-map
npm install
npm run dev              # Vite on :5173 + catalog-check-server on :3333

npm run data:scrape:write  # update course catalog from catalog.northeastern.edu
npm run data:patch:write   # apply YAML corrections from data/patches/
npm run build              # production bundle → dist/