mirror of
https://github.com/ash-project/ash_admin.git
synced 2024-09-19 21:03:52 +12:00
improvement: add pagination support
This commit is contained in:
parent
cc3121e6eb
commit
90edb4e266
11 changed files with 291 additions and 176 deletions
|
@ -75,6 +75,12 @@ Hooks.FormChange = {
|
|||
}
|
||||
}
|
||||
|
||||
Hooks.MaintainAttrs = {
|
||||
attrs() { return this.el.getAttribute("data-attrs").split(", ") },
|
||||
beforeUpdate() { this.prevAttrs = this.attrs().map(name => [name, this.el.getAttribute(name)]) },
|
||||
updated() { this.prevAttrs.forEach(([name, val]) => this.el.setAttribute(name, val)) }
|
||||
}
|
||||
|
||||
let liveSocket = new LiveSocket(socketPath, Socket, {
|
||||
hooks: Hooks,
|
||||
dom: {
|
||||
|
|
|
@ -10,6 +10,7 @@ defmodule Demo.Tickets.Ticket do
|
|||
|
||||
admin do
|
||||
show_action :read
|
||||
table_columns [:id, :representative_id, :reporter_id, :subject, :status]
|
||||
form do
|
||||
field :description, type: :long_text
|
||||
end
|
||||
|
@ -47,11 +48,18 @@ defmodule Demo.Tickets.Ticket do
|
|||
pagination [
|
||||
offset?: true,
|
||||
keyset?: true,
|
||||
default_limit: 20,
|
||||
default_limit: 10,
|
||||
countable: :by_default
|
||||
]
|
||||
end
|
||||
|
||||
read :keyset do
|
||||
pagination [
|
||||
keyset?: true,
|
||||
default_limit: 10
|
||||
]
|
||||
end
|
||||
|
||||
create :open do
|
||||
accept [:subject, :reporter]
|
||||
end
|
||||
|
|
|
@ -18,13 +18,16 @@ defmodule AshAdmin.Components.Resource.DataTable do
|
|||
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)
|
||||
arguments = socket.assigns[:params]["args"] || %{}
|
||||
params = socket.assigns[:params] || %{}
|
||||
arguments = params["args"] || %{}
|
||||
|
||||
query =
|
||||
socket.assigns[:resource]
|
||||
|
@ -33,17 +36,59 @@ defmodule AshAdmin.Components.Resource.DataTable do
|
|||
|
||||
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, page_opts ->
|
||||
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
|
||||
|
||||
assigns[:api].read(socket.assigns.query,
|
||||
action: socket.assigns[:action].name,
|
||||
actor: socket.assigns[:actor],
|
||||
authorize?: socket.assigns[:authorizing],
|
||||
page: page_opts || []
|
||||
page: page_params
|
||||
)
|
||||
end,
|
||||
load_until_connected?: true
|
||||
|
@ -71,11 +116,11 @@ defmodule AshAdmin.Components.Resource.DataTable do
|
|||
|
||||
def render(assigns) do
|
||||
~H"""
|
||||
<div class="pt-10 sm:mt-0 bg-gray-300 min-h-screen">
|
||||
<div class="md:grid md:grid-cols-3 md:gap-6 mx-16 mt-10">
|
||||
<div class="mt-5 md:mt-0 md:col-span-2">
|
||||
<div :if={{@action.arguments != []}} class="shadow-lg overflow-hidden sm:rounded-md bg-white">
|
||||
<div class="px-4 py-5 sm:p-6">
|
||||
<div class="sm:mt-0 bg-gray-300 min-h-screen">
|
||||
<div class="md:grid md:grid-cols-3 md:gap-6 md:mx-16 md:pt-10">
|
||||
<div class="md:mt-0 md:col-span-2">
|
||||
<div :if={{@action.arguments != []}} class="shadow-lg overflow-hidden pt-2 sm:rounded-md bg-white">
|
||||
<div class="px-4 sm:p-6">
|
||||
<Form :if={{@query}} as="query" for={{@query}} change="validate" submit="save" :let={{form: form}}>
|
||||
{{AshAdmin.Components.Resource.Form.render_attributes(assigns, @resource, @action, form)}}
|
||||
<div class="px-4 py-3 text-right sm:px-6">
|
||||
|
@ -91,8 +136,8 @@ defmodule AshAdmin.Components.Resource.DataTable do
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div :if={{@action.arguments == [] || @params["args"]}} class="h-full mt-8 overflow-scroll">
|
||||
<div class="shadow-lg overflow-hidden sm:rounded-md bg-white">
|
||||
<div :if={{@action.arguments == [] || @params["args"]}} class="h-full overflow-scroll">
|
||||
<div class="shadow-lg overflow-scroll sm:rounded-md bg-white">
|
||||
<div :if={{ match?({:error, _}, @data) }}>
|
||||
{{ {:error, %{query: query}} = @data
|
||||
nil }}
|
||||
|
@ -102,19 +147,36 @@ defmodule AshAdmin.Components.Resource.DataTable do
|
|||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<Table :if={{ match?({:ok, _data}, @data) }} data={{data(@data)}} resource={{@resource}} api={{@api}} set_actor={{@set_actor}}/>
|
||||
<div class="px-2">
|
||||
{{render_pagination_links(assigns, :top)}}
|
||||
<Table :if={{ match?({:ok, _data}, @data) }} data={{data(@data)}} resource={{@resource}} api={{@api}} set_actor={{@set_actor}} attributes={{AshAdmin.Resource.table_columns(@resource)}}/>
|
||||
{{render_pagination_links(assigns, :bottom)}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
"""
|
||||
end
|
||||
|
||||
defp message(error) do
|
||||
if is_exception(error) do
|
||||
Exception.message(error)
|
||||
else
|
||||
inspect(error)
|
||||
def handle_event("next_page", _, socket) do
|
||||
params = %{"page" => page_link_params(socket.assigns.data, "next")}
|
||||
|
||||
{:noreply,
|
||||
push_patch(socket, to: self_path(socket.assigns.url_path, socket.assigns.params, params))}
|
||||
end
|
||||
|
||||
def handle_event("prev_page", _, socket) do
|
||||
params = %{"page" => page_link_params(socket.assigns.data, "prev")}
|
||||
|
||||
{:noreply,
|
||||
push_patch(socket, to: self_path(socket.assigns.url_path, socket.assigns.params, params))}
|
||||
end
|
||||
|
||||
def handle_event("specific_page", %{"page" => page}, socket) do
|
||||
params = %{"page" => page_link_params(socket.assigns.data, String.to_integer(page))}
|
||||
|
||||
{:noreply,
|
||||
push_patch(socket, to: self_path(socket.assigns.url_path, socket.assigns.params, params))}
|
||||
end
|
||||
|
||||
def handle_event("validate", %{"query" => query}, socket) do
|
||||
|
@ -131,74 +193,176 @@ defmodule AshAdmin.Components.Resource.DataTable do
|
|||
)}
|
||||
end
|
||||
|
||||
# defp middle_page_num(num, trailing_page_nums) do
|
||||
# if num in trailing_page_nums || num <= 3 do
|
||||
# "..."
|
||||
# else
|
||||
# "...#{num}..."
|
||||
# end
|
||||
# end
|
||||
defp render_pagination_links(assigns, placement) do
|
||||
~H"""
|
||||
<div :if={{(offset?(@data) || keyset?(@data)) && show_pagination_links?(@data, placement)}} class="w-5/6 mx-auto">
|
||||
<div class="bg-white px-4 py-3 flex items-center justify-between border-t border-gray-200 sm:px-6">
|
||||
<div class="flex-1 flex justify-between sm:hidden">
|
||||
<button :if={{!(keyset?(@data) && is_nil(@params["page"])) && prev_page?(@data)}} :on-click="prev_page" class="relative inline-flex items-center px-4 py-2 border border-gray-300 text-sm font-medium rounded-md text-gray-700 bg-white hover:text-gray-500">
|
||||
Previous
|
||||
</button>
|
||||
{{render_pagination_information(assigns, true)}}
|
||||
<button :if={{next_page?(@data)}} :on-click="next_page" class="ml-3 relative inline-flex items-center px-4 py-2 border border-gray-300 text-sm font-medium rounded-md text-gray-700 bg-white hover:text-gray-500">
|
||||
Next
|
||||
</button>
|
||||
</div>
|
||||
<div class="hidden sm:flex-1 sm:flex sm:items-center sm:justify-between">
|
||||
<div>
|
||||
{{render_pagination_information(assigns)}}
|
||||
</div>
|
||||
<div>
|
||||
<nav class="relative z-0 inline-flex rounded-md shadow-sm -space-x-px" aria-label="Pagination">
|
||||
<button :if={{!(keyset?(@data) && is_nil(@params["page"])) && prev_page?(@data)}} :on-click="prev_page" class="relative inline-flex items-center px-2 py-2 rounded-l-md border border-gray-300 bg-white text-sm font-medium text-gray-500 hover:bg-gray-50">
|
||||
<span class="sr-only">Previous</span>
|
||||
<!-- Heroicon name: solid/chevron-left -->
|
||||
<svg class="h-5 w-5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
|
||||
<path fill-rule="evenodd" d="M12.707 5.293a1 1 0 010 1.414L9.414 10l3.293 3.293a1 1 0 01-1.414 1.414l-4-4a1 1 0 010-1.414l4-4a1 1 0 011.414 0z" clip-rule="evenodd" />
|
||||
</svg>
|
||||
</button>
|
||||
<span :if={{offset?(@data)}}>
|
||||
{{render_page_links(assigns, leading_page_nums(@data))}}
|
||||
{{render_middle_page_num(assigns, @page_num, trailing_page_nums(@data))}}
|
||||
{{render_page_links(assigns, trailing_page_nums(@data))}}
|
||||
</span>
|
||||
<button :if={{next_page?(@data)}} :on-click="next_page" class="relative inline-flex items-center px-2 py-2 rounded-r-md border border-gray-300 bg-white text-sm font-medium text-gray-500 hover:bg-gray-50">
|
||||
<span class="sr-only">Next</span>
|
||||
<!-- Heroicon name: solid/chevron-right -->
|
||||
<svg class="h-5 w-5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
|
||||
<path fill-rule="evenodd" d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" clip-rule="evenodd" />
|
||||
</svg>
|
||||
</button>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
"""
|
||||
end
|
||||
|
||||
# defp page_link_params({:ok, page}, target), do: page_link_params(page, target)
|
||||
defp render_page_links(assigns, page_nums) do
|
||||
~H"""
|
||||
<button :on-click="specific_page" phx-value-page={{i}} :for={{i <- page_nums}} class={{"relative inline-flex items-center px-4 py-2 border border-gray-300 bg-white text-sm font-medium text-gray-700 hover:bg-gray-50", "bg-gray-300": @page_num == i}}>
|
||||
{{i}}
|
||||
</button>
|
||||
"""
|
||||
end
|
||||
|
||||
# defp page_link_params(page, target) do
|
||||
# case AshPhoenix.LiveView.page_link_params(page, target) do
|
||||
# :invalid ->
|
||||
# nil
|
||||
defp render_pagination_information(assigns, small? \\ false) do
|
||||
~H"""
|
||||
<p class={{"text-sm text-gray-700", "sm:hidden": small?}}>
|
||||
<span :if={{offset?(@data)}}>
|
||||
Showing
|
||||
<span class="font-medium">{{first(@data)}}</span>
|
||||
to
|
||||
<span class="font-medium">{{last(@data)}}</span>
|
||||
of
|
||||
</span>
|
||||
<span :if={{count(@data)}}>
|
||||
<span class="font-medium">{{count(@data)}}</span>
|
||||
results
|
||||
</span>
|
||||
</p>
|
||||
"""
|
||||
end
|
||||
|
||||
# params ->
|
||||
# [page: params]
|
||||
# end
|
||||
# end
|
||||
defp page_num_from_page_params(params) do
|
||||
cond do
|
||||
!params[:offset] || params[:after] || params[:before] ->
|
||||
1
|
||||
|
||||
# defp show_ellipses?(%Ash.Page.Offset{count: count, limit: limit}) when not is_nil(count) do
|
||||
# page_nums =
|
||||
# count
|
||||
# |> Kernel./(limit)
|
||||
# |> Float.ceil()
|
||||
# |> trunc()
|
||||
params[:offset] && params[:limit] ->
|
||||
trunc(Float.ceil(params[:offset] / params[:limit])) + 1
|
||||
|
||||
# page_nums > 6
|
||||
# end
|
||||
true ->
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
# defp show_ellipses?({:ok, data}), do: show_ellipses?(data)
|
||||
# defp show_ellipses?(_), do: false
|
||||
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
|
||||
|
||||
# def leading_page_nums({:ok, data}), do: leading_page_nums(data)
|
||||
# def leading_page_nums(%Ash.Page.Offset{count: nil}), do: []
|
||||
defp first({:ok, %Ash.Page.Offset{offset: offset}}) do
|
||||
(offset || 0) + 1
|
||||
end
|
||||
|
||||
# def leading_page_nums(%Ash.Page.Offset{limit: limit, count: count}) do
|
||||
# page_nums =
|
||||
# count
|
||||
# |> Kernel./(limit)
|
||||
# |> Float.ceil()
|
||||
# |> trunc()
|
||||
defp first(_), do: nil
|
||||
|
||||
# 1..min(3, page_nums)
|
||||
# end
|
||||
defp last({:ok, %Ash.Page.Offset{offset: offset, results: results}}) do
|
||||
Enum.count(results) + offset
|
||||
end
|
||||
|
||||
# def leading_page_nums(_), do: []
|
||||
defp last(_), do: nil
|
||||
|
||||
# def trailing_page_nums({:ok, data}), do: trailing_page_nums(data)
|
||||
# def trailing_page_nums(%Ash.Page.Offset{count: nil}), do: []
|
||||
defp message(error) do
|
||||
if is_exception(error) do
|
||||
Exception.message(error)
|
||||
else
|
||||
inspect(error)
|
||||
end
|
||||
end
|
||||
|
||||
# def trailing_page_nums(%Ash.Page.Offset{limit: limit, count: count}) do
|
||||
# page_nums =
|
||||
# count
|
||||
# |> Kernel./(limit)
|
||||
# |> Float.ceil()
|
||||
# |> trunc()
|
||||
defp render_middle_page_num(assigns, num, trailing_page_nums) do
|
||||
ellipsis? = num in trailing_page_nums || num <= 3
|
||||
|
||||
# if page_nums > 3 do
|
||||
# max(page_nums - 2, 0)..page_nums
|
||||
# else
|
||||
# []
|
||||
# end
|
||||
# end
|
||||
~H"""
|
||||
<span
|
||||
:if={{show_ellipses?(@data)}}
|
||||
class={{"relative inline-flex items-center px-4 py-2 border border-gray-300 bg-white text-sm font-medium text-gray-700", "bg-gray-300": !ellipsis?}}>
|
||||
<span :if={{ellipsis?}}>
|
||||
...
|
||||
</span>
|
||||
<span :if={{!ellipsis?}}>
|
||||
{{num}}
|
||||
</span>
|
||||
</span>
|
||||
"""
|
||||
end
|
||||
|
||||
# def handle_event("toggle_filter", _, socket) do
|
||||
# {:noreply, assign(socket, :filter_open, !socket.assigns.filter_open)}
|
||||
# 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: []
|
||||
|
@ -206,49 +370,15 @@ defmodule AshAdmin.Components.Resource.DataTable do
|
|||
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 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 keyset?({:ok, data}), do: keyset?(data)
|
||||
defp keyset?(%Ash.Page.Keyset{}), do: true
|
||||
defp keyset?(_), do: false
|
||||
|
||||
# defp offset({:ok, data}), do: offset(data)
|
||||
# defp offset(%Ash.Page.Offset{offset: offset}), do: offset
|
||||
# defp offset(_), do: 0
|
||||
|
||||
# defp limit({:ok, data}), do: limit(data)
|
||||
# defp limit(%Ash.Page.Offset{limit: limit}), do: limit
|
||||
# defp limit(_), do: 0
|
||||
|
||||
# defp count({:ok, %{count: count}}), do: count
|
||||
# defp count(%{count: count}), do: count
|
||||
# defp count(_), do: nil
|
||||
# defp run_query() do
|
||||
# fn filter, sort, fields, context ->
|
||||
# page_params =
|
||||
# case context.action.pagination do
|
||||
# false ->
|
||||
# false
|
||||
|
||||
# %{offset?: true} ->
|
||||
# context[:page_params] || [offset: 0]
|
||||
|
||||
# _ ->
|
||||
# context[:page_params]
|
||||
# end
|
||||
|
||||
# context.resource
|
||||
# |> Ash.Query.filter(^filter)
|
||||
# |> Ash.Query.sort(sort)
|
||||
# |> Ash.Query.load(fields)
|
||||
# |> Ash.Query.set_tenant(context.tenant)
|
||||
# |> context.api.read(
|
||||
# page: page_params,
|
||||
# action: context.action.name,
|
||||
# actor: context.actor,
|
||||
# authorize?: context.authorizing?
|
||||
# )
|
||||
# end
|
||||
defp count({:ok, %{count: count}}), do: count
|
||||
defp count(%{count: count}), do: count
|
||||
defp count(_), do: nil
|
||||
end
|
||||
|
|
|
@ -40,8 +40,8 @@ defmodule AshAdmin.Components.Resource.Form do
|
|||
|
||||
def render(assigns) do
|
||||
~H"""
|
||||
<div class="pt-10 sm:mt-0 bg-gray-300 min-h-screen">
|
||||
<div class="md:grid md:grid-cols-3 md:gap-6 mx-16 mt-10">
|
||||
<div class="md:pt-10 sm:mt-0 bg-gray-300 min-h-screen">
|
||||
<div class="md:grid md:grid-cols-3 md:gap-6 md:mx-16 md:mt-10">
|
||||
<div class="mt-5 md:mt-0 md:col-span-2">
|
||||
{{ render_form(assigns) }}
|
||||
</div>
|
||||
|
@ -400,7 +400,7 @@ defmodule AshAdmin.Components.Resource.Form do
|
|||
<TextArea
|
||||
form={{ form }}
|
||||
field={{ name }}
|
||||
opts={{ type: text_input_type(attribute), placeholder: placeholder(default) }}
|
||||
opts={{ type: text_input_type(attribute), placeholder: placeholder(default), phx_hook: "MaintainAttrs", data_attrs: "style" }}
|
||||
class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm sm:text-sm border-gray-300 rounded-md resize-y"
|
||||
/>
|
||||
"""
|
||||
|
|
|
@ -15,8 +15,6 @@ defmodule AshAdmin.Components.Resource do
|
|||
prop(authorizing, :boolean, required: true)
|
||||
prop(tenant, :string, required: true)
|
||||
prop(recover_filter, :any)
|
||||
prop(page_params, :any, default: [])
|
||||
prop(page_num, :integer, default: 1)
|
||||
prop(url_path, :string, default: "")
|
||||
prop(params, :map, default: %{})
|
||||
prop(primary_key, :any, default: nil)
|
||||
|
|
|
@ -4,7 +4,7 @@ defmodule AshAdmin.Components.Resource.Table do
|
|||
import AshAdmin.Helpers
|
||||
alias Surface.Components.LiveRedirect
|
||||
|
||||
prop(attributes, :list, default: nil)
|
||||
prop(attributes, :any, default: nil)
|
||||
prop(data, :list, default: nil)
|
||||
prop(resource, :any, required: true)
|
||||
prop(actions, :boolean, default: true)
|
||||
|
@ -14,17 +14,15 @@ defmodule AshAdmin.Components.Resource.Table do
|
|||
def render(assigns) do
|
||||
~H"""
|
||||
<div>
|
||||
<table class="rounded-t-lg m-5 w-5/6 mx-auto">
|
||||
<table class="rounded-t-lg m-5 w-5/6 mx-auto text-left">
|
||||
<thead class="text-left border-b-2">
|
||||
<th :for={{ attribute <- Ash.Resource.Info.attributes(@resource) |> Enum.filter(&(is_nil(@attributes) || &1 in @attributes)) }}>
|
||||
<th :for={{ attribute <- attributes(@resource, @attributes) }}>
|
||||
{{ to_name(attribute.name) }}
|
||||
</th>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr :for={{ record <- @data }} class="text-left border-b-2">
|
||||
<td :for={{ attribute <- Ash.Resource.Info.attributes(@resource) |> Enum.filter(&(is_nil(@attributes) || &1 in @attributes)) }} class="px-4 py-3">
|
||||
{{ render_attribute(record, attribute) }}
|
||||
</td>
|
||||
<tr :for={{ record <- @data }} class="border-b-2">
|
||||
<td :for={{ attribute <- attributes(@resource, @attributes) }} class="py-3">{{ render_attribute(record, attribute) }}</td>
|
||||
<td :if={{@actions && actions?(@resource)}}>
|
||||
<div class="flex h-max justify-items-center">
|
||||
<div :if={{ AshAdmin.Resource.show_action(@resource) }}>
|
||||
|
@ -63,6 +61,16 @@ defmodule AshAdmin.Components.Resource.Table do
|
|||
"""
|
||||
end
|
||||
|
||||
defp attributes(resource, nil) do
|
||||
Ash.Resource.Info.attributes(resource)
|
||||
end
|
||||
|
||||
defp attributes(resource, attributes) do
|
||||
attributes
|
||||
|> Enum.map(&Ash.Resource.Info.attribute(resource, &1))
|
||||
|> Enum.filter(& &1)
|
||||
end
|
||||
|
||||
defp render_attribute(record, attribute) do
|
||||
if Ash.Type.embedded_type?(attribute.type) do
|
||||
"..."
|
||||
|
|
|
@ -27,10 +27,6 @@ defmodule AshAdmin.Helpers do
|
|||
end
|
||||
|
||||
def self_path(url_path, socket_params, new_params) do
|
||||
IO.inspect(url_path)
|
||||
IO.inspect(socket_params)
|
||||
IO.inspect(new_params)
|
||||
|
||||
url_path <>
|
||||
"?" <>
|
||||
Plug.Conn.Query.encode(Map.merge(socket_params || %{}, Enum.into(new_params, %{})))
|
||||
|
|
|
@ -59,8 +59,7 @@ defmodule AshAdmin.PageLive do
|
|||
AshAdmin.ActorPlug.session_bool(session["actor_authorizing"]) || false
|
||||
)
|
||||
|> assign(:recover_filter, nil)
|
||||
|> assign(:actor_paused, actor_paused)
|
||||
|> assign(:page_num, 1)}
|
||||
|> assign(:actor_paused, actor_paused)}
|
||||
end
|
||||
|
||||
@impl true
|
||||
|
@ -94,8 +93,6 @@ defmodule AshAdmin.PageLive do
|
|||
tab={{ @tab }}
|
||||
url_path={{ @url_path }}
|
||||
params={{ @params }}
|
||||
page_params={{ @page_params }}
|
||||
page_num={{ @page_num }}
|
||||
action={{ @action }}
|
||||
tenant={{ @tenant }}
|
||||
actor={{ unless @actor_paused, do: @actor }}
|
||||
|
@ -126,31 +123,6 @@ defmodule AshAdmin.PageLive do
|
|||
socket
|
||||
end
|
||||
|
||||
socket =
|
||||
if params["page"] do
|
||||
default_limit =
|
||||
socket.assigns[:action] && socket.assigns.action.pagination &&
|
||||
socket.assigns.action.pagination.default_limit
|
||||
|
||||
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 params["primary_key"] do
|
||||
case decode_primary_key(socket.assigns.resource, params["primary_key"]) do
|
||||
|
@ -200,19 +172,6 @@ defmodule AshAdmin.PageLive do
|
|||
|> Enum.map(& &1.name)
|
||||
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
|
||||
|
||||
@impl true
|
||||
def handle_event("toggle_authorizing", _, socket) do
|
||||
{:noreply,
|
||||
|
|
|
@ -39,12 +39,20 @@ defmodule AshAdmin.Resource do
|
|||
type: {:list, :atom},
|
||||
doc:
|
||||
"A list of read actions that can be used to show resource details. These actions should accept arguments that produce one record e.g `get_user_by_id`."
|
||||
],
|
||||
table_columns: [
|
||||
type: {:list, :atom},
|
||||
doc: "The list of attributes to render on the table view."
|
||||
]
|
||||
]
|
||||
}
|
||||
|
||||
use Ash.Dsl.Extension, sections: [@admin]
|
||||
|
||||
def table_columns(resource) do
|
||||
Ash.Dsl.Extension.get_opt(resource, [:admin], :table_columns, nil, true)
|
||||
end
|
||||
|
||||
def name(resource) do
|
||||
Ash.Dsl.Extension.get_opt(resource, [:admin], :name, nil, true) ||
|
||||
resource
|
||||
|
|
6
mix.exs
6
mix.exs
|
@ -46,8 +46,10 @@ defmodule AshAdmin.MixProject do
|
|||
# Run "mix help deps" to learn about dependencies.
|
||||
defp deps do
|
||||
[
|
||||
{:ash, "~> 1.36 and >= 1.36.12"},
|
||||
{:ash_phoenix, "~> 0.4 and >= 0.4.2"},
|
||||
# {:ash, "~> 1.36 and >= 1.36.12"},
|
||||
{:ash, path: "../ash", override: true},
|
||||
# {:ash_phoenix, "~> 0.4 and >= 0.4.2"},
|
||||
{:ash_phoenix, path: "../ash_phoenix"},
|
||||
{:surface, "~> 0.3.1"},
|
||||
{:phoenix_live_view, "~> 0.15.4"},
|
||||
{:phoenix_html, "~> 2.14.1 or ~> 2.15"},
|
||||
|
|
File diff suppressed because one or more lines are too long
Loading…
Reference in a new issue