"""Tab 1: Room Assessment. Single room input with dimensions and assessment context. MVP Simplification: No multi-room support. """ import gradio as gr from typing import Any from ui.state import SessionState from ui.constants import CEILING_HEIGHT_PRESETS # Facility classification options FACILITY_OPTIONS = [ ("Operational", "operational"), ("Non-Operational", "non-operational"), ("Public/Childcare", "public-childcare"), ] # Construction era options CONSTRUCTION_ERA_OPTIONS = [ ("Pre-1980 (potential LBP/ACM)", "pre-1980"), ("1980-2000", "1980-2000"), ("Post-2000", "post-2000"), ] def create_tab() -> dict[str, Any]: """Create Tab 1 UI components. Returns: Dictionary of component references for event wiring. """ gr.Markdown("### Room Assessment") gr.Markdown("*Enter room details and assessment context.*") # Room identification room_name = gr.Textbox( label="Room/Area Name *", placeholder="e.g., Warehouse Bay A, Office 101", elem_id="room_name", ) # Dimensions gr.Markdown("#### Dimensions") with gr.Row(): room_length = gr.Number( label="Length (ft) *", minimum=1, value=None, elem_id="room_length", ) room_width = gr.Number( label="Width (ft) *", minimum=1, value=None, elem_id="room_width", ) with gr.Row(): room_height_preset = gr.Dropdown( label="Ceiling Height *", choices=CEILING_HEIGHT_PRESETS, elem_id="room_height_preset", info="Select preset or choose Custom", ) room_height_custom = gr.Number( label="Custom Height (ft)", minimum=1, value=None, visible=False, elem_id="room_height_custom", ) # Calculated displays with gr.Row(): floor_area = gr.Textbox( label="Floor Area (SF)", value="0", interactive=False, ) room_volume = gr.Textbox( label="Volume (CF)", value="0", interactive=False, ) # Assessment context (moved from Project tab) gr.Markdown("#### Assessment Context") facility_classification = gr.Radio( label="Facility Classification *", choices=FACILITY_OPTIONS, value="non-operational", elem_id="facility_classification", info="Affects clearance thresholds", ) construction_era = gr.Radio( label="Construction Era *", choices=CONSTRUCTION_ERA_OPTIONS, value="post-2000", elem_id="construction_era", info="Pre-1980 triggers LBP/ACM flags", ) # Validation status validation_status = gr.HTML( value="", elem_id="tab1_validation", ) validate_btn = gr.Button( "Continue to Images →", variant="primary", ) return { "room_name": room_name, "room_length": room_length, "room_width": room_width, "room_height_preset": room_height_preset, "room_height_custom": room_height_custom, "floor_area": floor_area, "room_volume": room_volume, "facility_classification": facility_classification, "construction_era": construction_era, "validation_status": validation_status, "validate_btn": validate_btn, } def on_height_preset_change(preset_value: int | None) -> dict: """Show/hide custom height input based on preset selection. Args: preset_value: The selected preset value, or None for "Custom". Returns: Gradio update dict for custom height visibility. """ return gr.update(visible=(preset_value is None)) def update_calculated_values( length: float | None, width: float | None, height_preset: int | None, height_custom: float | None, ) -> tuple[str, str]: """Calculate and return floor area and volume. Returns: Tuple of (floor_area_str, volume_str). """ # Get effective values length_val = float(length) if length and length > 0 else 0 width_val = float(width) if width and width > 0 else 0 if height_preset is not None: height_val = float(height_preset) elif height_custom is not None and height_custom > 0: height_val = float(height_custom) else: height_val = 0 # Calculate area = length_val * width_val volume = area * height_val return f"{area:,.0f}", f"{volume:,.0f}" def save_room_to_session( session: SessionState, name: str, length: float | None, width: float | None, height_preset: int | None, height_custom: float | None, facility_classification: str, construction_era: str, ) -> SessionState: """Save room data to session (called on field changes). Returns: Updated session. """ # Determine actual ceiling height if height_preset is not None: height = float(height_preset) elif height_custom is not None and height_custom > 0: height = float(height_custom) else: height = 0 # Update session room data session.room.name = name.strip() if name else "" session.room.length_ft = float(length) if length and length > 0 else 0 session.room.width_ft = float(width) if width and width > 0 else 0 session.room.ceiling_height_ft = height session.room.facility_classification = facility_classification session.room.construction_era = construction_era session.update_timestamp() return session def validate_and_continue(session: SessionState) -> tuple[SessionState, str, int]: """Validate Tab 1 and proceed to Tab 2. Returns: Tuple of (session, validation_html, next_tab_index). """ is_valid, errors = session.validate_tab1() if is_valid: session.tab1_complete = True session.update_timestamp() html = """