# lib/moldb_web/live/search_live.ex
defmodule MoldbWeb.SearchLive do
  use MoldbWeb, :live_view
  alias Moldb.Search
  alias MoldbWeb.Components.SearchForm
  alias MoldbWeb.Components.SearchResults
  alias MoldbWeb.Components.Pagination

  @per_page 20

  @impl true
  def mount(_params, _session, socket) do
    {:ok,
     socket
     |> assign(query: "", page: 1, has_more: false, loading: false, results_count: 0)
     |> stream(:results, [])}
  end

  @impl true
  def handle_params(%{"query" => query, "page" => page}, _url, socket) do
    {page, _} = Integer.parse(page)
    {:noreply, socket |> assign(query: query, page: page, loading: true) |> fetch_results()}
  end

  @impl true
  def handle_params(_params, _url, socket), do: {:noreply, socket}

  @impl true
  def render(assigns) do
    ~H"""
    <div class="max-w-2xl mx-auto p-6">
      <h1 class="text-3xl font-bold mb-6 text-center text-gray-800">Molecular Database Search</h1>
      <.live_component module={SearchForm} id="search_form" query={@query} />
      <.live_component module={SearchResults} id="search_results" results={@streams.results} results_count={@results_count} />
      <.live_component module={Pagination} id="pagination" has_more={@has_more} page={@page} loading={@loading} />
    </div>
    """
  end

  @impl true
  def handle_event("search", %{"query" => query}, socket) do
    {:noreply, push_navigate(socket, to: ~p"/search?query=#{query}&page=1")}
  end

  @impl true
  def handle_event("suggest", %{"query" => query}, socket) when byte_size(query) >= 2 do
    suggestions = Search.suggest(query, 5)
    {:noreply, push_event(socket, "update_suggestions", %{suggestions: suggestions})}
  end

  @impl true
  def handle_event("suggest", _params, socket) do
    {:noreply, push_event(socket, "update_suggestions", %{suggestions: []})}
  end

  @impl true
  def handle_event("load_more", _, socket) do
    %{query: query, page: page} = socket.assigns
    {:noreply, push_navigate(socket, to: ~p"/search?query=#{query}&page=#{page + 1}")}
  end

  defp fetch_results(%{assigns: %{query: query, page: page}} = socket) do
    filters = %{}  # Define your filters here as needed
    results = Search.search(query, page, @per_page, filters)
    has_more = length(results) == @per_page

    socket
    |> stream(:results, results, reset: page == 1)
    |> assign(has_more: has_more, loading: false, results_count: length(results))
  end
end

# lib/moldb_web/live/components/search_results_component.ex
defmodule MoldbWeb.Components.SearchResults do
  @moduledoc """
  LiveComponent for displaying search results with optimized rendering and better error handling.
  """
  use MoldbWeb, :live_component

  @impl true
  def render(assigns) do
    ~H"""
    <div id="search-results" class="space-y-4 mt-8">
      <.results_content results={@results} results_count={@results_count} />
    </div>
    """
  end

  # Extracted template logic into separate function component for better maintainability
  attr :results, :map, required: true
  attr :results_count, :integer, required: true
  def results_content(assigns) do
    ~H"""
    <%= if @results_count == 0 do %>
      <.empty_state />
    <% else %>
      <div id="results" phx-update="stream">
        <%= for {id, result} <- @results do %>
          <.result_card id={id} result={result} />
        <% end %>
      </div>
    <% end %>
    """
  end

  def empty_state(assigns) do
    ~H"""
    <p class="text-center text-gray-600">No results found. Try a different search term.</p>
    """
  end

  attr :id, :string, required: true
  attr :result, :map, required: true
  def result_card(assigns) do
    ~H"""
    <div
      id={@id}
      class="result bg-white p-4 rounded-lg shadow hover:shadow-md transition-shadow duration-200 ease-in-out cursor-pointer"
      phx-click={JS.navigate(result_path(@result))}
    >
      <h3 class="text-lg font-semibold text-gray-800 mb-2"><%= @result.name %></h3>
      <p class="text-sm text-gray-600"><%= @result.description %></p>
      <.result_type type={@result.type} />
    </div>
    """
  end

  attr :type, :string, required: true
  def result_type(assigns) do
    ~H"""
    <span class="inline-block mt-2 px-2 py-1 text-xs font-semibold text-gray-700 bg-gray-200 rounded-full">
      <%= String.capitalize(@type) %>
    </span>
    """
  end

  defp result_path(%{type: type, id: id}) do
    case type do
      "compound" -> ~p"/compound/#{id}"
      "substance" -> ~p"/substance/#{id}"
      _ -> "#"
    end
  end
