Add loading state indicators to all form submit buttons

Add hx_disabled_elt="this" to submit buttons across all forms to
disable them during form submission, preventing double-clicks and
providing visual feedback that the action is processing.

Buttons updated:
- actions.py: promote, cohort, hatch, tag-add, tag-end, attrs,
  outcome, status-correct forms and their diff_panel confirmations
- eggs.py: collect and sell forms
- feed.py: give and purchase forms
- locations.py: create and rename forms
- move.py: move form and diff_panel confirmation
- products.py: create form
- registry.py: filter apply button

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-09 12:07:15 +00:00
parent 2fc98155c3
commit b09d3088eb
7 changed files with 34 additions and 16 deletions

View File

@@ -266,7 +266,7 @@ def cohort_form(
# Hidden nonce for idempotency # Hidden nonce for idempotency
Hidden(name="nonce", value=str(ULID())), Hidden(name="nonce", value=str(ULID())),
# Submit button # Submit button
Button("Create Cohort", type="submit", cls=ButtonT.primary), Button("Create Cohort", type="submit", cls=ButtonT.primary, hx_disabled_elt="this"),
# Form submission via standard action/method (hx-boost handles AJAX) # Form submission via standard action/method (hx-boost handles AJAX)
action=action, action=action,
method="post", method="post",
@@ -395,7 +395,7 @@ def hatch_form(
# Hidden nonce for idempotency # Hidden nonce for idempotency
Hidden(name="nonce", value=str(ULID())), Hidden(name="nonce", value=str(ULID())),
# Submit button # Submit button
Button("Record Hatch", type="submit", cls=ButtonT.primary), Button("Record Hatch", type="submit", cls=ButtonT.primary, hx_disabled_elt="this"),
# Form submission via standard action/method (hx-boost handles AJAX) # Form submission via standard action/method (hx-boost handles AJAX)
action=action, action=action,
method="post", method="post",
@@ -522,6 +522,7 @@ def promote_form(
"Save Changes" if is_rename else "Promote to Identified", "Save Changes" if is_rename else "Promote to Identified",
type="submit", type="submit",
cls=ButtonT.primary, cls=ButtonT.primary,
hx_disabled_elt="this",
), ),
# Form submission via standard action/method (hx-boost handles AJAX) # Form submission via standard action/method (hx-boost handles AJAX)
action=action, action=action,
@@ -648,7 +649,7 @@ def tag_add_form(
Hidden(name="confirmed", value=""), Hidden(name="confirmed", value=""),
Hidden(name="nonce", value=str(ULID())), Hidden(name="nonce", value=str(ULID())),
# Submit button # Submit button
Button("Add Tag", type="submit", cls=ButtonT.primary), Button("Add Tag", type="submit", cls=ButtonT.primary, hx_disabled_elt="this"),
# Form submission via standard action/method (hx-boost handles AJAX) # Form submission via standard action/method (hx-boost handles AJAX)
action=action, action=action,
method="post", method="post",
@@ -712,6 +713,7 @@ def tag_add_diff_panel(
f"Confirm Tag ({diff.server_count} animals)", f"Confirm Tag ({diff.server_count} animals)",
type="submit", type="submit",
cls=ButtonT.primary, cls=ButtonT.primary,
hx_disabled_elt="this",
), ),
cls="flex gap-3 mt-4", cls="flex gap-3 mt-4",
), ),
@@ -867,7 +869,13 @@ def tag_end_form(
Hidden(name="confirmed", value=""), Hidden(name="confirmed", value=""),
Hidden(name="nonce", value=str(ULID())), Hidden(name="nonce", value=str(ULID())),
# Submit button # Submit button
Button("End Tag", type="submit", cls=ButtonT.primary, disabled=not active_tags), Button(
"End Tag",
type="submit",
cls=ButtonT.primary,
disabled=not active_tags,
hx_disabled_elt="this",
),
# Form submission via standard action/method (hx-boost handles AJAX) # Form submission via standard action/method (hx-boost handles AJAX)
action=action, action=action,
method="post", method="post",
@@ -931,6 +939,7 @@ def tag_end_diff_panel(
f"Confirm End Tag ({diff.server_count} animals)", f"Confirm End Tag ({diff.server_count} animals)",
type="submit", type="submit",
cls=ButtonT.primary, cls=ButtonT.primary,
hx_disabled_elt="this",
), ),
cls="flex gap-3 mt-4", cls="flex gap-3 mt-4",
), ),
@@ -1110,7 +1119,7 @@ def attrs_form(
Hidden(name="confirmed", value=""), Hidden(name="confirmed", value=""),
Hidden(name="nonce", value=str(ULID())), Hidden(name="nonce", value=str(ULID())),
# Submit button # Submit button
Button("Update Attributes", type="submit", cls=ButtonT.primary), Button("Update Attributes", type="submit", cls=ButtonT.primary, hx_disabled_elt="this"),
# Form submission via standard action/method (hx-boost handles AJAX) # Form submission via standard action/method (hx-boost handles AJAX)
action=action, action=action,
method="post", method="post",
@@ -1180,6 +1189,7 @@ def attrs_diff_panel(
f"Confirm Update ({diff.server_count} animals)", f"Confirm Update ({diff.server_count} animals)",
type="submit", type="submit",
cls=ButtonT.primary, cls=ButtonT.primary,
hx_disabled_elt="this",
), ),
cls="flex gap-3 mt-4", cls="flex gap-3 mt-4",
), ),
@@ -1405,7 +1415,7 @@ def outcome_form(
Hidden(name="confirmed", value=""), Hidden(name="confirmed", value=""),
Hidden(name="nonce", value=str(ULID())), Hidden(name="nonce", value=str(ULID())),
# Submit button # Submit button
Button("Record Outcome", type="submit", cls=ButtonT.destructive), Button("Record Outcome", type="submit", cls=ButtonT.destructive, hx_disabled_elt="this"),
# Form submission via standard action/method (hx-boost handles AJAX) # Form submission via standard action/method (hx-boost handles AJAX)
action=action, action=action,
method="post", method="post",
@@ -1484,6 +1494,7 @@ def outcome_diff_panel(
f"Confirm Outcome ({diff.server_count} animals)", f"Confirm Outcome ({diff.server_count} animals)",
type="submit", type="submit",
cls=ButtonT.destructive, cls=ButtonT.destructive,
hx_disabled_elt="this",
), ),
cls="flex gap-3 mt-4", cls="flex gap-3 mt-4",
), ),
@@ -1626,7 +1637,7 @@ def status_correct_form(
Hidden(name="confirmed", value=""), Hidden(name="confirmed", value=""),
Hidden(name="nonce", value=str(ULID())), Hidden(name="nonce", value=str(ULID())),
# Submit button # Submit button
Button("Correct Status", type="submit", cls=ButtonT.destructive), Button("Correct Status", type="submit", cls=ButtonT.destructive, hx_disabled_elt="this"),
# Form submission via standard action/method (hx-boost handles AJAX) # Form submission via standard action/method (hx-boost handles AJAX)
action=action, action=action,
method="post", method="post",
@@ -1693,6 +1704,7 @@ def status_correct_diff_panel(
f"Confirm Correction ({diff.server_count} animals)", f"Confirm Correction ({diff.server_count} animals)",
type="submit", type="submit",
cls=ButtonT.destructive, cls=ButtonT.destructive,
hx_disabled_elt="this",
), ),
cls="flex gap-3 mt-4", cls="flex gap-3 mt-4",
), ),

