-
Notifications
You must be signed in to change notification settings - Fork 72
Paginate superuser users/projects lists with server-side search #4474
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 9 commits
17d082f
0679a9d
4c85c38
fb9006d
4681703
7990c77
1ee5649
fcf4b25
67ca8db
5ab22f0
a560814
d84db8b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -114,6 +114,31 @@ defmodule Lightning.Accounts do | |||||
| Repo.all(User) | ||||||
| end | ||||||
|
|
||||||
| @admin_user_default_sort "email" | ||||||
| @admin_user_allowed_sorts ~w(first_name last_name email role enabled support_user scheduled_deletion) | ||||||
| @admin_user_default_page_size 10 | ||||||
| @admin_user_max_page_size 100 | ||||||
|
|
||||||
| @doc """ | ||||||
| Returns a paginated list of users for the superuser admin table with | ||||||
| server-side filtering and sorting. | ||||||
| """ | ||||||
| @spec list_users_for_admin(map()) :: Scrivener.Page.t() | ||||||
| def list_users_for_admin(params \\ %{}) do | ||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
The context function should take the typed params instead of building it inside the body. This way we establish a "contract" with the callers. We should also rename the function, to |
||||||
| %{ | ||||||
| filter: filter, | ||||||
| sort: sort, | ||||||
| dir: dir, | ||||||
| page: page, | ||||||
| page_size: page_size | ||||||
| } = normalize_admin_user_params(params) | ||||||
|
|
||||||
| User | ||||||
| |> filter_admin_users(filter) | ||||||
| |> order_admin_users(sort, dir) | ||||||
| |> Repo.paginate(page: page, page_size: page_size) | ||||||
| end | ||||||
|
|
||||||
| @doc """ | ||||||
| Returns the list of users with the given emails | ||||||
| """ | ||||||
|
|
@@ -160,6 +185,109 @@ defmodule Lightning.Accounts do | |||||
| if User.valid_password?(user, password), do: user | ||||||
| end | ||||||
|
|
||||||
| defp normalize_admin_user_params(params) do | ||||||
| params = stringify_param_keys(params) | ||||||
|
|
||||||
| sort = normalize_admin_sort(Map.get(params, "sort")) | ||||||
| dir = normalize_admin_dir(Map.get(params, "dir")) | ||||||
| page = parse_positive_int(Map.get(params, "page"), 1) | ||||||
|
|
||||||
| page_size = | ||||||
| Map.get(params, "page_size") | ||||||
| |> parse_positive_int(@admin_user_default_page_size) | ||||||
| |> min(@admin_user_max_page_size) | ||||||
|
|
||||||
| %{ | ||||||
| filter: normalize_admin_filter(Map.get(params, "filter")), | ||||||
| sort: sort, | ||||||
| dir: dir, | ||||||
| page: page, | ||||||
| page_size: page_size | ||||||
| } | ||||||
| end | ||||||
|
|
||||||
| defp filter_admin_users(query, ""), do: query | ||||||
|
|
||||||
| defp filter_admin_users(query, filter) do | ||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
| search = "%#{filter}%" | ||||||
|
|
||||||
| where( | ||||||
| query, | ||||||
| [u], | ||||||
| ilike(u.first_name, ^search) or | ||||||
| ilike(u.last_name, ^search) or | ||||||
| ilike(fragment("?::text", u.email), ^search) or | ||||||
| ilike(fragment("?::text", u.role), ^search) | ||||||
| ) | ||||||
| end | ||||||
|
|
||||||
| defp order_admin_users(query, "enabled", "asc"), | ||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think we need to sort by a boolean. This should be toggle somewhere on the form. Let's skip it for now |
||||||
| do: order_by(query, [u], desc: u.disabled) | ||||||
|
|
||||||
| defp order_admin_users(query, "enabled", "desc"), | ||||||
| do: order_by(query, [u], asc: u.disabled) | ||||||
|
|
||||||
| defp order_admin_users(query, "scheduled_deletion", "asc"), | ||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
| do: order_by(query, [u], asc_nulls_last: u.scheduled_deletion) | ||||||
|
|
||||||
| defp order_admin_users(query, "scheduled_deletion", "desc"), | ||||||
| do: order_by(query, [u], desc_nulls_first: u.scheduled_deletion) | ||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Given |
||||||
|
|
||||||
| defp order_admin_users(query, "role", dir) do | ||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I also don't see the need to sort by |
||||||
| direction = dir_to_atom(dir) | ||||||
| order_by(query, [u], [{^direction, fragment("?::text", u.role)}]) | ||||||
| end | ||||||
|
|
||||||
| defp order_admin_users(query, sort, dir) do | ||||||
| direction = dir_to_atom(dir) | ||||||
| sort_field = String.to_existing_atom(sort) | ||||||
|
|
||||||
| order_by(query, [u], [{^direction, field(u, ^sort_field)}]) | ||||||
| end | ||||||
|
|
||||||
| defp normalize_admin_sort(sort) when is_binary(sort) do | ||||||
| if sort in @admin_user_allowed_sorts, | ||||||
| do: sort, | ||||||
| else: @admin_user_default_sort | ||||||
| end | ||||||
|
|
||||||
| defp normalize_admin_sort(sort) when is_atom(sort) do | ||||||
| sort | ||||||
| |> Atom.to_string() | ||||||
| |> normalize_admin_sort() | ||||||
| end | ||||||
|
|
||||||
| defp normalize_admin_sort(_), do: @admin_user_default_sort | ||||||
|
|
||||||
| defp normalize_admin_dir(dir) when dir in ["asc", :asc], do: "asc" | ||||||
| defp normalize_admin_dir(dir) when dir in ["desc", :desc], do: "desc" | ||||||
| defp normalize_admin_dir(_), do: "asc" | ||||||
|
|
||||||
| defp normalize_admin_filter(nil), do: "" | ||||||
|
|
||||||
| defp normalize_admin_filter(filter) do | ||||||
| filter | ||||||
| |> to_string() | ||||||
| |> String.trim() | ||||||
| end | ||||||
|
|
||||||
| defp stringify_param_keys(params) when is_map(params) do | ||||||
| Map.new(params, fn {key, value} -> {to_string(key), value} end) | ||||||
| end | ||||||
|
|
||||||
| defp parse_positive_int(value, _default) when is_integer(value) and value > 0, | ||||||
| do: value | ||||||
|
|
||||||
| defp parse_positive_int(value, default) do | ||||||
| case Integer.parse(to_string(value || "")) do | ||||||
| {int, ""} when int > 0 -> int | ||||||
| _ -> default | ||||||
| end | ||||||
| end | ||||||
|
|
||||||
| defp dir_to_atom("asc"), do: :asc | ||||||
| defp dir_to_atom("desc"), do: :desc | ||||||
|
|
||||||
| @doc """ | ||||||
| Gets a single user. | ||||||
|
|
||||||
|
|
||||||
Uh oh!
There was an error while loading. Please reload this page.