refactor: move route handlers to module level for idiomatic FastHTML
- Routes are now at module level, accessible for import by templates - Templates accept action parameter (route function or URL string) - Routes pass themselves to templates for type-safe form actions - Changes DB access pattern from app.state.db to request.app.state.db - Registration uses rt(...)(func) pattern instead of @rt decorator This enables the idiomatic FastHTML pattern where forms can use action=route_function instead of action="/path/string", providing type safety and refactoring support. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -49,33 +49,24 @@ def resolve_ducks_at_location(db: Any, location_id: str, ts_utc: int) -> list[st
|
||||
return [row[0] for row in rows]
|
||||
|
||||
|
||||
def register_egg_routes(rt, app):
|
||||
"""Register egg capture routes.
|
||||
|
||||
Args:
|
||||
rt: FastHTML route decorator.
|
||||
app: FastHTML application instance.
|
||||
"""
|
||||
|
||||
@rt("/")
|
||||
def index(request: Request):
|
||||
def egg_index(request: Request):
|
||||
"""GET / - Egg Quick Capture form."""
|
||||
db = app.state.db
|
||||
db = request.app.state.db
|
||||
locations = LocationRepository(db).list_active()
|
||||
|
||||
# Check for pre-selected location from query params
|
||||
selected_location_id = request.query_params.get("location_id")
|
||||
|
||||
return page(
|
||||
egg_form(locations, selected_location_id=selected_location_id),
|
||||
egg_form(locations, selected_location_id=selected_location_id, action=product_collected),
|
||||
title="Egg - AnimalTrack",
|
||||
active_nav="egg",
|
||||
)
|
||||
|
||||
@rt("/actions/product-collected", methods=["POST"])
|
||||
|
||||
async def product_collected(request: Request):
|
||||
"""POST /actions/product-collected - Record egg collection."""
|
||||
db = app.state.db
|
||||
db = request.app.state.db
|
||||
form = await request.form()
|
||||
|
||||
# Extract form data
|
||||
@@ -148,7 +139,7 @@ def register_egg_routes(rt, app):
|
||||
response = HTMLResponse(
|
||||
content=str(
|
||||
page(
|
||||
egg_form(locations, selected_location_id=location_id),
|
||||
egg_form(locations, selected_location_id=location_id, action=product_collected),
|
||||
title="Egg - AnimalTrack",
|
||||
active_nav="egg",
|
||||
)
|
||||
@@ -163,6 +154,17 @@ def register_egg_routes(rt, app):
|
||||
return response
|
||||
|
||||
|
||||
def register_egg_routes(rt, app):
|
||||
"""Register egg capture routes.
|
||||
|
||||
Args:
|
||||
rt: FastHTML route decorator.
|
||||
app: FastHTML application instance.
|
||||
"""
|
||||
rt("/")(egg_index)
|
||||
rt("/actions/product-collected", methods=["POST"])(product_collected)
|
||||
|
||||
|
||||
def _render_error_form(locations, selected_location_id, error_message):
|
||||
"""Render form with error message.
|
||||
|
||||
@@ -181,6 +183,7 @@ def _render_error_form(locations, selected_location_id, error_message):
|
||||
locations,
|
||||
selected_location_id=selected_location_id,
|
||||
error=error_message,
|
||||
action=product_collected,
|
||||
),
|
||||
title="Egg - AnimalTrack",
|
||||
active_nav="egg",
|
||||
|
||||
@@ -38,18 +38,9 @@ def get_feed_balance(db: Any, feed_type_code: str) -> int | None:
|
||||
return row[0] if row else None
|
||||
|
||||
|
||||
def register_feed_routes(rt, app):
|
||||
"""Register feed capture routes.
|
||||
|
||||
Args:
|
||||
rt: FastHTML route decorator.
|
||||
app: FastHTML application instance.
|
||||
"""
|
||||
|
||||
@rt("/feed")
|
||||
def feed_index(request: Request):
|
||||
"""GET /feed - Feed Quick Capture page."""
|
||||
db = app.state.db
|
||||
db = request.app.state.db
|
||||
locations = LocationRepository(db).list_active()
|
||||
feed_types = FeedTypeRepository(db).list_active()
|
||||
|
||||
@@ -63,15 +54,17 @@ def register_feed_routes(rt, app):
|
||||
locations,
|
||||
feed_types,
|
||||
active_tab=active_tab,
|
||||
give_action=feed_given,
|
||||
purchase_action=feed_purchased,
|
||||
),
|
||||
title="Feed - AnimalTrack",
|
||||
active_nav="feed",
|
||||
)
|
||||
|
||||
@rt("/actions/feed-given", methods=["POST"])
|
||||
|
||||
async def feed_given(request: Request):
|
||||
"""POST /actions/feed-given - Record feed given."""
|
||||
db = app.state.db
|
||||
db = request.app.state.db
|
||||
form = await request.form()
|
||||
|
||||
# Extract form data
|
||||
@@ -191,6 +184,8 @@ def register_feed_routes(rt, app):
|
||||
selected_feed_type_code=feed_type_code,
|
||||
default_amount_kg=default_amount_kg,
|
||||
balance_warning=balance_warning,
|
||||
give_action=feed_given,
|
||||
purchase_action=feed_purchased,
|
||||
),
|
||||
title="Feed - AnimalTrack",
|
||||
active_nav="feed",
|
||||
@@ -210,10 +205,10 @@ def register_feed_routes(rt, app):
|
||||
|
||||
return response
|
||||
|
||||
@rt("/actions/feed-purchased", methods=["POST"])
|
||||
|
||||
async def feed_purchased(request: Request):
|
||||
"""POST /actions/feed-purchased - Record feed purchase."""
|
||||
db = app.state.db
|
||||
db = request.app.state.db
|
||||
form = await request.form()
|
||||
|
||||
# Extract form data
|
||||
@@ -339,6 +334,8 @@ def register_feed_routes(rt, app):
|
||||
locations,
|
||||
feed_types,
|
||||
active_tab="purchase",
|
||||
give_action=feed_given,
|
||||
purchase_action=feed_purchased,
|
||||
),
|
||||
title="Feed - AnimalTrack",
|
||||
active_nav="feed",
|
||||
@@ -359,6 +356,18 @@ def register_feed_routes(rt, app):
|
||||
return response
|
||||
|
||||
|
||||
def register_feed_routes(rt, app):
|
||||
"""Register feed capture routes.
|
||||
|
||||
Args:
|
||||
rt: FastHTML route decorator.
|
||||
app: FastHTML application instance.
|
||||
"""
|
||||
rt("/feed")(feed_index)
|
||||
rt("/actions/feed-given", methods=["POST"])(feed_given)
|
||||
rt("/actions/feed-purchased", methods=["POST"])(feed_purchased)
|
||||
|
||||
|
||||
def _render_give_error(
|
||||
locations,
|
||||
feed_types,
|
||||
@@ -388,6 +397,8 @@ def _render_give_error(
|
||||
selected_location_id=selected_location_id,
|
||||
selected_feed_type_code=selected_feed_type_code,
|
||||
give_error=error_message,
|
||||
give_action=feed_given,
|
||||
purchase_action=feed_purchased,
|
||||
),
|
||||
title="Feed - AnimalTrack",
|
||||
active_nav="feed",
|
||||
@@ -416,6 +427,8 @@ def _render_purchase_error(locations, feed_types, error_message):
|
||||
feed_types,
|
||||
active_tab="purchase",
|
||||
purchase_error=error_message,
|
||||
give_action=feed_given,
|
||||
purchase_action=feed_purchased,
|
||||
),
|
||||
title="Feed - AnimalTrack",
|
||||
active_nav="feed",
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
# ABOUTME: Templates for Egg Quick Capture form.
|
||||
# ABOUTME: Provides form components for recording egg collections.
|
||||
|
||||
from collections.abc import Callable
|
||||
from typing import Any
|
||||
|
||||
from fasthtml.common import H2, Form, Hidden, Option
|
||||
from monsterui.all import Button, ButtonT, LabelInput, LabelSelect, LabelTextArea
|
||||
from ulid import ULID
|
||||
@@ -12,6 +15,7 @@ def egg_form(
|
||||
locations: list[Location],
|
||||
selected_location_id: str | None = None,
|
||||
error: str | None = None,
|
||||
action: Callable[..., Any] | str = "/actions/product-collected",
|
||||
) -> Form:
|
||||
"""Create the Egg Quick Capture form.
|
||||
|
||||
@@ -19,6 +23,7 @@ def egg_form(
|
||||
locations: List of active locations for the dropdown.
|
||||
selected_location_id: Pre-selected location ID (sticks after submission).
|
||||
error: Optional error message to display.
|
||||
action: Route function or URL string for form submission.
|
||||
|
||||
Returns:
|
||||
Form component for egg collection.
|
||||
@@ -82,7 +87,7 @@ def egg_form(
|
||||
# Submit button
|
||||
Button("Record Eggs", type="submit", cls=ButtonT.primary),
|
||||
# Form submission via standard action/method (hx-boost handles AJAX)
|
||||
action="/actions/product-collected",
|
||||
action=action,
|
||||
method="post",
|
||||
cls="space-y-4",
|
||||
)
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
# ABOUTME: Templates for Feed Quick Capture forms.
|
||||
# ABOUTME: Provides form components for recording feed given and purchases.
|
||||
|
||||
from collections.abc import Callable
|
||||
from typing import Any
|
||||
|
||||
from fasthtml.common import H1, H2, A, Div, Form, Hidden, Li, Option, P, Ul
|
||||
from monsterui.all import (
|
||||
Button,
|
||||
@@ -25,6 +28,8 @@ def feed_page(
|
||||
give_error: str | None = None,
|
||||
purchase_error: str | None = None,
|
||||
balance_warning: str | None = None,
|
||||
give_action: Callable[..., Any] | str = "/actions/feed-given",
|
||||
purchase_action: Callable[..., Any] | str = "/actions/feed-purchased",
|
||||
):
|
||||
"""Create the Feed Quick Capture page with tabbed forms.
|
||||
|
||||
@@ -38,6 +43,8 @@ def feed_page(
|
||||
give_error: Error message for give form.
|
||||
purchase_error: Error message for purchase form.
|
||||
balance_warning: Warning about negative inventory balance.
|
||||
give_action: Route function or URL for give feed form.
|
||||
purchase_action: Route function or URL for purchase feed form.
|
||||
|
||||
Returns:
|
||||
Page content with tabbed forms.
|
||||
@@ -76,11 +83,12 @@ def feed_page(
|
||||
default_amount_kg=default_amount_kg,
|
||||
error=give_error,
|
||||
balance_warning=balance_warning,
|
||||
action=give_action,
|
||||
),
|
||||
cls="uk-active" if give_active else "",
|
||||
),
|
||||
Li(
|
||||
purchase_feed_form(feed_types, error=purchase_error),
|
||||
purchase_feed_form(feed_types, error=purchase_error, action=purchase_action),
|
||||
cls="" if give_active else "uk-active",
|
||||
),
|
||||
),
|
||||
@@ -96,6 +104,7 @@ def give_feed_form(
|
||||
default_amount_kg: int | None = None,
|
||||
error: str | None = None,
|
||||
balance_warning: str | None = None,
|
||||
action: Callable[..., Any] | str = "/actions/feed-given",
|
||||
) -> Form:
|
||||
"""Create the Give Feed form.
|
||||
|
||||
@@ -107,6 +116,7 @@ def give_feed_form(
|
||||
default_amount_kg: Default value for amount field.
|
||||
error: Error message to display.
|
||||
balance_warning: Warning about negative balance.
|
||||
action: Route function or URL for form submission.
|
||||
|
||||
Returns:
|
||||
Form component for giving feed.
|
||||
@@ -196,7 +206,7 @@ def give_feed_form(
|
||||
Hidden(name="nonce", value=str(ULID())),
|
||||
# Submit button
|
||||
Button("Record Feed Given", type="submit", cls=ButtonT.primary),
|
||||
action="/actions/feed-given",
|
||||
action=action,
|
||||
method="post",
|
||||
cls="space-y-4",
|
||||
)
|
||||
@@ -205,12 +215,14 @@ def give_feed_form(
|
||||
def purchase_feed_form(
|
||||
feed_types: list[FeedType],
|
||||
error: str | None = None,
|
||||
action: Callable[..., Any] | str = "/actions/feed-purchased",
|
||||
) -> Form:
|
||||
"""Create the Purchase Feed form.
|
||||
|
||||
Args:
|
||||
feed_types: List of active feed types.
|
||||
error: Error message to display.
|
||||
action: Route function or URL for form submission.
|
||||
|
||||
Returns:
|
||||
Form component for purchasing feed.
|
||||
@@ -290,7 +302,7 @@ def purchase_feed_form(
|
||||
Hidden(name="nonce", value=str(ULID())),
|
||||
# Submit button
|
||||
Button("Record Purchase", type="submit", cls=ButtonT.primary),
|
||||
action="/actions/feed-purchased",
|
||||
action=action,
|
||||
method="post",
|
||||
cls="space-y-4",
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user