fix: keep search term while sorting
This commit is contained in:
parent
c687868e5c
commit
215b4fd265
2 changed files with 89 additions and 43 deletions
|
|
@ -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() # Ash‑Field 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,25 +19,27 @@ defmodule MvWeb.Components.SortHeaderComponent do
|
||||||
@impl true
|
@impl true
|
||||||
def render(assigns) do
|
def render(assigns) do
|
||||||
~H"""
|
~H"""
|
||||||
<button
|
<div class="tooltip" data-tip={aria_sort(@field, @sort_field, @sort_order)}>
|
||||||
type="button"
|
<button
|
||||||
aria-label={aria_sort(@field, @sort_field, @sort_order)}
|
type="button"
|
||||||
class="btn btn-ghost select-none"
|
aria-label={aria_sort(@field, @sort_field, @sort_order)}
|
||||||
phx-click="sort"
|
class="btn btn-ghost select-none"
|
||||||
phx-value-field={@field}
|
phx-click="sort"
|
||||||
phx-target={@myself}
|
phx-value-field={@field}
|
||||||
data-testid={@field}
|
phx-target={@myself}
|
||||||
>
|
data-testid={@field}
|
||||||
{@label}
|
>
|
||||||
<%= if @sort_field == @field do %>
|
{@label}
|
||||||
<.icon name={if @sort_order == :asc, do: "hero-chevron-up", else: "hero-chevron-down"} />
|
<%= if @sort_field == @field do %>
|
||||||
<% else %>
|
<.icon name={if @sort_order == :asc, do: "hero-chevron-up", else: "hero-chevron-down"} />
|
||||||
<.icon
|
<% else %>
|
||||||
name="hero-chevron-up-down"
|
<.icon
|
||||||
class="opacity-40"
|
name="hero-chevron-up-down"
|
||||||
/>
|
class="opacity-40"
|
||||||
<% end %>
|
/>
|
||||||
</button>
|
<% end %>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
"""
|
"""
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -48,7 +50,7 @@ defmodule MvWeb.Components.SortHeaderComponent do
|
||||||
end
|
end
|
||||||
|
|
||||||
# -------------------------------------------------
|
# -------------------------------------------------
|
||||||
# Hilfsfunktionen für ARIA‑Attribute & Icon‑SVG
|
# 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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue