From cccd76a44c58643f9ce78246971daf84f9008a5a Mon Sep 17 00:00:00 2001 From: Petru Paler Date: Fri, 2 Jan 2026 12:16:56 +0000 Subject: [PATCH] fix: 409 responses now swap and event log filtering works MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add 409 to HTMX responseHandling config so selection conflict dialogs are displayed instead of silently ignored - Fix event type dropdown using value="" which caused browsers to send display text "All types" instead of empty string - Use value="all" for "All types" option (matching location selector) - Handle event_type="all" in route as no filter - Remove dead code (location_name lookup duplicated in template) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- src/animaltrack/web/app.py | 6 ++++-- src/animaltrack/web/routes/events.py | 16 +++++----------- src/animaltrack/web/templates/events.py | 2 +- 3 files changed, 10 insertions(+), 14 deletions(-) diff --git a/src/animaltrack/web/app.py b/src/animaltrack/web/app.py index dc8a2eb..901b5fc 100644 --- a/src/animaltrack/web/app.py +++ b/src/animaltrack/web/app.py @@ -126,11 +126,13 @@ def create_app( # So static_path should be the parent of static_base static_path_for_fasthtml = str(static_base.parent) if static_base.exists() else "." - # Configure HTMX to swap 422 responses for validation errors + # Configure HTMX to swap certain error responses so UI feedback is visible + # 409 (Conflict) returns confirmation dialogs for selection mismatches + # 422 (Validation Error) returns forms with error messages # Without this, hx-boost ignores non-2xx responses and errors appear to do nothing htmx_config = Meta( name="htmx-config", - content='{"responseHandling":[{"code":"204","swap":false},{"code":"[23]..","swap":true},{"code":"422","swap":true},{"code":"[45]..","swap":false,"error":true}]}', + content='{"responseHandling":[{"code":"204","swap":false},{"code":"[23]..","swap":true},{"code":"409","swap":true},{"code":"422","swap":true},{"code":"[45]..","swap":false,"error":true}]}', ) # Create FastHTML app with HTMX extensions, MonsterUI theme, and static path diff --git a/src/animaltrack/web/routes/events.py b/src/animaltrack/web/routes/events.py index aa2352b..f991b80 100644 --- a/src/animaltrack/web/routes/events.py +++ b/src/animaltrack/web/routes/events.py @@ -200,8 +200,10 @@ def event_log_index(request: Request): location_id = request.query_params.get("location_id", "") event_type = request.query_params.get("event_type", "") - # "all" means show all events (no location filter) + # "all" means no filter for both location and event type show_all = location_id == "all" or location_id == "" + if event_type == "all": + event_type = "" # If no query param and not explicitly "all", try user defaults if not location_id and not event_type and username: @@ -214,21 +216,13 @@ def event_log_index(request: Request): location_repo = LocationRepository(db) locations = location_repo.list_active() - # Find location name if we have a specific location_id - location_name = None - if location_id and location_id != "all": - for loc in locations: - if loc.id == location_id: - location_name = loc.name - break - # Get events based on filter events = [] if show_all or not location_id: # Show all events (from main events table) events = get_all_events(db, event_type=event_type or None) - elif location_id and location_name: - # Show events for specific location + elif location_id: + # Show events for specific location (location_name only used for header display) events = get_event_log(db, location_id) # Filter by event type if specified if event_type: diff --git a/src/animaltrack/web/templates/events.py b/src/animaltrack/web/templates/events.py index 1ae834d..a54ed25 100644 --- a/src/animaltrack/web/templates/events.py +++ b/src/animaltrack/web/templates/events.py @@ -179,7 +179,7 @@ EVENT_TYPES = [ def event_type_selector(selected_event_type: str = "") -> Any: """Render event type filter dropdown.""" - options = [Option("All types", value="", selected=not selected_event_type)] + options = [Option("All types", value="all", selected=selected_event_type in ("", "all"))] for event_type in EVENT_TYPES: options.append( Option(event_type, value=event_type, selected=event_type == selected_event_type)