Skip to content

Commit

Permalink
Add pagination
Browse files Browse the repository at this point in the history
  • Loading branch information
tspenov committed Jan 22, 2024
1 parent 79e32f6 commit 0fe32c9
Show file tree
Hide file tree
Showing 4 changed files with 197 additions and 19 deletions.
79 changes: 79 additions & 0 deletions lib/sanbase_web/components/table.ex
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,15 @@ defmodule SanbaseWeb.TableComponent do
<% end %>
</tbody>
</table>
<SanbaseWeb.PaginationComponent.pagination
resource={@resource}
rows_count={@rows_count}
page_size={@page_size}
current_page={@current_page}
action={@action}
search_text={@search_text}
/>
</div>
"""
end
Expand Down Expand Up @@ -71,10 +80,12 @@ defmodule SanbaseWeb.TableComponent do
</span>
<.form
:let={f}
method="get"
for={%{}}
as={:search}
action={Routes.generic_path(SanbaseWeb.Endpoint, :search, resource: @resource)}
>
<%= hidden_input(f, :resource, value: @resource) %>
<%= text_input(f, :generic_search,
value: @search_value,
class:
Expand All @@ -100,3 +111,71 @@ defmodule SanbaseWeb.TableComponent do
"""
end
end

defmodule SanbaseWeb.PaginationComponent do
use Phoenix.Component

alias SanbaseWeb.Router.Helpers, as: Routes

def pagination(assigns) do
~H"""
<div class="flex justify-between items-center p-4">
<.pagination_buttons
resource={@resource}
rows_count={@rows_count}
page_size={@page_size}
current_page={@current_page}
action={@action}
search_text={@search_text}
/>
<span class="text-sm text-gray-700">
Showing <%= @current_page * @page_size + 1 %> to <%= Enum.min([
(@current_page + 1) * @page_size,
@rows_count
]) %> of <%= @rows_count %> entries
</span>
</div>
"""
end

def pagination_buttons(assigns) do
~H"""
<div class="inline-flex">
<%= unless @current_page == 0 do %>
<.link
href={pagination_path(@resource, @action, @search_text, @current_page - 1)}
class="px-4 py-2 mx-1 bg-gray-200 rounded hover:bg-gray-300"
>
Previous
</.link>
<% end %>
<%= unless @current_page >= div(@rows_count - 1, @page_size) do %>
<.link
href={pagination_path(@resource, @action, @search_text, @current_page + 1)}
class="px-4 py-2 mx-1 bg-gray-200 rounded hover:bg-gray-300"
>
Next
</.link>
<% end %>
</div>
"""
end

defp pagination_path(resource, action, search_text, page_number) do
case action do
:index ->
Routes.generic_path(SanbaseWeb.Endpoint, action, %{resource: resource, page: page_number})

:search ->
Routes.generic_path(SanbaseWeb.Endpoint, action, %{
"resource" => resource,
"page" => page_number,
"search" => %{
"generic_search" => search_text,
"resource" => resource,
"page" => page_number
}
})
end
end
end
120 changes: 104 additions & 16 deletions lib/sanbase_web/controllers/generic_controller.ex
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,32 @@ defmodule SanbaseWeb.GenericController do

@resource_module_map SanbaseWeb.GenericAdmin.resource_module_map()

def index(conn, %{"resource" => resource}) do
render(conn, "index.html", table: resource_to_table_params(resource))
def index(conn, %{"resource" => resource} = params) do
page = params["page"] || 0
page_size = params["page_size"] || 10

render(conn, "index.html",
table: resource_to_table_params(resource, %{page: page, page_size: page_size})
)
end

def index(conn, _) do
render(conn, "error.html")
end

def search(conn, %{"resource" => resource, "search" => %{"generic_search" => search_text}}) do
def search(
conn,
%{"search" => %{"generic_search" => search_text, "resource" => resource}} = params
) do
module = module_from_resource(resource)
preloads = @resource_module_map[resource][:preloads] || []
page = to_integer(params["page"] || 0)
page_size = to_integer(params["page_size"] || 10)

rows =
{total_rows, paginated_rows} =
case parse_field_value(search_text) do
{:ok, field, value} ->
search_by_field_value(module, field, value, preloads)
search_by_field_value(module, field, value, preloads, page, page_size)

:error ->
case Integer.parse(search_text) do
Expand All @@ -32,7 +42,16 @@ defmodule SanbaseWeb.GenericController do
end
end

render(conn, "index.html", table: resource_to_table_params(resource, rows))
render(conn, "index.html",
table:
resource_to_table_params(resource, %{
total_rows: total_rows,
rows: paginated_rows,
page: page,
page_size: page_size,
search_text: search_text
})
)
end

