From fd6770e82ba98f033a495cc19cc32af32c561026 Mon Sep 17 00:00:00 2001 From: Moritz Date: Wed, 9 Jul 2025 20:47:04 +0200 Subject: [PATCH] feat: migration to phoenix 1.8 - generate new ash live views --- lib/mv_web/live/member_live/form.ex | 107 ++++++++++++++++++++ lib/mv_web/live/member_live/index.ex | 60 +++++++++++ lib/mv_web/live/member_live/show.ex | 36 +++++++ lib/mv_web/live/property_live/form.ex | 87 ++++++++++++++++ lib/mv_web/live/property_live/index.ex | 60 +++++++++++ lib/mv_web/live/property_live/show.ex | 36 +++++++ lib/mv_web/live/property_type_live/form.ex | 95 +++++++++++++++++ lib/mv_web/live/property_type_live/index.ex | 64 ++++++++++++ lib/mv_web/live/property_type_live/show.ex | 43 ++++++++ 9 files changed, 588 insertions(+) create mode 100644 lib/mv_web/live/member_live/form.ex create mode 100644 lib/mv_web/live/member_live/index.ex create mode 100644 lib/mv_web/live/member_live/show.ex create mode 100644 lib/mv_web/live/property_live/form.ex create mode 100644 lib/mv_web/live/property_live/index.ex create mode 100644 lib/mv_web/live/property_live/show.ex create mode 100644 lib/mv_web/live/property_type_live/form.ex create mode 100644 lib/mv_web/live/property_type_live/index.ex create mode 100644 lib/mv_web/live/property_type_live/show.ex diff --git a/lib/mv_web/live/member_live/form.ex b/lib/mv_web/live/member_live/form.ex new file mode 100644 index 0000000..df5a2c1 --- /dev/null +++ b/lib/mv_web/live/member_live/form.ex @@ -0,0 +1,107 @@ +defmodule MvWeb.MemberLive.Form do + use MvWeb, :live_view + + @impl true + def render(assigns) do + ~H""" + + <.header> + {@page_title} + <:subtitle>Use this form to manage member records in your database. + + + <.form for={@form} id="member-form" phx-change="validate" phx-submit="save"> + <.input field={@form[:properties]} type="select" multiple label="Properties" options={[]} /> + <.input field={@form[:first_name]} type="text" label="First name" /><.input + field={@form[:last_name]} + type="text" + label="Last name" + /><.input field={@form[:email]} type="text" label="Email" /><.input + field={@form[:birth_date]} + type="date" + label="Birth date" + /><.input field={@form[:paid]} type="checkbox" label="Paid" /><.input + field={@form[:phone_number]} + type="text" + label="Phone number" + /><.input field={@form[:join_date]} type="date" label="Join date" /><.input + field={@form[:exit_date]} + type="date" + label="Exit date" + /><.input field={@form[:notes]} type="text" label="Notes" /><.input + field={@form[:city]} + type="text" + label="City" + /><.input field={@form[:street]} type="text" label="Street" /><.input + field={@form[:house_number]} + type="text" + label="House number" + /><.input field={@form[:postal_code]} type="text" label="Postal code" /> + + <.button phx-disable-with="Saving..." variant="primary">Save Member + <.button navigate={return_path(@return_to, @member)}>Cancel + + + """ + end + + @impl true + def mount(params, _session, socket) do + member = + case params["id"] do + nil -> nil + id -> Ash.get!(Mv.Membership.Member, id) + end + + action = if is_nil(member), do: "New", else: "Edit" + page_title = action <> " " <> "Member" + + {:ok, + socket + |> assign(:return_to, return_to(params["return_to"])) + |> assign(member: member) + |> assign(:page_title, page_title) + |> assign_form()} + end + + defp return_to("show"), do: "show" + defp return_to(_), do: "index" + + @impl true + def handle_event("validate", %{"member" => member_params}, socket) do + {:noreply, assign(socket, form: AshPhoenix.Form.validate(socket.assigns.form, member_params))} + end + + def handle_event("save", %{"member" => member_params}, socket) do + case AshPhoenix.Form.submit(socket.assigns.form, params: member_params) do + {:ok, member} -> + notify_parent({:saved, member}) + + socket = + socket + |> put_flash(:info, "Member #{socket.assigns.form.source.type}d successfully") + |> push_navigate(to: return_path(socket.assigns.return_to, member)) + + {:noreply, socket} + + {:error, form} -> + {:noreply, assign(socket, form: form)} + end + end + + defp notify_parent(msg), do: send(self(), {__MODULE__, msg}) + + defp assign_form(%{assigns: %{member: member}} = socket) do + form = + if member do + AshPhoenix.Form.for_update(member, :update_member, as: "member") + else + AshPhoenix.Form.for_create(Mv.Membership.Member, :create_member, as: "member") + end + + assign(socket, form: to_form(form)) + end + + defp return_path("index", _member), do: ~p"/members" + defp return_path("show", member), do: ~p"/members/#{member.id}" +end diff --git a/lib/mv_web/live/member_live/index.ex b/lib/mv_web/live/member_live/index.ex new file mode 100644 index 0000000..79db12f --- /dev/null +++ b/lib/mv_web/live/member_live/index.ex @@ -0,0 +1,60 @@ +defmodule MvWeb.MemberLive.Index do + use MvWeb, :live_view + + @impl true + def render(assigns) do + ~H""" + + <.header> + Listing Members + <:actions> + <.button variant="primary" navigate={~p"/members/new"}> + <.icon name="hero-plus" /> New Member + + + + + <.table + id="members" + rows={@streams.members} + row_click={fn {_id, member} -> JS.navigate(~p"/members/#{member}") end} + > + <:col :let={{_id, member}} label="Id">{member.id} + + <:action :let={{_id, member}}> +
+ <.link navigate={~p"/members/#{member}"}>Show +
+ + <.link navigate={~p"/members/#{member}/edit"}>Edit + + + <:action :let={{id, member}}> + <.link + phx-click={JS.push("delete", value: %{id: member.id}) |> hide("##{id}")} + data-confirm="Are you sure?" + > + Delete + + + +
+ """ + end + + @impl true + def mount(_params, _session, socket) do + {:ok, + socket + |> assign(:page_title, "Listing Members") + |> stream(:members, Ash.read!(Mv.Membership.Member))} + end + + @impl true + def handle_event("delete", %{"id" => id}, socket) do + member = Ash.get!(Mv.Membership.Member, id) + Ash.destroy!(member) + + {:noreply, stream_delete(socket, :members, member)} + end +end diff --git a/lib/mv_web/live/member_live/show.ex b/lib/mv_web/live/member_live/show.ex new file mode 100644 index 0000000..a7fa16e --- /dev/null +++ b/lib/mv_web/live/member_live/show.ex @@ -0,0 +1,36 @@ +defmodule MvWeb.MemberLive.Show do + use MvWeb, :live_view + + @impl true + def render(assigns) do + ~H""" + + <.header> + Member {@member.id} + <:subtitle>This is a member record from your database. + + <:actions> + <.button navigate={~p"/members"}> + <.icon name="hero-arrow-left" /> + + <.button variant="primary" navigate={~p"/members/#{@member}/edit?return_to=show"}> + <.icon name="hero-pencil-square" /> Edit Member + + + + + <.list> + <:item title="Id">{@member.id} + + + """ + end + + @impl true + def mount(%{"id" => id}, _session, socket) do + {:ok, + socket + |> assign(:page_title, "Show Member") + |> assign(:member, Ash.get!(Mv.Membership.Member, id))} + end +end diff --git a/lib/mv_web/live/property_live/form.ex b/lib/mv_web/live/property_live/form.ex new file mode 100644 index 0000000..6df53ef --- /dev/null +++ b/lib/mv_web/live/property_live/form.ex @@ -0,0 +1,87 @@ +defmodule MvWeb.PropertyLive.Form do + use MvWeb, :live_view + + @impl true + def render(assigns) do + ~H""" + + <.header> + {@page_title} + <:subtitle>Use this form to manage property records in your database. + + + <.form for={@form} id="property-form" phx-change="validate" phx-submit="save"> + <.input field={@form[:value]} type="text" label="Value" /><.input + field={@form[:member_id]} + type="text" + label="Member" + /><.input field={@form[:property_type_id]} type="text" label="Property type" /> + + <.button phx-disable-with="Saving..." variant="primary">Save Property + <.button navigate={return_path(@return_to, @property)}>Cancel + + + """ + end + + @impl true + def mount(params, _session, socket) do + property = + case params["id"] do + nil -> nil + id -> Ash.get!(Mv.Membership.Property, id) + end + + action = if is_nil(property), do: "New", else: "Edit" + page_title = action <> " " <> "Property" + + {:ok, + socket + |> assign(:return_to, return_to(params["return_to"])) + |> assign(property: property) + |> assign(:page_title, page_title) + |> assign_form()} + end + + defp return_to("show"), do: "show" + defp return_to(_), do: "index" + + @impl true + def handle_event("validate", %{"property" => property_params}, socket) do + {:noreply, + assign(socket, form: AshPhoenix.Form.validate(socket.assigns.form, property_params))} + end + + def handle_event("save", %{"property" => property_params}, socket) do + case AshPhoenix.Form.submit(socket.assigns.form, params: property_params) do + {:ok, property} -> + notify_parent({:saved, property}) + + socket = + socket + |> put_flash(:info, "Property #{socket.assigns.form.source.type}d successfully") + |> push_navigate(to: return_path(socket.assigns.return_to, property)) + + {:noreply, socket} + + {:error, form} -> + {:noreply, assign(socket, form: form)} + end + end + + defp notify_parent(msg), do: send(self(), {__MODULE__, msg}) + + defp assign_form(%{assigns: %{property: property}} = socket) do + form = + if property do + AshPhoenix.Form.for_update(property, :update, as: "property") + else + AshPhoenix.Form.for_create(Mv.Membership.Property, :create, as: "property") + end + + assign(socket, form: to_form(form)) + end + + defp return_path("index", _property), do: ~p"/properties" + defp return_path("show", property), do: ~p"/properties/#{property.id}" +end diff --git a/lib/mv_web/live/property_live/index.ex b/lib/mv_web/live/property_live/index.ex new file mode 100644 index 0000000..7e27344 --- /dev/null +++ b/lib/mv_web/live/property_live/index.ex @@ -0,0 +1,60 @@ +defmodule MvWeb.PropertyLive.Index do + use MvWeb, :live_view + + @impl true + def render(assigns) do + ~H""" + + <.header> + Listing Properties + <:actions> + <.button variant="primary" navigate={~p"/properties/new"}> + <.icon name="hero-plus" /> New Property + + + + + <.table + id="properties" + rows={@streams.properties} + row_click={fn {_id, property} -> JS.navigate(~p"/properties/#{property}") end} + > + <:col :let={{_id, property}} label="Id">{property.id} + + <:action :let={{_id, property}}> +
+ <.link navigate={~p"/properties/#{property}"}>Show +
+ + <.link navigate={~p"/properties/#{property}/edit"}>Edit + + + <:action :let={{id, property}}> + <.link + phx-click={JS.push("delete", value: %{id: property.id}) |> hide("##{id}")} + data-confirm="Are you sure?" + > + Delete + + + +
+ """ + end + + @impl true + def mount(_params, _session, socket) do + {:ok, + socket + |> assign(:page_title, "Listing Properties") + |> stream(:properties, Ash.read!(Mv.Membership.Property))} + end + + @impl true + def handle_event("delete", %{"id" => id}, socket) do + property = Ash.get!(Mv.Membership.Property, id) + Ash.destroy!(property) + + {:noreply, stream_delete(socket, :properties, property)} + end +end diff --git a/lib/mv_web/live/property_live/show.ex b/lib/mv_web/live/property_live/show.ex new file mode 100644 index 0000000..bc45ff5 --- /dev/null +++ b/lib/mv_web/live/property_live/show.ex @@ -0,0 +1,36 @@ +defmodule MvWeb.PropertyLive.Show do + use MvWeb, :live_view + + @impl true + def render(assigns) do + ~H""" + + <.header> + Property {@property.id} + <:subtitle>This is a property record from your database. + + <:actions> + <.button navigate={~p"/properties"}> + <.icon name="hero-arrow-left" /> + + <.button variant="primary" navigate={~p"/properties/#{@property}/edit?return_to=show"}> + <.icon name="hero-pencil-square" /> Edit Property + + + + + <.list> + <:item title="Id">{@property.id} + + + """ + end + + @impl true + def mount(%{"id" => id}, _session, socket) do + {:ok, + socket + |> assign(:page_title, "Show Property") + |> assign(:property, Ash.get!(Mv.Membership.Property, id))} + end +end diff --git a/lib/mv_web/live/property_type_live/form.ex b/lib/mv_web/live/property_type_live/form.ex new file mode 100644 index 0000000..6dfe7c9 --- /dev/null +++ b/lib/mv_web/live/property_type_live/form.ex @@ -0,0 +1,95 @@ +defmodule MvWeb.PropertyTypeLive.Form do + use MvWeb, :live_view + + @impl true + def render(assigns) do + ~H""" + + <.header> + {@page_title} + <:subtitle>Use this form to manage property_type records in your database. + + + <.form for={@form} id="property_type-form" phx-change="validate" phx-submit="save"> + <.input field={@form[:name]} type="text" label="Name" /><.input + field={@form[:value_type]} + type="select" + label="Value type" + options={ + Ash.Resource.Info.attribute(Mv.Membership.PropertyType, :value_type).constraints[:one_of] + } + /> + <.input field={@form[:description]} type="text" label="Description" /><.input + field={@form[:immutable]} + type="checkbox" + label="Immutable" + /><.input field={@form[:required]} type="checkbox" label="Required" /> + + <.button phx-disable-with="Saving..." variant="primary">Save Property type + <.button navigate={return_path(@return_to, @property_type)}>Cancel + + + """ + end + + @impl true + def mount(params, _session, socket) do + property_type = + case params["id"] do + nil -> nil + id -> Ash.get!(Mv.Membership.PropertyType, id) + end + + action = if is_nil(property_type), do: "New", else: "Edit" + page_title = action <> " " <> "Property type" + + {:ok, + socket + |> assign(:return_to, return_to(params["return_to"])) + |> assign(property_type: property_type) + |> assign(:page_title, page_title) + |> assign_form()} + end + + defp return_to("show"), do: "show" + defp return_to(_), do: "index" + + @impl true + def handle_event("validate", %{"property_type" => property_type_params}, socket) do + {:noreply, + assign(socket, form: AshPhoenix.Form.validate(socket.assigns.form, property_type_params))} + end + + def handle_event("save", %{"property_type" => property_type_params}, socket) do + case AshPhoenix.Form.submit(socket.assigns.form, params: property_type_params) do + {:ok, property_type} -> + notify_parent({:saved, property_type}) + + socket = + socket + |> put_flash(:info, "Property type #{socket.assigns.form.source.type}d successfully") + |> push_navigate(to: return_path(socket.assigns.return_to, property_type)) + + {:noreply, socket} + + {:error, form} -> + {:noreply, assign(socket, form: form)} + end + end + + defp notify_parent(msg), do: send(self(), {__MODULE__, msg}) + + defp assign_form(%{assigns: %{property_type: property_type}} = socket) do + form = + if property_type do + AshPhoenix.Form.for_update(property_type, :update, as: "property_type") + else + AshPhoenix.Form.for_create(Mv.Membership.PropertyType, :create, as: "property_type") + end + + assign(socket, form: to_form(form)) + end + + defp return_path("index", _property_type), do: ~p"/property_types" + defp return_path("show", property_type), do: ~p"/property_types/#{property_type.id}" +end diff --git a/lib/mv_web/live/property_type_live/index.ex b/lib/mv_web/live/property_type_live/index.ex new file mode 100644 index 0000000..ed9ff7d --- /dev/null +++ b/lib/mv_web/live/property_type_live/index.ex @@ -0,0 +1,64 @@ +defmodule MvWeb.PropertyTypeLive.Index do + use MvWeb, :live_view + + @impl true + def render(assigns) do + ~H""" + + <.header> + Listing Property types + <:actions> + <.button variant="primary" navigate={~p"/property_types/new"}> + <.icon name="hero-plus" /> New Property type + + + + + <.table + id="property_types" + rows={@streams.property_types} + row_click={fn {_id, property_type} -> JS.navigate(~p"/property_types/#{property_type}") end} + > + <:col :let={{_id, property_type}} label="Id">{property_type.id} + + <:col :let={{_id, property_type}} label="Name">{property_type.name} + + <:col :let={{_id, property_type}} label="Description">{property_type.description} + + <:action :let={{_id, property_type}}> +
+ <.link navigate={~p"/property_types/#{property_type}"}>Show +
+ + <.link navigate={~p"/property_types/#{property_type}/edit"}>Edit + + + <:action :let={{id, property_type}}> + <.link + phx-click={JS.push("delete", value: %{id: property_type.id}) |> hide("##{id}")} + data-confirm="Are you sure?" + > + Delete + + + +
+ """ + end + + @impl true + def mount(_params, _session, socket) do + {:ok, + socket + |> assign(:page_title, "Listing Property types") + |> stream(:property_types, Ash.read!(Mv.Membership.PropertyType))} + end + + @impl true + def handle_event("delete", %{"id" => id}, socket) do + property_type = Ash.get!(Mv.Membership.PropertyType, id) + Ash.destroy!(property_type) + + {:noreply, stream_delete(socket, :property_types, property_type)} + end +end diff --git a/lib/mv_web/live/property_type_live/show.ex b/lib/mv_web/live/property_type_live/show.ex new file mode 100644 index 0000000..027baa6 --- /dev/null +++ b/lib/mv_web/live/property_type_live/show.ex @@ -0,0 +1,43 @@ +defmodule MvWeb.PropertyTypeLive.Show do + use MvWeb, :live_view + + @impl true + def render(assigns) do + ~H""" + + <.header> + Property type {@property_type.id} + <:subtitle>This is a property_type record from your database. + + <:actions> + <.button navigate={~p"/property_types"}> + <.icon name="hero-arrow-left" /> + + <.button + variant="primary" + navigate={~p"/property_types/#{@property_type}/edit?return_to=show"} + > + <.icon name="hero-pencil-square" /> Edit Property type + + + + + <.list> + <:item title="Id">{@property_type.id} + + <:item title="Name">{@property_type.name} + + <:item title="Description">{@property_type.description} + + + """ + end + + @impl true + def mount(%{"id" => id}, _session, socket) do + {:ok, + socket + |> assign(:page_title, "Show Property type") + |> assign(:property_type, Ash.get!(Mv.Membership.PropertyType, id))} + end +end