defmodule AshAdmin.Components.Resource.DataTable do @moduledoc false use Surface.LiveComponent import AshAdmin.Helpers import AshPhoenix.LiveView alias Surface.Components.Form alias AshAdmin.Components.Resource.Table prop(resource, :atom) prop(api, :atom) prop(action, :any) prop(authorizing, :boolean) prop(set_actor, :event, required: true) prop(actor, :any) prop(url_path, :any) prop(params, :any) prop(table, :any, required: true) prop(tables, :any, required: true) prop(prefix, :any, required: true) prop(tenant, :any, required: true) data(initialized, :boolean, default: false) data(data, :any) data(query, :any, default: nil) data(page_params, :any, default: nil) data(page_num, :any, default: nil) def update(assigns, socket) do if assigns[:initialized] do {:ok, socket} else socket = assign(socket, assigns) params = socket.assigns[:params] || %{} arguments = params["args"] || %{} query = socket.assigns[:resource] |> Ash.Query.for_read(socket.assigns.action.name, arguments) |> Ash.Query.set_tenant(socket.assigns[:tenant]) |> AshPhoenix.hide_errors() socket = assign(socket, :query, query) socket = if params["page"] do default_limit = (socket.assigns[:action] && socket.assigns.action.pagination && socket.assigns.action.pagination.default_limit) || socket.assigns.action.pagination.max_page_size || 25 count? = socket.assigns[:action] && socket.assigns.action.pagination && socket.assigns.action.pagination.countable page_params = AshPhoenix.LiveView.page_from_params(params["page"], default_limit, !!count?) socket |> assign( :page_params, page_params ) |> assign( :page_num, page_num_from_page_params(page_params) ) else socket |> assign(:page_params, nil) |> assign(:page_num, 1) end socket = if assigns[:action].pagination do keep_live( socket, :data, fn socket -> default_limit = socket.assigns[:action].pagination.default_limit || socket.assigns[:action].pagination.max_page_size || 25 count? = socket.assigns[:action].pagination.countable page_params = if socket.assigns[:params]["page"] do page_from_params(socket.assigns[:params]["page"], default_limit, !!count?) else [] end if AshAdmin.Resource.polymorphic?(socket.assigns.resource) && !socket.assigns[:table] do {:ok, []} else socket.assigns.query |> set_table(socket.assigns[:table]) |> assigns[:api].read( action: socket.assigns[:action].name, actor: socket.assigns[:actor], authorize?: socket.assigns[:authorizing], page: page_params ) end end, load_until_connected?: true ) else keep_live( socket, :data, fn socket -> if AshAdmin.Resource.polymorphic?(socket.assigns.resource) && !socket.assigns[:table] do {:ok, []} else socket.assigns.query |> set_table(socket.assigns[:table]) |> assigns[:api].read( action: socket.assigns[:action], actor: socket.assigns[:actor], authorize?: socket.assigns[:authorizing] ) end end, load_until_connected?: true ) end {:ok, socket |> assign(:initialized, true)} end end def render(assigns) do ~H"""
Showing {{ first(@data) }} to {{ last(@data) }} of {{ count(@data) }} results
""" end defp page_num_from_page_params(params) do cond do !params[:offset] || params[:after] || params[:before] -> 1 params[:offset] && params[:limit] -> trunc(Float.ceil(params[:offset] / params[:limit])) + 1 true -> nil end end defp show_pagination_links?({:ok, _page}, :bottom), do: true defp show_pagination_links?({:ok, page}, :top), do: page.limit >= 20 defp show_pagination_links?(_, _), do: false defp first({:ok, %Ash.Page.Offset{offset: offset}}) do (offset || 0) + 1 end defp first(_), do: nil defp last({:ok, %Ash.Page.Offset{offset: offset, results: results}}) do Enum.count(results) + offset end defp last(_), do: nil defp message(error) do if is_exception(error) do Exception.message(error) else inspect(error) end end defp render_middle_page_num(assigns, num, trailing_page_nums) do ellipsis? = num in trailing_page_nums || num <= 3 ~H""" ... {{ num }} """ end defp show_ellipses?(%Ash.Page.Offset{count: count, limit: limit}) when not is_nil(count) do page_nums = count |> Kernel./(limit) |> Float.ceil() |> trunc() page_nums > 6 end defp show_ellipses?({:ok, data}), do: show_ellipses?(data) defp show_ellipses?(_), do: false def leading_page_nums({:ok, data}), do: leading_page_nums(data) def leading_page_nums(%Ash.Page.Offset{count: nil}), do: [] def leading_page_nums(%Ash.Page.Offset{limit: limit, count: count}) do page_nums = count |> Kernel./(limit) |> Float.ceil() |> trunc() 1..min(3, page_nums) end def leading_page_nums(_), do: [] def trailing_page_nums({:ok, data}), do: trailing_page_nums(data) def trailing_page_nums(%Ash.Page.Offset{count: nil}), do: [] def trailing_page_nums(%Ash.Page.Offset{limit: limit, count: count}) do page_nums = count |> Kernel./(limit) |> Float.ceil() |> trunc() if page_nums > 3 do max(page_nums - 2, 4)..page_nums else [] end end defp data({:ok, data}), do: data(data) defp data({:error, _}), do: [] defp data(%Ash.Page.Offset{results: results}), do: results defp data(%Ash.Page.Keyset{results: results}), do: results defp data(data), do: data defp offset?({:ok, data}), do: offset?(data) defp offset?(%Ash.Page.Offset{}), do: true defp offset?(_), do: false defp keyset?({:ok, data}), do: keyset?(data) defp keyset?(%Ash.Page.Keyset{}), do: true defp keyset?(_), do: false defp count({:ok, %{count: count}}), do: count defp count(%{count: count}), do: count defp count(_), do: nil end