diff --git a/lib/algora/bounties/bounties.ex b/lib/algora/bounties/bounties.ex index 6e3054570..db16169d5 100644 --- a/lib/algora/bounties/bounties.ex +++ b/lib/algora/bounties/bounties.ex @@ -30,11 +30,13 @@ defmodule Algora.Bounties do @type criterion :: {:id, String.t()} | {:limit, non_neg_integer() | :infinity} + | {:offset, non_neg_integer()} | {:ticket_id, String.t()} | {:owner_id, String.t()} | {:owner_handles, [String.t()]} | {:status, :open | :paid} | {:tech_stack, [String.t()]} + | {:order, :date | :amount | :technology} | {:before, %{inserted_at: DateTime.t(), id: String.t()}} | {:amount_gt, Money.t()} | {:current_user, User.t()} @@ -1199,6 +1201,9 @@ defmodule Algora.Bounties do {:limit, limit}, query -> from([b] in query, limit: ^limit) + {:offset, offset}, query -> + from([b] in query, offset: ^offset) + {:ticket_id, ticket_id}, query -> from([b] in query, where: b.ticket_id == ^ticket_id) @@ -1340,7 +1345,7 @@ defmodule Algora.Bounties do base_query |> list_bounties_query(criteria) # TODO: sort by b.paid_at if criteria[:status] == :paid - |> order_by([b], desc: b.inserted_at, desc: b.id) + |> order_bounties(criteria[:order]) |> select([b, o: o, t: t, ro: ro, r: r], %{ id: b.id, inserted_at: b.inserted_at, @@ -1378,6 +1383,22 @@ defmodule Algora.Bounties do |> Repo.all() end + defp order_bounties(query, :amount) do + order_by(query, [b], desc: fragment("amount(?)", b.amount), desc: b.inserted_at, desc: b.id) + end + + defp order_bounties(query, :technology) do + order_by(query, [b, r: r], + asc: fragment("COALESCE(LOWER((?)[1]::text), '~')", r.tech_stack), + desc: b.inserted_at, + desc: b.id + ) + end + + defp order_bounties(query, _order) do + order_by(query, [b], desc: b.inserted_at, desc: b.id) + end + def list_tech(criteria \\ []) do base_query() |> list_bounties_query(Keyword.put(criteria, :limit, :infinity)) diff --git a/lib/algora_web/live/bounties_live.ex b/lib/algora_web/live/bounties_live.ex index 05c7dfe5c..b7d9b13e7 100644 --- a/lib/algora_web/live/bounties_live.ex +++ b/lib/algora_web/live/bounties_live.ex @@ -10,102 +10,51 @@ defmodule AlgoraWeb.BountiesLive do require Logger + @default_sort "date" + @sort_options [{"date", "Date"}, {"price", "Price"}, {"technology", "Technology"}] + @impl true - def handle_params(%{"tech" => tech}, _uri, socket) when is_binary(tech) do - selected_techs = tech |> String.split(",") |> Enum.reject(&(&1 == "")) |> Enum.map(&String.downcase/1) - valid_techs = Enum.map(socket.assigns.techs, fn {tech, _} -> String.downcase(tech) end) - # Only keep valid techs that exist in the available tech list - selected_techs = Enum.filter(selected_techs, &(&1 in valid_techs)) + def handle_params(params, _uri, socket) do + selected_techs = parse_selected_techs(params["tech"], socket.assigns.techs) + selected_sort = parse_sort(params["sort"]) query_opts = - if selected_techs == [] do - Keyword.delete(socket.assigns.query_opts, :tech_stack) - else - Keyword.put(socket.assigns.query_opts, :tech_stack, selected_techs) - end + socket.assigns.query_opts + |> Keyword.put(:order, sort_order(selected_sort)) + |> put_selected_techs(selected_techs) {:noreply, socket - |> assign(:page_title, "#{Enum.map_join(selected_techs, "/", &String.capitalize/1)} Bounties") + |> assign(:page_title, page_title(selected_techs)) |> assign(:selected_techs, selected_techs) + |> assign(:selected_sort, selected_sort) |> assign(:query_opts, query_opts) |> assign_bounties()} end - def handle_params(_params, _uri, socket) do - {:noreply, - socket - |> assign(:page_title, "Bounties") - |> assign(:selected_techs, []) - |> assign(:query_opts, Keyword.delete(socket.assigns.query_opts, :tech_stack)) - |> assign_bounties()} - end - @impl true - def mount(%{"tech" => tech}, _session, socket) when is_binary(tech) do + def mount(params, _session, socket) do if connected?(socket) do Bounties.subscribe() end - # Parse selected techs from URL params and ensure lowercase - selected_techs = - tech - |> String.split(",") - |> Enum.reject(&(&1 == "")) - |> Enum.map(&String.downcase/1) - - query_opts = - [ - status: :open, - limit: page_size(), - current_user: socket.assigns[:current_user] - ] ++ - if socket.assigns[:current_user] do - [amount_gt: Money.new(:USD, 100)] - else - [amount_gt: Money.new(:USD, 500)] - end - + query_opts = base_query_opts(socket) techs = Bounties.list_tech(query_opts) - # Only keep valid techs that exist in the available tech list (case insensitive) - valid_techs = Enum.map(techs, fn {tech, _} -> String.downcase(tech) end) - selected_techs = Enum.filter(selected_techs, &(&1 in valid_techs)) - - query_opts = if selected_techs == [], do: query_opts, else: Keyword.put(query_opts, :tech_stack, selected_techs) - - {:ok, - socket - |> assign(:techs, techs) - |> assign(:selected_techs, selected_techs) - |> assign(:query_opts, query_opts) - |> assign_bounties() - |> assign_events()} - end - - def mount(_params, _session, socket) do - if connected?(socket) do - Bounties.subscribe() - end + selected_techs = parse_selected_techs(params["tech"], techs) + selected_sort = parse_sort(params["sort"]) query_opts = - [ - status: :open, - limit: page_size(), - current_user: socket.assigns[:current_user] - ] ++ - if socket.assigns[:current_user] do - [amount_gt: Money.new(:USD, 100)] - else - [amount_gt: Money.new(:USD, 500)] - end - - techs = Bounties.list_tech(query_opts) + query_opts + |> Keyword.put(:order, sort_order(selected_sort)) + |> put_selected_techs(selected_techs) {:ok, socket |> assign(:techs, techs) - |> assign(:selected_techs, []) + |> assign(:sort_options, @sort_options) + |> assign(:selected_techs, selected_techs) + |> assign(:selected_sort, selected_sort) |> assign(:query_opts, query_opts) |> assign_bounties() |> assign_events()} @@ -116,21 +65,42 @@ defmodule AlgoraWeb.BountiesLive do ~H"""