> Source URL: /unit-3/project-paths/thu-h/thu-h-2026-04-23.guide
# Thu's Project Guide

**Project:** OPT Pal — H-1B Employer Data Hub
**Category:** Web App (Flask) + Data Science
**Last updated:** April 23

---

> Note: This guide reflects the latest state of your project repo. It may not match the most up-to-date version if you've worked since.

In class today we lined up two polish items before Checkpoint 3: a proper landing page at `/` and turning the state quiz step into checkboxes showing only states that actually appear in the data.

---

## Phase 1: Landing page at `/`

### Objective

Replace the redirect-to-quiz with a real home page that explains what OPT Pal is, has a CTA (Call To Action) button to start the quiz, and shows credits in a footer on every page.

### Instructions

- [ ] In `app.py`, change the `/` route to render `home.html` instead of redirecting
- [ ] Create `templates/home.html` with a title, a one-paragraph "What this is about" blurb, and a CTA button that links to `/quiz?step=1`
- [ ] In `templates/base.html`, add a `<footer>` at the bottom with your name, the course, and the USCIS data source

### Hints

**`app.py` — update the home route:**

```python
@app.route("/")
def home():
    return render_template("home.html", title="OPT Pal")
```

**`templates/home.html`:**

```html
{% extends "base.html" %} {% block content %}
<section class="panel hero">
  <h1>OPT Pal</h1>
  <p class="lede">
    Find H-1B-friendly employers that actually hire in your industry. OPT Pal
    turns public USCIS approval data into a simple quiz — answer 4 questions and
    get a dashboard of employers that match.
  </p>

  <h2>What this is about</h2>
  <p>
    Every year USCIS publishes the list of employers who sponsored H-1B
    petitions. That dataset is huge and hard to read. OPT Pal filters it by
    industry, state, city, and company size so you can focus on the employers
    that fit you.
  </p>

  <a class="btn btn-primary" href="{{ url_for('quiz', step=1) }}">
    Find employers
  </a>
</section>
{% endblock %}
```

**`templates/base.html` — add a footer inside `<body>`, after the main content block:**

```html
<footer class="site-footer">
  <p>
    Built by Thu Huynh for CSC 121 Final Project. Data source:
    <a
      href="https://www.uscis.gov/tools/reports-and-studies/h-1b-employer-data-hub"
    >
      USCIS H-1B Employer Data Hub
    </a>
    .
  </p>
</footer>
```

Since every page extends `base.html`, the footer will show up automatically on the quiz and dashboard too.

---

## Phase 2: State checkboxes (dataset-only, multi-select)

### Objective

On quiz step 2, show a checkbox for every state that appears in the data and let the user pick more than one. Pass the picks through the rest of the quiz and into the dashboard filter.

### Instructions

- [ ] In `search.py`, update `_build_where()` so a list of states becomes a `state IN (?, ?, ...)` clause
- [ ] In `app.py`, read state with `request.args.getlist("state")` in both `/quiz` and `/dashboard` (replacing `.get("state", "")`)
- [ ] In `templates/quiz.html` step 2, replace the `<select>` with one checkbox per entry from `unique_states()`
- [ ] In steps 3 and 4 and in the dashboard link, pass state forward as one hidden `<input>` per selected state

### Hints

**`search.py` — handle a list in `_build_where()`:**

```python
state = filters.get("state")
if isinstance(state, list) and state:
    placeholders = ", ".join("?" for _ in state)
    clauses.append(f"state IN ({placeholders})")
    params.extend(s.strip().upper() for s in state)
elif isinstance(state, str) and state:
    clauses.append("state = ?")
    params.append(state.strip().upper())
```

`unique_states()` already uses `SELECT DISTINCT state FROM employers`, so the checkbox list is automatically "just states present in the dataset" — no extra filtering needed.

**`app.py` — read state as a list:**

```python
filters = {
    "industry": request.args.get("industry", ""),
    "state":    request.args.getlist("state"),
    "city":     request.args.get("city", ""),
    "size":     request.args.get("size", ""),
}
```

Use the same `filters` dict shape in both `/quiz` and `/dashboard` so the rest of your code doesn't change.

**`templates/quiz.html` step 2 — checkboxes:**

```html
{% elif step == 2 %}
<form method="get" action="{{ url_for('quiz') }}">
  <input type="hidden" name="step" value="3" />
  <input type="hidden" name="industry" value="{{ answers.industry }}" />

  <fieldset>
    <legend>Which states are you open to?</legend>
    {% for s in states %}
    <label class="state-check">
      <input
        type="checkbox"
        name="state"
        value="{{ s }}"
        {%
        if
        s
        in
        answers.state
        %}checked{%
        endif
        %}
      />
      {{ s }}
    </label>
    {% endfor %}
  </fieldset>

  <button type="submit">Next</button>
</form>
```

**Passing state forward in later steps** — one hidden input per selected state:

```html
{% for s in answers.state %}
<input type="hidden" name="state" value="{{ s }}" />
{% endfor %}
```

Drop that block into step 3, step 4, and any other form that forwards answers. Flask's `request.args.getlist("state")` reads them all back as a list.

---

## Done when

- [ ] Visiting `/` shows the home page with the blurb and a working "Find employers" button
- [ ] Every page (home, quiz, dashboard) shows the footer with your credit + USCIS link
- [ ] Step 2 of the quiz shows checkboxes for each state in the dataset
- [ ] Picking two states on step 2 shows both states' employers on the dashboard
- [ ] Picking zero states works (no state filter applied)


---

## Backlinks

The following sources link to this document:

- [April 23 -- Home page + state checkboxes](/unit-3/project-paths/thu-h/thu-h.path.llm.md)
