2026-09-17 01:19:19 -04:00
2026-09-17 01:19:19 -04:00
2026-09-17 01:19:19 -04:00
2026-09-17 01:19:19 -04:00
2026-09-17 01:19:19 -04:00
2026-09-17 01:19:19 -04:00
2026-09-17 01:19:19 -04:00
2026-09-17 01:19:19 -04:00
2026-09-17 01:19:19 -04:00
2026-09-17 01:19:19 -04:00

CMU Meal Estimator

A small single-page web app that estimates how many meal blocks and FLEX dollars a Carnegie Mellon student should have consumed by a given date — and how many should remain — based on their dining plan and time spent on campus.

The tool spreads a plan's totals evenly across the days you're expected to be on campus, then reports three views of that budget: a per-week rate, an amount consumed so far, and an estimated remaining balance, for both meal blocks and FLEX.

Estimates are informational and based on even distribution across on-campus days. They are not official dining balances.

Features

  • Pick from all eight dining plans (Green, Blue, Red, Yellow, Tartan Flex, Scotty's Choice, Whitfield's Favor, Piper Select).
  • "As of" date picker that defaults to today and is clamped to the meal period.
  • Checkboxes for configured breaks ("missing periods") you spent off campus.
  • A numeric input for any additional days you don't want counted.
  • Live-updating results — every input recomputes the estimate instantly.
  • Everything is config-driven: plans, dates, and breaks live in one JSON file. No code changes needed to adjust them.
  • Dark theme with restrained CMU Tartan-red accents.

Getting started

Requires Node.js 18+.

npm install      # install dependencies
npm run dev      # start the dev server at http://localhost:5173

Other scripts

npm run build    # type-check and produce a production build in dist/
npm run preview  # serve the production build locally
npm test         # run the estimation-engine tests

Configuration

All tunable values live in src/config.json. Edit that file, then reload the page — the UI updates reactively.

{
  "mealStartDate": "2026-08-24",   // first day of the meal period (YYYY-MM-DD)
  "mealEndDate": "2026-12-13",     // last day of the meal period (YYYY-MM-DD)

  "plans": [
    {
      "id": "green",               // stable unique key
      "displayName": "Green",      // shown in the dropdown
      "totalMealBlocks": 210,      // total blocks for the whole meal period
      "totalFlexDollars": 500      // total FLEX dollars for the whole meal period
    }
    // ...more plans
  ],

  "missingPeriods": [
    {
      "id": "fall-break",          // stable unique key
      "displayName": "Fall Break", // shown next to the checkbox
      "startDate": "2026-10-19",   // inclusive
      "endDate": "2026-10-20"      // inclusive
    }
    // ...add as many as you like; each becomes a checkbox
  ]
}

Notes:

  • The shipped plan numbers are placeholders. Replace totalMealBlocks and totalFlexDollars with the real figures from your CMU dining agreement, and set the correct mealStartDate / mealEndDate for your semester.
  • Add or remove plans and missing periods freely. Every missing period automatically becomes a checkbox in the UI.
  • If the config is malformed (missing fields, bad dates, duplicate ids, negative totals), the app shows a readable list of problems instead of rendering broken estimates.

How the estimate works

  1. Total active days = every calendar day in [mealStartDate, mealEndDate], minus the days covered by any break you check.
  2. Daily rate = plan total / total active days, computed separately for blocks and FLEX.
  3. Active days consumed = active days from the start up to your "as of" date, minus checked breaks in that window, minus any additional excluded days.
  4. Consumed = daily rate × active days consumed.
  5. Remaining = plan total − consumed.
  6. Per week = daily rate × 7.

Checking a break or adding excluded days shifts your expected consumption down, which is what you want when you were away and not eating on campus.

Project structure

index.html            # app shell
src/
  config.json         # all tunable values (plans, dates, breaks)
  types.ts            # shared types
  dates.ts            # whole-day date helpers
  configLoader.ts     # validates + normalizes config
  engine.ts           # pure estimation math (no DOM)
  store.ts            # tiny reactive state store
  main.ts             # builds the UI, re-renders on change
  style.css           # dark CMU-accented theme
  engine.test.ts      # engine unit tests
  sanity.test.ts      # checks against the shipped config

Tech stack

  • Vite for dev server and bundling
  • TypeScript (strict mode)
  • Vitest for tests
  • No UI framework — plain DOM with a small reactive store

Tests

The estimation engine is a set of pure functions, kept separate from the DOM so it can be tested directly:

npm test

Covers day counting across boundaries, budget conservation, monotonicity when excluding days, the zero-active-days (division-by-zero) edge case, and sanity checks against the shipped config.

S
Description
Estimates the number of meals and flex that a CMU student should have used by a certain date
Readme
181 KiB
Languages
TypeScript 74.6%
CSS 12.8%
JavaScript 10%
HTML 2.6%