persistence.js Data adapter

Three functions that wrap localStorage. The simplest module in the project. All logic for what to save lives in PlannerContext — this module only handles the serialization/deserialization.

Parent
Called by
PlannerContext.jsx (loadSaved on mount, saveState on every change, clearState on reset)

Exported functions

loadSaved() → SavedState | null
Reads plan index and active plan from localStorage. Reads the active plan's data JSON. Returns a fully parsed object, or null if nothing is saved (first visit).
Returns: SavedState object with placements, cohort, major selections, etc. — or null
saveState(persist: boolean, obj: PlanState) → void
If persist = true, serializes obj to JSON and writes it to ncp-plan-data-{activePlanId}. If persist = false, writes nothing (user has disabled auto-save). Also updates ncp-plan-index with the current plan's lastSaved timestamp.
clearState() → void
Removes all Nu-Map keys from localStorage. Called when the user clicks "Reset plan." Removes ncp-plan-index, ncp-active-plan, and all ncp-plan-data-* keys.

localStorage key reference

KeyContentsLifetime
ncp-plan-index[{id, name, lastSaved}]Until clearState()
ncp-active-planPlan ID stringUntil clearState()
ncp-plan-data-{id}Full plan JSON (see below)Until plan deleted or clearState()
ncp-themeTheme name stringPermanent (not cleared by reset)
ncp-zoomNumberPermanent
ncp-ent-sem/yearCohort settingsPermanent
ncp-grad-sem/yearCohort settingsPermanent
ncp-starredstring[] of courseIdsPermanent
ncp-sticky-coursesbooleanPermanent
ncp-collapse-other-creditsboolean (default true)Permanent
ncp-show-cont-logoboolean (default true) — show company logo on co-op/internship continuation rowsPermanent
ncp-seen-disclaimerbooleanPermanent
ncp-state-v2Legacy full state (old system)Migrated on load

Plan JSON structure

{
  "version":      1,
  "exported":     "2026-04-04T12:00:00Z",
  "entSem":       "fall",  "entYear":  2026,
  "gradSem":      "spring", "gradYear": 2031,
  "placements":   { "CS3500": "fall2027", "CS2810": "spring2028" },
  "workPl":       { "coop-1234": { "semId": "sumA2028", "duration": 6, "company": "Microsoft", "companyDomain": "microsoft.com", "subline": "SWE Intern" } },
  "internPl":     {},
  "semOrders":    { "fall2027": ["CS3500", "CS1800"] },
  "shOverrides":  { "CS1234": 3 },
  "bonusSH":      { "fall2026": 8 },
  "major":        "2024/computer-information-science/computer_science_bscs",
  "conc":         null,
  "minor1":       null, "minor2": null,
  "placedOut":    ["CS1200"],
  "substitutions": [{ "from": "ENGW3302", "to": "ENGW3315" }]
}