fix: preserve datetime picker value when form validation fails

The event_datetime_field component now accepts initial_value and
initial_ts parameters. When re-rendering a form after validation
error, the datetime picker value is preserved and shown expanded.

🤖 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-01 13:36:07 +00:00
parent 680227f53f
commit b74ca53f20
2 changed files with 25 additions and 5 deletions

View File

@@ -230,6 +230,10 @@ def _render_cohort_error(
selected_sex=form_data.get("sex", "unknown") if form_data else "", selected_sex=form_data.get("sex", "unknown") if form_data else "",
selected_origin=form_data.get("origin", "") if form_data else "", selected_origin=form_data.get("origin", "") if form_data else "",
count_value=form_data.get("count", "") if form_data else "", count_value=form_data.get("count", "") if form_data else "",
datetime_value=(
form_data.get("cohort_datetime_value", "") if form_data else ""
),
datetime_ts=form_data.get("ts_utc", "0") if form_data else "0",
), ),
title="Create Cohort - AnimalTrack", title="Create Cohort - AnimalTrack",
active_nav=None, active_nav=None,

View File

@@ -26,7 +26,11 @@ from animaltrack.selection.validation import SelectionDiff
# ============================================================================= # =============================================================================
def event_datetime_field(field_id: str = "event_datetime") -> Div: def event_datetime_field(
field_id: str = "event_datetime",
initial_value: str = "",
initial_ts: str = "0",
) -> Div:
"""Create a collapsible datetime picker for backdating events. """Create a collapsible datetime picker for backdating events.
When collapsed, shows "Now" and the event will use the current time. When collapsed, shows "Now" and the event will use the current time.
@@ -35,6 +39,8 @@ def event_datetime_field(field_id: str = "event_datetime") -> Div:
Args: Args:
field_id: Unique ID prefix for the datetime field elements. field_id: Unique ID prefix for the datetime field elements.
initial_value: Initial value for the datetime-local input (for form preservation).
initial_ts: Initial value for the hidden ts_utc field (for form preservation).
Returns: Returns:
Div containing the datetime picker with toggle functionality. Div containing the datetime picker with toggle functionality.
@@ -43,6 +49,11 @@ def event_datetime_field(field_id: str = "event_datetime") -> Div:
picker_id = f"{field_id}_picker" picker_id = f"{field_id}_picker"
input_id = f"{field_id}_input" input_id = f"{field_id}_input"
# If initial value is set, start with picker expanded
has_initial = bool(initial_value)
picker_style = "display: block;" if has_initial else "display: none;"
toggle_text = "Use current time" if has_initial else "Set custom date"
# JavaScript for toggle and conversion # JavaScript for toggle and conversion
script = f""" script = f"""
(function() {{ (function() {{
@@ -84,7 +95,7 @@ def event_datetime_field(field_id: str = "event_datetime") -> Div:
Span("Now", cls="text-stone-400"), Span("Now", cls="text-stone-400"),
" - ", " - ",
Span( Span(
"Set custom date", toggle_text,
id=toggle_id, id=toggle_id,
cls="text-blue-400 hover:text-blue-300 cursor-pointer underline", cls="text-blue-400 hover:text-blue-300 cursor-pointer underline",
), ),
@@ -95,6 +106,7 @@ def event_datetime_field(field_id: str = "event_datetime") -> Div:
id=input_id, id=input_id,
name=f"{field_id}_value", name=f"{field_id}_value",
type="datetime-local", type="datetime-local",
value=initial_value,
cls="uk-input w-full mt-2", cls="uk-input w-full mt-2",
), ),
P( P(
@@ -102,11 +114,11 @@ def event_datetime_field(field_id: str = "event_datetime") -> Div:
cls="text-xs text-stone-500 mt-1", cls="text-xs text-stone-500 mt-1",
), ),
id=picker_id, id=picker_id,
style="display: none;", style=picker_style,
), ),
cls="mt-1", cls="mt-1",
), ),
Hidden(name="ts_utc", value="0"), Hidden(name="ts_utc", value=initial_ts),
Script(script), Script(script),
cls="space-y-1", cls="space-y-1",
) )
@@ -127,6 +139,8 @@ def cohort_form(
selected_sex: str = "unknown", selected_sex: str = "unknown",
selected_origin: str = "", selected_origin: str = "",
count_value: str = "", count_value: str = "",
datetime_value: str = "",
datetime_ts: str = "0",
action: Callable[..., Any] | str = "/actions/animal-cohort", action: Callable[..., Any] | str = "/actions/animal-cohort",
) -> Form: ) -> Form:
"""Create the Cohort Creation form. """Create the Cohort Creation form.
@@ -141,6 +155,8 @@ def cohort_form(
selected_sex: Pre-selected sex. selected_sex: Pre-selected sex.
selected_origin: Pre-selected origin. selected_origin: Pre-selected origin.
count_value: Pre-filled count value. count_value: Pre-filled count value.
datetime_value: Pre-filled datetime-local input value.
datetime_ts: Pre-filled ts_utc hidden field value.
action: Route function or URL string for form submission. action: Route function or URL string for form submission.
Returns: Returns:
@@ -253,7 +269,7 @@ def cohort_form(
name="origin", name="origin",
), ),
# Event datetime picker (for backdating) # Event datetime picker (for backdating)
event_datetime_field("cohort_datetime"), event_datetime_field("cohort_datetime", datetime_value, datetime_ts),
# Hidden nonce for idempotency # Hidden nonce for idempotency
Hidden(name="nonce", value=str(ULID())), Hidden(name="nonce", value=str(ULID())),
# Submit button # Submit button