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 @@
- Name |
+ <.live_component
+ module={AscendWeb.Live.SortingComponent}
+ id={"sorting-name"}
+ key={:name}
+ sorting={@sorting} />
+ |
Dobih |
- Metres |
- Feet |
+ <.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 |
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