fix: New Build opens wizard directly instead of page-within-a-page (#72) (#76)

This commit is contained in:
mwisnowski 2026-04-06 08:22:10 -07:00 committed by GitHub
parent e9ee280b09
commit 65a6100d13
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 25 additions and 25 deletions

View file

@ -12,11 +12,12 @@ This format follows Keep a Changelog principles and aims for Semantic Versioning
_No unreleased changes yet_
### Changed
_No unreleased changes yet_
- **Deck name no longer auto-fills on commander selection**: The deck name field in the new deck wizard stays blank when a commander is chosen. If left empty on submit, the builder defaults to the commander name as before.
### Fixed
- **Card hover preview in theme browser (#70)**: Example card thumbnails in the theme detail/browser page were showing the wrong card image (a fuzzy search for "Card") when hovered. The `<img>` elements inside `.ex-card` containers lacked `data-card-name` attributes, so the hover system fell back to the literal string "Card". Added `data-card-name`, `data-original-name`, `data-role`, and `data-tags` to example card `<img>` elements in `detail_fragment.html` to match the existing commander image pattern.
- **Enter key cancels commander search in new deck modal (#71)**: Pressing Enter while typing a commander name in the new deck wizard would submit the form before the autocomplete candidates loaded (due to the 220 ms search delay), resulting in "Commander not found". A capture-phase keydown handler now intercepts Enter on the commander field, and a direct `fetch()` call bypasses HTMX timing entirely — it fetches and auto-selects the first match immediately, then triggers the inspect/theme load. When candidates are already visible, Enter selects the highlighted one as before.
- **New Build creates a page within a page (#72)**: Clicking "New Build" from the build summary page embedded the entire build page inside the `#wizard` div instead of opening the new deck wizard. The `reset-all` endpoint was returning a `302` redirect which HTMX followed and rendered inline. "New Build" and "Start over" buttons across all wizard steps now directly open the new deck modal overlay (via `hx-get="/build/new?reset=1"`), clearing the session server-side and presenting a fresh wizard without a full page navigation. Deck name no longer auto-fills with the commander name when a commander is selected — it remains blank and defaults to the commander name only if submitted empty.
### Removed
_No unreleased changes yet_

View file

@ -5,11 +5,12 @@
_No unreleased changes yet_
### Changed
_No unreleased changes yet_
- **Deck name no longer auto-fills on commander selection**: The deck name field in the new deck wizard stays blank when a commander is chosen. If left empty on submit, the builder defaults to the commander name as before.
### Fixed
- Card hover preview now works correctly for example cards in the theme browser
- Pressing Enter while typing a commander name now correctly selects the first match instead of showing "Commander not found"
- **Card hover preview in theme browser (#70)**: Example card thumbnails in the theme detail/browser page were showing the wrong card image (a fuzzy search for "Card") when hovered. The `<img>` elements inside `.ex-card` containers lacked `data-card-name` attributes, so the hover system fell back to the literal string "Card". Added `data-card-name`, `data-original-name`, `data-role`, and `data-tags` to example card `<img>` elements in `detail_fragment.html` to match the existing commander image pattern.
- **Enter key cancels commander search in new deck modal (#71)**: Pressing Enter while typing a commander name in the new deck wizard would submit the form before the autocomplete candidates loaded (due to the 220 ms search delay), resulting in "Commander not found". A capture-phase keydown handler now intercepts Enter on the commander field, and a direct `fetch()` call bypasses HTMX timing entirely — it fetches and auto-selects the first match immediately, then triggers the inspect/theme load. When candidates are already visible, Enter selects the highlighted one as before.
- **New Build creates a page within a page (#72)**: Clicking "New Build" from the build summary page embedded the entire build page inside the `#wizard` div instead of opening the new deck wizard. The `reset-all` endpoint was returning a `302` redirect which HTMX followed and rendered inline. "New Build" and "Start over" buttons across all wizard steps now directly open the new deck modal overlay, clearing the session server-side and presenting a fresh wizard without a full page navigation. Deck name no longer auto-fills with the commander name when a commander is selected — it remains blank and defaults to the commander name only if submitted empty.
### Removed
_No unreleased changes yet_

View file

@ -64,11 +64,15 @@ _ARCHETYPE_JS_MAP: dict[str, dict] = {
# ==============================================================================
@router.get("/new", response_class=HTMLResponse)
async def build_new_modal(request: Request) -> HTMLResponse:
async def build_new_modal(request: Request, reset: str = Query("")) -> HTMLResponse:
"""Return the New Deck modal content (for an overlay)."""
sid = request.cookies.get("sid") or new_sid()
sess = get_session(sid)
# Full session reset when requested (e.g. from "New Build" on summary page)
if reset == "1":
sess.clear()
# Clear build context to allow skip controls to work
# (Otherwise toggle endpoint thinks build is in progress)
if "build_ctx" in sess:

View file

@ -364,7 +364,13 @@ async def build_step1_confirm(request: Request) -> HTMLResponse:
@router.post("/reset-all", response_class=HTMLResponse)
async def build_reset_all(request: Request) -> HTMLResponse:
return RedirectResponse("/build", status_code=302)
sid = request.cookies.get("sid") or new_sid()
sess = get_session(sid)
sess.clear()
resp = HTMLResponse("", status_code=200)
resp.headers["HX-Redirect"] = "/build"
resp.set_cookie("sid", sid, httponly=True, samesite="lax")
return resp
# ============================================================================

View file

@ -3,7 +3,7 @@
{% for cand in candidates %}
<li>
<button type="button" id="cand-{{ loop.index0 }}" class="chip candidate-btn" role="option" data-idx="{{ loop.index0 }}" data-name="{{ cand.value|e }}" data-display="{{ cand.display|e }}"
onclick="(function(){ try{ var preferred=this.getAttribute('data-name')||''; var displayed=this.getAttribute('data-display')||preferred; var ci = document.querySelector('input[name=commander]'); if(ci){ ci.value=preferred; ci.dataset.confirmedName=preferred; try{ ci.selectionStart = ci.selectionEnd = ci.value.length; }catch(_){} } var nm = document.querySelector('input[name=name]'); if(nm && (!nm.value || !nm.value.trim())){ nm.value=displayed; } }catch(_){ } }).call(this)"
onclick="(function(){ try{ var preferred=this.getAttribute('data-name')||''; var ci = document.querySelector('input[name=commander]'); if(ci){ ci.value=preferred; ci.dataset.confirmedName=preferred; try{ ci.selectionStart = ci.selectionEnd = ci.value.length; }catch(_){} } }catch(_){ } }).call(this)"
hx-get="/build/new/inspect?name={{ cand.value|urlencode }}"
hx-target="#newdeck-tags-slot" hx-swap="innerHTML">
{{ cand.display }}

View file

@ -1152,8 +1152,6 @@
var display = btn.getAttribute('data-display') || name;
ci.value = name;
ci.dataset.confirmedName = name;
var nm = f ? f.querySelector('input[name="name"]') : null;
if (nm && !nm.value.trim()) nm.value = display;
var candidateList = document.getElementById('newdeck-candidates');
if (candidateList) {
candidateList.innerHTML = html;

View file

@ -195,9 +195,7 @@
</form>
<div style="margin-top:.5rem;">
<form hx-post="/build/reset-all" hx-target="#wizard" hx-swap="innerHTML" style="display:inline; margin:0;">
<button type="submit">Start over</button>
</form>
<button type="button" hx-get="/build/new?reset=1" hx-target="body" hx-swap="beforeend">Start over</button>
</div>
</div>
</div>

View file

@ -38,9 +38,7 @@
</div>
</form>
<div style="margin-top:.5rem;">
<form hx-post="/build/reset-all" hx-target="#wizard" hx-swap="innerHTML" style="display:inline; margin:0;">
<button type="submit">Start over</button>
</form>
<button type="button" hx-get="/build/new?reset=1" hx-target="body" hx-swap="beforeend">Start over</button>
</div>
</div>
</div>

View file

@ -37,9 +37,7 @@
<button type="submit" class="btn-continue" data-action="continue">Build Deck</button>
</form>
<button type="button" class="btn-back" data-action="back" hx-get="/build/step3" hx-target="#wizard" hx-swap="innerHTML">Back</button>
<form hx-post="/build/reset-all" hx-target="#wizard" hx-swap="innerHTML" style="display:inline; margin:0;">
<button type="submit">Start over</button>
</form>
<button type="button" hx-get="/build/new?reset=1" hx-target="body" hx-swap="beforeend">Start over</button>
</div>
</div>
</div>

View file

@ -342,9 +342,7 @@
<input type="hidden" name="show_skipped" value="{{ '1' if show_skipped else '0' }}" />
<button type="submit" class="btn-continue" data-action="continue">Restart Build</button>
</form>
<form hx-post="/build/reset-all" hx-target="#wizard" hx-swap="innerHTML" class="inline-form">
<button type="submit" class="btn" title="Start a brand new build (clears selections)">New build</button>
</form>
<button type="button" class="btn" hx-get="/build/new?reset=1" hx-target="body" hx-swap="beforeend" title="Start a brand new build (clears selections)">New build</button>
<button type="button" class="btn-back" data-action="back" hx-get="/build/step4" hx-target="#wizard" hx-swap="innerHTML">Back</button>
</div>
{% else %}
@ -378,9 +376,7 @@
<form hx-post="/build/step5/reset-stage" hx-target="#wizard" hx-swap="innerHTML" class="inline-form">
<button type="submit" class="btn" title="Reset this stage to pre-stage picks">Reset stage</button>
</form>
<form hx-post="/build/reset-all" hx-target="#wizard" hx-swap="innerHTML" class="inline-form">
<button type="submit" class="btn" title="Start a brand new build (clears selections)">New build</button>
</form>
<button type="button" class="btn" hx-get="/build/new?reset=1" hx-target="body" hx-swap="beforeend" title="Start a brand new build (clears selections)">New build</button>
<label class="muted form-label-icon ml-2">
<input type="checkbox" name="__toggle_show_skipped" data-pref="build:show_skipped" {% if show_skipped %}checked{% endif %}
onchange="const val=this.checked?'1':'0'; for(const f of this.closest('section').querySelectorAll('form')){ const h=f.querySelector('input[name=show_skipped]'); if(h) h.value=val; }" />