feat(rover-locations): edit status + notes alongside marker drag
This commit is contained in:
parent
08ac76a4b8
commit
bd77d1a5b0
2 changed files with 174 additions and 13 deletions
|
|
@ -13,14 +13,14 @@ defmodule MicrowavepropWeb.RoverLocationsLive.Show do
|
|||
case load_location(id) do
|
||||
%Location{} = loc ->
|
||||
{:ok,
|
||||
assign(socket,
|
||||
socket
|
||||
|> assign(
|
||||
page_title: "Rover Location",
|
||||
location: loc,
|
||||
editing: false,
|
||||
working_lat: loc.lat,
|
||||
working_lon: loc.lon,
|
||||
grid: Maidenhead.from_latlon(loc.lat, loc.lon, 10)
|
||||
)}
|
||||
status_options: status_options()
|
||||
)
|
||||
|> assign_working_fields(loc)}
|
||||
|
||||
nil ->
|
||||
{:ok,
|
||||
|
|
@ -43,11 +43,11 @@ defmodule MicrowavepropWeb.RoverLocationsLive.Show do
|
|||
end
|
||||
|
||||
def handle_event("cancel_edit", _params, socket) do
|
||||
%Location{lat: lat, lon: lon} = socket.assigns.location
|
||||
%Location{lat: lat, lon: lon} = location = socket.assigns.location
|
||||
|
||||
{:noreply,
|
||||
socket
|
||||
|> assign_working_coords(lat, lon)
|
||||
|> assign_working_fields(location)
|
||||
|> assign(editing: false)
|
||||
|> push_event("reset_marker", %{lat: lat, lon: lon})
|
||||
|> push_event("set_marker_draggable", %{draggable: false})}
|
||||
|
|
@ -59,17 +59,33 @@ defmodule MicrowavepropWeb.RoverLocationsLive.Show do
|
|||
{:noreply, assign_working_coords(socket, lat, lon)}
|
||||
end
|
||||
|
||||
def handle_event("update_form", %{"location" => params}, socket) do
|
||||
{:noreply,
|
||||
assign(socket,
|
||||
working_status: parse_status(Map.get(params, "status"), socket.assigns.working_status),
|
||||
working_notes: Map.get(params, "notes", socket.assigns.working_notes)
|
||||
)}
|
||||
end
|
||||
|
||||
def handle_event("save_edit", _params, socket) do
|
||||
%{working_lat: lat, working_lon: lon, location: %Location{id: id}} = socket.assigns
|
||||
%{
|
||||
working_lat: lat,
|
||||
working_lon: lon,
|
||||
working_status: status,
|
||||
working_notes: notes,
|
||||
location: %Location{id: id}
|
||||
} = socket.assigns
|
||||
|
||||
attrs = %{lat: lat, lon: lon, status: status, notes: notes}
|
||||
|
||||
with {:ok, user} <- authenticated(socket),
|
||||
{:ok, updated} <- Rover.update_location(user, id, %{lat: lat, lon: lon}) do
|
||||
{:ok, updated} <- Rover.update_location(user, id, attrs) do
|
||||
updated = Repo.preload(updated, :user)
|
||||
|
||||
{:noreply,
|
||||
socket
|
||||
|> assign(location: updated, editing: false)
|
||||
|> assign_working_coords(updated.lat, updated.lon)
|
||||
|> assign_working_fields(updated)
|
||||
|> put_flash(:info, "Location updated.")
|
||||
|> push_event("set_marker_draggable", %{draggable: false})}
|
||||
else
|
||||
|
|
@ -100,6 +116,18 @@ defmodule MicrowavepropWeb.RoverLocationsLive.Show do
|
|||
)
|
||||
end
|
||||
|
||||
defp assign_working_fields(socket, %Location{lat: lat, lon: lon, status: status, notes: notes}) do
|
||||
socket
|
||||
|> assign_working_coords(lat, lon)
|
||||
|> assign(working_status: status, working_notes: notes || "")
|
||||
end
|
||||
|
||||
defp status_options, do: [{"Good", "good"}, {"Bad", "bad"}]
|
||||
|
||||
defp parse_status("good", _fallback), do: :good
|
||||
defp parse_status("bad", _fallback), do: :bad
|
||||
defp parse_status(_, fallback), do: fallback
|
||||
|
||||
defp load_location(id) do
|
||||
case Ecto.UUID.cast(id) do
|
||||
{:ok, uuid} -> Location |> Repo.get(uuid) |> Repo.preload(:user)
|
||||
|
|
@ -195,8 +223,8 @@ defmodule MicrowavepropWeb.RoverLocationsLive.Show do
|
|||
<div>
|
||||
<dt class="text-base-content/60">Status</dt>
|
||||
<dd>
|
||||
<span class={status_class(@location.status)}>
|
||||
{status_label(@location.status)}
|
||||
<span class={status_class(@working_status)}>
|
||||
{status_label(@working_status)}
|
||||
</span>
|
||||
</dd>
|
||||
</div>
|
||||
|
|
@ -217,10 +245,51 @@ defmodule MicrowavepropWeb.RoverLocationsLive.Show do
|
|||
</div>
|
||||
</dl>
|
||||
|
||||
<div :if={@location.notes && @location.notes != ""} class="mt-4">
|
||||
<div
|
||||
:if={(not @editing and @location.notes) && @location.notes != ""}
|
||||
class="mt-4"
|
||||
>
|
||||
<h3 class="text-sm text-base-content/60 mb-1">Notes</h3>
|
||||
<p class="whitespace-pre-line text-sm">{@location.notes}</p>
|
||||
</div>
|
||||
|
||||
<form
|
||||
:if={@editing}
|
||||
id="location-edit-form"
|
||||
phx-change="update_form"
|
||||
phx-submit="save_edit"
|
||||
class="mt-4 space-y-3"
|
||||
>
|
||||
<div>
|
||||
<label class="label" for="location-status">
|
||||
<span class="label-text text-sm text-base-content/60">Status</span>
|
||||
</label>
|
||||
<select
|
||||
id="location-status"
|
||||
name="location[status]"
|
||||
class="select select-bordered select-sm w-full max-w-xs"
|
||||
>
|
||||
<option
|
||||
:for={{label, value} <- @status_options}
|
||||
value={value}
|
||||
selected={to_string(@working_status) == value}
|
||||
>
|
||||
{label}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label class="label" for="location-notes">
|
||||
<span class="label-text text-sm text-base-content/60">Notes</span>
|
||||
</label>
|
||||
<textarea
|
||||
id="location-notes"
|
||||
name="location[notes]"
|
||||
rows="3"
|
||||
class="textarea textarea-bordered w-full"
|
||||
>{@working_notes}</textarea>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div
|
||||
|
|
|
|||
|
|
@ -152,6 +152,98 @@ defmodule MicrowavepropWeb.RoverLocationsLive.ShowTest do
|
|||
assert Repo.reload(loc).lat == 32.5
|
||||
end
|
||||
|
||||
test "edit mode exposes status + notes inputs populated with current values",
|
||||
%{conn: conn} do
|
||||
user = Microwaveprop.AccountsFixtures.user_fixture()
|
||||
|
||||
{:ok, loc} =
|
||||
Rover.create_location(user, %{
|
||||
lat: 32.5,
|
||||
lon: -97.5,
|
||||
status: :good,
|
||||
notes: "first pass"
|
||||
})
|
||||
|
||||
conn = log_in_user(conn, user)
|
||||
|
||||
{:ok, lv, html} = live(conn, ~p"/rover-locations/#{loc.id}")
|
||||
# Inputs are not visible until edit mode is entered.
|
||||
refute has_element?(lv, "select[name='location[status]']")
|
||||
refute has_element?(lv, "textarea[name='location[notes]']")
|
||||
assert html =~ "first pass"
|
||||
|
||||
_ = lv |> element("button", "Edit") |> render_click()
|
||||
|
||||
assert has_element?(lv, "select[name='location[status]']")
|
||||
|
||||
assert has_element?(
|
||||
lv,
|
||||
"textarea[name='location[notes]']",
|
||||
"first pass"
|
||||
)
|
||||
end
|
||||
|
||||
test "save persists status + notes edits", %{conn: conn} do
|
||||
user = Microwaveprop.AccountsFixtures.user_fixture()
|
||||
|
||||
{:ok, loc} =
|
||||
Rover.create_location(user, %{
|
||||
lat: 32.5,
|
||||
lon: -97.5,
|
||||
status: :good,
|
||||
notes: "old"
|
||||
})
|
||||
|
||||
conn = log_in_user(conn, user)
|
||||
|
||||
{:ok, lv, _html} = live(conn, ~p"/rover-locations/#{loc.id}")
|
||||
_ = lv |> element("button", "Edit") |> render_click()
|
||||
|
||||
_ =
|
||||
lv
|
||||
|> form("#location-edit-form",
|
||||
location: %{status: "bad", notes: "private property"}
|
||||
)
|
||||
|> render_change()
|
||||
|
||||
html = lv |> element("button[phx-click='save_edit']") |> render_click()
|
||||
assert html =~ "Location updated"
|
||||
|
||||
reloaded = Repo.reload(loc)
|
||||
assert reloaded.status == :bad
|
||||
assert reloaded.notes == "private property"
|
||||
end
|
||||
|
||||
test "cancel reverts pending status + notes changes", %{conn: conn} do
|
||||
user = Microwaveprop.AccountsFixtures.user_fixture()
|
||||
|
||||
{:ok, loc} =
|
||||
Rover.create_location(user, %{
|
||||
lat: 32.5,
|
||||
lon: -97.5,
|
||||
status: :good,
|
||||
notes: "keeper"
|
||||
})
|
||||
|
||||
conn = log_in_user(conn, user)
|
||||
|
||||
{:ok, lv, _html} = live(conn, ~p"/rover-locations/#{loc.id}")
|
||||
_ = lv |> element("button", "Edit") |> render_click()
|
||||
|
||||
_ =
|
||||
lv
|
||||
|> form("#location-edit-form",
|
||||
location: %{status: "bad", notes: "scratch this"}
|
||||
)
|
||||
|> render_change()
|
||||
|
||||
_ = lv |> element("button[phx-click='cancel_edit']") |> render_click()
|
||||
|
||||
reloaded = Repo.reload(loc)
|
||||
assert reloaded.status == :good
|
||||
assert reloaded.notes == "keeper"
|
||||
end
|
||||
|
||||
test "non-owner does not see the Edit button", %{conn: conn} do
|
||||
owner = Microwaveprop.AccountsFixtures.user_fixture()
|
||||
visitor = Microwaveprop.AccountsFixtures.user_fixture()
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue