diff --git a/lib/ascend/ecto_helper.ex b/lib/ascend/ecto_helper.ex new file mode 100644 index 0000000..4bff3e1 --- /dev/null +++ b/lib/ascend/ecto_helper.ex @@ -0,0 +1,5 @@ +defmodule Ascend.EctoHelper do + def enum(values) do + {:parameterized, Ecto.Enum, Ecto.Enum.init(values: values)} + end +end diff --git a/lib/ascend/hills.ex b/lib/ascend/hills.ex index 64552a7..97acbc6 100644 --- a/lib/ascend/hills.ex +++ b/lib/ascend/hills.ex @@ -16,8 +16,20 @@ defmodule Ascend.Hills do [%Hill{}, ...] """ - def list_hills do - Repo.all(Hill) + def list_hills(opts) do + from(h in Hill) + |> sort(opts) + |> Repo.all() + end + + defp sort(query, %{sort_by: sort_by, sort_dir: sort_dir}) + when sort_by in [:dobih_id, :name, :metres, :feet] and + sort_dir in [:asc, :desc] do + order_by(query, {^sort_dir, ^sort_by}) + end + + defp sort(query, %{}) do + order_by(query, {:asc, :metres}) end @doc """ diff --git a/lib/ascend_web/forms/sorting_form.ex b/lib/ascend_web/forms/sorting_form.ex new file mode 100644 index 0000000..1218310 --- /dev/null +++ b/lib/ascend_web/forms/sorting_form.ex @@ -0,0 +1,24 @@ +defmodule AscendWeb.Forms.SortingForm do + import Ecto.Changeset + alias Ascend.EctoHelper + + @sortable_fields [:dobih_id, :name, :metres, :feet] + + @fields %{ + sort_by: EctoHelper.enum(@sortable_fields), + sort_dir: EctoHelper.enum([:asc, :desc]) + } + + @default_values %{ + sort_by: :metres, + sort_dir: :desc + } + + def parse(params) do + {@default_values, @fields} + |> cast(params, Map.keys(@fields)) + |> apply_action(:insert) + end + + def default_values(), do: @default_values +end diff --git a/lib/ascend_web/live/hill_live/index.ex b/lib/ascend_web/live/hill_live/index.ex index d2172ff..5eec075 100644 --- a/lib/ascend_web/live/hill_live/index.ex +++ b/lib/ascend_web/live/hill_live/index.ex @@ -2,37 +2,45 @@ defmodule AscendWeb.HillLive.Index do use AscendWeb, :live_view alias Ascend.Hills - alias Ascend.Hills.Hill + alias AscendWeb.Forms.SortingForm @impl true - def mount(_params, _session, socket) do - {:ok, assign(socket, :hills, list_hills())} - end + def mount(_params, _session, socket), do: {:ok, socket} @impl true def handle_params(params, _url, socket) do - {:noreply, apply_action(socket, socket.assigns.live_action, params)} + socket = + socket + |> assign(:page_title, "Listing Hills") + |> parse_params(params) + |> assign_hills() + + {:noreply, socket} end - defp apply_action(socket, :edit, %{"id" => id}) do - socket - |> assign(:page_title, "Edit Hill") - |> assign(:hill, Hills.get_hill!(id)) + @impl true + def handle_info({:update, opts}, socket) do + path = Routes.hill_index_path(socket, :index, opts) + {:noreply, push_patch(socket, to: path, replace: true)} end - defp apply_action(socket, :new, _params) do - socket - |> assign(:page_title, "New Hill") - |> assign(:hill, %Hill{}) + defp parse_params(socket, params) do + with {:ok, sorting_opts} <- SortingForm.parse(params) do + assign_sorting(socket, sorting_opts) + else + _error -> + assign_sorting(socket) + end end - defp apply_action(socket, :index, _params) do - socket - |> assign(:page_title, "Listing Hills") - |> assign(:hill, nil) + defp assign_sorting(socket, overrides \\ %{}) do + opts = Map.merge(SortingForm.default_values(), overrides) + assign(socket, :sorting, opts) end - defp list_hills do - Hills.list_hills() + defp assign_hills(socket) do + %{sorting: sorting} = socket.assigns + + assign(socket, :hills, Hills.list_hills(sorting)) end end diff --git a/lib/ascend_web/live/hill_live/index.html.heex b/lib/ascend_web/live/hill_live/index.html.heex index a2bc1f6..9c7a216 100644 --- a/lib/ascend_web/live/hill_live/index.html.heex +++ b/lib/ascend_web/live/hill_live/index.html.heex @@ -3,10 +3,25 @@ - + - - + + diff --git a/lib/ascend_web/live/sorting_component.ex b/lib/ascend_web/live/sorting_component.ex new file mode 100644 index 0000000..3b91fe6 --- /dev/null +++ b/lib/ascend_web/live/sorting_component.ex @@ -0,0 +1,26 @@ +defmodule AscendWeb.Live.SortingComponent do + use AscendWeb, :live_component + + def render(assigns) do + ~H""" +
+ <%= @key %> <%= chevron(@sorting, @key) %> +
+ """ + end + + def handle_event("sort", _params, socket) do + %{sorting: %{sort_dir: sort_dir}, key: key} = socket.assigns + sort_dir = if sort_dir == :asc, do: :desc, else: :asc + opts = %{sort_by: key, sort_dir: sort_dir} + + send(self(), {:update, opts}) + {:noreply, assign(socket, :sorting, opts)} + end + + def chevron(%{sort_by: sort_by, sort_dir: sort_dir}, key) when sort_by == key do + if sort_dir == :asc, do: "▼", else: "▲" + end + + def chevron(_opts, _key), do: "" +end diff --git a/lib/ascend_web/router.ex b/lib/ascend_web/router.ex index 8de2bdc..07ab2d0 100644 --- a/lib/ascend_web/router.ex +++ b/lib/ascend_web/router.ex @@ -17,9 +17,7 @@ defmodule AscendWeb.Router do scope "/", AscendWeb do pipe_through :browser - # get "/", PageController, :index live "/", HillLive.Index, :index - live "/hills", HillLive.Index, :index live "/hills/:id", HillLive.Show, :show end diff --git a/test/ascend/hills_test.exs b/test/ascend/hills_test.exs index 2daaeec..4de4c1b 100644 --- a/test/ascend/hills_test.exs +++ b/test/ascend/hills_test.exs @@ -6,7 +6,7 @@ defmodule Ascend.HillsTest do describe "hills" do alias Ascend.Hills.Hill - import Ascend.HillsFixtures + import Ascend.Factory @invalid_attrs %{ area: nil, @@ -22,13 +22,22 @@ defmodule Ascend.HillsTest do wainwright_outlying_fell: nil } - test "list_hills/0 returns all hills" do - hill = hill_fixture() - assert Hills.list_hills() == [hill] + test "list_hills/1 returns all hills" do + hill1 = insert(:hill, metres: 1000, feet: 3280.84) + hill2 = insert(:hill, metres: 500, feet: 1640.42) + assert Hills.list_hills(%{}) == [hill2, hill1] + end + + test "list hills/1 with name sort" do + hill1 = insert(:hill, name: "Z Hill") + hill2 = insert(:hill, name: "A Hill") + + sort = %{sort_by: :name, sort_dir: :asc} + assert Hills.list_hills(sort) == [hill2, hill1] end test "get_hill!/1 returns the hill with given id" do - hill = hill_fixture() + hill = insert(:hill) assert Hills.get_hill!(hill.id) == hill end @@ -66,7 +75,7 @@ defmodule Ascend.HillsTest do end test "update_hill/2 with valid data updates the hill" do - hill = hill_fixture() + hill = insert(:hill) update_attrs = %{ area: "some updated area", @@ -97,19 +106,19 @@ defmodule Ascend.HillsTest do end test "update_hill/2 with invalid data returns error changeset" do - hill = hill_fixture() + hill = insert(:hill) assert {:error, %Ecto.Changeset{}} = Hills.update_hill(hill, @invalid_attrs) assert hill == Hills.get_hill!(hill.id) end test "delete_hill/1 deletes the hill" do - hill = hill_fixture() + hill = insert(:hill) assert {:ok, %Hill{}} = Hills.delete_hill(hill) assert_raise Ecto.NoResultsError, fn -> Hills.get_hill!(hill.id) end end test "change_hill/1 returns a hill changeset" do - hill = hill_fixture() + hill = insert(:hill) assert %Ecto.Changeset{} = Hills.change_hill(hill) end end diff --git a/test/ascend_web/live/hill_live_test.exs b/test/ascend_web/live/hill_live_test.exs index af566ff..42b1e08 100644 --- a/test/ascend_web/live/hill_live_test.exs +++ b/test/ascend_web/live/hill_live_test.exs @@ -2,10 +2,10 @@ defmodule AscendWeb.HillLiveTest do use AscendWeb.ConnCase import Phoenix.LiveViewTest - import Ascend.HillsFixtures + import Ascend.Factory defp create_hill(_) do - hill = hill_fixture() + hill = insert(:hill) %{hill: hill} end diff --git a/test/support/factory.ex b/test/support/factory.ex new file mode 100644 index 0000000..6d765b0 --- /dev/null +++ b/test/support/factory.ex @@ -0,0 +1,20 @@ +defmodule Ascend.Factory do + use ExMachina.Ecto, repo: Ascend.Repo + alias Ascend.Hills.Hill + + def hill_factory do + %Hill{ + area: "some area", + classification: "some classification", + dobih_id: sequence(:dobih_id, & &1), + feet: 1640.42, + grid_ref: "GR 123 456", + metres: 500, + munro: true, + name: "some name", + region: "some region", + wainwright: false, + wainwright_outlying_fell: false + } + end +end diff --git a/test/support/fixtures/hills_fixtures.ex b/test/support/fixtures/hills_fixtures.ex deleted file mode 100644 index 3de8683..0000000 --- a/test/support/fixtures/hills_fixtures.ex +++ /dev/null @@ -1,30 +0,0 @@ -defmodule Ascend.HillsFixtures do - @moduledoc """ - This module defines test helpers for creating - entities via the `Ascend.Hills` context. - """ - - @doc """ - Generate a hill. - """ - def hill_fixture(attrs \\ %{}) do - {:ok, hill} = - attrs - |> Enum.into(%{ - area: "some area", - classification: "some classification", - dobih_id: 42, - feet: 120.5, - grid_ref: "some grid_ref", - metres: 120.5, - munro: true, - name: "some name", - region: "some region", - wainwright: true, - wainwright_outlying_fell: true - }) - |> Ascend.Hills.create_hill() - - hill - end -end
Name<.live_component + module={AscendWeb.Live.SortingComponent} + id={"sorting-name"} + key={:name} + sorting={@sorting} /> + DobihMetresFeet<.live_component + module={AscendWeb.Live.SortingComponent} + id={"sorting-metres"} + key={:metres} + sorting={@sorting} /> + <.live_component + module={AscendWeb.Live.SortingComponent} + id={"sorting-feet"} + key={:feet} + sorting={@sorting} /> + Grid ref Classification Region