""" Full-text search screen for the AUTOPARTES console application. Prompts the user for a search query and displays matching parts using the FTS5 full-text search engine (with LIKE fallback). Results are paginated and selecting a row navigates to the part detail screen. """ from console.core.screens import Screen from console.core.keybindings import Key from console.config import APP_NAME, VERSION from console.utils.formatting import truncate # Footer labels _FOOTER_INPUT = [ ("ENTER", "Buscar"), ("ESC", "Atras"), ] _FOOTER_RESULTS = [ ("1-9", "Ver parte"), ("ENTER", "Ver parte"), ("PgUp/Dn", "Paginar"), ("F3", "Nueva busqueda"), ("ESC", "Atras"), ] class BuscarTextoScreen(Screen): """Full-text search by description / name.""" def __init__(self): super().__init__(name="buscar_texto", title="Buscar por Descripcion") self._results = None self._search_term = None self._selected = 0 self._page = 1 self._per_page = 15 self._needs_input = True # ------------------------------------------------------------------ # Render # ------------------------------------------------------------------ def render(self, context, db, renderer): # Header renderer.draw_header( f" {APP_NAME} v{VERSION}", " BUSCAR POR DESCRIPCION ", ) if self._needs_input: renderer.draw_footer(_FOOTER_INPUT) return if self._results is None: renderer.draw_text(5, 4, "Presione F3 para buscar", "info") renderer.draw_footer(_FOOTER_RESULTS) return if not self._results: renderer.draw_text( 5, 4, f'No se encontraron resultados para "{self._search_term}"', "info", ) renderer.draw_footer(_FOOTER_RESULTS) return # Display results table headers = ["NUMERO OEM", "NOMBRE", "CATEGORIA", "GRUPO"] widths = [18, 28, 18, 18] rows = [] for r in self._results: rows.append(( truncate(r.get("oem_part_number", ""), 18), truncate(r.get("name_es") or r.get("name", ""), 28), truncate(r.get("category_name", ""), 18), truncate(r.get("group_name", ""), 18), )) renderer.draw_table( headers, rows, widths, page_info={ "page": self._page, "total_pages": self._page, "total_rows": len(rows), }, selected_row=self._selected, ) renderer.draw_footer(_FOOTER_RESULTS) # ------------------------------------------------------------------ # Key handling # ------------------------------------------------------------------ def _do_search(self, db): """Execute the full-text search with current parameters.""" self._results = db.search_parts( self._search_term, page=self._page, per_page=self._per_page, ) self._selected = 0 def on_key(self, key, context, db, renderer, nav): # If we need input, show the input dialog if self._needs_input: self._needs_input = False value = renderer.show_input("Buscar", max_len=40) if value is None: # User pressed ESC in input dialog if self._results is not None: return None return "back" if value.strip(): self._search_term = value.strip() self._page = 1 self._do_search(db) return None # ESC: go back if key == Key.ESCAPE: return "back" # F3: new search if key == Key.F3: self._needs_input = True return None # Arrow navigation if key == Key.UP: if self._selected > 0: self._selected -= 1 return None if key == Key.DOWN: if self._results and self._selected < len(self._results) - 1: self._selected += 1 return None # ENTER: view selected part if key == Key.ENTER: if self._results and 0 <= self._selected < len(self._results): part = self._results[self._selected] return ("parte_detalle", {"part_id": part["id"]}, "Parte") return None # Number keys: direct selection (1-9) if 49 <= key <= 57: # '1'..'9' idx = key - 49 # 0-based if self._results and 0 <= idx < len(self._results): part = self._results[idx] return ("parte_detalle", {"part_id": part["id"]}, "Parte") return None # PgDn: next page if key == Key.PGDN: if self._results and len(self._results) >= self._per_page: self._page += 1 self._do_search(db) return None # PgUp: previous page if key == Key.PGUP: if self._page > 1: self._page -= 1 self._do_search(db) return None return None