fix: keep search term while sorting

This commit is contained in:
carla 2025-10-23 15:43:08 +02:00 committed by moritz
parent 9d98ec2494
commit 85e1f370f6
2 changed files with 89 additions and 43 deletions

View file

@ -2,9 +2,9 @@ defmodule MvWeb.Components.SortHeaderComponent do
@moduledoc """ @moduledoc """
Sort Header that can be used as column header and sorts a table: Sort Header that can be used as column header and sorts a table:
Props: Props:
- field: atom() # AshField for sorting - field: atom() # Ash Field for sorting
- label: string() # Column Heading (can be aan heex templyte) - label: string() # Column Heading (can be an heex template)
- sort_field: atom() | nil # current sort-field from parent liveview - sort_field: atom() | nil # current sort field from parent liveview
- sort_order: :asc | :desc | nil # current sorting order - sort_order: :asc | :desc | nil # current sorting order
""" """
use MvWeb, :live_component use MvWeb, :live_component
@ -19,6 +19,7 @@ defmodule MvWeb.Components.SortHeaderComponent do
@impl true @impl true
def render(assigns) do def render(assigns) do
~H""" ~H"""
<div class="tooltip" data-tip={aria_sort(@field, @sort_field, @sort_order)}>
<button <button
type="button" type="button"
aria-label={aria_sort(@field, @sort_field, @sort_order)} aria-label={aria_sort(@field, @sort_field, @sort_order)}
@ -38,6 +39,7 @@ defmodule MvWeb.Components.SortHeaderComponent do
/> />
<% end %> <% end %>
</button> </button>
</div>
""" """
end end
@ -48,7 +50,7 @@ defmodule MvWeb.Components.SortHeaderComponent do
end end
# ------------------------------------------------- # -------------------------------------------------
# Hilfsfunktionen für ARIAAttribute & IconSVG # Hilfsfunktionen für ARIA Attribute & Icon SVG
# ------------------------------------------------- # -------------------------------------------------
defp aria_sort(field, sort_field, dir) when field == sort_field do defp aria_sort(field, sort_field, dir) when field == sort_field do
case dir do case dir do

View file

