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, {
|
let liveSocket = new LiveSocket(socketPath, Socket, {
|
||||||
hooks: Hooks,
|
hooks: Hooks,
|
||||||
dom: {
|
dom: {
|
||||||
|
|
|
@ -10,6 +10,7 @@ defmodule Demo.Tickets.Ticket do
|
||||||
|
|
||||||
admin do
|
admin do
|
||||||
show_action :read
|
show_action :read
|
||||||
|
table_columns [:id, :representative_id, :reporter_id, :subject, :status]
|
||||||
form do
|
form do
|
||||||
field :description, type: :long_text
|
field :description, type: :long_text
|
||||||
end
|
end
|
||||||
|
@ -47,11 +48,18 @@ defmodule Demo.Tickets.Ticket do
|
||||||
pagination [
|
pagination [
|
||||||
offset?: true,
|
offset?: true,
|
||||||
keyset?: true,
|
keyset?: true,
|
||||||
default_limit: 20,
|
default_limit: 10,
|
||||||
countable: :by_default
|
countable: :by_default
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
read :keyset do
|
||||||
|
pagination [
|
||||||
|
keyset?: true,
|
||||||
|
default_limit: 10
|
||||||
|
]
|
||||||
|
end
|
||||||
|
|
||||||
create :open do
|
create :open do
|
||||||
accept [:subject, :reporter]
|
accept [:subject, :reporter]
|
||||||
end
|
end
|
||||||
|
|
|
@ -18,13 +18,16 @@ defmodule AshAdmin.Components.Resource.DataTable do
|
||||||
data(initialized, :boolean, default: false)
|
data(initialized, :boolean, default: false)
|
||||||
data(data, :any)
|
data(data, :any)
|
||||||
data(query, :any, default: nil)
|
data(query, :any, default: nil)
|
||||||
|
data(page_params, :any, default: nil)
|
||||||
|
data(page_num, :any, default: nil)
|
||||||
|
|
||||||
def update(assigns, socket) do
|
def update(assigns, socket) do
|
||||||
if assigns[:initialized] do
|
if assigns[:initialized] do
|
||||||
{:ok, socket}
|
{:ok, socket}
|
||||||
else
|
else
|
||||||
socket = assign(socket, assigns)
|
socket = assign(socket, assigns)
|
||||||
arguments = socket.assigns[:params]["args"] || %{}
|
params = socket.assigns[:params] || %{}
|
||||||
|
arguments = params["args"] || %{}
|
||||||
|
|
||||||
query =
|
query =
|
||||||
socket.assigns[:resource]
|
socket.assigns[:resource]
|
||||||
|
@ -33,17 +36,59 @@ defmodule AshAdmin.Components.Resource.DataTable do
|
||||||
|
|
||||||
socket = assign(socket, :query, query)
|
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 =
|
socket =
|
||||||
if assigns[:action].pagination do
|
if assigns[:action].pagination do
|
||||||
keep_live(
|
keep_live(
|
||||||
socket,
|
socket,
|
||||||
:data,
|
: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,
|
assigns[:api].read(socket.assigns.query,
|
||||||
action: socket.assigns[:action].name,
|
action: socket.assigns[:action].name,
|
||||||
actor: socket.assigns[:actor],
|
actor: socket.assigns[:actor],
|
||||||
authorize?: socket.assigns[:authorizing],
|
authorize?: socket.assigns[:authorizing],
|
||||||
page: page_opts || []
|
page: page_params
|
||||||
)
|
)
|
||||||
end,
|
end,
|
||||||
load_until_connected?: true
|
load_until_connected?: true
|
||||||
|
@ -71,11 +116,11 @@ defmodule AshAdmin.Components.Resource.DataTable do
|
||||||
|
|
||||||
def render(assigns) do
|
def render(assigns) do
|
||||||
~H"""
|
~H"""
|
||||||
<div class="pt-10 sm:mt-0 bg-gray-300 min-h-screen">
|
<div class="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:grid md:grid-cols-3 md:gap-6 md:mx-16 md:pt-10">
|
||||||
<div class="mt-5 md:mt-0 md:col-span-2">
|
<div class="md:mt-0 md:col-span-2">
|
||||||
<div :if={{@action.arguments != []}} class="shadow-lg overflow-hidden sm:rounded-md bg-white">
|
<div :if={{@action.arguments != []}} class="shadow-lg overflow-hidden pt-2 sm:rounded-md bg-white">
|
||||||
<div class="px-4 py-5 sm:p-6">
|
<div class="px-4 sm:p-6">
|
||||||
<Form :if={{@query}} as="query" for={{@query}} change="validate" submit="save" :let={{form: form}}>
|
<Form :if={{@query}} as="query" for={{@query}} change="validate" submit="save" :let={{form: form}}>
|
||||||
{{AshAdmin.Components.Resource.Form.render_attributes(assigns, @resource, @action, form)}}
|
{{AshAdmin.Components.Resource.Form.render_attributes(assigns, @resource, @action, form)}}
|
||||||
<div class="px-4 py-3 text-right sm:px-6">
|
<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>
|
||||||
</div>
|
</div>
|
||||||
<div :if={{@action.arguments == [] || @params["args"]}} class="h-full mt-8 overflow-scroll">
|
<div :if={{@action.arguments == [] || @params["args"]}} class="h-full overflow-scroll">
|
||||||
<div class="shadow-lg overflow-hidden sm:rounded-md bg-white">
|
<div class="shadow-lg overflow-scroll sm:rounded-md bg-white">
|
||||||
<div :if={{ match?({:error, _}, @data) }}>
|
<div :if={{ match?({:error, _}, @data) }}>
|
||||||
{{ {:error, %{query: query}} = @data
|
{{ {:error, %{query: query}} = @data
|
||||||
nil }}
|
nil }}
|
||||||
|
@ -102,19 +147,36 @@ defmodule AshAdmin.Components.Resource.DataTable do
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
"""
|
"""
|
||||||
end
|
end
|
||||||
|
|
||||||
defp message(error) do
|
def handle_event("next_page", _, socket) do
|
||||||
if is_exception(error) do
|
params = %{"page" => page_link_params(socket.assigns.data, "next")}
|
||||||
Exception.message(error)
|
|
||||||
else
|
{:noreply,
|
||||||
inspect(error)
|
push_patch(socket, to: self_path(socket.assigns.url_path, socket.assigns.params, params))}
|
||||||
end
|
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
|
end
|
||||||
|
|
||||||
def handle_event("validate", %{"query" => query}, socket) do
|
def handle_event("validate", %{"query" => query}, socket) do
|
||||||
|
@ -131,74 +193,176 @@ defmodule AshAdmin.Components.Resource.DataTable do
|
||||||
)}
|
)}
|
||||||
end
|
end
|
||||||
|
|
||||||
# defp middle_page_num(num, trailing_page_nums) do
|
defp render_pagination_links(assigns, placement) do
|
||||||
# if num in trailing_page_nums || num <= 3 do
|
~H"""
|
||||||
# "..."
|
<div :if={{(offset?(@data) || keyset?(@data)) && show_pagination_links?(@data, placement)}} class="w-5/6 mx-auto">
|
||||||
# else
|
<div class="bg-white px-4 py-3 flex items-center justify-between border-t border-gray-200 sm:px-6">
|
||||||
# "...#{num}..."
|
<div class="flex-1 flex justify-between sm:hidden">
|
||||||
# end
|
<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">
|
||||||
# end
|
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
|
defp render_pagination_information(assigns, small? \\ false) do
|
||||||
# case AshPhoenix.LiveView.page_link_params(page, target) do
|
~H"""
|
||||||
# :invalid ->
|
<p class={{"text-sm text-gray-700", "sm:hidden": small?}}>
|
||||||
# nil
|
<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 ->
|
defp page_num_from_page_params(params) do
|
||||||
# [page: params]
|
cond do
|
||||||
# end
|
!params[:offset] || params[:after] || params[:before] ->
|
||||||
# end
|
1
|
||||||
|
|
||||||
# defp show_ellipses?(%Ash.Page.Offset{count: count, limit: limit}) when not is_nil(count) do
|
params[:offset] && params[:limit] ->
|
||||||
# page_nums =
|
trunc(Float.ceil(params[:offset] / params[:limit])) + 1
|
||||||
# count
|
|
||||||
# |> Kernel./(limit)
|
|
||||||
# |> Float.ceil()
|
|
||||||
# |> trunc()
|
|
||||||
|
|
||||||
# page_nums > 6
|
true ->
|
||||||
# end
|
nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
# defp show_ellipses?({:ok, data}), do: show_ellipses?(data)
|
defp show_pagination_links?({:ok, _page}, :bottom), do: true
|
||||||
# defp show_ellipses?(_), do: false
|
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)
|
defp first({:ok, %Ash.Page.Offset{offset: offset}}) do
|
||||||
# def leading_page_nums(%Ash.Page.Offset{count: nil}), do: []
|
(offset || 0) + 1
|
||||||
|
end
|
||||||
|
|
||||||
# def leading_page_nums(%Ash.Page.Offset{limit: limit, count: count}) do
|
defp first(_), do: nil
|
||||||
# page_nums =
|
|
||||||
# count
|
|
||||||
# |> Kernel./(limit)
|
|
||||||
# |> Float.ceil()
|
|
||||||
# |> trunc()
|
|
||||||
|
|
||||||
# 1..min(3, page_nums)
|
defp last({:ok, %Ash.Page.Offset{offset: offset, results: results}}) do
|
||||||
# end
|
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)
|
defp message(error) do
|
||||||
# def trailing_page_nums(%Ash.Page.Offset{count: nil}), 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
|
defp render_middle_page_num(assigns, num, trailing_page_nums) do
|
||||||
# page_nums =
|
ellipsis? = num in trailing_page_nums || num <= 3
|
||||||
# count
|
|
||||||
# |> Kernel./(limit)
|
|
||||||
# |> Float.ceil()
|
|
||||||
# |> trunc()
|
|
||||||
|
|
||||||
# if page_nums > 3 do
|
~H"""
|
||||||
# max(page_nums - 2, 0)..page_nums
|
<span
|
||||||
# else
|
: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?}}>
|
||||||
# end
|
<span :if={{ellipsis?}}>
|
||||||
# end
|
...
|
||||||
|
</span>
|
||||||
|
<span :if={{!ellipsis?}}>
|
||||||
|
{{num}}
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
"""
|
||||||
|
end
|
||||||
|
|
||||||
# def handle_event("toggle_filter", _, socket) do
|
defp show_ellipses?(%Ash.Page.Offset{count: count, limit: limit}) when not is_nil(count) do
|
||||||
# {:noreply, assign(socket, :filter_open, !socket.assigns.filter_open)}
|
page_nums =
|
||||||
# end
|
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({:ok, data}), do: data(data)
|
||||||
defp data({:error, _}), do: []
|
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(%Ash.Page.Keyset{results: results}), do: results
|
||||||
defp data(data), do: data
|
defp data(data), do: data
|
||||||
|
|
||||||
# defp offset?({:ok, data}), do: offset?(data)
|
defp offset?({:ok, data}), do: offset?(data)
|
||||||
# defp offset?(%Ash.Page.Offset{}), do: true
|
defp offset?(%Ash.Page.Offset{}), do: true
|
||||||
# defp offset?(_), do: false
|
defp offset?(_), do: false
|
||||||
|
|
||||||
# defp keyset?({:ok, data}), do: keyset?(data)
|
defp keyset?({:ok, data}), do: keyset?(data)
|
||||||
# defp keyset?(%Ash.Page.Keyset{}), do: true
|
defp keyset?(%Ash.Page.Keyset{}), do: true
|
||||||
# defp keyset?(_), do: false
|
defp keyset?(_), do: false
|
||||||
|
|
||||||
# defp offset({:ok, data}), do: offset(data)
|
defp count({:ok, %{count: count}}), do: count
|
||||||
# defp offset(%Ash.Page.Offset{offset: offset}), do: offset
|
defp count(%{count: count}), do: count
|
||||||
# defp offset(_), do: 0
|
defp count(_), do: nil
|
||||||
|
|
||||||
# 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
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -40,8 +40,8 @@ defmodule AshAdmin.Components.Resource.Form do
|
||||||
|
|
||||||
def render(assigns) do
|
def render(assigns) do
|
||||||
~H"""
|
~H"""
|
||||||
<div class="pt-10 sm:mt-0 bg-gray-300 min-h-screen">
|
<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 mx-16 mt-10">
|
<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">
|
<div class="mt-5 md:mt-0 md:col-span-2">
|
||||||
{{ render_form(assigns) }}
|
{{ render_form(assigns) }}
|
||||||
</div>
|
</div>
|
||||||
|
@ -400,7 +400,7 @@ defmodule AshAdmin.Components.Resource.Form do
|
||||||
<TextArea
|
<TextArea
|
||||||
form={{ form }}
|
form={{ form }}
|
||||||
field={{ name }}
|
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"
|
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(authorizing, :boolean, required: true)
|
||||||
prop(tenant, :string, required: true)
|
prop(tenant, :string, required: true)
|
||||||
prop(recover_filter, :any)
|
prop(recover_filter, :any)
|
||||||
prop(page_params, :any, default: [])
|
|
||||||
prop(page_num, :integer, default: 1)
|
|
||||||
prop(url_path, :string, default: "")
|
prop(url_path, :string, default: "")
|
||||||
prop(params, :map, default: %{})
|
prop(params, :map, default: %{})
|
||||||
prop(primary_key, :any, default: nil)
|
prop(primary_key, :any, default: nil)
|
||||||
|
|
|
@ -4,7 +4,7 @@ defmodule AshAdmin.Components.Resource.Table do
|
||||||
import AshAdmin.Helpers
|
import AshAdmin.Helpers
|
||||||
alias Surface.Components.LiveRedirect
|
alias Surface.Components.LiveRedirect
|
||||||
|
|
||||||
prop(attributes, :list, default: nil)
|
prop(attributes, :any, default: nil)
|
||||||
prop(data, :list, default: nil)
|
prop(data, :list, default: nil)
|
||||||
prop(resource, :any, required: true)
|
prop(resource, :any, required: true)
|
||||||
prop(actions, :boolean, default: true)
|
prop(actions, :boolean, default: true)
|
||||||
|
@ -14,17 +14,15 @@ defmodule AshAdmin.Components.Resource.Table do
|
||||||
def render(assigns) do
|
def render(assigns) do
|
||||||
~H"""
|
~H"""
|
||||||
<div>
|
<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">
|
<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) }}
|
{{ to_name(attribute.name) }}
|
||||||
</th>
|
</th>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr :for={{ record <- @data }} class="text-left border-b-2">
|
<tr :for={{ record <- @data }} class="border-b-2">
|
||||||
<td :for={{ attribute <- Ash.Resource.Info.attributes(@resource) |> Enum.filter(&(is_nil(@attributes) || &1 in @attributes)) }} class="px-4 py-3">
|
<td :for={{ attribute <- attributes(@resource, @attributes) }} class="py-3">{{ render_attribute(record, attribute) }}</td>
|
||||||
{{ render_attribute(record, attribute) }}
|
|
||||||
</td>
|
|
||||||
<td :if={{@actions && actions?(@resource)}}>
|
<td :if={{@actions && actions?(@resource)}}>
|
||||||
<div class="flex h-max justify-items-center">
|
<div class="flex h-max justify-items-center">
|
||||||
<div :if={{ AshAdmin.Resource.show_action(@resource) }}>
|
<div :if={{ AshAdmin.Resource.show_action(@resource) }}>
|
||||||
|
@ -63,6 +61,16 @@ defmodule AshAdmin.Components.Resource.Table do
|
||||||
"""
|
"""
|
||||||
end
|
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
|
defp render_attribute(record, attribute) do
|
||||||
if Ash.Type.embedded_type?(attribute.type) do
|
if Ash.Type.embedded_type?(attribute.type) do
|
||||||
"..."
|
"..."
|
||||||
|
|
|
@ -27,10 +27,6 @@ defmodule AshAdmin.Helpers do
|
||||||
end
|
end
|
||||||
|
|
||||||
def self_path(url_path, socket_params, new_params) do
|
def self_path(url_path, socket_params, new_params) do
|
||||||
IO.inspect(url_path)
|
|
||||||
IO.inspect(socket_params)
|
|
||||||
IO.inspect(new_params)
|
|
||||||
|
|
||||||
url_path <>
|
url_path <>
|
||||||
"?" <>
|
"?" <>
|
||||||
Plug.Conn.Query.encode(Map.merge(socket_params || %{}, Enum.into(new_params, %{})))
|
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
|
AshAdmin.ActorPlug.session_bool(session["actor_authorizing"]) || false
|
||||||
)
|
)
|
||||||
|> assign(:recover_filter, nil)
|
|> assign(:recover_filter, nil)
|
||||||
|> assign(:actor_paused, actor_paused)
|
|> assign(:actor_paused, actor_paused)}
|
||||||
|> assign(:page_num, 1)}
|
|
||||||
end
|
end
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
|
@ -94,8 +93,6 @@ defmodule AshAdmin.PageLive do
|
||||||
tab={{ @tab }}
|
tab={{ @tab }}
|
||||||
url_path={{ @url_path }}
|
url_path={{ @url_path }}
|
||||||
params={{ @params }}
|
params={{ @params }}
|
||||||
page_params={{ @page_params }}
|
|
||||||
page_num={{ @page_num }}
|
|
||||||
action={{ @action }}
|
action={{ @action }}
|
||||||
tenant={{ @tenant }}
|
tenant={{ @tenant }}
|
||||||
actor={{ unless @actor_paused, do: @actor }}
|
actor={{ unless @actor_paused, do: @actor }}
|
||||||
|
@ -126,31 +123,6 @@ defmodule AshAdmin.PageLive do
|
||||||
socket
|
socket
|
||||||
end
|
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 =
|
socket =
|
||||||
if params["primary_key"] do
|
if params["primary_key"] do
|
||||||
case decode_primary_key(socket.assigns.resource, 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)
|
|> Enum.map(& &1.name)
|
||||||
end
|
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
|
@impl true
|
||||||
def handle_event("toggle_authorizing", _, socket) do
|
def handle_event("toggle_authorizing", _, socket) do
|
||||||
{:noreply,
|
{:noreply,
|
||||||
|
|
|
@ -39,12 +39,20 @@ defmodule AshAdmin.Resource do
|
||||||
type: {:list, :atom},
|
type: {:list, :atom},
|
||||||
doc:
|
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`."
|
"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]
|
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
|
def name(resource) do
|
||||||
Ash.Dsl.Extension.get_opt(resource, [:admin], :name, nil, true) ||
|
Ash.Dsl.Extension.get_opt(resource, [:admin], :name, nil, true) ||
|
||||||
resource
|
resource
|
||||||
|
|
6
mix.exs
6
mix.exs
|
@ -46,8 +46,10 @@ defmodule AshAdmin.MixProject do
|
||||||
# Run "mix help deps" to learn about dependencies.
|
# Run "mix help deps" to learn about dependencies.
|
||||||
defp deps do
|
defp deps do
|
||||||
[
|
[
|
||||||
{:ash, "~> 1.36 and >= 1.36.12"},
|
# {:ash, "~> 1.36 and >= 1.36.12"},
|
||||||
{:ash_phoenix, "~> 0.4 and >= 0.4.2"},
|
{:ash, path: "../ash", override: true},
|
||||||
|
# {:ash_phoenix, "~> 0.4 and >= 0.4.2"},
|
||||||
|
{:ash_phoenix, path: "../ash_phoenix"},
|
||||||
{:surface, "~> 0.3.1"},
|
{:surface, "~> 0.3.1"},
|
||||||
{:phoenix_live_view, "~> 0.15.4"},
|
{:phoenix_live_view, "~> 0.15.4"},
|
||||||
{:phoenix_html, "~> 2.14.1 or ~> 2.15"},
|
{: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