diff --git a/lib/bike_brigade/riders/rider_search.ex b/lib/bike_brigade/riders/rider_search.ex index 14c941a9..515464c1 100644 --- a/lib/bike_brigade/riders/rider_search.ex +++ b/lib/bike_brigade/riders/rider_search.ex @@ -357,4 +357,31 @@ defmodule BikeBrigade.Riders.RiderSearch do query |> where(as(:latest_campaign).delivery_start > ago(1, ^period)) end + + defp apply_filter(%Filter{type: :signed_up, search: period}, query, _filters) + when period in ~w(today yesterday) do + today = Date.utc_today() + + {start_date, end_date} = + case period do + "yesterday" -> {Date.add(today, -1), today} + _today -> {today, Date.add(today, 1)} + end + + start_dt = DateTime.new!(start_date, ~T[00:00:00]) + end_dt = DateTime.new!(end_date, ~T[00:00:00]) + + query + |> where(as(:rider).signed_up_on >= ^start_dt and as(:rider).signed_up_on < ^end_dt) + end + + defp apply_filter(%Filter{type: :signed_up, search: period}, query, _filters) + when period in ~w(week month year) do + query + |> where(as(:rider).signed_up_on > ago(1, ^period)) + end + + defp apply_filter(%Filter{type: :signed_up}, query, _filters) do + query + end end diff --git a/lib/bike_brigade_web/live/rider_live/index.ex b/lib/bike_brigade_web/live/rider_live/index.ex index e844c480..5d2663d5 100644 --- a/lib/bike_brigade_web/live/rider_live/index.ex +++ b/lib/bike_brigade_web/live/rider_live/index.ex @@ -26,20 +26,29 @@ defmodule BikeBrigadeWeb.RiderLive.Index do |> Enum.map(&%Filter{type: :active, search: &1}) @capacities ~w(large medium small) |> Enum.map(&%Filter{type: :capacity, search: &1}) + @signed_ups ~w(today yesterday week month year) + |> Enum.map(&%Filter{type: :signed_up, search: &1}) - defstruct name: nil, phone: nil, tags: [], programs: [], active: [], capacity: [] + defstruct name: nil, + phone: nil, + tags: [], + programs: [], + active: [], + capacity: [], + signed_up: [] @type t :: %__MODULE__{ name: Filter.t() | nil, tags: list(Filter.t()), programs: list(Filter.t()), active: list(Filter.t()), - capacity: list(Filter.t()) + capacity: list(Filter.t()), + signed_up: list(Filter.t()) } @spec suggest(t(), String.t()) :: t() def suggest(suggestions, "") do - %{suggestions | name: nil, tags: [], programs: [], active: [], capacity: []} + %{suggestions | name: nil, tags: [], programs: [], active: [], capacity: [], signed_up: []} end def suggest(suggestions, search) do @@ -72,6 +81,13 @@ defmodule BikeBrigadeWeb.RiderLive.Index do %__MODULE__{capacity: capacity} + ["signed_up", signed_up] -> + signed_ups = + @signed_ups + |> Enum.filter(&String.starts_with?(&1.search, signed_up)) + + %__MODULE__{signed_up: signed_ups} + [search] -> tags = if String.length(search) < 3 do @@ -105,7 +121,8 @@ defmodule BikeBrigadeWeb.RiderLive.Index do tags: tags, programs: programs, active: @actives, - capacity: @capacities + capacity: @capacities, + signed_up: @signed_ups } [_, _] -> @@ -498,6 +515,14 @@ defmodule BikeBrigadeWeb.RiderLive.Index do <% end %> <% end %> + <%= if @suggestions.signed_up != [] do %> +

+ Signed Up +

