Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Error Pages interface #61

Merged
merged 11 commits into from
Oct 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions lib/beacon/live_admin/content.ex
Original file line number Diff line number Diff line change
Expand Up @@ -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
276 changes: 276 additions & 0 deletions lib/beacon/live_admin/live/error_page_editor_live/index.ex
Original file line number Diff line number Diff line change
@@ -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"""
<div>
<.header>
<%= @page_title %>
<:actions>
<.button type="button" id="new-error-page-button" phx-click="create_new" class="uppercase">
New Error Page
</.button>
</:actions>
</.header>

<.main_content class="h-[calc(100vh_-_223px)]">
<.modal :if={@show_nav_modal} id="confirm-nav" on_cancel={JS.push("stay_here")} show>
<p>You've made unsaved changes to this error page!</p>
<p>Navigating to another error page without saving will cause these changes to be lost.</p>
<.button type="button" phx-click="stay_here">
Stay here
</.button>
<.button type="button" phx-click="discard_changes">
Discard changes
</.button>
</.modal>

<.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</.button>
</:actions>
</.simple_form>
</.modal>

<.modal :if={@show_delete_modal} id="delete-modal" on_cancel={JS.push("delete_cancel")} show>
<p>Are you sure you want to delete this error page?</p>
<.button type="button" id="confirm-delete-button" phx-click="delete_confirm">
Delete
</.button>
<.button type="button" phx-click="delete_cancel">
Cancel
</.button>
</.modal>

<div class="grid items-start grid-cols-1 grid-rows-1 mx-auto gap-x-8 gap-y-8 lg:mx-0 lg:max-w-none lg:grid-cols-3">
<div class="h-full lg:overflow-y-auto pb-4 lg:h-[calc(100vh_-_239px)]">
<.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) %>
</:col>
</.table>
</div>

<div :if={@form} class="w-full col-span-2">
<.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>
<.button id="delete-error-page-button" type="button" phx-click="delete" class="">Delete</.button>
</.form>

<div class="w-full mt-10 space-y-8">
<div class="py-6 rounded-[1.25rem] bg-[#0D1829] [&_.monaco-editor-background]:!bg-[#0D1829] [&_.margin]:!bg-[#0D1829]">
<LiveMonacoEditor.code_editor
path="error_page_template"
class="col-span-full lg:col-span-2"
value={@selected.template}
opts={Map.merge(LiveMonacoEditor.default_opts(), %{"language" => "html"})}
/>
</div>
</div>
</div>
</div>
</.main_content>
</div>
"""
end
end
14 changes: 11 additions & 3 deletions lib/beacon/live_admin/live/home_live.ex
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,13 @@ defmodule Beacon.LiveAdmin.HomeLive do
Layouts
</.link>

<.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>

<.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"
Expand All @@ -49,11 +56,12 @@ defmodule Beacon.LiveAdmin.HomeLive do
</.link>

<.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>

<.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"
Expand Down
6 changes: 4 additions & 2 deletions lib/beacon/live_admin/page_live.ex
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
13 changes: 10 additions & 3 deletions lib/beacon/live_admin/router.ex
Original file line number Diff line number Diff line change
Expand Up @@ -122,13 +122,19 @@ 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, %{}},
{"/layouts/:id/meta_tags", Beacon.LiveAdmin.LayoutEditorLive.MetaTags, :meta_tags, %{}},
{"/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, %{}},
Expand All @@ -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, %{}}
Expand Down
Loading
Loading