def show(conn, %{"resource" => resource, "id" => id}) do
Expand Down Expand Up @@ -95,11 +114,14 @@ defmodule SanbaseWeb.GenericController do

def module_from_resource(resource), do: @resource_module_map[resource][:module]

def resource_to_table_params(resource, rows \\ nil) do
def resource_to_table_params(resource, params) do
name = String.capitalize(resource)
module = @resource_module_map[resource][:module]
preloads = @resource_module_map[resource][:preloads] || []
funcs = @resource_module_map[resource][:funcs] || %{}
rows = params[:rows]
page = to_integer(params[:page])
page_size = to_integer(params[:page_size])

index_fields =
case @resource_module_map[resource][:index_fields] do
Expand All @@ -108,13 +130,48 @@ defmodule SanbaseWeb.GenericController do
fields when is_list(fields) -> fields
end

total_count =
case rows do
nil -> Repo.aggregate(module, :count, :id)
_ -> params[:total_rows]
end

action =
case rows do
nil -> :index
_ -> :search
end

offset = page * page_size

fetched_rows =
case rows do
nil ->
Repo.all(
from(m in module,
order_by: [desc: m.id],
preload: ^preloads,
limit: ^page_size,
offset: ^offset
)
)

_ ->
rows
end

%{
resource: resource,
resource_name: name,
rows: rows || all(module, preloads),
rows: fetched_rows,
rows_count: total_count,
fields: index_fields,
funcs: funcs,
actions: @resource_module_map[resource][:actions]
actions: @resource_module_map[resource][:actions],
current_page: page,
page_size: page_size,
action: action,
search_text: params[:search_text] || ""
}
end

Expand Down Expand Up @@ -152,26 +209,57 @@ defmodule SanbaseWeb.GenericController do
end

def parse_field_value(str) do
case Regex.run(~r/(\w+)\s*=\s*(\w+)/, str) do
case Regex.run(~r/(\S+)\s*=\s*(\S+)/, str) do
[_, field, value] -> {:ok, field, value}
_ -> :error
end
end

def search_by_field_value(module, field, value, preloads) do
def search_by_field_value(module, field, value, preloads, page, page_size) do
case Integer.parse(value) do
{id, ""} ->
search_by_id(module, id, preloads)

_ ->
value = String.trim(value)
search_text = "%" <> value <> "%"

value = "%" <> value <> "%"
field = String.to_existing_atom(field)

from(u in module,
where: like(field(u, ^field), ^search_text)
)
|> Repo.all()
field_type = module.__schema__(:type, field)

query =
if field_type == :string do
from(u in module,
where: like(field(u, ^field), ^value),
preload: ^preloads,
order_by: [desc: u.id]
)
else
from(u in module,
where: field(u, ^field) == ^value,
preload: ^preloads,
order_by: [desc: u.id]
)
end

total_rows = Repo.aggregate(query, :count, :id)

paginated_rows =
query
|> limit(^page_size)
|> offset(^(page_size * page))
|> Repo.all()

{total_rows, paginated_rows}
end
end

def to_integer(value) do
case value do
nil -> nil
value when is_integer(value) -> value
value when is_binary(value) -> String.to_integer(value)
end
end
end
2 changes: 1 addition & 1 deletion lib/sanbase_web/router.ex
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ defmodule SanbaseWeb.Router do
resources("/sheets_templates", SheetsTemplateController)
resources("/webinars", WebinarController)
resources("/custom_plans", CustomPlanController)
post("/generic/search", GenericController, :search)
get("/generic/search", GenericController, :search)
get("/generic/show_action", GenericController, :show_action)
resources("/generic", GenericController)
end
Expand Down
15 changes: 13 additions & 2 deletions lib/sanbase_web/templates/generic_html/index.html.heex
Original file line number Diff line number Diff line change
@@ -1,2 +1,13 @@
<SanbaseWeb.TableComponent.table resource={@table.resource} model={@table.resource_name} rows={@table.rows}
fields={@table.fields} funcs={@table.funcs} actions={@table.actions} />
<SanbaseWeb.TableComponent.table
resource={@table.resource}
action={@table.action}
model={@table.resource_name}
rows={@table.rows}
rows_count={@table.rows_count}
page_size={@table.page_size}
current_page={@table.current_page}
fields={@table.fields}
funcs={@table.funcs}
actions={@table.actions}
search_text={@table.search_text}
/>

0 comments on commit 0fe32c9

Please sign in to comment.