@ -4,7 +4,7 @@ defmodule MvWeb.MemberLive.Index do
import Ash.Query import Ash.Query
@impl true @impl true
def mount(params, _session, socket) do def mount(_params, _session, socket) do
socket = socket =
socket socket
|> assign(:page_title, gettext("Members")) |> assign(:page_title, gettext("Members"))
@ -14,7 +14,6 @@ defmodule MvWeb.MemberLive.Index do
|> assign(:selected_members, []) |> assign(:selected_members, [])
# We call handle params to use the query from the URL # We call handle params to use the query from the URL
{:noreply, socket} = handle_params(params, nil, socket)
{:ok, socket} {:ok, socket}
end end
@ -70,6 +69,7 @@ defmodule MvWeb.MemberLive.Index do
@impl true @impl true
def handle_info({:sort, field_str}, socket) do def handle_info({:sort, field_str}, socket) do
field = String.to_existing_atom(field_str) field = String.to_existing_atom(field_str)
old_field = socket.assigns.sort_field
{new_order, new_field} = {new_order, new_field} =
if socket.assigns.sort_field == field do if socket.assigns.sort_field == field do
@ -79,28 +79,38 @@ defmodule MvWeb.MemberLive.Index do
end end
active_id = :"sort_#{new_field}" active_id = :"sort_#{new_field}"
old_id = :"sort_#{old_field}"
# Update the SortHeader to # Update the new SortHeader
send_update(MvWeb.Components.SortHeaderComponent, send_update(MvWeb.Components.SortHeaderComponent,
id: active_id, id: active_id,
sort_field: new_field, sort_field: new_field,
sort_order: new_order sort_order: new_order
) )
# Reset the current SortHeader
send_update(MvWeb.Components.SortHeaderComponent,
id: old_id,
sort_field: new_field,
sort_order: new_order
)
existing_search_query = socket.assigns.query
# Build the URL with queries # Build the URL with queries
query_params = %{ query_params = %{
"query" => existing_search_query,
"sort_field" => Atom.to_string(new_field), "sort_field" => Atom.to_string(new_field),
"sort_order" => Atom.to_string(new_order) "sort_order" => Atom.to_string(new_order)
} }
# "/members" is the path you defined in router.ex # Set the new path with params
new_path = "/members?" <> URI.encode_query(query_params) new_path = ~p"/members?#{query_params}"
# Push the new URL # Push the new URL
{:noreply, {:noreply,
push_patch(socket, push_patch(socket,
to: new_path, to: new_path,
# replace true
replace: true replace: true
)} )}
end end
@ -108,19 +118,27 @@ defmodule MvWeb.MemberLive.Index do
# Function to handle search # Function to handle search
@impl true @impl true
def handle_info({:search_changed, q}, socket) do def handle_info({:search_changed, q}, socket) do
members = socket = load_members(socket, q)
if String.trim(q) == "" do
Ash.read!(Mv.Membership.Member)
else
Mv.Membership.Member
|> filter(expr(fragment("search_vector @@ plainto_tsquery('simple', ?)", ^q)))
|> Ash.read!()
end
existing_field_query = socket.assigns.sort_field
existing_sort_query = socket.assigns.sort_order
# Build the URL with queries
query_params = %{
"query" => q,
"sort_field" => existing_field_query,
"sort_order" => existing_sort_query
}
# Set the new path with params
new_path = ~p"/members?#{query_params}"
# Push the new URL
{:noreply, {:noreply,
socket push_patch(socket,
|> assign(:query, q) to: new_path,
|> assign(:members, members)} replace: true
)}
end end
# ----------------------------------------------------------------- # -----------------------------------------------------------------
@ -130,8 +148,9 @@ defmodule MvWeb.MemberLive.Index do
def handle_params(params, _url, socket) do def handle_params(params, _url, socket) do
socket = socket =
socket socket
|> maybe_update_search(params)
|> maybe_update_sort(params) |> maybe_update_sort(params)
|> load_members() |> load_members(params["query"])
{:noreply, socket} {:noreply, socket}
end end
@ -140,7 +159,7 @@ defmodule MvWeb.MemberLive.Index do
# FUNCTIONS # FUNCTIONS
# ------------------------------------------------------------- # -------------------------------------------------------------
# Load members eg based on a query for sorting # Load members eg based on a query for sorting
defp load_members(socket) do defp load_members(socket, search_query) do
query = query =
Mv.Membership.Member Mv.Membership.Member
|> Ash.Query.new() |> Ash.Query.new()
@ -156,7 +175,12 @@ defmodule MvWeb.MemberLive.Index do
:phone_number, :phone_number,
:join_date :join_date
]) ])
|> maybe_sort(socket.assigns.sort_field, socket.assigns.sort_order)
# Apply the search filter first
query = apply_search_filter(query, search_query)
# Apply sorting based on current socket state
query = maybe_sort(query, socket.assigns.sort_field, socket.assigns.sort_order)
members = Ash.read!(query) members = Ash.read!(query)
assign(socket, :members, members) assign(socket, :members, members)
@ -166,6 +190,16 @@ defmodule MvWeb.MemberLive.Index do
# Helper Functions # Helper Functions
# ------------------------------------------------------------- # -------------------------------------------------------------
# Function to apply search query
defp apply_search_filter(query, search_query) do
if search_query && String.trim(search_query) != "" do
query
|> filter(expr(fragment("search_vector @@ plainto_tsquery('simple', ?)", ^search_query)))
else
query
end
end
# Functions to toggle sorting order # Functions to toggle sorting order
defp toggle_order(:asc), do: :desc defp toggle_order(:asc), do: :desc
defp toggle_order(:desc), do: :asc defp toggle_order(:desc), do: :asc
@ -193,4 +227,14 @@ defmodule MvWeb.MemberLive.Index do
end end
defp maybe_update_sort(socket, _), do: socket defp maybe_update_sort(socket, _), do: socket
# Function to update search parameters
defp maybe_update_search(socket, %{"query" => query}) when query != "" do
assign(socket, :query, query)
end
defp maybe_update_search(socket, _params) do
# Keep the previous search query if no new one is provided
socket
end
end end