diff --git a/lib/mv_web/live/components/sort_header_component.ex b/lib/mv_web/live/components/sort_header_component.ex index 3817d90..d548efa 100644 --- a/lib/mv_web/live/components/sort_header_component.ex +++ b/lib/mv_web/live/components/sort_header_component.ex @@ -26,7 +26,6 @@ defmodule MvWeb.Components.SortHeaderComponent do class="btn btn-ghost select-none" phx-click="sort" phx-value-field={@field} - phx-target={@myself} data-testid={@field} > {@label} @@ -43,12 +42,6 @@ defmodule MvWeb.Components.SortHeaderComponent do """ end - @impl true - def handle_event("sort", %{"field" => field_str}, socket) do - send(self(), {:sort, field_str}) - {:noreply, socket} - end - # ------------------------------------------------- # Hilfsfunktionen für ARIA Attribute & Icon SVG # ------------------------------------------------- diff --git a/lib/mv_web/live/member_live/index.ex b/lib/mv_web/live/member_live/index.ex index 59ee8f9..b370e9a 100644 --- a/lib/mv_web/live/member_live/index.ex +++ b/lib/mv_web/live/member_live/index.ex @@ -163,6 +163,7 @@ defmodule MvWeb.MemberLive.Index do - `"delete"` - Removes a member from the database - `"select_member"` - Toggles individual member selection - `"select_all"` - Toggles selection of all visible members + - `"sort"` - Sort event from SortHeaderComponent. Updates sort field/order and syncs URL """ @impl true def handle_event("delete", %{"id" => id}, socket) do @@ -305,6 +306,46 @@ defmodule MvWeb.MemberLive.Index do end end + @impl true + def handle_event("sort", %{"field" => field_str}, socket) do + # Handle both atom and string field names (for custom fields) + field = + try do + String.to_existing_atom(field_str) + rescue + ArgumentError -> field_str + end + + {new_field, new_order} = determine_new_sort(field, socket) + old_field = socket.assigns.sort_field + + socket = + socket + |> assign(:sort_field, new_field) + |> assign(:sort_order, new_order) + |> update_sort_components(old_field, new_field, new_order) + |> load_members() + |> update_selection_assigns() + + # URL sync - push_patch happens synchronously in the event handler + query_params = + build_query_params( + socket.assigns.query, + export_sort_field(socket.assigns.sort_field), + export_sort_order(socket.assigns.sort_order), + socket.assigns.cycle_status_filter, + socket.assigns[:group_filters], + socket.assigns.show_current_cycle, + socket.assigns.boolean_custom_field_filters + ) + |> maybe_add_field_selection( + socket.assigns[:user_field_selection], + socket.assigns[:fields_in_url?] || false + ) + + {:noreply, push_patch(socket, to: ~p"/members?#{query_params}", replace: true)} + end + # Helper to format errors for display defp format_error(%Ash.Error.Invalid{errors: errors}) do error_messages = @@ -329,50 +370,10 @@ defmodule MvWeb.MemberLive.Index do Handles messages from child components. ## Supported messages: - - `{:sort, field}` - Sort event from SortHeaderComponent. Updates sort field/order and syncs URL - `{:search_changed, query}` - Search event from SearchBarComponent. Filters members and syncs URL - `{:field_toggled, field, visible}` - Field toggle event from FieldVisibilityDropdownComponent - `{:fields_selected, selection}` - Select all/deselect all event from FieldVisibilityDropdownComponent """ - @impl true - def handle_info({:sort, field_str}, socket) do - # Handle both atom and string field names (for custom fields) - field = - try do - String.to_existing_atom(field_str) - rescue - ArgumentError -> field_str - end - - {new_field, new_order} = determine_new_sort(field, socket) - old_field = socket.assigns.sort_field - - socket = - socket - |> assign(:sort_field, new_field) - |> assign(:sort_order, new_order) - |> update_sort_components(old_field, new_field, new_order) - |> load_members() - |> update_selection_assigns() - - # URL sync - query_params = - build_query_params( - socket.assigns.query, - export_sort_field(socket.assigns.sort_field), - export_sort_order(socket.assigns.sort_order), - socket.assigns.cycle_status_filter, - socket.assigns[:group_filters], - socket.assigns.show_current_cycle, - socket.assigns.boolean_custom_field_filters - ) - |> maybe_add_field_selection( - socket.assigns[:user_field_selection], - socket.assigns[:fields_in_url?] || false - ) - - {:noreply, push_patch(socket, to: ~p"/members?#{query_params}", replace: true)} - end @impl true def handle_info({:search_changed, q}, socket) do diff --git a/test/mv_web/components/sort_header_component_test.exs b/test/mv_web/components/sort_header_component_test.exs index 6d23ab4..bdde4ae 100644 --- a/test/mv_web/components/sort_header_component_test.exs +++ b/test/mv_web/components/sort_header_component_test.exs @@ -223,7 +223,7 @@ defmodule MvWeb.Components.SortHeaderComponentTest do end describe "component behavior" do - test "clicking sends sort message to parent", %{conn: conn} do + test "clicking triggers sort event on parent LiveView", %{conn: conn} do conn = conn_with_oidc_user(conn) {:ok, view, _html} = live(conn, "/members") @@ -232,7 +232,7 @@ defmodule MvWeb.Components.SortHeaderComponentTest do |> element("button[phx-value-field='first_name']") |> render_click() - # The component should send a message to the parent LiveView + # The component triggers a "sort" event on the parent LiveView # This is tested indirectly through the URL change in integration tests end