end

# lib/moldb_web/live/components/search_form.ex
defmodule MoldbWeb.Components.SearchForm do
  @moduledoc """
  LiveComponent for handling search form interactions with debouncing and validation.
  """
  use MoldbWeb, :live_component

  @min_query_length 2

  @impl true
  def render(assigns) do
    ~H"""
    <form phx-submit="search" phx-change="suggest" id="search_form" class="relative">
      <.search_input query={@query} />
      <.search_button />
      <.suggestions_container />
    </form>
    """
  end

  attr :query, :string, required: true
  def search_input(assigns) do
    ~H"""
    <.input type="text"
            name="query"
            id="search-input"
            value={@query}
            placeholder="Search for compounds or substances..."
            autocomplete="off"
            class="w-full p-4 pr-12 text-sm border-gray-200 rounded-lg shadow-sm focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
            phx-hook="Autocomplete"
    />
    """
  end

  def search_button(assigns) do
    ~H"""
    <button type="submit" class="absolute inset-y-0 right-0 px-4 text-gray-600 hover:text-blue-600">
      <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-5 h-5">
        <path stroke-linecap="round" stroke-linejoin="round" d="M21 21l-5.197-5.197m0 0A7.5 7.5 0 105.196 5.196a7.5 7.5 0 0010.607 10.607z" />
      </svg>
    </button>
    """
  end

  def suggestions_container(assigns) do
    ~H"""
    <div id="suggestions" class="absolute z-10 w-full mt-2 bg-white border border-gray-200 rounded-lg shadow-lg hidden">
    </div>
    """
  end

  @impl true
  def handle_event("search", %{"query" => query}, socket) do
    {:noreply, push_navigate(socket, to: ~p"/search?query=#{query}&page=1")}
  end

  @impl true
  def handle_event("suggest", %{"query" => query}, socket) when byte_size(query) >= @min_query_length do
    suggestions = Moldb.Search.suggest(query, 5)
    {:noreply, push_event(socket, "update_suggestions", %{suggestions: suggestions})}
  end

  @impl true
  def handle_event("suggest", _params, socket) do
    {:noreply, push_event(socket, "update_suggestions", %{suggestions: []})}
  end
end

# lib/moldb_web/live/home_live.ex
defmodule MoldbWeb.HomeLive do
  use MoldbWeb, :live_view
  alias MoldbWeb.Components.SearchForm

  @impl true
  def mount(_params, _session, socket) do
    {:ok, assign(socket, query: "")}
  end

  @impl true
  def render(assigns) do
    ~H"""
    <div class="max-w-2xl mx-auto p-6">
      <h1 class="text-3xl font-bold mb-6 text-center text-gray-800">Welcome to Molecular Database</h1>
      <.live_component module={SearchForm} id="home_search_form" query={@query} />
    </div>
    """
  end

  @impl true
  def handle_event("search", %{"query" => query}, socket) do
    {:noreply, push_navigate(socket, to: ~p"/search?query=#{query}&page=1")}
  end

  @impl true
  def handle_event("suggest", %{"query" => query}, socket) when byte_size(query) >= 2 do
    suggestions = Moldb.Search.suggest(query, 5)
    {:noreply, push_event(socket, "update_suggestions", %{suggestions: suggestions})}
  end

  @impl true
  def handle_event("suggest", _params, socket) do
    {:noreply, push_event(socket, "update_suggestions", %{suggestions: []})}
  end
