lib/live_admin/components/resource/view.ex

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

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

  import LiveAdmin.View, only: [field_class: 1, get_function_keys: 3]
  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="ViewPage" phx-target={@myself}>
      <div class="resource__table">
        <dl>
          <%= for {field, type, _} <- Resource.fields(@resource, @config) do %>
            <% assoc_resource =
              LiveAdmin.associated_resource(
                Keyword.fetch!(@resource.__live_admin_config__(), :schema),
                field,
                @resources
              ) %>
            <% label = Resource.render(@record, field, @resource, assoc_resource, @session, @config) %>
            <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">
          <%= if LiveAdmin.fetch_config(@resource, :update_with, @config) != false do %>
            <.link
              navigate={route_with_params(assigns, segments: [:edit, @record])}
              class="resource__action--btn"
            >
              <%= trans("Edit") %>
            </.link>
          <% end %>
          <%= if LiveAdmin.fetch_config(@resource, :delete_with, @config) != false do %>
            <button
              class="resource__action--danger"
              data-confirm="Are you sure?"
              phx-click={
                JS.push("delete",
                  value: %{key: Map.fetch!(@record, LiveAdmin.primary_key!(@resource))},
                  page_loading: true,
                  target: @myself
                )
              }
            >
              <%= trans("Delete") %>
            </button>
          <% end %>
          <.dropdown
            :let={action}
            orientation={:up}
            label={trans("Run action")}
            items={get_function_keys(@resource, @config, :actions)}
            disabled={Enum.empty?(LiveAdmin.fetch_config(@resource, :actions, @config))}
          >
            <.action_control action={action} session={@session} resource={@resource} />
          </.dropdown>
        </div>
      </div>
    </div>
    """
  end

  @impl true
  def handle_event(
        "delete",
        %{"key" => key},
        %{
          assigns: %{
            resource: resource,
            session: session,
            config: config
          }
        } = socket
      ) do
    socket =
      key
      |> Resource.find!(resource, socket.assigns.prefix, socket.assigns.repo)
      |> Resource.delete(resource, session, socket.assigns.repo, config)
      |> case do
        {:ok, record} ->
          socket
          |> put_flash(
            :info,
            trans("Deleted %{label}", inter: [label: record_label(record, resource, config)])
          )
          |> 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 = %{"name" => action},
        socket = %{assigns: %{resource: resource, prefix: prefix, repo: repo, session: session}}
      ) do
    record =
      socket.assigns[:record] || Resource.find!(params["id"], resource, prefix, repo)

    {_, m, f, _, _} =
      LiveAdmin.fetch_function(resource, session, :actions, String.to_existing_atom(action))

    socket =
      case apply(m, f, [record, socket.assigns.session] ++ Map.get(params, "args", [])) do
        {:ok, result} ->
          if is_struct(result, Keyword.fetch!(resource.__live_admin_config__(), :schema)) do
            socket
            |> push_event("success", %{
              msg: trans("%{action} succeeded", inter: [action: action])
            })
            |> assign(:record, record)
          else
            socket
            |> push_event("success", %{msg: result})
            |> assign(
              :record,
              Resource.find!(
                Map.fetch!(record, LiveAdmin.primary_key!(resource)),
                resource,
                prefix,
                repo
              )
            )
          end

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

    {:noreply, socket}
  end
end