View File

@@ -206,7 +206,7 @@ def harvest_form(
# Hidden nonce for idempotency # Hidden nonce for idempotency
Hidden(name="nonce", value=str(ULID())), Hidden(name="nonce", value=str(ULID())),
# Submit button # Submit button
Button("Record Harvest", type="submit", cls=ButtonT.primary), Button("Record Harvest", type="submit", cls=ButtonT.primary, hx_disabled_elt="this"),
# Form submission via standard action/method (hx-boost handles AJAX) # Form submission via standard action/method (hx-boost handles AJAX)
action=action, action=action,
method="post", method="post",
@@ -347,7 +347,7 @@ def sell_form(
# Hidden nonce for idempotency # Hidden nonce for idempotency
Hidden(name="nonce", value=str(ULID())), Hidden(name="nonce", value=str(ULID())),
# Submit button # Submit button
Button("Record Sale", type="submit", cls=ButtonT.primary), Button("Record Sale", type="submit", cls=ButtonT.primary, hx_disabled_elt="this"),
# Form submission via standard action/method (hx-boost handles AJAX) # Form submission via standard action/method (hx-boost handles AJAX)
action=action, action=action,
method="post", method="post",

View File

@@ -260,7 +260,7 @@ def give_feed_form(
# Hidden nonce # Hidden nonce
Hidden(name="nonce", value=str(ULID())), Hidden(name="nonce", value=str(ULID())),
# Submit button # Submit button
Button("Record Feed Given", type="submit", cls=ButtonT.primary), Button("Record Feed Given", type="submit", cls=ButtonT.primary, hx_disabled_elt="this"),
action=action, action=action,
method="post", method="post",
cls="space-y-4", cls="space-y-4",
@@ -404,7 +404,7 @@ def purchase_feed_form(
# Hidden nonce # Hidden nonce
Hidden(name="nonce", value=str(ULID())), Hidden(name="nonce", value=str(ULID())),
# Submit button # Submit button
Button("Record Purchase", type="submit", cls=ButtonT.primary), Button("Record Purchase", type="submit", cls=ButtonT.primary, hx_disabled_elt="this"),
action=action, action=action,
method="post", method="post",
cls="space-y-4", cls="space-y-4",

View File

@@ -47,7 +47,7 @@ def location_list(
placeholder="Enter location name", placeholder="Enter location name",
), ),
Hidden(name="nonce", value=str(uuid4())), Hidden(name="nonce", value=str(uuid4())),
Button("Create Location", type="submit", cls=ButtonT.primary), Button("Create Location", type="submit", cls=ButtonT.primary, hx_disabled_elt="this"),
hx_post="/actions/location-created", hx_post="/actions/location-created",
hx_target="#location-list", hx_target="#location-list",
hx_swap="outerHTML", hx_swap="outerHTML",
@@ -160,7 +160,7 @@ def rename_form(
Hidden(name="nonce", value=str(uuid4())), Hidden(name="nonce", value=str(uuid4())),
DivFullySpaced( DivFullySpaced(
Button("Cancel", type="button", cls=ButtonT.ghost, hx_get="/locations"), Button("Cancel", type="button", cls=ButtonT.ghost, hx_get="/locations"),
Button("Rename", type="submit", cls=ButtonT.primary), Button("Rename", type="submit", cls=ButtonT.primary, hx_disabled_elt="this"),
), ),
hx_post="/actions/location-renamed", hx_post="/actions/location-renamed",
hx_target="#location-list", hx_target="#location-list",

View File

@@ -174,7 +174,7 @@ def move_form(
Hidden(name="confirmed", value=""), Hidden(name="confirmed", value=""),
Hidden(name="nonce", value=str(ULID())), Hidden(name="nonce", value=str(ULID())),
# Submit button # Submit button
Button("Move Animals", type="submit", cls=ButtonT.primary), Button("Move Animals", type="submit", cls=ButtonT.primary, hx_disabled_elt="this"),
# Form submission via standard action/method (hx-boost handles AJAX) # Form submission via standard action/method (hx-boost handles AJAX)
action=action, action=action,
method="post", method="post",
@@ -263,6 +263,7 @@ def diff_panel(
f"Confirm Move ({diff.server_count} animals)", f"Confirm Move ({diff.server_count} animals)",
type="submit", type="submit",
cls=ButtonT.primary, cls=ButtonT.primary,
hx_disabled_elt="this",
), ),
cls="flex gap-3 mt-4", cls="flex gap-3 mt-4",
), ),

View File

@@ -102,7 +102,7 @@ def product_sold_form(
# Hidden nonce for idempotency # Hidden nonce for idempotency
Hidden(name="nonce", value=str(ULID())), Hidden(name="nonce", value=str(ULID())),
# Submit button # Submit button
Button("Record Sale", type="submit", cls=ButtonT.primary), Button("Record Sale", type="submit", cls=ButtonT.primary, hx_disabled_elt="this"),
# Form submission via standard action/method (hx-boost handles AJAX) # Form submission via standard action/method (hx-boost handles AJAX)
action=action, action=action,
method="post", method="post",

View File

@@ -107,7 +107,12 @@ def registry_header(filter_str: str, total_count: int) -> Div:
), ),
# Buttons container # Buttons container
Div( Div(
Button("Apply", type="submit", cls=f"{ButtonT.primary} px-4"), Button(
"Apply",
type="submit",
cls=f"{ButtonT.primary} px-4",
hx_disabled_elt="this",
),
# Clear button (only shown if filter is active) # Clear button (only shown if filter is active)
A( A(
"Clear", "Clear",