end

# lib/moldb_web/live/components/pagination_component.ex
defmodule MoldbWeb.Components.Pagination do
  use MoldbWeb, :live_component

  @impl true
  def render(assigns) do
    ~H"""
    <div class="mt-4 text-center">
      <%= if @has_more do %>
        <button
          phx-click="load_more"
          class="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600 transition-colors"
          disabled={@loading}
        >
          <%= if @loading, do: "Loading...", else: "Load More" %>
        </button>
      <% end %>
    </div>
    """
  end
end

# lib/moldb/search.ex
defmodule Moldb.Search do
  @moduledoc """
  Handles search functionality across compounds and substances with optimized queries
  and proper input sanitization.
  """
  import Ecto.Query, warn: false
  alias Moldb.Repo
  alias Moldb.Compounds.{Compound, Substance}

  @type search_result :: %{
    type: String.t(),
    id: Ecto.UUID.t(),
    name: String.t(),
    description: String.t()
  }

  @doc """
  Performs a paginated search across compounds and substances with filtering options.
  Returns a list of search results matching the query.
  """
  @spec search(String.t(), non_neg_integer(), pos_integer(), map()) :: [search_result()]
  def search(query, page, per_page, filters) when is_binary(query) and is_integer(page) and is_integer(per_page) and is_map(filters) do
    offset = (page - 1) * per_page
    sanitized_query = sanitize_query(query)

    base_search_query(sanitized_query)
    |> apply_filters(filters)
    |> paginate(offset, per_page)
    |> Repo.all()
  end

  @doc """
  Provides search suggestions based on partial matches.
  Returns a list of matching names ordered by similarity.
  """
  @spec suggest(String.t(), pos_integer()) :: [String.t()]
  def suggest(query, limit) when is_binary(query) and is_integer(limit) do
    sanitized_query = sanitize_query(query)

    suggestion_query(sanitized_query)
    |> limit_suggestions(limit)
    |> Repo.all()
  end

  # Private functions for query building
  defp base_search_query(query) do
    compound_query = from(c in Compound,
      select: %{
        type: fragment("'compound'"),
        id: c.id,
        name: c.name,
        description: c.description
      }
    )

    substance_query = from(s in Substance,
      select: %{
        type: fragment("'substance'"),
        id: s.id,
        name: s.name,
        description: s.description  # Fixed: Changed from c.description to s.description
      }
    )

    from q in subquery(
      from u in subquery(
        compound_query
        |> union_all(^substance_query)
      ),
      where: ilike(u.name, ^"%#{query}%"),
      select: %{
        type: u.type,
        id: u.id,
        name: u.name,
        description: fragment("LEFT(?, 100)", u.description)
      }
    ),
    order_by: [desc: fragment("similarity(?, ?)", q.name, ^query)]
  end

  defp suggestion_query(query) do
    compound_names = from(c in Compound, select: c.name)
    substance_names = from(s in Substance, select: s.name)

    from q in subquery(
      from u in subquery(
        compound_names
        |> union_all(^substance_names)
      ),
      where: ilike(u.name, ^"#{query}%"),
      select: %{
        name: u.name,
        similarity: fragment("similarity(?, ?)", u.name, ^query)
      }
    ),
    order_by: [desc: :similarity],
    distinct: :name
  end

  defp apply_filters(query, filters) do
    Enum.reduce(filters, query, &apply_filter/2)
  end

  defp apply_filter({"status", status}, query) do
    where(query, [q], q.status == ^status)
  end
  defp apply_filter({"category", category}, query) do
    where(query, [q], q.category == ^category)
  end
  defp apply_filter(_, query), do: query

  defp paginate(query, offset, limit) do
    query
    |> offset(^offset)
    |> limit(^limit)
  end

  defp limit_suggestions(query, limit) do
    query |> limit(^limit)
  end

  defp sanitize_query(query) do
    query
    |> String.replace("%", "\\%")
    |> String.replace("_", "\\_")
  end