+
+ <.suggestion filter={period} /> +
+ <% end %>
<%= if @suggestions.capacity != [] do %> @@ -631,6 +656,7 @@ defmodule BikeBrigadeWeb.RiderLive.Index do :active -> "text-amber-900 bg-amber-100" :capacity -> "text-rose-900 bg-rose-100" :phone -> "text-cyan-900 bg-cyan-100" + :signed_up -> "text-blue-900 bg-blue-100" end end diff --git a/lib/bike_brigade_web/live/rider_live/index.html.heex b/lib/bike_brigade_web/live/rider_live/index.html.heex index f19862a4..a5e538eb 100644 --- a/lib/bike_brigade_web/live/rider_live/index.html.heex +++ b/lib/bike_brigade_web/live/rider_live/index.html.heex @@ -10,7 +10,7 @@ value={display_search(@search)} autocomplete="off" class="w-full placeholder-gray-400 border-transparent appearance-none focus:border-transparent outline-transparent ring-transparent focus:ring-0" - placeholder="Name, tag, capacity, last active" + placeholder="Name, tag, capacity, last active, signed up" tabindex="1" /> <%= if @rider_search.filters != [] do %> diff --git a/test/bike_brigade/riders/rider_search_test.exs b/test/bike_brigade/riders/rider_search_test.exs index 7bb0e2cb..9086f59a 100644 --- a/test/bike_brigade/riders/rider_search_test.exs +++ b/test/bike_brigade/riders/rider_search_test.exs @@ -175,7 +175,103 @@ defmodule BikeBrigade.Riders.RiderSearchTest do link_rider_to_campaign(rider_id, campaign.id) end + describe "signed_up filter" do + test "today - includes rider who signed up today" do + today_start = DateTime.new!(Date.utc_today(), ~T[00:00:00]) + rider = fixture(:rider, %{signed_up_on: DateTime.add(today_start, 1, :hour)}) + + {_rs, results} = + RiderSearch.new(filters: [%Filter{type: :signed_up, search: "today"}]) + |> RiderSearch.fetch() + + assert rider_in_results?(results, rider.id) + end + + test "today - excludes rider who signed up yesterday" do + yesterday = Date.add(Date.utc_today(), -1) + yesterday_time = DateTime.new!(yesterday, ~T[12:00:00]) + rider = fixture(:rider, %{signed_up_on: yesterday_time}) + + {_rs, results} = + RiderSearch.new(filters: [%Filter{type: :signed_up, search: "today"}]) + |> RiderSearch.fetch() + + refute rider_in_results?(results, rider.id) + end + + test "yesterday - includes rider who signed up yesterday" do + yesterday = Date.add(Date.utc_today(), -1) + yesterday_time = DateTime.new!(yesterday, ~T[12:00:00]) + rider = fixture(:rider, %{signed_up_on: yesterday_time}) + + {_rs, results} = + RiderSearch.new(filters: [%Filter{type: :signed_up, search: "yesterday"}]) + |> RiderSearch.fetch() + + assert rider_in_results?(results, rider.id) + end + + test "yesterday - excludes rider who signed up today" do + today_start = DateTime.new!(Date.utc_today(), ~T[00:00:00]) + rider = fixture(:rider, %{signed_up_on: DateTime.add(today_start, 1, :hour)}) + + {_rs, results} = + RiderSearch.new(filters: [%Filter{type: :signed_up, search: "yesterday"}]) + |> RiderSearch.fetch() + + refute rider_in_results?(results, rider.id) + end + + test "week - includes rider who signed up within the past week" do + rider = fixture(:rider, %{signed_up_on: signed_up_ago(5, :day)}) + + {_rs, results} = + RiderSearch.new(filters: [%Filter{type: :signed_up, search: "week"}]) + |> RiderSearch.fetch() + + assert rider_in_results?(results, rider.id) + end + + test "week - excludes rider who signed up more than a week ago" do + rider = fixture(:rider, %{signed_up_on: signed_up_ago(8, :day)}) + + {_rs, results} = + RiderSearch.new(filters: [%Filter{type: :signed_up, search: "week"}]) + |> RiderSearch.fetch() + + refute rider_in_results?(results, rider.id) + end + + test "month - includes rider who signed up within the past month" do + rider = fixture(:rider, %{signed_up_on: signed_up_ago(14, :day)}) + + {_rs, results} = + RiderSearch.new(filters: [%Filter{type: :signed_up, search: "month"}]) + |> RiderSearch.fetch() + + assert rider_in_results?(results, rider.id) + end + + test "year - includes rider who signed up within the past year" do + rider = fixture(:rider, %{signed_up_on: signed_up_ago(180, :day)}) + + {_rs, results} = + RiderSearch.new(filters: [%Filter{type: :signed_up, search: "year"}]) + |> RiderSearch.fetch() + + assert rider_in_results?(results, rider.id) + end + end + defp rider_in_results?(results, id) do Enum.find(results.page, &(&1.id == id)) end + + # Helper to create a DateTime a specified duration in the past + # @param amount - number of time units (positive, will be negated) + # @param unit - time unit (:day, :hour, :second, etc.) + # @return DateTime in UTC + defp signed_up_ago(amount, unit) do + DateTime.utc_now() |> DateTime.add(-amount, unit) + end end