Files
Inlander-Restaurant-Week-Pi…/README.md

10 KiB
Raw Blame History

Inlander Restaurant Week Picker

A web app for browsing, marking favorites, and randomly selecting a restaurant during Inlander Restaurant Week.

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. This section covers the data schema and common pitfalls to avoid when entering or updating restaurant records.

Restaurant Object Schema

{
  "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):

{ "name": "GFA", "desc": "Margherita PizzaSan Marzano Tomato Sauce. Fresh Mozzarella." }

Correct:

{ "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).


Incomplete Course Data (Missing or Too Few Menu Items)

Why It Happens

The Inlander Restaurant Week website is JavaScript-rendered — the actual menu content is loaded dynamically after the page shell is delivered. Automated tools (AI assistants, curl, simple web fetchers) only receive the bare HTML shell and never see the menu items. This causes courses to be entered with 0 or 1 item instead of the expected 3.

Signs a restaurant has this problem:

  • A course array has 0 items — the key exists but the array is empty ([])
  • A course array has 1 item — only one option was captured instead of all three
  • A course array has 2 items — one option was missed

How to Find Affected Restaurants

Run the following Python script against the JSON file to identify any restaurant where a course does not have exactly 3 items:

import json

with open('2026-restaurants.json', encoding='utf-8') as f:
    restaurants = json.load(f)

for r in restaurants:
    name = r.get('name', 'Unknown')
    courses = r.get('menu', {}).get('courses', {})
    issues = []
    for course_name, items in courses.items():
        if len(items) != 3:
            issues.append(f"{course_name}: {len(items)} item(s)")
    if issues:
        print(f"{name}: {', '.join(issues)}")

Any restaurant printed by this script needs to be manually verified and corrected.

How to Manually Fix Incomplete Entries

  1. Open the restaurant's IRW page in a browser (the url field in the JSON has the direct link, e.g. https://inlanderrestaurantweek.com/project/flyinggoat/).
  2. Let the page fully load — the menu content is rendered by JavaScript and will not appear until the page has finished loading.
  3. For each course, confirm there are 3 options and copy all dish names and descriptions.
  4. Update the corresponding entry in YYYY-restaurants.json, adding the missing dish objects to the course array. Each dish follows this format:
    { "name": "Dish Name", "desc": "Full description here GFA" }
    
  5. Re-run the Python script above to confirm no courses are still flagged.

Note: Some restaurants may genuinely offer fewer than 3 options for a course. If the live IRW page confirms only 1 or 2 choices are available, that count is correct and the entry does not need further changes.