Admin direct edit on contact show page, suggest edit for non-admins

This commit is contained in:
Graham McIntire 2026-04-11 16:46:21 -05:00
parent e651c1e481
commit 08ffc7e028
2 changed files with 44 additions and 20 deletions

View file

@ -379,7 +379,8 @@ defmodule Microwaveprop.Radio do
|> Repo.insert() |> Repo.insert()
end end
defp normalize_proposed(changes) do @doc false
def normalize_proposed(changes) do
changes changes
|> normalize_string_field("station1") |> normalize_string_field("station1")
|> normalize_string_field("station2") |> normalize_string_field("station2")
@ -396,7 +397,8 @@ defmodule Microwaveprop.Radio do
end end
end end
defp diff_against_contact(contact, proposed) do @doc false
def diff_against_contact(contact, proposed) do
Enum.reduce(proposed, %{}, fn {key, new_val}, acc -> Enum.reduce(proposed, %{}, fn {key, new_val}, acc ->
current = current_value(contact, key) current = current_value(contact, key)
@ -500,7 +502,8 @@ defmodule Microwaveprop.Radio do
|> Repo.update() |> Repo.update()
end end
defp apply_edit_to_contact(contact, proposed_changes) do @doc "Apply proposed changes directly to a contact (admin use)."
def apply_edit_to_contact(contact, proposed_changes) do
changes = build_contact_changes(contact, proposed_changes) changes = build_contact_changes(contact, proposed_changes)
grids_or_band_changed = grids_or_band_changed =

View file

@ -95,6 +95,9 @@ defmodule MicrowavepropWeb.ContactLive.Show do
defp current_user(%{current_scope: %{user: %{} = user}}), do: user defp current_user(%{current_scope: %{user: %{} = user}}), do: user
defp current_user(_), do: nil defp current_user(_), do: nil
defp admin?(%{current_scope: %{user: %{is_admin: true}}}), do: true
defp admin?(_), do: false
@impl true @impl true
def handle_event("sort", %{"field" => field, "table" => "obs"}, socket) do def handle_event("sort", %{"field" => field, "table" => "obs"}, socket) do
{sort_by, sort_order} = {sort_by, sort_order} =
@ -177,25 +180,42 @@ defmodule MicrowavepropWeb.ContactLive.Show do
user = current_user(socket.assigns) user = current_user(socket.assigns)
if is_nil(user) do if is_nil(user) do
{:noreply, put_flash(socket, :error, "You must be logged in to suggest edits.")} {:noreply, put_flash(socket, :error, "You must be logged in to edit.")}
else else
contact = socket.assigns.contact contact = socket.assigns.contact
case Radio.create_contact_edit(contact, user, params) do if admin?(socket.assigns) do
{:ok, _edit} -> # Admin: apply changes directly
proposed = Radio.diff_against_contact(contact, Radio.normalize_proposed(params))
if proposed == %{} do
{:noreply, put_flash(socket, :error, "No changes detected.")}
else
Radio.apply_edit_to_contact(contact, proposed)
updated = Radio.get_contact!(contact.id)
{:noreply, {:noreply,
socket socket
|> assign(editing: false, edit_form: nil, pending_edit: Radio.pending_edit_for_user(contact.id, user.id)) |> assign(contact: updated, editing: false, edit_form: nil)
|> put_flash(:info, "Edit submitted for review.")} |> put_flash(:info, "Contact updated.")}
end
else
case Radio.create_contact_edit(contact, user, params) do
{:ok, _edit} ->
{:noreply,
socket
|> assign(editing: false, edit_form: nil, pending_edit: Radio.pending_edit_for_user(contact.id, user.id))
|> put_flash(:info, "Edit submitted for review.")}
{:error, changeset} -> {:error, changeset} ->
messages = messages =
changeset changeset
|> Ecto.Changeset.traverse_errors(fn {msg, _opts} -> msg end) |> Ecto.Changeset.traverse_errors(fn {msg, _opts} -> msg end)
|> Enum.flat_map(fn {_field, msgs} -> msgs end) |> Enum.flat_map(fn {_field, msgs} -> msgs end)
|> Enum.join(", ") |> Enum.join(", ")
{:noreply, put_flash(socket, :error, "Could not submit edit: #{messages}")} {:noreply, put_flash(socket, :error, "Could not submit edit: #{messages}")}
end
end end
end end
end end
@ -529,7 +549,7 @@ defmodule MicrowavepropWeb.ContactLive.Show do
name={if @editing, do: "hero-x-mark", else: "hero-pencil-square"} name={if @editing, do: "hero-x-mark", else: "hero-pencil-square"}
class="w-4 h-4" class="w-4 h-4"
/> />
{if @editing, do: "Cancel Edit", else: "Suggest Edit"} {if @editing, do: "Cancel", else: if(admin?(assigns), do: "Edit", else: "Suggest Edit")}
</button> </button>
<% end %> <% end %>
<button <button
@ -548,7 +568,7 @@ defmodule MicrowavepropWeb.ContactLive.Show do
</:actions> </:actions>
</.header> </.header>
<%= if @pending_edit && !@editing do %> <%= if @pending_edit && !@editing && !admin?(assigns) do %>
<div class="bg-info/10 border border-info/30 rounded-box px-4 py-2 text-sm mb-4"> <div class="bg-info/10 border border-info/30 rounded-box px-4 py-2 text-sm mb-4">
<.icon name="hero-clock" class="w-4 h-4 inline" /> <.icon name="hero-clock" class="w-4 h-4 inline" />
You have a pending edit for this contact awaiting admin review. You have a pending edit for this contact awaiting admin review.
@ -557,9 +577,9 @@ defmodule MicrowavepropWeb.ContactLive.Show do
<%= if @editing do %> <%= if @editing do %>
<div class="bg-base-200 rounded-box p-4 mb-4"> <div class="bg-base-200 rounded-box p-4 mb-4">
<h3 class="font-semibold mb-3">Suggest Edit</h3> <h3 class="font-semibold mb-3">{if admin?(assigns), do: "Edit Contact", else: "Suggest Edit"}</h3>
<p class="text-sm opacity-70 mb-4"> <p class="text-sm opacity-70 mb-4">
Change any fields below. Only fields you modify will be submitted for review. {if admin?(assigns), do: "Change any fields below. Changes will be applied immediately.", else: "Change any fields below. Only fields you modify will be submitted for review."}
</p> </p>
<.form <.form
for={@edit_form} for={@edit_form}
@ -585,7 +605,8 @@ defmodule MicrowavepropWeb.ContactLive.Show do
</div> </div>
<div class="flex gap-2"> <div class="flex gap-2">
<.button type="submit" class="btn btn-primary btn-sm"> <.button type="submit" class="btn btn-primary btn-sm">
<.icon name="hero-paper-airplane" class="w-4 h-4" /> Submit for Review <.icon name="hero-paper-airplane" class="w-4 h-4" />
{if admin?(assigns), do: "Save Changes", else: "Submit for Review"}
</.button> </.button>
<button type="button" phx-click="toggle_edit" class="btn btn-ghost btn-sm"> <button type="button" phx-click="toggle_edit" class="btn btn-ghost btn-sm">
Cancel Cancel