Packages

phoenix_kit

1.7.21
1.7.208 1.7.207 1.7.206 1.7.205 1.7.204 1.7.203 1.7.202 1.7.201 1.7.200 1.7.199 1.7.198 1.7.197 1.7.196 1.7.194 1.7.193 1.7.192 1.7.191 1.7.190 1.7.189 1.7.187 1.7.186 1.7.185 1.7.184 1.7.183 1.7.182 1.7.181 1.7.180 1.7.179 1.7.178 1.7.177 1.7.176 1.7.175 1.7.174 1.7.173 1.7.172 1.7.171 1.7.170 1.7.169 1.7.168 1.7.167 1.7.166 1.7.165 1.7.164 1.7.162 1.7.161 1.7.160 1.7.159 1.7.157 1.7.156 1.7.155 1.7.154 1.7.153 1.7.152 1.7.151 1.7.150 1.7.149 1.7.146 1.7.145 1.7.144 1.7.143 1.7.138 1.7.133 1.7.132 1.7.131 1.7.130 1.7.128 1.7.126 1.7.125 1.7.121 1.7.120 1.7.119 1.7.118 1.7.117 1.7.116 1.7.115 1.7.114 1.7.113 1.7.112 1.7.111 1.7.110 1.7.109 1.7.108 1.7.107 1.7.106 1.7.105 1.7.104 1.7.103 1.7.102 1.7.101 1.7.100 1.7.99 1.7.98 1.7.97 1.7.96 1.7.95 1.7.94 1.7.93 1.7.92 1.7.91 1.7.90 1.7.89 1.7.88 1.7.87 1.7.86 1.7.85 1.7.84 1.7.83 1.7.82 1.7.81 1.7.80 1.7.79 1.7.78 1.7.77 1.7.76 1.7.75 1.7.74 1.7.71 1.7.70 1.7.69 1.7.66 1.7.65 1.7.64 1.7.63 1.7.62 1.7.61 1.7.59 1.7.58 1.7.57 1.7.56 1.7.55 1.7.54 1.7.53 1.7.52 1.7.51 1.7.49 1.7.44 1.7.43 1.7.42 1.7.41 1.7.39 1.7.38 1.7.37 1.7.36 1.7.34 1.7.33 1.7.31 1.7.30 1.7.29 1.7.28 1.7.27 1.7.26 1.7.25 1.7.24 1.7.23 1.7.22 1.7.21 1.7.20 1.7.19 1.7.18 1.7.17 1.7.16 1.7.15 1.7.14 1.7.13 1.7.12 1.7.11 1.7.10 1.7.9 1.7.8 1.7.7 1.7.6 1.7.5 1.7.4 1.7.3 1.7.2 1.7.1 1.7.0 1.6.20 1.6.19 1.6.18 1.6.17 1.6.16 1.6.15 1.6.14 1.6.13 1.6.12 1.6.11 1.6.10 1.6.9 1.6.8 1.6.7 1.6.6 1.6.5 1.6.4 1.6.3 1.5.2 1.5.1 1.5.0 1.4.9 1.4.8 1.4.7 1.4.6 1.4.5 1.4.4 1.4.3 1.4.2 1.4.1 1.4.0 1.3.2 1.3.1 1.3.0 1.2.10 1.2.9 1.2.8 1.2.7 1.2.5 1.2.4 1.2.2 1.2.1 1.2.0 1.1.0 1.0.0

A foundation for building Elixir Phoenix apps — SaaS, social networks, ERP systems, marketplaces, and more

Current section

Files

Jump to
phoenix_kit lib phoenix_kit_web live modules pages editor.ex
Raw

lib/phoenix_kit_web/live/modules/pages/editor.ex

