Skip to content

Commit

Permalink
Add Unpublish button to page editor (#319)
Browse files Browse the repository at this point in the history
* add unpublish button and status to page editor

* changelog

* avoid fetching latest page event for new page form

* don't navigate to index

* fix test

* improve status badge

* only show unpublish button for published pages

* auto format code

---------

Co-authored-by: APB9785 <APB9785@users.noreply.github.com>
  • Loading branch information
APB9785 and APB9785 authored Jan 3, 2025
1 parent 05726e7 commit f0aa90c
Show file tree
Hide file tree
Showing 7 changed files with 131 additions and 71 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
### Enhancements
- Updated UI styling with StationUI
- Unsaved changes will now be saved when publishing, instead of discarded
- Adds Unpublish button to Page editor
- Page editor now shows draft/published status

## 0.3.1 (2024-12-12)

Expand Down
4 changes: 4 additions & 0 deletions lib/beacon/live_admin/client/content.ex
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,10 @@ defmodule Beacon.LiveAdmin.Client.Content do
call(site, Beacon.Content, :publish_page, [site, id])
end

def unpublish_page(page) do
call(page.site, Beacon.Content, :unpublish_page, [page])
end

def get_page(site, id) do
call(site, Beacon.Content, :get_page, [site, id])
end
Expand Down
140 changes: 88 additions & 52 deletions lib/beacon/live_admin/live/page_editor_live/form_component.ex
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,14 @@ defmodule Beacon.LiveAdmin.PageEditorLive.FormComponent do
def update(%{site: site, page: page} = assigns, socket) do
changeset =
case socket.assigns do
%{form: form} ->
Content.change_page(site, page, form.params)
%{form: form} -> Content.change_page(site, page, form.params)
_ -> Content.change_page(site, page)
end

_ ->
Content.change_page(site, page)
page_status =
case page.id && Content.get_latest_page_event(site, page.id) do
nil -> nil
%{event: event} -> event
end

{:ok,
Expand All @@ -27,6 +30,7 @@ defmodule Beacon.LiveAdmin.PageEditorLive.FormComponent do
|> assign_template(Changeset.get_field(changeset, :template))
|> maybe_assign_builder_page(changeset)
|> assign(:language, language(page.format))
|> assign(:page_status, page_status)
|> assign_extra_fields(changeset)
|> assign_new(:show_modal, fn -> nil end)
|> assign_new(:tailwind_config, fn -> RuntimeCSS.asset_url(site) end)
Expand Down Expand Up @@ -118,6 +122,24 @@ defmodule Beacon.LiveAdmin.PageEditorLive.FormComponent do
{:noreply, assign(socket, show_modal: nil)}
end

def handle_event("unpublish", _params, socket) do
%{site: site, page: page} = socket.assigns

socket =
case Content.unpublish_page(page) do
{:ok, _page} -> put_flash(socket, :info, "Page unpublished successfully")
{:error, _changeset} -> put_flash(socket, :error, "Something went wrong")
end

socket =
socket
|> assign(show_modal: nil)
|> assign(page_status: :unpublished)
|> push_patch(to: beacon_live_admin_path(socket, site, "/pages/#{page.id}"))

{:noreply, socket}
end

def handle_event("validate", %{"page" => page_params}, socket) do
changeset =
socket.assigns.site
Expand All @@ -131,65 +153,33 @@ defmodule Beacon.LiveAdmin.PageEditorLive.FormComponent do
|> assign_extra_fields(changeset)}
end

def handle_event("save", %{"save" => "save", "page" => page_params}, socket) do
%{site: site, template: template, live_action: live_action} = socket.assigns
def handle_event("save", %{"save" => user_action, "page" => page_params}, socket) do
%{site: site, template: template, page: page, live_action: live_action} = socket.assigns
page_params = Map.merge(page_params, %{"site" => site, "template" => template})

save_page(socket, live_action, page_params)
end

def handle_event("save", %{"save" => "publish", "page" => page_params}, socket) do
%{site: site, page: page, live_action: live_action} = socket.assigns

save_result =
case live_action do
:new -> Content.create_page(site, page_params)
:edit -> Content.update_page(site, page, page_params)
end

maybe_publish_fn =
case user_action do
"save" -> fn _, _ -> {:ok, []} end
"publish" -> &Content.publish_page/2
end

with {:ok, page} <- save_result,
{:ok, _} <- Content.publish_page(site, page.id) do
{:ok, _} <- maybe_publish_fn.(site, page.id) do
{:noreply,
socket
|> put_flash(:info, "Page published successfully")
|> push_navigate(to: beacon_live_admin_path(socket, site, "/pages"), replace: true)}
|> assign(page: page, show_modal: nil)
|> update(:page_status, &if(user_action == "publish", do: :published, else: &1))
|> put_flash(:info, "Page #{String.trim_trailing(user_action, "e")}ed successfully")
|> push_patch(to: beacon_live_admin_path(socket, site, "/pages/#{page.id}"))}
else
{:error, changeset} ->
changeset = Map.put(changeset, :action, :publish)
{:noreply, assign_form(socket, changeset)}
end
end

