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

Add Unpublish button to page editor #319

Merged
merged 8 commits into from
Jan 3, 2025
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
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