end

# assets/js/autocomplete.js
const Autocomplete = {
  mounted() {
    this.setupEventListeners();
  },

  setupEventListeners() {
    this.handleInput();
    this.handleFocus();
    this.handleClickOutside();
    this.handleSuggestionUpdates();
  },

  handleInput() {
    this.el.addEventListener("input", () => {
      this.pushEventTo("#search_form", "suggest", {query: this.el.value});
    });
  },

  handleFocus() {
    this.el.addEventListener("focus", () => {
      if (this.el.value.length > 0) {
        this.pushEventTo("#search_form", "suggest", {query: this.el.value});
      }
    });
  },

  handleClickOutside() {
    document.addEventListener("click", (e) => {
      if (!this.el.contains(e.target)) {
        this.hideSuggestions();
      }
    });
  },

  handleSuggestionUpdates() {
    this.handleEvent("update_suggestions", ({suggestions}) => {
      this.showSuggestions(suggestions);
    });
  },

  showSuggestions(suggestions) {
    const suggestionsEl = document.getElementById("suggestions");
    suggestionsEl.innerHTML = "";

    if (suggestions.length > 0) {
      suggestionsEl.classList.remove("hidden");
      this.renderSuggestions(suggestions, suggestionsEl);
    } else {
      this.hideSuggestions();
    }
  },

  renderSuggestions(suggestions, container) {
    suggestions.forEach((suggestion) => {
      const li = this.createSuggestionElement(suggestion);
      container.appendChild(li);
    });
  },

  createSuggestionElement(suggestion) {
    const li = document.createElement("li");
    li.textContent = suggestion;
    li.className = "px-4 py-2 cursor-pointer hover:bg-gray-100";
    li.addEventListener("click", () => this.handleSuggestionClick(suggestion));
    return li;
  },

  handleSuggestionClick(suggestion) {
    this.el.value = suggestion;
    this.hideSuggestions();
    this.pushEventTo("#search_form", "search", { query: suggestion });
  },

  hideSuggestions() {
    const suggestionsEl = document.getElementById("suggestions");
    suggestionsEl.classList.add("hidden");
  }
};

export default Autocomplete;

// assets/js/app.js
// If you want to use Phoenix channels, run `mix help phx.gen.channel`
// to get started and then uncomment the line below.
// import "./user_socket.js"

// You can include dependencies in two ways.
//
// The simplest option is to put them in assets/vendor and
// import them using relative paths:
//
//     import "../vendor/some-package.js"
//
// Alternatively, you can `npm install some-package --prefix assets` and import
// them using a path starting with the package name:
//
//     import "some-package"
//

// Include phoenix_html to handle method=PUT/DELETE in forms and buttons.
import "phoenix_html"
// Establish Phoenix Socket and LiveView configuration.
import {Socket} from "phoenix"
import {LiveSocket} from "phoenix_live_view"
import topbar from "../vendor/topbar"
import Autocomplete from "./autocomplete"


let csrfToken = document.querySelector("meta[name='csrf-token']").getAttribute("content")
let liveSocket = new LiveSocket("/live", Socket, {
  longPollFallbackMs: 2500,
  params: {_csrf_token: csrfToken},
  hooks: {Autocomplete}  // Register the Autocomplete hook here
})

// Show progress bar on live navigation and form submits
topbar.config({barColors: {0: "#29d"}, shadowColor: "rgba(0, 0, 0, .3)"})
window.addEventListener("phx:page-loading-start", _info => topbar.show(300))
window.addEventListener("phx:page-loading-stop", _info => topbar.hide())

// connect if there are any LiveViews on the page
liveSocket.connect()

// expose liveSocket on window for web console debug logs and latency simulation:
// >> liveSocket.enableDebug()
// >> liveSocket.enableLatencySim(1000)  // enabled for duration of browser session
// >> liveSocket.disableLatencySim()
window.liveSocket = liveSocket

