159 lines
7.7 KiB
Markdown
159 lines
7.7 KiB
Markdown
# Inlander Restaurant Week Picker
|
||
|
||
A web app for browsing, marking favorites, and randomly selecting a restaurant during [Inlander Restaurant Week](https://inlanderrestaurantweek.com/).
|
||
|
||
## Overview
|
||
|
||
The app presents all participating IRW restaurants in a two-panel layout — a scrollable list on the left and a detail view on the right — making it easy to browse full multi-course menus before deciding where to eat.
|
||
|
||
Each couple gets their own set of interest checkboxes ("His Picks" / "Her Picks") that persist in `localStorage` across sessions. Once both people have marked their favorites, the built-in randomizer picks a winner from the mutually-agreed pool.
|
||
|
||
## Features
|
||
|
||
- **Browse & search** — filter restaurants by name, price tier ($25 / $35 / $45), neighborhood/area, and cuisine type
|
||
- **Full menu detail** — click any restaurant to view its complete multi-course IRW menu with descriptions
|
||
- **Dual interest tracking** — independently mark restaurants as interesting for two people; selections are saved automatically in the browser
|
||
- **Interest filter** — narrow the list to His Picks, Her Picks, Both Picked, or Either Picked
|
||
- **Random picker** — "Pick for Us!" draws a random winner from the currently filtered pool, with an option to re-roll
|
||
- **Course browse** — dedicated First Course, Second Course, and Third Course buttons open a modal listing every dish for that course across all restaurants; click any dish to jump straight to that restaurant's detail view
|
||
- **Clear Filters** — one-click button to reset all search and filter fields back to their defaults
|
||
- **Reset Selected** — clears all saved picks (with a confirmation prompt) so you can start fresh
|
||
- **Mobile-friendly** — responsive single-panel layout on small screens; the list and detail views swap in place, with a Back button to return to the list
|
||
- **No dependencies** — pure HTML, CSS, and vanilla JavaScript; requires a local web server (see Usage)
|
||
|
||
## Usage
|
||
|
||
1. Serve the folder from a web server and open `restaurant-picker.html` in a browser. (The app fetches `2026-restaurants.json` at runtime, so it cannot be opened directly as a local `file://` URL — a server is required. A simple option is the VS Code Live Server extension, or `npx serve .` in the project folder.)
|
||
2. Browse the restaurant list or use the toolbar filters to narrow choices.
|
||
3. Use the **First Course**, **Second Course**, or **Third Course** buttons to browse all dishes for a given course and jump to any restaurant that interests you.
|
||
4. Check the **You** and **Wife** boxes on any restaurant you're each interested in.
|
||
5. Click **Pick for Us!** to randomly select from the restaurants you both want.
|
||
6. Re-roll as needed, or click **View Details** to jump to the winner's menu.
|
||
7. Use **Clear Filters** to reset the toolbar, or **Reset Selected** to clear all picks and start over.
|
||
|
||
## Tech Stack
|
||
|
||
| Layer | Details |
|
||
|-------|---------|
|
||
| Structure | HTML5 |
|
||
| Styling | Inline CSS (dark red/gold IRW theme) |
|
||
| Logic | Vanilla JavaScript |
|
||
| Persistence | Browser `localStorage` |
|
||
| Dependencies | None |
|
||
|
||
## File Structure
|
||
|
||
```
|
||
restaurant-picker.html # App shell — HTML, CSS, and JS logic
|
||
2026-restaurants.json # Restaurant data for the 2026 event (loaded at runtime via fetch)
|
||
LICENSE
|
||
README.md
|
||
```
|
||
|
||
Each year, create a new `YYYY-restaurants.json` and update the filename in the `fetch()` call near the bottom of `restaurant-picker.html`.
|
||
|
||
---
|
||
|
||
## Data Entry Guide
|
||
|
||
Each year's restaurant data is stored as a JSON file (`YYYY-restaurants.json`) loaded by `restaurant-picker.html` at runtime. The source data comes from the [IRW restaurant listing pages](https://inlanderrestaurantweek.com/restaurants/). This section covers the data schema and common pitfalls to avoid when entering or updating restaurant records.
|
||
|
||
### Restaurant Object Schema
|
||
|
||
```json
|
||
{
|
||
"name": "Restaurant Name",
|
||
"slug": "restaurantname",
|
||
"price": 35,
|
||
"areas": ["Downtown"],
|
||
"cuisine": "Italian",
|
||
"url": "https://inlanderrestaurantweek.com/project/restaurantname/",
|
||
"menu": {
|
||
"hours": "Menu served Tue-Sat, 5 pm-close",
|
||
"phone": "(509) 555-1234",
|
||
"courses": {
|
||
"First Course": [
|
||
{ "name": "Dish Name", "desc": "Ingredients and description GF" }
|
||
],
|
||
"Second Course": [
|
||
{ "name": "Dish Name", "desc": "Ingredients and description" }
|
||
],
|
||
"Third Course": [
|
||
{ "name": "Dish Name", "desc": "Ingredients and description V+" }
|
||
]
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
**Field notes:**
|
||
- `slug` — lowercase, no spaces or special characters; used as an internal identifier
|
||
- `price` — must be `25`, `35`, or `45` (the three IRW price tiers)
|
||
- `areas` — array; a restaurant can belong to more than one area (e.g. `["Downtown", "Coeur d'Alene"]`)
|
||
- `phone` — use empty string `""` if not listed
|
||
- Each course is an **array** — restaurants sometimes offer a choice of two or more dishes per course; add one object per option
|
||
|
||
### Valid Area Values
|
||
|
||
```
|
||
Airway Heights Athol Coeur d'Alene Downtown
|
||
Hayden Liberty Lake North Spokane Post Falls
|
||
South Spokane Spokane Valley West Spokane Worley
|
||
ID (Coeur d'Alene-area catch-all)
|
||
```
|
||
|
||
### Valid Cuisine Values
|
||
|
||
```
|
||
American Asian Barbecue Bistro Eclectic European French
|
||
Fusion Gastropub German Indian Irish Italian Latin
|
||
Mediterranean Mexican Middle Eastern Northwest Pizza
|
||
Seafood Southern Steakhouse Thai
|
||
```
|
||
|
||
New cuisine types can be added, but check existing values first to keep filters consistent.
|
||
|
||
### Dietary Tags
|
||
|
||
Dietary tags go at the **end of the `desc` field**, separated by a space. Multiple tags are space-separated.
|
||
|
||
| Tag | Meaning |
|
||
|-----|---------|
|
||
| `GF` | Gluten Free |
|
||
| `GFA` | Gluten Free Available (on request or with substitution) |
|
||
| `V` | Vegetarian |
|
||
| `V+` | Vegan |
|
||
| `DF` | Dairy Free |
|
||
|
||
**Examples:**
|
||
```
|
||
"desc": "Mixed greens, candied walnuts, goat cheese, balsamic vinaigrette GF V"
|
||
"desc": "House pasta with roasted vegetables – sub GF pasta available GFA V"
|
||
"desc": "Coconut curry with tofu and seasonal vegetables V+ GF DF"
|
||
```
|
||
|
||
Tags that are inline notes within a description (e.g. `– sub GF crust for additional charge`) are fine to leave mid-sentence; only the primary applicable tags belong at the end.
|
||
|
||
### Common Data Entry Pitfall: Dietary Tag as Dish Name
|
||
|
||
The IRW website displays a dietary icon **before** the dish name. When copying menu data, it is easy to accidentally paste the dietary tag as the `name` field and run the dish name directly into the start of `desc` with no separator.
|
||
|
||
**Wrong (broken):**
|
||
```json
|
||
{ "name": "GFA", "desc": "Margherita PizzaSan Marzano Tomato Sauce. Fresh Mozzarella." }
|
||
```
|
||
|
||
**Correct:**
|
||
```json
|
||
{ "name": "Margherita Pizza", "desc": "San Marzano Tomato Sauce. Fresh Mozzarella. GFA" }
|
||
```
|
||
|
||
Signs that an entry has this problem:
|
||
- The `name` field is just a dietary tag (`"GF"`, `"GFA"`, `"V+"`, etc.)
|
||
- The `desc` field starts with what looks like a dish name run together with the description (no space or punctuation between them, e.g. `"Chocolate TorteA rich slice of flourless paradise"`)
|
||
|
||
### Special Characters
|
||
|
||
The data lives in a JSON file, so be careful with quotation marks inside descriptions. Use a **curly/smart right double-quote** (`"`, U+201D) for inch marks (e.g. `7" pizza`) rather than a straight ASCII `"`, which would break the JSON string. Em dashes (`–`) and curly apostrophes (`'`) from the source website copy fine as-is.
|
||
|
||
**Critical: JSON structural quotes must be straight ASCII double quotes.** Some editors and AI tools auto-correct straight `"` to curly `"` / `"` (U+201C / U+201D). If curly quotes end up wrapping property names or values (e.g. `"name"` instead of `"name"`), the JSON file will fail to parse and no restaurants will appear in the app. Always verify that the structural quotes in the data use the straight ASCII `"` character (U+0022). |