From 1e8619de40f7662fc6a812872090957ffaa2cef4 Mon Sep 17 00:00:00 2001 From: KKlochko Date: Sun, 16 Mar 2025 09:38:44 +0200 Subject: [PATCH] Add LiveViews for Author. --- .../live/author_live/form_component.ex | 84 ++++++++++++++ .../live/author_live/index.ex | 105 ++++++++++++++++++ .../live/author_live/show.ex | 63 +++++++++++ lib/decentralised_book_index_web/router.ex | 7 ++ 4 files changed, 259 insertions(+) create mode 100644 lib/decentralised_book_index_web/live/author_live/form_component.ex create mode 100644 lib/decentralised_book_index_web/live/author_live/index.ex create mode 100644 lib/decentralised_book_index_web/live/author_live/show.ex diff --git a/lib/decentralised_book_index_web/live/author_live/form_component.ex b/lib/decentralised_book_index_web/live/author_live/form_component.ex new file mode 100644 index 0000000..4f14031 --- /dev/null +++ b/lib/decentralised_book_index_web/live/author_live/form_component.ex @@ -0,0 +1,84 @@ +defmodule DecentralisedBookIndexWeb.AuthorLive.FormComponent do + use DecentralisedBookIndexWeb, :live_component + + @impl true + def render(assigns) do + ~H""" +
+ <.header> + {@title} + <:subtitle>Use this form to manage author records in your database. + + + <.simple_form + for={@form} + id="author-form" + phx-target={@myself} + phx-change="validate" + phx-submit="save" + > + <%= if @form.source.type == :create do %> + <.input field={@form[:name]} type="text" label="Name" /><.input + field={@form[:description]} + type="text" + label="Description" + /><.input + field={@form[:author_alias_registry_id]} + type="text" + label="Author alias registry" + /> + <% end %> + <%= if @form.source.type == :update do %> + <% end %> + + <:actions> + <.button phx-disable-with="Saving...">Save Author + + +
+ """ + end + + @impl true + def update(assigns, socket) do + {:ok, + socket + |> assign(assigns) + |> assign_form()} + end + + @impl true + def handle_event("validate", %{"author" => author_params}, socket) do + {:noreply, assign(socket, form: AshPhoenix.Form.validate(socket.assigns.form, author_params))} + end + + def handle_event("save", %{"author" => author_params}, socket) do + case AshPhoenix.Form.submit(socket.assigns.form, params: author_params) do + {:ok, author} -> + notify_parent({:saved, author}) + + socket = + socket + |> put_flash(:info, "Author #{socket.assigns.form.source.type}d successfully") + |> push_patch(to: socket.assigns.patch) + + {:noreply, socket} + + {:error, form} -> + {:noreply, assign(socket, form: form)} + end + end + + defp notify_parent(msg), do: send(self(), {__MODULE__, msg}) + + defp assign_form(%{assigns: %{author: author}} = socket) do + form = + if author do + AshPhoenix.Form.for_update(author, :update, as: "author") + else + AshPhoenix.Form.for_create(DecentralisedBookIndex.Metadata.Author, :create, as: "author") + end + + assign(socket, form: to_form(form)) + end +end diff --git a/lib/decentralised_book_index_web/live/author_live/index.ex b/lib/decentralised_book_index_web/live/author_live/index.ex new file mode 100644 index 0000000..d4c0684 --- /dev/null +++ b/lib/decentralised_book_index_web/live/author_live/index.ex @@ -0,0 +1,105 @@ +defmodule DecentralisedBookIndexWeb.AuthorLive.Index do + use DecentralisedBookIndexWeb, :live_view + + @impl true + def render(assigns) do + ~H""" + <.header> + Listing Authors + <:actions> + <.link patch={~p"/authors/new"}> + <.button>New Author + + + + + <.table + id="authors" + rows={@streams.authors} + row_click={fn {_id, author} -> JS.navigate(~p"/authors/#{author}") end} + > + <:col :let={{_id, author}} label="Id">{author.id} + + <:col :let={{_id, author}} label="Name">{author.name} + + <:col :let={{_id, author}} label="Description">{author.description} + + <:col :let={{_id, author}} label="Avatar url">{author.avatar_url} + + <:action :let={{_id, author}}> +
+ <.link navigate={~p"/authors/#{author}"}>Show +
+ + <.link patch={~p"/authors/#{author}/edit"}>Edit + + + <:action :let={{id, author}}> + <.link + phx-click={JS.push("delete", value: %{id: author.id}) |> hide("##{id}")} + data-confirm="Are you sure?" + > + Delete + + + + + <.modal + :if={@live_action in [:new, :edit]} + id="author-modal" + show + on_cancel={JS.patch(~p"/authors")} + > + <.live_component + module={DecentralisedBookIndexWeb.AuthorLive.FormComponent} + id={(@author && @author.id) || :new} + title={@page_title} + action={@live_action} + author={@author} + patch={~p"/authors"} + /> + + """ + end + + @impl true + def mount(_params, _session, socket) do + {:ok, stream(socket, :authors, Ash.read!(DecentralisedBookIndex.Metadata.Author))} + end + + @impl true + def handle_params(params, _url, socket) do + {:noreply, apply_action(socket, socket.assigns.live_action, params)} + end + + defp apply_action(socket, :edit, %{"id" => id}) do + socket + |> assign(:page_title, "Edit Author") + |> assign(:author, Ash.get!(DecentralisedBookIndex.Metadata.Author, id)) + end + + defp apply_action(socket, :new, _params) do + socket + |> assign(:page_title, "New Author") + |> assign(:author, nil) + end + + defp apply_action(socket, :index, _params) do + socket + |> assign(:page_title, "Listing Authors") + |> assign(:author, nil) + end + + @impl true + def handle_info({DecentralisedBookIndexWeb.AuthorLive.FormComponent, {:saved, author}}, socket) do + {:noreply, stream_insert(socket, :authors, author)} + end + + @impl true + def handle_event("delete", %{"id" => id}, socket) do + author = Ash.get!(DecentralisedBookIndex.Metadata.Author, id) + Ash.destroy!(author) + + {:noreply, stream_delete(socket, :authors, author)} + end +end diff --git a/lib/decentralised_book_index_web/live/author_live/show.ex b/lib/decentralised_book_index_web/live/author_live/show.ex new file mode 100644 index 0000000..8d0d809 --- /dev/null +++ b/lib/decentralised_book_index_web/live/author_live/show.ex @@ -0,0 +1,63 @@ +defmodule DecentralisedBookIndexWeb.AuthorLive.Show do + use DecentralisedBookIndexWeb, :live_view + + @impl true + def render(assigns) do + ~H""" + <.header> + Author {@author.id} + <:subtitle>This is a author record from your database. + + <:actions> + <.link patch={~p"/authors/#{@author}/show/edit"} phx-click={JS.push_focus()}> + <.button>Edit author + + + + + <.list> + <:item title="Id">{@author.id} + + <:item title="Name">{@author.name} + + <:item title="Description">{@author.description} + + <:item title="Avatar url">{@author.avatar_url} + + + <.back navigate={~p"/authors"}>Back to authors + + <.modal + :if={@live_action == :edit} + id="author-modal" + show + on_cancel={JS.patch(~p"/authors/#{@author}")} + > + <.live_component + module={DecentralisedBookIndexWeb.AuthorLive.FormComponent} + id={@author.id} + title={@page_title} + action={@live_action} + author={@author} + patch={~p"/authors/#{@author}"} + /> + + """ + end + + @impl true + def mount(_params, _session, socket) do + {:ok, socket} + end + + @impl true + def handle_params(%{"id" => id}, _, socket) do + {:noreply, + socket + |> assign(:page_title, page_title(socket.assigns.live_action)) + |> assign(:author, Ash.get!(DecentralisedBookIndex.Metadata.Author, id))} + end + + defp page_title(:show), do: "Show Author" + defp page_title(:edit), do: "Edit Author" +end diff --git a/lib/decentralised_book_index_web/router.ex b/lib/decentralised_book_index_web/router.ex index 559cea3..07a610d 100644 --- a/lib/decentralised_book_index_web/router.ex +++ b/lib/decentralised_book_index_web/router.ex @@ -35,6 +35,13 @@ defmodule DecentralisedBookIndexWeb.Router do scope "/", DecentralisedBookIndexWeb do pipe_through :browser + live "/authors", AuthorLive.Index, :index + live "/authors/new", AuthorLive.Index, :new + live "/authors/:id/edit", AuthorLive.Index, :edit + + live "/authors/:id", AuthorLive.Show, :show + live "/authors/:id/show/edit", AuthorLive.Show, :edit + ash_authentication_live_session :authenticated_routes do # in each liveview, add one of the following at the top of the module: #