// lib/moldb_web/router.ex
defmodule MoldbWeb.Router do
  use MoldbWeb, :router

  import MoldbWeb.UserAuth

  pipeline :browser do
    plug :accepts, ["html"]
    plug :fetch_session
    plug :fetch_live_flash
    plug :put_root_layout, html: {MoldbWeb.Layouts, :root}
    plug :protect_from_forgery
    plug :put_secure_browser_headers
    plug :fetch_current_user
  end

  pipeline :api do
    plug :accepts, ["json"]
  end

  scope "/", MoldbWeb do
    pipe_through :browser

    live "/", HomeLive, :index

    live "/search", SearchLive, :index

    live "/compounds", CompoundLive.Index, :index
    live "/compounds/new", CompoundLive.Index, :new
    live "/compound/:id/edit", CompoundLive.Index, :edit

    live "/compound/:id", CompoundLive.Show, :show
    live "/compound/:id/show/edit", CompoundLive.Show, :edit

    live "/substances", SubstanceLive.Index, :index
    live "/substances/new", SubstanceLive.Index, :new
    live "/substance/:id/edit", SubstanceLive.Index, :edit

    live "/substance/:id", SubstanceLive.Show, :show
    live "/substance/:id/show/edit", SubstanceLive.Show, :edit
  end

  # Other scopes may use custom stacks.
  # scope "/api", MoldbWeb do
  #   pipe_through :api
  # end

  # Enable LiveDashboard and Swoosh mailbox preview in development
  if Application.compile_env(:moldb, :dev_routes) do
    # If you want to use the LiveDashboard in production, you should put
    # it behind authentication and allow only admins to access it.
    # If your application does not have an admins-only section yet,
    # you can use Plug.BasicAuth to set up some basic authentication
    # as long as you are also using SSL (which you should anyway).
    import Phoenix.LiveDashboard.Router

    scope "/dev" do
      pipe_through :browser

      live_dashboard "/dashboard", metrics: MoldbWeb.Telemetry
      forward "/mailbox", Plug.Swoosh.MailboxPreview
    end
  end

  ## Authentication routes

  scope "/", MoldbWeb do
    pipe_through [:browser, :redirect_if_user_is_authenticated]

    live_session :redirect_if_user_is_authenticated,
      on_mount: [{MoldbWeb.UserAuth, :redirect_if_user_is_authenticated}] do
      live "/users/register", UserRegistrationLive, :new
      live "/users/log_in", UserLoginLive, :new
      live "/users/reset_password", UserForgotPasswordLive, :new
      live "/users/reset_password/:token", UserResetPasswordLive, :edit
    end

    post "/users/log_in", UserSessionController, :create
  end

  scope "/", MoldbWeb do
    pipe_through [:browser, :require_authenticated_user]

    live_session :require_authenticated_user,
      on_mount: [{MoldbWeb.UserAuth, :ensure_authenticated}] do
      live "/users/settings", UserSettingsLive, :edit
      live "/users/settings/confirm_email/:token", UserSettingsLive, :confirm_email
    end
  end

  scope "/", MoldbWeb do
    pipe_through [:browser]

    delete "/users/log_out", UserSessionController, :delete

    live_session :current_user,
      on_mount: [{MoldbWeb.UserAuth, :mount_current_user}] do
      live "/users/confirm/:token", UserConfirmationLive, :edit
      live "/users/confirm", UserConfirmationInstructionsLive, :new
    end
  end
end 
Please review and analyze the attached code from my Elixir Phoenix web application using LiveView and PostgreSQL. Focus on improving readability, efficiency, and performance while removing any redundant or unused code. Enhance scalability and modularity, ensuring adherence to idiomatic Elixir practices. Incorporate modern principles, such as optimizing LiveView usage, Ecto interactions, and error handling. Provide a revised version of the code with comments explaining your changes and their benefits. Do not implement caching or third party software/libaries.