lib/live_admin/components/resource/view.ex

defmodule LiveAdmin.Components.Container.View do
  use Phoenix.LiveComponent
  use Phoenix.HTML

  import LiveAdmin,
    only: [route_with_params: 1, route_with_params: 2, trans: 1, record_label: 2, trans: 2]

  import LiveAdmin.View, only: [field_class: 1]
  import LiveAdmin.Components

  alias LiveAdmin.Resource
  alias Phoenix.LiveView.JS

  @impl true
  def render(assigns = %{record: nil}) do
    ~H"""
    <div><%= trans("No record found") %></div>
    """
  end

  @impl true
  def render(assigns) do
    ~H"""
    <div id="view-page" class="view__container" phx-hook="IndexPage" phx-target={@myself}>
      <div class="resource__table">
        <dl>
          <%= for {field, type, _} <- Resource.fields(@resource) do %>
            <% assoc_resource =
              LiveAdmin.associated_resource(
                @resource.__live_admin_config__(:schema),
                field,
                @resources
              ) %>
            <% label = Resource.render(@record, field, @resource, assoc_resource, @session) %>
            <dt class="field__label"><%= trans(humanize(field)) %></dt>
            <dd class={"field__#{field_class(type)}"}>
              <%= if assoc_resource && Map.fetch!(@record, field) do %>
                <.link
                  class="field__assoc--link"
                  target="_blank"
                  navigate={
                    route_with_params(assigns,
                      resource_path: elem(assoc_resource, 0),
                      segments: [Map.fetch!(@record, field)]
                    )
                  }
                >
                  <%= label %>
                </.link>
              <% else %>
                <%= label %>
              <% end %>
            </dd>
          <% end %>
        </dl>
        <div class="form__actions">
          <.link
            navigate={route_with_params(assigns, segments: [:edit, @record])}
            class="resource__action--btn"
          >
            <%= trans("Edit") %>
          </.link>
          <%= if @resource.__live_admin_config__(:delete_with) != false do %>
            <button
              class="resource__action--danger"
              data-confirm="Are you sure?"
              phx-click={
                JS.push("delete", value: %{id: @record.id}, page_loading: true, target: @myself)
              }
            >
              <%= trans("Delete") %>
            </button>
          <% end %>
          <.dropdown
            :let={action}
            orientation={:up}
            label={trans("Run action")}
            items={@resource.__live_admin_config__(:actions)}
            disabled={Enum.empty?(@resource.__live_admin_config__(:actions))}
          >
            <button
              class="resource__action--link"
              data-action={action}
              phx-click={JS.dispatch("live_admin:action")}
              data-confirm="Are you sure?"
            >
              <%= action |> to_string() |> humanize() %>
            </button>
          </.dropdown>
        </div>
      </div>
    </div>
    """
  end

  @impl true
  def handle_event(
        "delete",
        %{"id" => id},
        %{
          assigns: %{
            resource: resource,
            session: session
          }
        } = socket
      ) do
    socket =
      id
      |> Resource.find!(resource, socket.assigns.prefix, socket.assigns.repo)
      |> Resource.delete(resource, session, socket.assigns.repo)
      |> case do
        {:ok, record} ->
          socket
          |> put_flash(
            :info,
            trans("Deleted %{label}", inter: [label: record_label(record, resource)])
          )
          |> push_navigate(to: route_with_params(socket.assigns))

        {:error, _} ->
          push_event(socket, "error", %{
            msg: trans("Delete failed!")
          })
      end

    {:noreply, socket}
  end

  @impl true
  def handle_event(
        "action",
        params = %{"action" => action},
        socket = %{assigns: %{resource: resource, prefix: prefix, repo: repo}}
      ) do
    record = socket.assigns[:record] || Resource.find!(params["id"], resource, prefix, repo)

    action_name = String.to_existing_atom(action)

    {m, f, a} =
      :actions
      |> resource.__live_admin_config__()
      |> Enum.find_value(fn
        {^action_name, mfa} -> mfa
        ^action_name -> {resource, action_name, []}
        _ -> false
      end)

    socket =
      case apply(m, f, [record, socket.assigns.session] ++ a) do
        {:ok, record} ->
          socket
          |> push_event("success", %{
            msg: trans("%{action} succeeded", inter: [action: action])
          })
          |> assign(:record, record)

        {:error, error} ->
          push_event(
            socket,
            "error",
            trans("%{action} failed: %{error}",
              inter: [
                action: action,
                error: error
              ]
            )
          )
      end

    {:noreply, socket}
  end
end