defp save_page(socket, :new, page_params) do
case Content.create_page(socket.assigns.site, page_params) do
{:ok, page} ->
to = beacon_live_admin_path(socket, socket.assigns.site, "/pages/#{page.id}")

{:noreply,
socket
|> put_flash(:info, "Page created successfully")
|> push_navigate(to: to)}

{:error, changeset} ->
changeset = Map.put(changeset, :action, :insert)
{:noreply, assign_form(socket, changeset)}
end
end

defp save_page(socket, :edit, page_params) do
case Content.update_page(socket.assigns.site, socket.assigns.page, page_params) do
{:ok, page} ->
changeset = Content.change_page(socket.assigns.site, page)

{:noreply,
socket
|> assign(:page, page)
|> assign_form(changeset)
|> assign_extra_fields(changeset)
|> put_flash(:info, "Page updated successfully")}

{:error, changeset} ->
changeset = Map.put(changeset, :action, :update)
changeset = Map.put(changeset, :action, :save)
{:noreply, assign_form(socket, changeset)}
end
end
Expand Down Expand Up @@ -265,15 +255,21 @@ defmodule Beacon.LiveAdmin.PageEditorLive.FormComponent do
<Beacon.LiveAdmin.AdminComponents.page_header socket={@socket} flash={@flash} page={@page} live_action={@live_action} />
<.header>
<%= @page_title %>
<div class="flex gap-x-6">
<div><%= @page_title %></div>
<.page_status status={@page_status} />
</div>
<:actions>
<.button :if={@live_action in [:new, :edit] && @editor == "code" && @page.format == :heex} type="button" phx-click="enable_editor" phx-value-editor="visual" class="sui-primary uppercase">
Visual Editor
</.button>
<.button :if={@live_action in [:new, :edit] && @editor == "visual"} type="button" phx-click="enable_editor" phx-value-editor="code" class="sui-primary uppercase">Code Editor</.button>
<.button :if={@live_action == :new} phx-disable-with="Saving..." form="page-form" class="sui-primary uppercase">Create Draft Page</.button>
<.button :if={@live_action == :new} phx-disable-with="Saving..." form="page-form" name="save" value="save" class="sui-primary uppercase">Create Draft Page</.button>
<.button :if={@live_action == :edit} phx-disable-with="Saving..." form="page-form" name="save" value="save" class="sui-primary uppercase">Save Changes</.button>
<.button :if={@live_action == :edit} phx-click="show_modal" phx-value-confirm="publish" phx-target={@myself} class="sui-primary uppercase">Publish</.button>
<.button :if={@live_action == :edit and @page_status == :published} phx-click="show_modal" phx-value-confirm="unpublish" phx-target={@myself} class="sui-primary-destructive uppercase">
Unpublish
</.button>
</:actions>
</.header>
Expand Down Expand Up @@ -302,6 +298,30 @@ defmodule Beacon.LiveAdmin.PageEditorLive.FormComponent do
</div>
</.modal>
<.modal :if={@show_modal == :unpublish_confirm} id="unpublish-confirm-modal" on_cancel={JS.push("close_modal", target: @myself)} show>
<:title>Unpublish Page</:title>
<div class="mt-2">
<p class="text-sm text-gray-500">Are you sure you want to unpublish this page? Requests to this path will show your site's 404 Error Page.</p>
</div>
<div class="py-4">
<.button
type="button"
class="sui-secondary inline-flex justify-center w-full px-3 py-2 mt-3 text-sm font-semibold text-gray-900 bg-white rounded-md shadow-sm ring-1 ring-inset ring-gray-300 hover:bg-gray-50 sm:mt-0 sm:w-auto"
phx-click={JS.push("close_modal", target: @myself)}
>
Cancel
</.button>
<.button
type="button"
class="sui-primary-destructive inline-flex justify-center w-full px-3 py-2 text-sm font-semibold text-white bg-blue-600 rounded-md shadow-sm hover:bg-blue-500 sm:w-auto"
phx-click="unpublish"
phx-target={@myself}
>
Confirm
</.button>
</div>
</.modal>
<.svelte
:if={@editor == "visual"}
name="components/UiBuilder"
Expand Down Expand Up @@ -352,4 +372,20 @@ defmodule Beacon.LiveAdmin.PageEditorLive.FormComponent do
</div>
"""
end

defp page_status(%{status: :published} = assigns) do
~H"""
<div class="rounded-md bg-lime-400 text-sm px-4 py-1 flex items-center">
<div>Published</div>
</div>
"""
end

defp page_status(assigns) do
~H"""
<div class="rounded-md bg-yellow-300 text-sm px-4 py-1 flex items-center">
<div>Draft</div>
</div>
"""
end
end
29 changes: 16 additions & 13 deletions lib/beacon/live_admin/live/page_editor_live/index.ex
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ defmodule Beacon.LiveAdmin.PageEditorLive.Index do
use Beacon.LiveAdmin.PageBuilder, table: [sort_by: "path"]

alias Beacon.LiveAdmin.Client.Content
alias Beacon.LiveAdmin.PageBuilder.Table

@impl true
def menu_link(_, :index), do: {:root, "Pages"}
Expand All @@ -15,18 +16,21 @@ defmodule Beacon.LiveAdmin.PageEditorLive.Index do

@impl true
def handle_params(params, _uri, socket) do
socket =
Table.handle_params(socket, params, &Content.count_pages(&1.site, query: params["query"]))
socket = Table.handle_params(socket, params, &Content.count_pages(&1.site, query: params["query"]))

%{site: site, table: %{per_page: per_page, current_page: page, query: query, sort_by: sort_by}} = socket.assigns.beacon_page
%{site: site, table: table} = socket.assigns.beacon_page

pages =
list_pages(site,
per_page: per_page,
page: page,
query: query,
sort: sort_by
)
if table do
list_pages(site,
per_page: table.per_page,
page: table.current_page,
query: table.query,
sort: table.sort_by
)
else
[]
end

{:noreply, stream(socket, :pages, pages, reset: true)}
end
Expand All @@ -45,10 +49,10 @@ defmodule Beacon.LiveAdmin.PageEditorLive.Index do
<div class="flex justify-between">
<div class="basis-8/12">
<.table_search table={@beacon_page.table} placeholder="Search by path or title (showing up to 15 results)" />
<.table_search table={@beacon_page.table || %Table{query: ""}} placeholder="Search by path or title (showing up to 15 results)" />
</div>
<div class="basis-2/12">
<.table_sort table={@beacon_page.table} options={[{"Title", "title"}, {"Path", "path"}]} />
<.table_sort table={@beacon_page.table || %Table{sort_by: "title"}} options={[{"Title", "title"}, {"Path", "path"}]} />
</div>
</div>
Expand Down Expand Up @@ -80,7 +84,6 @@ defmodule Beacon.LiveAdmin.PageEditorLive.Index do
end)
end

defp display_status(:unpublished), do: "Unpublished"
defp display_status(:published), do: "Published"
defp display_status(:created), do: "Draft"
defp display_status(_), do: "Draft"
end
15 changes: 15 additions & 0 deletions priv/static/beacon_live_admin.css

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion priv/static/beacon_live_admin.min.css

Large diffs are not rendered by default.

10 changes: 5 additions & 5 deletions test/beacon/live_admin/live/page_editor_live/new_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,12 @@ defmodule Beacon.LiveAdmin.PageEditorLive.NewTest do
test "create new page and patch to edit page", %{conn: conn} do
{:ok, live, _html} = live(conn, "/admin/site_a/pages/new")

assert {:ok, live, _html} =
live
|> form("#page-form", page: %{path: "/my/page", title: "My Page", format: "heex"})
|> render_submit(%{page: %{"template" => "<div>test</div>"}, save: "save"})
|> follow_redirect(conn)
live
|> form("#page-form", page: %{path: "/my/page", title: "My Page", format: "heex"})
|> render_submit(%{page: %{"template" => "<div>test</div>"}, save: "save"})

assert_patch(live)
assert has_element?(live, "#flash", "Page saved successfully")
assert has_element?(live, "h1", "Edit Page")
assert has_element?(live, "button", "Save Changes")
end
Expand Down

0 comments on commit f0aa90c

Please sign in to comment.