feat(l4d2-web): blueprint rename moves to footer modal — matches overlay/server pattern

Drops the inline Name input from the blueprint edit form. A Rename link
sits next to Delete in the page footer; clicking opens a one-line modal
that posts to a new POST /blueprints/<id>/rename route. The main edit
form keeps the current name as a hidden input so its full Save still
works unchanged.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
mwiegand 2026-05-09 01:37:29 +02:00
parent ed12280cf0
commit aacd95012e
No known key found for this signature in database
2 changed files with 39 additions and 1 deletions

View file

@ -142,6 +142,29 @@ def update_blueprint_form(blueprint_id: int) -> Response:
return redirect(f"/blueprints/{blueprint_id}") return redirect(f"/blueprints/{blueprint_id}")
@bp.post("/blueprints/<int:blueprint_id>/rename")
@require_login
def rename_blueprint(blueprint_id: int) -> Response:
user = current_user()
assert user is not None
name = request.form.get("name", "").strip()
if not name:
return Response("name is required", status=400)
with session_scope() as db:
blueprint = db.scalar(
select(BlueprintModel).where(
BlueprintModel.id == blueprint_id,
BlueprintModel.user_id == user.id,
)
)
if blueprint is None:
return Response(status=404)
blueprint.name = name
return redirect(f"/blueprints/{blueprint_id}")
def _delete_blueprint(db, user_id: int, blueprint_id: int) -> Response | None: def _delete_blueprint(db, user_id: int, blueprint_id: int) -> Response | None:
blueprint = db.scalar( blueprint = db.scalar(
select(BlueprintModel).where( select(BlueprintModel).where(

View file

@ -9,7 +9,7 @@
</div> </div>
<form method="post" action="/blueprints/{{ blueprint.id }}" class="stack"> <form method="post" action="/blueprints/{{ blueprint.id }}" class="stack">
<input type="hidden" name="csrf_token" value="{{ session.get('csrf_token', '') }}"> <input type="hidden" name="csrf_token" value="{{ session.get('csrf_token', '') }}">
<label><span class="section-title">Name</span><input name="name" value="{{ blueprint.name }}" required></label> <input type="hidden" name="name" value="{{ blueprint.name }}">
<label><span class="section-title">Arguments</span><textarea name="arguments" rows="2" spellcheck="false">{{ arguments | join('\n') }}</textarea></label> <label><span class="section-title">Arguments</span><textarea name="arguments" rows="2" spellcheck="false">{{ arguments | join('\n') }}</textarea></label>
<span class="section-title">Overlays</span> <span class="section-title">Overlays</span>
<div class="overlay-picker"> <div class="overlay-picker">
@ -58,8 +58,23 @@
<div class="page-footer-actions"> <div class="page-footer-actions">
<button type="button" class="danger-outline" data-modal-open="delete-blueprint-modal">Delete blueprint</button> <button type="button" class="danger-outline" data-modal-open="delete-blueprint-modal">Delete blueprint</button>
<a href="#" class="link-button" data-modal-open="rename-blueprint-modal">Rename</a>
</div> </div>
<dialog id="rename-blueprint-modal" class="modal" aria-labelledby="rename-blueprint-title">
<div class="modal-header">
<h2 id="rename-blueprint-title">Rename blueprint</h2>
<button type="button" class="modal-close" data-modal-close aria-label="Close">&times;</button>
</div>
<div class="modal-body">
<form method="post" action="/blueprints/{{ blueprint.id }}/rename" class="inline-save">
<input type="hidden" name="csrf_token" value="{{ session.get('csrf_token', '') }}">
<input name="name" value="{{ blueprint.name }}" required autofocus>
<button type="submit">Save</button>
</form>
</div>
</dialog>
<dialog id="delete-blueprint-modal" class="modal" aria-labelledby="delete-blueprint-title"> <dialog id="delete-blueprint-modal" class="modal" aria-labelledby="delete-blueprint-title">
<div class="modal-header"> <div class="modal-header">
<h2 id="delete-blueprint-title">Delete blueprint "{{ blueprint.name }}"?</h2> <h2 id="delete-blueprint-title">Delete blueprint "{{ blueprint.name }}"?</h2>