From 71028bbb2d0051d4c128eb7ce1733606240b9567 Mon Sep 17 00:00:00 2001 From: Andrew Berrien <74077809+APB9785@users.noreply.github.com> Date: Tue, 17 Oct 2023 16:03:16 -0500 Subject: [PATCH] Error Pages interface (#61) * Getting started * Liveview + tests * Success message * Fix test * Select from valid error statuses * update layout * remove unused dep * update deps --------- Co-authored-by: Leandro Pereira --- lib/beacon/live_admin/content.ex | 24 ++ .../live/error_page_editor_live/index.ex | 276 ++++++++++++++++++ lib/beacon/live_admin/live/home_live.ex | 14 +- lib/beacon/live_admin/page_live.ex | 6 +- lib/beacon/live_admin/router.ex | 13 +- mix.lock | 14 +- .../error_page_editor_live/index_test.exs | 79 +++++ test/support/fixtures.ex | 14 + 8 files changed, 425 insertions(+), 15 deletions(-) create mode 100644 lib/beacon/live_admin/live/error_page_editor_live/index.ex create mode 100644 test/beacon/live_admin/live/error_page_editor_live/index_test.exs diff --git a/lib/beacon/live_admin/content.ex b/lib/beacon/live_admin/content.ex index 0bb8e662..4e518102 100644 --- a/lib/beacon/live_admin/content.ex +++ b/lib/beacon/live_admin/content.ex @@ -162,4 +162,28 @@ defmodule Beacon.LiveAdmin.Content do def update_component(site, component, attrs) do call(site, Beacon.Content, :update_component, [component, attrs]) end + + def change_error_page(site, error_page, attrs \\ %{}) do + call(site, Beacon.Content, :change_error_page, [error_page, attrs]) + end + + def create_error_page(site, attrs) do + call(site, Beacon.Content, :create_error_page, [attrs]) + end + + def list_error_pages(site) do + call(site, Beacon.Content, :list_error_pages, [site]) + end + + def update_error_page(site, error_page, attrs) do + call(site, Beacon.Content, :update_error_page, [error_page, attrs]) + end + + def delete_error_page(site, error_page) do + call(site, Beacon.Content, :delete_error_page, [error_page]) + end + + def valid_error_statuses(site) do + call(site, Beacon.Content.ErrorPage, :valid_statuses, []) + end end diff --git a/lib/beacon/live_admin/live/error_page_editor_live/index.ex b/lib/beacon/live_admin/live/error_page_editor_live/index.ex new file mode 100644 index 00000000..6afeb76a --- /dev/null +++ b/lib/beacon/live_admin/live/error_page_editor_live/index.ex @@ -0,0 +1,276 @@ +defmodule Beacon.LiveAdmin.ErrorPageEditorLive.Index do + @moduledoc false + use Beacon.LiveAdmin.PageBuilder + + alias Beacon.LiveAdmin.Content + + on_mount({Beacon.LiveAdmin.Hooks.Authorized, {:error_pages, :index}}) + + def menu_link(_, :index), do: {:root, "Error Pages"} + + def handle_params(params, _uri, socket) do + %{beacon_page: %{site: site}} = socket.assigns + + socket = + socket + |> assign(page_title: "Error Pages") + |> assign(unsaved_changes: false) + |> assign(show_create_modal: false) + |> assign(show_nav_modal: false) + |> assign(show_delete_modal: false) + |> assign(show_status_change_field: false) + |> assign(create_form: to_form(%{}, as: :error_page)) + |> assign_new(:error_pages, fn -> Content.list_error_pages(site) end) + |> assign_new(:layouts, fn -> Content.list_layouts(site) end) + |> assign_selected(params["status"]) + |> assign_form() + + {:noreply, socket} + end + + def handle_event("select-" <> status, _, socket) do + %{beacon_page: %{site: site}} = socket.assigns + + path = beacon_live_admin_path(socket, site, "/error_pages/#{status}") + + if socket.assigns.unsaved_changes do + {:noreply, assign(socket, show_nav_modal: true, confirm_nav_path: path)} + else + {:noreply, push_redirect(socket, to: path)} + end + end + + def handle_event("error_page_template_editor_lost_focus", %{"value" => template}, socket) do + %{selected: selected, beacon_page: %{site: site}, form: form} = socket.assigns + + changeset = + site + |> Content.change_error_page(selected, %{ + "site" => site, + "template" => template, + "status" => form.params["status"] || Map.fetch!(form.data, :status), + "layout_id" => form.params["layout_id"] || Map.fetch!(form.data, :layout_id) + }) + |> Map.put(:action, :validate) + + socket = + socket + |> assign(form: to_form(changeset)) + |> assign(changed_template: template) + |> assign(unsaved_changes: !(changeset.changes == %{})) + + {:noreply, socket} + end + + def handle_event("create_new", _, socket) do + {:noreply, assign(socket, show_create_modal: true)} + end + + def handle_event("change_status", _, socket) do + {:noreply, assign(socket, show_status_change_field: true)} + end + + def handle_event("save_new", %{"status" => status}, socket) do + %{beacon_page: %{site: site}, layouts: layouts} = socket.assigns + + attrs = %{ + "status" => status, + "site" => site, + "template" => "Something went wrong", + "layout_id" => Enum.find(layouts, &(&1.title == "Default")).id + } + + socket = + case Content.create_error_page(site, attrs) do + {:ok, _} -> + socket + |> assign(error_pages: Content.list_error_pages(site)) + |> assign_selected(status) + |> assign(show_create_modal: false) + |> push_redirect(to: beacon_live_admin_path(socket, site, "/error_pages/#{status}")) + + {:error, changeset} -> + assign(socket, create_form: to_form(changeset)) + end + + {:noreply, socket} + end + + def handle_event("save_changes", %{"error_page" => params}, socket) do + %{selected: selected, beacon_page: %{site: site}} = socket.assigns + + attrs = %{layout_id: params["layout_id"], template: params["template"]} + + socket = + case Content.update_error_page(site, selected, attrs) do + {:ok, updated_error_page} -> + socket + |> assign_error_page_update(updated_error_page) + |> assign_selected(selected.status) + |> assign_form() + |> assign(unsaved_changes: false) + |> put_flash(:info, "Error page updated successfully") + + {:error, changeset} -> + changeset = Map.put(changeset, :action, :update) + assign(socket, form: to_form(changeset)) + end + + {:noreply, socket} + end + + def handle_event("delete", _, socket) do + {:noreply, assign(socket, show_delete_modal: true)} + end + + def handle_event("delete_confirm", _, socket) do + %{selected: error_page, beacon_page: %{site: site}} = socket.assigns + + {:ok, _} = Content.delete_error_page(site, error_page) + + socket = + socket + |> assign(error_pages: Content.list_error_pages(site)) + |> push_patch(to: beacon_live_admin_path(socket, site, "/error_pages")) + + {:noreply, socket} + end + + def handle_event("delete_cancel", _, socket) do + {:noreply, assign(socket, show_delete_modal: false)} + end + + def handle_event("stay_here", _params, socket) do + {:noreply, assign(socket, show_nav_modal: false, confirm_nav_path: nil)} + end + + def handle_event("discard_changes", _params, socket) do + {:noreply, push_redirect(socket, to: socket.assigns.confirm_nav_path)} + end + + def handle_event("cancel_create", _params, socket) do + {:noreply, assign(socket, show_create_modal: false)} + end + + defp assign_selected(socket, nil) do + case socket.assigns.error_pages do + [] -> assign(socket, selected: nil, changed_template: "") + [hd | _] -> assign(socket, selected: hd, changed_template: hd.template) + end + end + + defp assign_selected(socket, status) when is_binary(status) do + assign_selected(socket, String.to_integer(status)) + end + + defp assign_selected(socket, status) when is_integer(status) do + selected = Enum.find(socket.assigns.error_pages, &(&1.status == status)) + assign(socket, selected: selected, changed_template: selected.template) + end + + defp assign_form(socket) do + form = + case socket.assigns do + %{selected: nil} -> + nil + + %{selected: selected, beacon_page: %{site: site}} -> + site + |> Content.change_error_page(selected) + |> to_form() + end + + assign(socket, form: form) + end + + defp assign_error_page_update(socket, updated_error_page) do + %{id: error_page_id} = updated_error_page + + error_pages = + Enum.map(socket.assigns.error_pages, fn + %{id: ^error_page_id} -> updated_error_page + other -> other + end) + + assign(socket, error_pages: error_pages) + end + + def render(assigns) do + ~H""" +
+ <.header> + <%= @page_title %> + <:actions> + <.button type="button" id="new-error-page-button" phx-click="create_new" class="uppercase"> + New Error Page + + + + + <.main_content class="h-[calc(100vh_-_223px)]"> + <.modal :if={@show_nav_modal} id="confirm-nav" on_cancel={JS.push("stay_here")} show> +

You've made unsaved changes to this error page!

+

Navigating to another error page without saving will cause these changes to be lost.

+ <.button type="button" phx-click="stay_here"> + Stay here + + <.button type="button" phx-click="discard_changes"> + Discard changes + + + + <.modal :if={@show_create_modal} id="create-modal" on_cancel={JS.push("cancel_create")} show> + <.simple_form :let={f} for={@create_form} id="create-form" phx-submit="save_new"> + <.input field={f[:status]} type="select" label="Status code for new error page:" options={Content.valid_error_statuses(@beacon_page.site)} /> + <:actions> + <.button>Save + + + + + <.modal :if={@show_delete_modal} id="delete-modal" on_cancel={JS.push("delete_cancel")} show> +

Are you sure you want to delete this error page?

+ <.button type="button" id="confirm-delete-button" phx-click="delete_confirm"> + Delete + + <.button type="button" phx-click="delete_cancel"> + Cancel + + + +
+
+ <.table id="error-pages" rows={@error_pages} row_click={fn row -> "select-#{row.status}" end}> + <:col :let={error_page} label="status"> + <%= Map.fetch!(error_page, :status) %> + + +
+ +
+ <.form :let={f} for={@form} id="error-page-form" class="flex items-end gap-4" phx-submit="save_changes"> + <.input label="Status" field={f[:status]} type="text" disabled readonly /> + <.input label="Layout" field={f[:layout_id]} options={Enum.map(@layouts, &{&1.title, &1.id})} value={@selected.layout_id} type="select" /> + <.input type="hidden" field={f[:template]} name="error_page[template]" id="error_page-form_template" value={@changed_template} /> + + <.button phx-disable-with="Saving..." class="ml-auto">Save Changes + <.button id="delete-error-page-button" type="button" phx-click="delete" class="">Delete + + +
+
+ "html"})} + /> +
+
+
+
+ +
+ """ + end +end diff --git a/lib/beacon/live_admin/live/home_live.ex b/lib/beacon/live_admin/live/home_live.ex index eda24cac..8aefe83e 100644 --- a/lib/beacon/live_admin/live/home_live.ex +++ b/lib/beacon/live_admin/live/home_live.ex @@ -41,6 +41,13 @@ defmodule Beacon.LiveAdmin.HomeLive do Layouts + <.link + href={Beacon.LiveAdmin.Router.beacon_live_admin_path(@socket, site, "/components")} + class="whitespace-nowrap text-sm leading-5 py-3.5 font-bold tracking-widest text-center uppercase bg-blue-600 rounded-lg hover:bg-blue-700 focus:outline-none focus-visible:ring-4 focus-visible:ring-blue-200 active:bg-blue-800 px-6 text-gray-50" + > + Components + + <.link href={Beacon.LiveAdmin.Router.beacon_live_admin_path(@socket, site, "/pages")} class="whitespace-nowrap text-sm leading-5 py-3.5 font-bold tracking-widest text-center uppercase bg-blue-600 rounded-lg hover:bg-blue-700 focus:outline-none focus-visible:ring-4 focus-visible:ring-blue-200 active:bg-blue-800 px-6 text-gray-50" @@ -49,11 +56,12 @@ defmodule Beacon.LiveAdmin.HomeLive do <.link - href={Beacon.LiveAdmin.Router.beacon_live_admin_path(@socket, site, "/components")} - class="whitespace-nowrap text-sm leading-5 py-3.5 font-bold tracking-widest text-center uppercase bg-blue-600 rounded-lg hover:bg-blue-700 focus:outline-none focus-visible:ring-4 focus-visible:ring-blue-200 active:bg-blue-800 px-6 text-gray-50" + href={Beacon.LiveAdmin.Router.beacon_live_admin_path(@socket, site, "/error_pages")} + class="whitespace-nowrap text-sm leading-5 py-3.5 font-bold tracking-widest text-center uppercase bg-blue-600 rounded-lg hover:bg-blue-700 focus:outline-none focus-visible:ring-4 focus-visible:ring-blue-200 active:bg-blue-800 px-6 text-gray-50" > - Components + Error Pages + <.link href={Beacon.LiveAdmin.Router.beacon_live_admin_path(@socket, site, "/media_library")} class="whitespace-nowrap text-sm leading-5 py-3.5 font-bold tracking-widest text-center uppercase bg-blue-600 rounded-lg hover:bg-blue-700 focus:outline-none focus-visible:ring-4 focus-visible:ring-blue-200 active:bg-blue-800 px-6 text-gray-50" diff --git a/lib/beacon/live_admin/page_live.ex b/lib/beacon/live_admin/page_live.ex index 549ab39c..2e1c0256 100644 --- a/lib/beacon/live_admin/page_live.ex +++ b/lib/beacon/live_admin/page_live.ex @@ -148,10 +148,12 @@ defmodule Beacon.LiveAdmin.PageLive do case {a, b} do {"/layouts", _} -> true {_, "/layouts"} -> false - {"/pages", _} -> true - {_, "/pages"} -> false {"/components", _} -> true {_, "/components"} -> false + {"/pages", _} -> true + {_, "/pages"} -> false + {"/error_pages", _} -> true + {_, "/error_pages"} -> false {"/media_library", _} -> true {_, "/media_library"} -> false {a, b} -> a <= b diff --git a/lib/beacon/live_admin/router.ex b/lib/beacon/live_admin/router.ex index b01fb021..4c156e45 100644 --- a/lib/beacon/live_admin/router.ex +++ b/lib/beacon/live_admin/router.ex @@ -122,6 +122,7 @@ defmodule Beacon.LiveAdmin.Router do end) [ + # layouts {"/layouts", Beacon.LiveAdmin.LayoutEditorLive.Index, :index, %{}}, {"/layouts/new", Beacon.LiveAdmin.LayoutEditorLive.New, :new, %{}}, {"/layouts/:id", Beacon.LiveAdmin.LayoutEditorLive.Edit, :edit, %{}}, @@ -129,6 +130,11 @@ defmodule Beacon.LiveAdmin.Router do {"/layouts/:id/revisions", Beacon.LiveAdmin.LayoutEditorLive.Revisions, :revisions, %{}}, {"/layouts/:id/resource_links", Beacon.LiveAdmin.LayoutEditorLive.ResourceLinks, :resource_links, %{}}, + # components + {"/components", Beacon.LiveAdmin.ComponentEditorLive.Index, :index, %{}}, + {"/components/new", Beacon.LiveAdmin.ComponentEditorLive.New, :new, %{}}, + {"/components/:id", Beacon.LiveAdmin.ComponentEditorLive.Edit, :edit, %{}}, + # pages {"/pages", Beacon.LiveAdmin.PageEditorLive.Index, :index, %{}}, {"/pages/new", Beacon.LiveAdmin.PageEditorLive.New, :new, %{}}, {"/pages/:id", Beacon.LiveAdmin.PageEditorLive.Edit, :edit, %{}}, @@ -141,9 +147,10 @@ defmodule Beacon.LiveAdmin.Router do {"/pages/:page_id/variants", Beacon.LiveAdmin.PageEditorLive.Variants, :variants, %{}}, {"/pages/:page_id/variants/:variant_id", Beacon.LiveAdmin.PageEditorLive.Variants, :variants, %{}}, - {"/components", Beacon.LiveAdmin.ComponentEditorLive.Index, :index, %{}}, - {"/components/new", Beacon.LiveAdmin.ComponentEditorLive.New, :new, %{}}, - {"/components/:id", Beacon.LiveAdmin.ComponentEditorLive.Edit, :edit, %{}}, + # error pages + {"/error_pages", Beacon.LiveAdmin.ErrorPageEditorLive.Index, :index, %{}}, + {"/error_pages/:status", Beacon.LiveAdmin.ErrorPageEditorLive.Index, :index, %{}}, + # media library {"/media_library", Beacon.LiveAdmin.MediaLibraryLive.Index, :index, %{}}, {"/media_library/upload", Beacon.LiveAdmin.MediaLibraryLive.Index, :upload, %{}}, {"/media_library/:id", Beacon.LiveAdmin.MediaLibraryLive.Index, :show, %{}} diff --git a/mix.lock b/mix.lock index c0ef99fe..6230b825 100644 --- a/mix.lock +++ b/mix.lock @@ -1,7 +1,6 @@ %{ "accent": {:hex, :accent, "1.1.1", "20257356446d45078b19b91608f74669b407b39af891ee3db9ee6824d1cae19d", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:plug, "~> 1.3", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "6d5afa50d4886e3370e04fa501468cbaa6c4b5fe926f72ccfa844ad9e259adae"}, - "beacon": {:git, "https://github.com/beaconCMS/beacon.git", "884fbf717eefc0c9fb51cbc795410e453fa50e17", []}, - "brotli": {:hex, :brotli, "0.3.2", "59cf45a399098516f1d34f70d8e010e5c9bf326659d3ef34c7cc56793339002b", [:rebar3], [], "hexpm", "9ec3ef9c753f80d0c657b4905193c55e5198f169fa1d1c044d8601d4d931a2ad"}, + "beacon": {:git, "https://github.com/beaconCMS/beacon.git", "f5681121068b642a0e9cd691bce94fd1e7549c6d", []}, "castore": {:hex, :castore, "1.0.4", "ff4d0fb2e6411c0479b1d965a814ea6d00e51eb2f58697446e9c41a97d940b28", [:mix], [], "hexpm", "9418c1b8144e11656f0be99943db4caf04612e3eaecefb5dae9a2a87565584f8"}, "cc_precompiler": {:hex, :cc_precompiler, "0.1.7", "77de20ac77f0e53f20ca82c563520af0237c301a1ec3ab3bc598e8a96c7ee5d9", [:mix], [{:elixir_make, "~> 0.7.3", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "2768b28bf3c2b4f788c995576b39b8cb5d47eb788526d93bd52206c1d8bf4b75"}, "cowboy": {:hex, :cowboy, "2.10.0", "ff9ffeff91dae4ae270dd975642997afe2a1179d94b1887863e43f681a203e26", [:make, :rebar3], [{:cowlib, "2.12.1", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "1.8.0", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "3afdccb7183cc6f143cb14d3cf51fa00e53db9ec80cdcd525482f5e99bc41d6b"}, @@ -17,6 +16,7 @@ "esbuild": {:hex, :esbuild, "0.7.1", "fa0947e8c3c3c2f86c9bf7e791a0a385007ccd42b86885e8e893bdb6631f5169", [:mix], [{:castore, ">= 0.0.0", [hex: :castore, repo: "hexpm", optional: false]}], "hexpm", "66661cdf70b1378ee4dc16573fcee67750b59761b2605a0207c267ab9d19f13c"}, "ex_aws": {:hex, :ex_aws, "2.4.3", "6c6d88ba7b9c07e3b0f4b70406d5fccb9f5358f5ef18138f7bd396f7863e8255", [:mix], [{:configparser_ex, "~> 4.0", [hex: :configparser_ex, repo: "hexpm", optional: true]}, {:hackney, "~> 1.16", [hex: :hackney, repo: "hexpm", optional: true]}, {:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: true]}, {:jsx, "~> 2.8 or ~> 3.0", [hex: :jsx, repo: "hexpm", optional: true]}, {:mime, "~> 1.2 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:sweet_xml, "~> 0.7", [hex: :sweet_xml, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "67f61f8b6aec740150d483a21f551fabce26a481d9917305ed2bb47717007519"}, "ex_aws_s3": {:hex, :ex_aws_s3, "2.4.0", "ce8decb6b523381812798396bc0e3aaa62282e1b40520125d1f4eff4abdff0f4", [:mix], [{:ex_aws, "~> 2.0", [hex: :ex_aws, repo: "hexpm", optional: false]}, {:sweet_xml, ">= 0.0.0", [hex: :sweet_xml, repo: "hexpm", optional: true]}], "hexpm", "85dda6e27754d94582869d39cba3241d9ea60b6aa4167f9c88e309dc687e56bb"}, + "ex_brotli": {:hex, :ex_brotli, "0.3.0", "69d5f3720df70d5c89d1395d8fbe49ba37466b626834aaf6d77c72e0c93cf975", [:mix], [{:phoenix, ">= 0.0.0", [hex: :phoenix, repo: "hexpm", optional: false]}, {:rustler, "~> 0.29", [hex: :rustler, repo: "hexpm", optional: true]}, {:rustler_precompiled, "~> 0.6", [hex: :rustler_precompiled, repo: "hexpm", optional: false]}], "hexpm", "8e46982f7d20069419ca8c8c54f9f3ebd9fa0e1d094c54cbf8ce3d636d84dfa7"}, "expo": {:hex, :expo, "0.4.1", "1c61d18a5df197dfda38861673d392e642649a9cef7694d2f97a587b2cfb319b", [:mix], [], "hexpm", "2ff7ba7a798c8c543c12550fa0e2cbc81b95d4974c65855d8d15ba7b37a1ce47"}, "file_system": {:hex, :file_system, "0.2.10", "fb082005a9cd1711c05b5248710f8826b02d7d1784e7c3451f9c1231d4fc162d", [:mix], [], "hexpm", "41195edbfb562a593726eda3b3e8b103a309b733ad25f3d642ba49696bf715dc"}, "floki": {:hex, :floki, "0.34.3", "5e2dcaec5d7c228ce5b1d3501502e308b2d79eb655e4191751a1fe491c37feac", [:mix], [], "hexpm", "9577440eea5b97924b4bf3c7ea55f7b8b6dce589f9b28b096cc294a8dc342341"}, @@ -29,16 +29,16 @@ "mime": {:hex, :mime, "2.0.5", "dc34c8efd439abe6ae0343edbb8556f4d63f178594894720607772a041b04b02", [:mix], [], "hexpm", "da0d64a365c45bc9935cc5c8a7fc5e49a0e0f9932a761c55d6c52b142780a05c"}, "mix_test_watch": {:hex, :mix_test_watch, "1.1.0", "330bb91c8ed271fe408c42d07e0773340a7938d8a0d281d57a14243eae9dc8c3", [:mix], [{:file_system, "~> 0.2.1 or ~> 0.3", [hex: :file_system, repo: "hexpm", optional: false]}], "hexpm", "52b6b1c476cbb70fd899ca5394506482f12e5f6b0d6acff9df95c7f1e0812ec3"}, "nimble_parsec": {:hex, :nimble_parsec, "1.3.1", "2c54013ecf170e249e9291ed0a62e5832f70a476c61da16f6aac6dca0189f2af", [:mix], [], "hexpm", "2682e3c0b2eb58d90c6375fc0cc30bc7be06f365bf72608804fb9cffa5e1b167"}, - "phoenix": {:hex, :phoenix, "1.7.7", "4cc501d4d823015007ba3cdd9c41ecaaf2ffb619d6fb283199fa8ddba89191e0", [:mix], [{:castore, ">= 0.0.0", [hex: :castore, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 2.1", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: true]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.6", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:plug_crypto, "~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:websock_adapter, "~> 0.5.3", [hex: :websock_adapter, repo: "hexpm", optional: false]}], "hexpm", "8966e15c395e5e37591b6ed0bd2ae7f48e961f0f60ac4c733f9566b519453085"}, + "phoenix": {:hex, :phoenix, "1.7.9", "9a2b873e2cb3955efdd18ad050f1818af097fa3f5fc3a6aaba666da36bdd3f02", [:mix], [{:castore, ">= 0.0.0", [hex: :castore, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 2.1", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: true]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.6", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:plug_crypto, "~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:websock_adapter, "~> 0.5.3", [hex: :websock_adapter, repo: "hexpm", optional: false]}], "hexpm", "83e32da028272b4bfd076c61a964e6d2b9d988378df2f1276a0ed21b13b5e997"}, "phoenix_ecto": {:hex, :phoenix_ecto, "4.4.2", "b21bd01fdeffcfe2fab49e4942aa938b6d3e89e93a480d4aee58085560a0bc0d", [:mix], [{:ecto, "~> 3.5", [hex: :ecto, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.14.2 or ~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:plug, "~> 1.9", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "70242edd4601d50b69273b057ecf7b684644c19ee750989fd555625ae4ce8f5d"}, - "phoenix_html": {:hex, :phoenix_html, "3.3.2", "d6ce982c6d8247d2fc0defe625255c721fb8d5f1942c5ac051f6177bffa5973f", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "44adaf8e667c1c20fb9d284b6b0fa8dc7946ce29e81ce621860aa7e96de9a11d"}, + "phoenix_html": {:hex, :phoenix_html, "3.3.3", "380b8fb45912b5638d2f1d925a3771b4516b9a78587249cabe394e0a5d579dc9", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "923ebe6fec6e2e3b3e569dfbdc6560de932cd54b000ada0208b5f45024bdd76c"}, "phoenix_live_reload": {:hex, :phoenix_live_reload, "1.4.1", "2aff698f5e47369decde4357ba91fc9c37c6487a512b41732818f2204a8ef1d3", [:mix], [{:file_system, "~> 0.2.1 or ~> 0.3", [hex: :file_system, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.4", [hex: :phoenix, repo: "hexpm", optional: false]}], "hexpm", "9bffb834e7ddf08467fe54ae58b5785507aaba6255568ae22b4d46e2bb3615ab"}, - "phoenix_live_view": {:hex, :phoenix_live_view, "0.19.5", "6e730595e8e9b8c5da230a814e557768828fd8dfeeb90377d2d8dbb52d4ec00a", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix, "~> 1.6.15 or ~> 1.7.0", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 3.3", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "b2eaa0dd3cfb9bd7fb949b88217df9f25aed915e986a28ad5c8a0d054e7ca9d3"}, + "phoenix_live_view": {:hex, :phoenix_live_view, "0.20.1", "92a37acf07afca67ac98bd326532ba8f44ad7d4bdf3e4361b03f7f02594e5ae9", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix, "~> 1.6.15 or ~> 1.7.0", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 3.3", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: true]}, {:plug, "~> 1.15", [hex: :plug, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "be494fd1215052729298b0e97d5c2ce8e719c00854b82cd8cf15c1cd7fcf6294"}, "phoenix_pubsub": {:hex, :phoenix_pubsub, "2.1.3", "3168d78ba41835aecad272d5e8cd51aa87a7ac9eb836eabc42f6e57538e3731d", [:mix], [], "hexpm", "bba06bc1dcfd8cb086759f0edc94a8ba2bc8896d5331a1e2c2902bf8e36ee502"}, "phoenix_template": {:hex, :phoenix_template, "1.0.3", "32de561eefcefa951aead30a1f94f1b5f0379bc9e340bb5c667f65f1edfa4326", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}], "hexpm", "16f4b6588a4152f3cc057b9d0c0ba7e82ee23afa65543da535313ad8d25d8e2c"}, - "plug": {:hex, :plug, "1.14.2", "cff7d4ec45b4ae176a227acd94a7ab536d9b37b942c8e8fa6dfc0fff98ff4d80", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "842fc50187e13cf4ac3b253d47d9474ed6c296a8732752835ce4a86acdf68d13"}, + "plug": {:hex, :plug, "1.15.1", "b7efd81c1a1286f13efb3f769de343236bd8b7d23b4a9f40d3002fc39ad8f74c", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "459497bd94d041d98d948054ec6c0b76feacd28eec38b219ca04c0de13c79d30"}, "plug_cowboy": {:hex, :plug_cowboy, "2.6.1", "9a3bbfceeb65eff5f39dab529e5cd79137ac36e913c02067dba3963a26efe9b2", [:mix], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:cowboy_telemetry, "~> 0.3", [hex: :cowboy_telemetry, repo: "hexpm", optional: false]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "de36e1a21f451a18b790f37765db198075c25875c64834bcc82d90b309eb6613"}, - "plug_crypto": {:hex, :plug_crypto, "1.2.5", "918772575e48e81e455818229bf719d4ab4181fcbf7f85b68a35620f78d89ced", [:mix], [], "hexpm", "26549a1d6345e2172eb1c233866756ae44a9609bd33ee6f99147ab3fd87fd842"}, + "plug_crypto": {:hex, :plug_crypto, "2.0.0", "77515cc10af06645abbfb5e6ad7a3e9714f805ae118fa1a70205f80d2d70fe73", [:mix], [], "hexpm", "53695bae57cc4e54566d993eb01074e4d894b65a3766f1c43e2c61a1b0f45ea9"}, "postgrex": {:hex, :postgrex, "0.17.1", "01c29fd1205940ee55f7addb8f1dc25618ca63a8817e56fac4f6846fc2cddcbe", [:mix], [{:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "14b057b488e73be2beee508fb1955d8db90d6485c6466428fe9ccf1d6692a555"}, "ranch": {:hex, :ranch, "1.8.0", "8c7a100a139fd57f17327b6413e4167ac559fbc04ca7448e9be9057311597a1d", [:make, :rebar3], [], "hexpm", "49fbcfd3682fab1f5d109351b61257676da1a2fdbe295904176d5e521a2ddfe5"}, "rustler_precompiled": {:hex, :rustler_precompiled, "0.7.0", "5d0834fc06dbc76dd1034482f17b1797df0dba9b491cef8bb045fcaca94bcade", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: false]}, {:rustler, "~> 0.23", [hex: :rustler, repo: "hexpm", optional: true]}], "hexpm", "fdf43a6835f4e4de5bfbc4c019bfb8c46d124bd4635fefa3e20d9a2bbbec1512"}, diff --git a/test/beacon/live_admin/live/error_page_editor_live/index_test.exs b/test/beacon/live_admin/live/error_page_editor_live/index_test.exs new file mode 100644 index 00000000..9fa431bb --- /dev/null +++ b/test/beacon/live_admin/live/error_page_editor_live/index_test.exs @@ -0,0 +1,79 @@ +defmodule Beacon.LiveAdmin.ErrorPageEditorLive.IndexTest do + use Beacon.LiveAdmin.ConnCase, async: false + + import Beacon.LiveAdminTest.Cluster, only: [rpc: 4] + + setup do + default_layout = layout_fixture(node1(), %{title: "Default"}) + another_layout = layout_fixture(node1(), %{title: "Another"}) + attrs = %{status: 404, layout_id: default_layout.id, template: "Not Found"} + error_page_fixture(node1(), attrs) + attrs = %{status: 500, layout_id: default_layout.id, template: "Internal Server Error"} + error_page_fixture(node1(), attrs) + + on_exit(fn -> + rpc(node1(), Beacon.Repo, :delete_all, [Beacon.Content.ErrorPage, [log: false]]) + rpc(node1(), Beacon.Repo, :delete_all, [Beacon.Content.Layout, [log: false]]) + end) + + [another_layout: another_layout] + end + + test "select error page via path", %{conn: conn} do + {:ok, view, _html} = live(conn, "/admin/site_a/error_pages") + assert has_element?(view, "input[name='error_page[status]'][value=404]") + + {:ok, view, _html} = live(conn, "/admin/site_a/error_pages/404") + assert has_element?(view, "input[name='error_page[status]'][value=404]") + + {:ok, view, _html} = live(conn, "/admin/site_a/error_pages/500") + assert has_element?(view, "input[name='error_page[status]'][value=500]") + end + + test "create a new error page", %{conn: conn} do + {:ok, view, _html} = live(conn, "/admin/site_a/error_pages") + + view |> element("#new-error-page-button") |> render_click() + + assert has_element?(view, "#create-modal") + + {:ok, view, _html} = + view + |> form("#create-form", %{status: 400}) + |> render_submit() + |> follow_redirect(conn, "/admin/site_a/error_pages/400") + + refute has_element?(view, "#create-modal") + assert has_element?(view, "input[name='error_page[status]'][value=400]") + end + + test "update an error page", %{conn: conn, another_layout: layout} do + {:ok, view, html} = live(conn, "/admin/site_a/error_pages/404") + + assert has_element?(view, "[selected=\"selected\"]", "Default") + + view + |> form("#error-page-form", error_page: %{layout_id: layout.id}) + |> render_submit() + + assert has_element?(view, "p", "Error page updated successfully") + + refute has_element?(view, "[selected=\"selected\"]", "Default") + assert has_element?(view, "[selected=\"selected\"]", "Another") + end + + test "delete error page", %{conn: conn} do + {:ok, view, _html} = live(conn, "/admin/site_a/error_pages/500") + + assert has_element?(view, "span", "500") + + view |> element("#delete-error-page-button") |> render_click() + + assert has_element?(view, "#delete-modal") + + view |> element("#confirm-delete-button") |> render_click() + + refute has_element?(view, "#delete-modal") + refute has_element?(view, "span", "500") + end +end diff --git a/test/support/fixtures.ex b/test/support/fixtures.ex index 4e62708e..2acec53d 100644 --- a/test/support/fixtures.ex +++ b/test/support/fixtures.ex @@ -42,6 +42,20 @@ defmodule Beacon.LiveAdmin.Fixtures do rpc(node, Beacon.Content, :create_page!, [attrs]) end + def error_page_fixture(node \\ node1(), attrs \\ %{}) do + layout_id = get_lazy(attrs, :layout_id, fn -> layout_fixture().id end) + + attrs = + Enum.into(attrs, %{ + site: "site_a", + status: Enum.random(111..999), + layout_id: layout_id, + template: "Oops" + }) + + rpc(node, Beacon.Content, :create_error_page!, [attrs]) + end + def media_library_asset_fixture(node \\ node1(), attrs \\ %{}) do file_metadata = file_metadata_fixture(node, attrs) rpc(node, Beacon.MediaLibrary, :upload, [file_metadata])