Files
cmu-meal-estimator/README.md
T
2026-09-17 01:19:19 -04:00

134 lines
4.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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](https://nodejs.org/) 18+.
```bash
npm install # install dependencies
npm run dev # start the dev server at http://localhost:5173
```
### Other scripts
```bash
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`](src/config.json). Edit that file, then reload the
page — the UI updates reactively.
```jsonc
{
"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](https://vitejs.dev/) for dev server and bundling
- TypeScript (strict mode)
- [Vitest](https://vitest.dev/) 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:
```bash
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.