From 3e2d75be8188f3db94703e284521ef7870470a4d Mon Sep 17 00:00:00 2001 From: Walton Hoops Date: Wed, 13 Nov 2024 13:08:07 -0700 Subject: [PATCH] feat: display shape on map in shuttle definition pages --- lib/arrow/shuttles.ex | 8 +++ .../live/shuttle_live/shuttle_live.ex | 55 +++++++++++++++++-- .../shuttle_live/shuttle_view_live.html.heex | 1 + test/arrow/shuttles_test.exs | 13 ++++- 4 files changed, 70 insertions(+), 7 deletions(-) diff --git a/lib/arrow/shuttles.ex b/lib/arrow/shuttles.ex index 18138366..d0e4322e 100644 --- a/lib/arrow/shuttles.ex +++ b/lib/arrow/shuttles.ex @@ -43,6 +43,14 @@ defmodule Arrow.Shuttles do """ def get_shape!(id), do: Repo.get!(Shape, id) + @doc """ + Gets the shapes specifiied by a list of ids. Does not raise if any of the ids are missing, + meaning the resulting list may be shorter than the input list. + """ + def get_shapes(ids) do + Repo.all(from s in Shape, where: s.id in ^ids) + end + @doc """ Gets a shapes upload struct associated with a given shape. diff --git a/lib/arrow_web/live/shuttle_live/shuttle_live.ex b/lib/arrow_web/live/shuttle_live/shuttle_live.ex index 97315460..a2025bf9 100644 --- a/lib/arrow_web/live/shuttle_live/shuttle_live.ex +++ b/lib/arrow_web/live/shuttle_live/shuttle_live.ex @@ -3,6 +3,7 @@ defmodule ArrowWeb.ShuttleViewLive do import Phoenix.HTML.Form alias Arrow.Shuttles alias Arrow.Shuttles.Shuttle + alias ArrowWeb.ShapeView embed_templates "shuttle_live/*" @@ -14,6 +15,7 @@ defmodule ArrowWeb.ShuttleViewLive do attr :http_action, :string attr :gtfs_disruptable_routes, :list, required: true attr :shapes, :list, required: true + attr :map_props, :map, required: false, default: %{} def shuttle_form(assigns) do ~H""" @@ -60,6 +62,7 @@ defmodule ArrowWeb.ShuttleViewLive do /> + <%= live_react_component("Components.ShapeViewMap", @map_props, id: "shuttle-view-map") %>

define route

<.inputs_for :let={f_route} field={f[:routes]}> @@ -107,6 +110,14 @@ defmodule ArrowWeb.ShuttleViewLive do """ end + defp shapes_to_shapeviews(shapes) do + shapes + |> Enum.map(&Shuttles.get_shapes_upload/1) + |> Enum.reject(&(&1 == {:ok, :disabled})) + |> Enum.map(&ShapeView.shapes_map_view/1) + |> Enum.map(&List.first(&1.shapes)) + end + def mount(%{"id" => id} = _params, session, socket) do logout_url = session["logout_url"] shuttle = Shuttles.get_shuttle!(id) @@ -115,6 +126,14 @@ defmodule ArrowWeb.ShuttleViewLive do shapes = Shuttles.list_shapes() form = to_form(changeset) + shuttle_shapes = + shuttle + |> Map.get(:routes) + |> Enum.map(&Map.get(&1, :shape)) + |> Enum.reject(&is_nil/1) + + shapes_map_view = shapes_to_shapeviews(shuttle_shapes) + socket = socket |> assign(:form, form) @@ -125,6 +144,7 @@ defmodule ArrowWeb.ShuttleViewLive do |> assign(:gtfs_disruptable_routes, gtfs_disruptable_routes) |> assign(:shapes, shapes) |> assign(:logout_url, logout_url) + |> assign(:map_props, %{shapes: shapes_map_view}) {:ok, socket} end @@ -151,17 +171,31 @@ defmodule ArrowWeb.ShuttleViewLive do |> assign(:gtfs_disruptable_routes, gtfs_disruptable_routes) |> assign(:shapes, shapes) |> assign(:logout_url, logout_url) + |> assign(:map_props, %{shapes: []}) {:ok, socket} end - def handle_event("validate", %{"shuttle" => shuttle_params}, socket) do - form = - socket.assigns.shuttle - |> Shuttles.change_shuttle(shuttle_params) - |> to_form(action: :validate) + # A new shape is selected + def handle_event( + "validate", + %{"_target" => ["shuttle", "routes", _direction_id, "shape_id"]} = params, + socket + ) do + shapes = + [ + params["shuttle"]["routes"]["0"]["shape_id"], + params["shuttle"]["routes"]["1"]["shape_id"] + ] + |> Enum.reject(&(&1 == "")) + |> Shuttles.get_shapes() + |> shapes_to_shapeviews() + + validate(params, assign(socket, :map_props, %{socket.assigns.map_props | shapes: shapes})) + end - {:noreply, assign(socket, form: form)} + def handle_event("validate", params, socket) do + validate(params, socket) end def handle_event("edit", %{"shuttle" => shuttle_params}, socket) do @@ -191,4 +225,13 @@ defmodule ArrowWeb.ShuttleViewLive do {:noreply, assign(socket, form: to_form(changeset))} end end + + defp validate(%{"shuttle" => shuttle_params}, socket) do + form = + socket.assigns.shuttle + |> Shuttles.change_shuttle(shuttle_params) + |> to_form(action: :validate) + + {:noreply, assign(socket, form: form)} + end end diff --git a/lib/arrow_web/live/shuttle_live/shuttle_view_live.html.heex b/lib/arrow_web/live/shuttle_live/shuttle_view_live.html.heex index 9648e3a5..21b34d37 100644 --- a/lib/arrow_web/live/shuttle_live/shuttle_view_live.html.heex +++ b/lib/arrow_web/live/shuttle_live/shuttle_view_live.html.heex @@ -8,6 +8,7 @@ http_action={@http_action} gtfs_disruptable_routes={@gtfs_disruptable_routes} shapes={@shapes} + map_props={@map_props} /> <.back navigate={~p"/shuttles"}>Back to shuttles diff --git a/test/arrow/shuttles_test.exs b/test/arrow/shuttles_test.exs index 228af94e..b84ebeff 100644 --- a/test/arrow/shuttles_test.exs +++ b/test/arrow/shuttles_test.exs @@ -22,7 +22,7 @@ defmodule Arrow.ShuttlesTest do end test "create_shape/1 with name that does not end in -S adds it" do - Application.put_env(:arrow, :shape_storage_enabled?, true) + reassign_env(:shape_storage_enabled?, true) Application.put_env(:arrow, :shape_storage_prefix, "prefix/#{Ecto.UUID.generate()}/") assert {:ok, %Shape{} = shape} = @@ -62,6 +62,17 @@ defmodule Arrow.ShuttlesTest do assert Shuttles.get_shape!(shape.id) == shape end + test "get_shapes returns all shapes with matching ids" do + shapes = [shape_fixture(), shape_fixture()] + shape_ids = Enum.map(shapes, fn shape -> shape.id end) + assert MapSet.new(Shuttles.get_shapes(shape_ids)) == MapSet.new(shapes) + end + + test "get_shapes returns empty list when no shapes match" do + shape_ids = [1, 2, 3] + assert [] == Shuttles.get_shapes(shape_ids) + end + test "create_shapes/1 with valid data creates a shape" do assert {:ok, [{:ok, %Shape{} = shape}]} = Shuttles.create_shapes([@valid_attrs]) assert shape.name == "some name-S"