defmodule PhoenixKitWeb.Live.Modules.Pages.Editor do
@moduledoc """
Full-screen editor for Pages files.
Provides a dedicated page for editing markdown content with metadata.
"""
use PhoenixKitWeb, :live_view
use Gettext, backend: PhoenixKitWeb.Gettext
alias PhoenixKit.Pages
alias PhoenixKit.Pages.FileOperations
alias PhoenixKit.Pages.Metadata
alias PhoenixKit.Utils.Routes
def mount(_params, _session, socket) do
# Set locale
locale = socket.assigns[:current_locale] || "en"
# Check if module is enabled
if Pages.enabled?() do
socket =
socket
|> assign(:page_title, "Edit Page")
|> assign(:file_path, nil)
|> assign(:file_content, "")
|> assign(:original_content, "")
|> assign(:has_changes, false)
|> assign(:project_title, PhoenixKit.Settings.get_setting("project_title", "PhoenixKit"))
|> assign(:current_locale, locale)
{:ok, socket}
else
socket =
socket
|> put_flash(:error, "Pages module is not enabled")
|> redirect(to: Routes.path("/admin/modules"))
{:ok, socket}
end
end
def handle_params(%{"path" => path}, _uri, socket) do
case FileOperations.read_file(path) do
{:ok, content} ->
# Extract current status from metadata
current_status =
case Metadata.parse(content) do
{:ok, metadata, _stripped} -> metadata.status
{:error, :no_metadata} -> "draft"
end
socket =
socket
|> assign(:file_path, path)
|> assign(:file_content, content)
|> assign(:original_content, content)
|> assign(:has_changes, false)
|> assign(:current_status, current_status)
|> assign(:page_title, "Edit: #{Path.basename(path)}")
# Notify JavaScript hook about initial changes status (no changes)
{:noreply, push_event(socket, "changes-status", %{has_changes: false})}
{:error, _reason} ->
socket =
socket
|> put_flash(:error, "File not found: #{path}")
|> redirect(to: Routes.path("/admin/pages"))
{:noreply, socket}
end
end
def handle_params(_params, _uri, socket) do
# No path provided, redirect to file list
socket =
socket
|> put_flash(:error, "No file path provided")
|> redirect(to: Routes.path("/admin/pages"))
{:noreply, socket}
end
## Event Handlers
def handle_event("update_content", %{"value" => content}, socket) do
has_changes = content != socket.assigns.original_content
socket =
socket
|> assign(:file_content, content)
|> assign(:has_changes, has_changes)
# Notify JavaScript hook about changes status
{:noreply, push_event(socket, "changes-status", %{has_changes: has_changes})}
end
def handle_event("save", _params, socket) do
file_path = socket.assigns.file_path
content = socket.assigns.file_content
# Update metadata's updated_at timestamp
updated_content = update_metadata_timestamp(content)
case FileOperations.write_file(file_path, updated_content) do
:ok ->
socket =
socket
|> assign(:original_content, updated_content)
|> assign(:file_content, updated_content)
|> assign(:has_changes, false)
|> put_flash(:info, "File saved: #{Path.basename(file_path)}")
# Notify JavaScript hook that changes have been saved
{:noreply, push_event(socket, "changes-status", %{has_changes: false})}
{:error, _reason} ->
socket = put_flash(socket, :error, "Failed to save file")
{:noreply, socket}
end
end
def handle_event("attempt_cancel", _params, socket) do
if socket.assigns.has_changes do
# Show browser's native confirm dialog via JavaScript
{:noreply,
push_event(socket, "confirm-navigation", %{
message: "You have unsaved changes. Are you sure you want to leave without saving?",
target: "/admin/pages"
})}
else
# No changes, navigate immediately
{:noreply, redirect(socket, to: Routes.path("/admin/pages"))}
end
end
def handle_event("cancel", _params, socket) do
# User confirmed via JavaScript, navigate away
{:noreply, redirect(socket, to: Routes.path("/admin/pages"))}
end
def handle_event("change_status", %{"status" => new_status}, socket) do
content = socket.assigns.file_content
file_path = socket.assigns.file_path
# Update metadata with new status and timestamp
updated_content = update_metadata_status(content, new_status)
case FileOperations.write_file(file_path, updated_content) do
:ok ->
socket =
socket
|> assign(:file_content, updated_content)
|> assign(:original_content, updated_content)
|> assign(:current_status, new_status)
|> assign(:has_changes, false)
|> put_flash(:info, "Status changed to: #{new_status}")
{:noreply, socket}
{:error, _reason} ->
socket = put_flash(socket, :error, "Failed to update status")
{:noreply, socket}
end
end
## Private Helpers
defp update_metadata_timestamp(content) do
case Metadata.parse(content) do
{:ok, metadata, _stripped_content} ->
# Update the updated_at timestamp
updated_metadata = Map.put(metadata, :updated_at, DateTime.utc_now())
Metadata.update_metadata(content, updated_metadata)
{:error, :no_metadata} ->
# No metadata, return content as-is
content
end
end
defp update_metadata_status(content, new_status) do
case Metadata.parse(content) do
{:ok, metadata, _stripped_content} ->
# Update both status and updated_at timestamp
updated_metadata =
metadata
|> Map.put(:status, new_status)
|> Map.put(:updated_at, DateTime.utc_now())
Metadata.update_metadata(content, updated_metadata)
{:error, :no_metadata} ->
# No metadata exists, create new metadata with the status
metadata =
Metadata.default_metadata()
|> Map.put(:status, new_status)
|> Map.put(:updated_at, DateTime.utc_now())
# Prepend metadata to content
Metadata.serialize(metadata) <> "\n\n" <> content
end
end
end