fix: event-log route handles missing location_id
The /event-log route now shows a location selector dropdown instead of returning a 422 error when no location_id is provided. This follows the same pattern used by the Eggs page. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -11,6 +11,7 @@ from starlette.requests import Request
|
||||
from starlette.responses import HTMLResponse
|
||||
|
||||
from animaltrack.repositories.locations import LocationRepository
|
||||
from animaltrack.repositories.user_defaults import UserDefaultsRepository
|
||||
from animaltrack.web.templates import page
|
||||
from animaltrack.web.templates.events import event_log_list, event_log_panel
|
||||
|
||||
@@ -56,25 +57,36 @@ def event_log_index(request: Request):
|
||||
"""GET /event-log - Event log for a location."""
|
||||
db = request.app.state.db
|
||||
|
||||
# Get auth info
|
||||
auth = request.scope.get("auth")
|
||||
username = auth.username if auth else None
|
||||
user_role = auth.role if auth else None
|
||||
|
||||
# Get location_id from query params
|
||||
location_id = request.query_params.get("location_id")
|
||||
if not location_id:
|
||||
return HTMLResponse(
|
||||
content="<p>Missing location_id parameter</p>",
|
||||
status_code=422,
|
||||
)
|
||||
|
||||
# Get location name
|
||||
# If no query param, try user defaults
|
||||
if not location_id and username:
|
||||
defaults = UserDefaultsRepository(db).get(username, "event_log")
|
||||
if defaults:
|
||||
location_id = defaults.location_id
|
||||
|
||||
# Get all locations for selector
|
||||
location_repo = LocationRepository(db)
|
||||
locations = location_repo.list_active()
|
||||
location_name = "Unknown"
|
||||
for loc in locations:
|
||||
if loc.id == location_id:
|
||||
location_name = loc.name
|
||||
break
|
||||
|
||||
# Get event log
|
||||
events = get_event_log(db, location_id)
|
||||
# Find location name if we have a location_id
|
||||
location_name = None
|
||||
if location_id:
|
||||
for loc in locations:
|
||||
if loc.id == location_id:
|
||||
location_name = loc.name
|
||||
break
|
||||
|
||||
# Get event log if we have a valid location
|
||||
events = []
|
||||
if location_id and location_name:
|
||||
events = get_event_log(db, location_id)
|
||||
|
||||
# Check if HTMX request
|
||||
is_htmx = request.headers.get("HX-Request") == "true"
|
||||
@@ -85,9 +97,11 @@ def event_log_index(request: Request):
|
||||
|
||||
# Full page render
|
||||
return page(
|
||||
event_log_panel(events, location_name),
|
||||
title=f"Event Log - {location_name}",
|
||||
active_nav=None,
|
||||
event_log_panel(events, locations, location_id),
|
||||
title="Event Log - AnimalTrack",
|
||||
active_nav="event_log",
|
||||
user_role=user_role,
|
||||
username=username,
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
from datetime import UTC, datetime
|
||||
from typing import Any
|
||||
|
||||
from fasthtml.common import H3, Div, Li, P, Span, Ul
|
||||
from fasthtml.common import H3, Div, Label, Li, Option, P, Select, Span, Ul
|
||||
|
||||
|
||||
def format_timestamp(ts_utc: int) -> str:
|
||||
@@ -120,11 +120,55 @@ def event_log_list(events: list[dict[str, Any]]) -> Any:
|
||||
return Ul(*items, cls="divide-y divide-stone-200")
|
||||
|
||||
|
||||
def event_log_panel(events: list[dict[str, Any]], location_name: str) -> Any:
|
||||
"""Render the full event log panel."""
|
||||
def location_selector(locations: list[Any], selected_location_id: str | None) -> Any:
|
||||
"""Render location selector dropdown."""
|
||||
options = [Option("Select a location...", value="", selected=not selected_location_id)]
|
||||
for loc in locations:
|
||||
options.append(Option(loc.name, value=loc.id, selected=loc.id == selected_location_id))
|
||||
|
||||
return Div(
|
||||
H3(f"Event Log - {location_name}", cls="text-lg font-semibold mb-4"),
|
||||
event_log_list(events),
|
||||
Label("Location", cls="text-sm font-medium text-stone-700"),
|
||||
Select(
|
||||
*options,
|
||||
name="location_id",
|
||||
cls="mt-1 block w-full rounded-md border-stone-300 shadow-sm "
|
||||
"focus:border-amber-500 focus:ring-amber-500 sm:text-sm",
|
||||
hx_get="/event-log",
|
||||
hx_trigger="change",
|
||||
hx_target="#event-log-content",
|
||||
hx_swap="innerHTML",
|
||||
hx_include="this",
|
||||
),
|
||||
cls="mb-4 max-w-xs",
|
||||
)
|
||||
|
||||
|
||||
def event_log_panel(
|
||||
events: list[dict[str, Any]], locations: list[Any], selected_location_id: str | None
|
||||
) -> Any:
|
||||
"""Render the full event log panel."""
|
||||
# Find location name for header
|
||||
location_name = None
|
||||
if selected_location_id:
|
||||
for loc in locations:
|
||||
if loc.id == selected_location_id:
|
||||
location_name = loc.name
|
||||
break
|
||||
|
||||
header_text = f"Event Log - {location_name}" if location_name else "Event Log"
|
||||
|
||||
return Div(
|
||||
H3(header_text, cls="text-lg font-semibold mb-4"),
|
||||
location_selector(locations, selected_location_id),
|
||||
Div(
|
||||
event_log_list(events)
|
||||
if selected_location_id
|
||||
else P(
|
||||
"Select a location to view events.",
|
||||
cls="text-stone-500 text-sm text-center py-4",
|
||||
),
|
||||
id="event-log-content",
|
||||
),
|
||||
cls="bg-white rounded-lg shadow p-4",
|
||||
id="event-log",
|
||||
)
|
||||
|
||||
@@ -107,10 +107,12 @@ def create_cohort(animal_service, location_id, count=3):
|
||||
class TestEventLogRoute:
|
||||
"""Tests for GET /event-log route."""
|
||||
|
||||
def test_event_log_requires_location_id(self, client):
|
||||
"""Event log requires location_id parameter."""
|
||||
def test_event_log_without_location_shows_selector(self, client):
|
||||
"""Event log without location_id shows location selector."""
|
||||
response = client.get("/event-log")
|
||||
assert response.status_code == 422
|
||||
assert response.status_code == 200
|
||||
# Should show location selector prompt
|
||||
assert "Select a location" in response.text
|
||||
|
||||
def test_event_log_returns_empty_for_new_location(self, client, valid_location_id):
|
||||
"""Event log returns empty state for location with no events."""
|
||||
|
||||
Reference in New Issue
Block a user