Skip to main content

lib/live_admin/components/resource/form.ex

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

  import LiveAdmin.Components
  import LiveAdmin

  alias Ecto.Changeset
  alias LiveAdmin.Resource

  @impl true
  def update(assigns = %{record: record}, socket) do
    socket =
      socket
      |> assign(assigns)
      |> assign(:enabled, false)
      |> assign(:changeset, Resource.change(assigns.resource, record, assigns.config))

    {:ok, socket}
  end

  @impl true
  def update(assigns, socket) do
    socket =
      socket
      |> assign(assigns)
      |> assign(:changeset, Resource.change(assigns.resource, assigns.config))

    {:ok, socket}
  end

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

  @impl true
  def render(assigns) do
    ~H"""
    <div phx-hook="Form" id="form-page">
      <div class="content-header">
        <h1 class="content-title">
          {resource_title(@resource, @config)}
          <%= if assigns[:record] do %>
            <span>{record_label(@record, @resource, @config)}</span>
          <% else %>
            <span>{trans("Create")}</span>
          <% end %>
        </h1>
      </div>

      <div class="content-card">
        <div class="card-section">
          <div class="edit-view">
            <.form
              :let={f}
              for={@changeset}
              as={:params}
              phx-change="validate"
              phx-submit={@action}
              phx-target={@myself}
            >
              <.form_grid
                form={f}
                resource={@resource}
                resources={@resources}
                session={@session}
                prefix={@prefix}
                repo={@repo}
                config={@config}
                fields={Resource.fields(@resource, @config)}
                target={@myself}
              />

              <div class="form-actions">
                <.link
                  class="btn btn-danger"
                  data-confirm="Are you sure?"
                  navigate={
                    if assigns[:record],
                      do: route_with_params(assigns, segments: [@record]),
                      else: route_with_params(assigns)
                  }
                >
                  {trans("Cancel")}
                </.link>
                <input
                  type="submit"
                  class="btn btn-primary"
                  value={trans("Save")}
                  disabled={Enum.any?(@changeset.errors)}
                />
              </div>
            </.form>
          </div>
        </div>
      </div>
    </div>
    """
  end

  @impl true
  def handle_event(
        "validate",
        %{"params" => params},
        socket = %{
          assigns: %{resource: resource, changeset: changeset, session: session, config: config}
        }
      ) do
    changeset = validate(resource, changeset, params, session, config)

    {:noreply, assign(socket, changeset: changeset)}
  end

  @impl true
  def handle_event(
        "create",
        %{"params" => params},
        %{assigns: %{resource: resource, session: session, repo: repo, config: config}} = socket
      ) do
    socket =
      case Resource.create(resource, params, session, repo, config) do
        {:ok, _} ->
          LiveAdmin.PubSub.announce(
            session.id,
            :success,
            trans("Record added")
          )

          push_navigate(socket,
            to: route_with_params(socket.assigns, params: [prefix: socket.assigns.prefix])
          )

        {:error, changeset} ->
          assign(socket, changeset: changeset)
      end

    {:noreply, socket}
  end

  @impl true
  def handle_event(
        "update",
        %{"params" => params},
        %{assigns: %{resource: resource, session: session, record: record, config: config}} =
          socket
      ) do
    socket =
      Resource.update(record, resource, params, session, config)
      |> case do
        {:ok, _} ->
          LiveAdmin.PubSub.announce(
            session.id,
            :success,
            trans("Changes saved")
          )

          push_navigate(socket, to: route_with_params(socket.assigns, segments: [record]))

        {:error, changeset} ->
          assign(socket, changeset: changeset)
      end

    {:noreply, socket}
  end

  def handle_event("remove_embed", params = %{"field" => field}, socket) do
    field = String.to_existing_atom(field)

    changeset =
      socket.assigns.changeset
      |> Changeset.get_change(field)
      |> case do
        val when is_list(val) ->
          Changeset.update_change(
            socket.assigns.changeset,
            field,
            &List.delete_at(&1, params |> Map.fetch!("index") |> String.to_integer())
          )

        _ ->
          Changeset.put_change(socket.assigns.changeset, field, nil)
      end

    {:noreply, assign(socket, :changeset, changeset)}
  end

  defp validate(resource, changeset, params, session, config) do
    resource
    |> Resource.change(changeset.data, params, config)
    |> Resource.validate(resource, session, config)
  end
end