Replace LabelSelect with raw Select to fix MonsterUI value bug

MonsterUI LabelSelect has a confirmed bug where it sends the option's
label text instead of the value attribute on form submission. This was
causing 422 validation errors in forms.

- Replace all LabelSelect usages with raw Div(FormLabel, Select) pattern
- Add comments documenting the MonsterUI bug workaround
- Matches pattern already used in feed.py since commit ad1f910

Files modified: eggs.py, move.py, actions.py, products.py

🤖 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 11:24:34 +00:00
parent eee8552345
commit 2fc98155c3
4 changed files with 102 additions and 113 deletions

View File

@@ -12,7 +12,6 @@ from monsterui.all import (
ButtonT, ButtonT,
FormLabel, FormLabel,
LabelInput, LabelInput,
LabelSelect,
LabelTextArea, LabelTextArea,
) )
from ulid import ULID from ulid import ULID
@@ -215,19 +214,17 @@ def cohort_form(
H2("Create Animal Cohort", cls="text-xl font-bold mb-4"), H2("Create Animal Cohort", cls="text-xl font-bold mb-4"),
# Error message if present # Error message if present
error_component, error_component,
# Species dropdown # Species dropdown - using raw Select due to MonsterUI LabelSelect value bug
LabelSelect( Div(
*species_options, FormLabel("Species", _for="species"),
label="Species", Select(*species_options, name="species", id="species", cls="uk-select"),
id="species", cls="space-y-2",
name="species",
), ),
# Location dropdown # Location dropdown - using raw Select due to MonsterUI LabelSelect value bug
LabelSelect( Div(
*location_options, FormLabel("Location", _for="location_id"),
label="Location", Select(*location_options, name="location_id", id="location_id", cls="uk-select"),
id="location_id", cls="space-y-2",
name="location_id",
), ),
# Count input # Count input
LabelInput( LabelInput(
@@ -239,26 +236,23 @@ def cohort_form(
value=count_value, value=count_value,
placeholder="Number of animals", placeholder="Number of animals",
), ),
# Life stage dropdown # Life stage dropdown - using raw Select due to MonsterUI LabelSelect value bug
LabelSelect( Div(
*life_stage_options, FormLabel("Life Stage", _for="life_stage"),
label="Life Stage", Select(*life_stage_options, name="life_stage", id="life_stage", cls="uk-select"),
id="life_stage", cls="space-y-2",
name="life_stage",
), ),
# Sex dropdown # Sex dropdown - using raw Select due to MonsterUI LabelSelect value bug
LabelSelect( Div(
*sex_options, FormLabel("Sex", _for="sex"),
label="Sex", Select(*sex_options, name="sex", id="sex", cls="uk-select"),
id="sex", cls="space-y-2",
name="sex",
), ),
# Origin dropdown # Origin dropdown - using raw Select due to MonsterUI LabelSelect value bug
LabelSelect( Div(
*origin_options, FormLabel("Origin", _for="origin"),
label="Origin", Select(*origin_options, name="origin", id="origin", cls="uk-select"),
id="origin", cls="space-y-2",
name="origin",
), ),
# Optional notes # Optional notes
LabelTextArea( LabelTextArea(
@@ -350,19 +344,17 @@ def hatch_form(
H2("Record Hatch", cls="text-xl font-bold mb-4"), H2("Record Hatch", cls="text-xl font-bold mb-4"),
# Error message if present # Error message if present
error_component, error_component,
# Species dropdown # Species dropdown - using raw Select due to MonsterUI LabelSelect value bug
LabelSelect( Div(
*species_options, FormLabel("Species", _for="species"),
label="Species", Select(*species_options, name="species", id="species", cls="uk-select"),
id="species", cls="space-y-2",
name="species",
), ),
# Hatch location dropdown # Hatch location dropdown - using raw Select due to MonsterUI LabelSelect value bug
LabelSelect( Div(
*location_options, FormLabel("Hatch Location", _for="location_id"),
label="Hatch Location", Select(*location_options, name="location_id", id="location_id", cls="uk-select"),
id="location_id", cls="space-y-2",
name="location_id",
), ),
# Hatched count input # Hatched count input
LabelInput( LabelInput(
@@ -374,13 +366,17 @@ def hatch_form(
value=hatched_live_value, value=hatched_live_value,
placeholder="Number hatched", placeholder="Number hatched",
), ),
# Brood location dropdown (optional) # Brood location dropdown (optional) - using raw Select due to MonsterUI LabelSelect value bug
Div( Div(
LabelSelect( Div(
FormLabel("Brood Location (optional)", _for="assigned_brood_location_id"),
Select(
*brood_location_options, *brood_location_options,
label="Brood Location (optional)",
id="assigned_brood_location_id",
name="assigned_brood_location_id", name="assigned_brood_location_id",
id="assigned_brood_location_id",
cls="uk-select",
),
cls="space-y-2",
), ),
P( P(
"If different from hatch location, hatchlings will be placed here", "If different from hatch location, hatchlings will be placed here",
@@ -845,12 +841,11 @@ def tag_end_form(
), ),
# Selection container - updated via HTMX when filter changes # Selection container - updated via HTMX when filter changes
selection_container, selection_container,
# Tag dropdown # Tag dropdown - using raw Select due to MonsterUI LabelSelect value bug
LabelSelect( Div(
*tag_options, FormLabel("Tag to End", _for="tag"),
label="Tag to End", Select(*tag_options, name="tag", id="tag", cls="uk-select"),
id="tag", cls="space-y-2",
name="tag",
) )
if active_tags if active_tags
else Div( else Div(
@@ -1318,20 +1313,22 @@ def outcome_form(
yield_section = Div( yield_section = Div(
H3("Yield Items", cls="text-lg font-semibold mt-4 mb-2"), H3("Yield Items", cls="text-lg font-semibold mt-4 mb-2"),
P("Optional: record products collected from harvest", cls="text-sm text-stone-500 mb-3"), P("Optional: record products collected from harvest", cls="text-sm text-stone-500 mb-3"),
# Using raw Select due to MonsterUI LabelSelect value bug
Div( Div(
LabelSelect( Div(
FormLabel("Product", _for="yield_product_code"),
Select(
*product_options, *product_options,
label="Product",
id="yield_product_code",
name="yield_product_code", name="yield_product_code",
cls="flex-1", id="yield_product_code",
cls="uk-select",
), ),
LabelSelect( cls="space-y-2 flex-1",
*unit_options, ),
label="Unit", Div(
id="yield_unit", FormLabel("Unit", _for="yield_unit"),
name="yield_unit", Select(*unit_options, name="yield_unit", id="yield_unit", cls="uk-select"),
cls="w-32", cls="space-y-2 w-32",
), ),
cls="flex gap-3", cls="flex gap-3",
), ),
@@ -1377,13 +1374,11 @@ def outcome_form(
), ),
# Selection container - updated via HTMX when filter changes # Selection container - updated via HTMX when filter changes
selection_container, selection_container,
# Outcome selection # Outcome selection - using raw Select due to MonsterUI LabelSelect value bug
LabelSelect( Div(
*outcome_options, FormLabel("Outcome", _for="outcome"),
label="Outcome", Select(*outcome_options, name="outcome", id="outcome", cls="uk-select", required=True),
id="outcome", cls="space-y-2",
name="outcome",
required=True,
), ),
# Reason field # Reason field
LabelInput( LabelInput(
@@ -1599,13 +1594,13 @@ def status_correct_form(
value=filter_str, value=filter_str,
placeholder="e.g., species:duck location:Coop1", placeholder="e.g., species:duck location:Coop1",
), ),
# New status selection # New status selection - using raw Select due to MonsterUI LabelSelect value bug
LabelSelect( Div(
*status_options, FormLabel("New Status", _for="new_status"),
label="New Status", Select(
id="new_status", *status_options, name="new_status", id="new_status", cls="uk-select", required=True
name="new_status", ),
required=True, cls="space-y-2",
), ),
# Reason field (required for admin actions) # Reason field (required for admin actions)
LabelInput( LabelInput(

View File

@@ -4,12 +4,12 @@
from collections.abc import Callable from collections.abc import Callable
from typing import Any from typing import Any
from fasthtml.common import H1, H2, A, Div, Form, Hidden, Li, Option, P, Ul from fasthtml.common import H1, H2, A, Div, Form, Hidden, Li, Option, P, Select, Ul
from monsterui.all import ( from monsterui.all import (
Button, Button,
ButtonT, ButtonT,
FormLabel,
LabelInput, LabelInput,
LabelSelect,
LabelTextArea, LabelTextArea,
TabContainer, TabContainer,
) )
@@ -177,12 +177,11 @@ def harvest_form(
H2("Harvest Eggs", cls="text-xl font-bold mb-4"), H2("Harvest Eggs", cls="text-xl font-bold mb-4"),
# Error message if present # Error message if present
error_component, error_component,
# Location dropdown # Location dropdown - using raw Select due to MonsterUI LabelSelect value bug
LabelSelect( Div(
*location_options, FormLabel("Location", _for="location_id"),
label="Location", Select(*location_options, name="location_id", id="location_id", cls="uk-select"),
id="location_id", cls="space-y-2",
name="location_id",
), ),
# Quantity input (integer only, 0 allowed for "checked but found none") # Quantity input (integer only, 0 allowed for "checked but found none")
LabelInput( LabelInput(
@@ -300,12 +299,11 @@ def sell_form(
H2("Sell Products", cls="text-xl font-bold mb-4"), H2("Sell Products", cls="text-xl font-bold mb-4"),
# Error message if present # Error message if present
error_component, error_component,
# Product dropdown # Product dropdown - using raw Select due to MonsterUI LabelSelect value bug
LabelSelect( Div(
*product_options, FormLabel("Product", _for="product_code"),
label="Product", Select(*product_options, name="product_code", id="product_code", cls="uk-select"),
id="product_code", cls="space-y-2",
name="product_code",
), ),
# Quantity input (integer only, min=1) # Quantity input (integer only, min=1)
LabelInput( LabelInput(

View File

@@ -4,8 +4,8 @@
from collections.abc import Callable from collections.abc import Callable
from typing import Any from typing import Any
from fasthtml.common import H2, Div, Form, Hidden, Option, P, Span from fasthtml.common import H2, Div, Form, Hidden, Option, P, Select, Span
from monsterui.all import Alert, AlertT, Button, ButtonT, LabelInput, LabelSelect, LabelTextArea from monsterui.all import Alert, AlertT, Button, ButtonT, FormLabel, LabelInput, LabelTextArea
from ulid import ULID from ulid import ULID
from animaltrack.models.events import Event from animaltrack.models.events import Event
@@ -151,12 +151,11 @@ def move_form(
), ),
# Selection container - updated via HTMX when filter changes # Selection container - updated via HTMX when filter changes
selection_container, selection_container,
# Destination dropdown # Destination dropdown - using raw Select due to MonsterUI LabelSelect value bug
LabelSelect( Div(
*location_options, FormLabel("Destination", _for="to_location_id"),
label="Destination", Select(*location_options, name="to_location_id", id="to_location_id", cls="uk-select"),
id="to_location_id", cls="space-y-2",
name="to_location_id",
), ),
# Optional notes # Optional notes
LabelTextArea( LabelTextArea(

View File

@@ -4,8 +4,8 @@
from collections.abc import Callable from collections.abc import Callable
from typing import Any from typing import Any
from fasthtml.common import H2, Form, Hidden, Option from fasthtml.common import H2, Div, Form, Hidden, Option, P, Select
from monsterui.all import Button, ButtonT, LabelInput, LabelSelect, LabelTextArea from monsterui.all import Button, ButtonT, FormLabel, LabelInput, LabelTextArea
from ulid import ULID from ulid import ULID
from animaltrack.models.reference import Product from animaltrack.models.reference import Product
@@ -47,8 +47,6 @@ def product_sold_form(
# Error display component # Error display component
error_component = None error_component = None
if error: if error:
from fasthtml.common import Div, P
error_component = Div( error_component = Div(
P(error, cls="text-red-500 text-sm"), P(error, cls="text-red-500 text-sm"),
cls="mb-4", cls="mb-4",
@@ -58,12 +56,11 @@ def product_sold_form(
H2("Record Sale", cls="text-xl font-bold mb-4"), H2("Record Sale", cls="text-xl font-bold mb-4"),
# Error message if present # Error message if present
error_component, error_component,
# Product dropdown # Product dropdown - using raw Select due to MonsterUI LabelSelect value bug
LabelSelect( Div(
*product_options, FormLabel("Product", _for="product_code"),
label="Product", Select(*product_options, name="product_code", id="product_code", cls="uk-select"),
id="product_code", cls="space-y-2",
name="product_code",
), ),
# Quantity input (integer only, min=1) # Quantity input (integer only, min=1)
LabelInput( LabelInput(