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 modules legal web settings.ex
Raw

lib/modules/legal/web/settings.ex

defmodule PhoenixKitWeb.Live.Modules.Legal.Settings do
@moduledoc """
LiveView for Legal module settings and page generation.
Route: {prefix}/admin/settings/legal
Sections:
1. Module enable/disable (with Publishing dependency check)
2. Compliance framework selection
3. Company information form
4. DPO contact form
5. Page generation controls
6. Generated pages list
"""
use PhoenixKitWeb, :live_view
use Gettext, backend: PhoenixKitWeb.Gettext
alias PhoenixKit.Modules.Legal
alias PhoenixKit.Settings
alias PhoenixKit.Utils.Routes
@impl true
def mount(_params, _session, socket) do
config = Legal.get_config()
widget_config = Legal.get_consent_widget_config()
# Get data for preview and import functionality
company_info = config.company_info
site_url = Settings.get_setting("site_url", "")
from_email = Settings.get_setting("from_email", "")
company_country_name = get_country_name(company_info["country"])
company_address_formatted = format_company_address_oneline(company_info)
socket =
socket
|> assign(:project_title, Settings.get_setting("project_title", "PhoenixKit"))
|> assign(:page_title, gettext("Legal Settings"))
|> assign(
:current_path,
Routes.path("/admin/settings/legal", locale: socket.assigns[:current_locale_base])
)
|> assign(:publishing_enabled, config.publishing_enabled)
|> assign(:legal_enabled, config.enabled)
|> assign(:available_frameworks, Legal.available_frameworks())
|> assign(:available_page_types, Legal.available_page_types())
|> assign(:selected_frameworks, config.frameworks)
|> assign(:company_info, company_info)
|> assign(:company_country_name, company_country_name)
|> assign(:site_url, site_url)
|> assign(:from_email, from_email)
|> assign(:company_address_formatted, company_address_formatted)
|> assign(:dpo_contact, config.dpo_contact)
|> assign(:generated_pages, config.generated_pages)
|> assign(:generating, false)
# Consent widget assigns (Phase 2)
|> assign(:consent_widget_enabled, widget_config.enabled)
|> assign(:consent_mode, widget_config.consent_mode)
|> assign(:hide_for_authenticated, widget_config.hide_for_authenticated)
|> assign(:icon_position, widget_config.icon_position)
|> assign(:policy_version, widget_config.policy_version)
|> assign(:google_consent_mode, widget_config.google_consent_mode)
|> assign(:show_consent_icon, widget_config.show_icon)
|> assign(:unpublished_pages, Legal.get_unpublished_required_pages())
{:ok, socket}
end
@impl true
def handle_params(_params, _uri, socket), do: {:noreply, socket}
@impl true
def handle_event("toggle_legal", _params, socket) do
if socket.assigns.legal_enabled do
case Legal.disable_system() do
{:ok, _} ->
{:noreply,
socket
|> assign(:legal_enabled, false)
|> put_flash(:info, gettext("Legal module disabled"))}
{:error, _} ->
{:noreply, put_flash(socket, :error, gettext("Failed to disable Legal module"))}
end
else
case Legal.enable_system() do
{:ok, _} ->
{:noreply,
socket
|> assign(:legal_enabled, true)
|> put_flash(:info, gettext("Legal module enabled"))}
{:error, :publishing_required} ->
{:noreply, put_flash(socket, :error, gettext("Please enable Publishing module first"))}
{:error, _} ->
{:noreply, put_flash(socket, :error, gettext("Failed to enable Legal module"))}
end
end
end
@impl true
def handle_event("toggle_framework", %{"id" => framework_id}, socket) do
current = socket.assigns.selected_frameworks
updated =
if framework_id in current do
List.delete(current, framework_id)
else
[framework_id | current]
end
case Legal.set_frameworks(updated) do
{:ok, _} ->
{:noreply, assign(socket, :selected_frameworks, updated)}
{:error, _} ->
{:noreply, put_flash(socket, :error, gettext("Failed to save frameworks"))}
end
end
@impl true
def handle_event("import_dpo_email", _params, socket) do
from_email = socket.assigns.from_email
dpo_contact = Map.put(socket.assigns.dpo_contact, "email", from_email)
case Legal.update_dpo_contact(dpo_contact) do
{:ok, _} ->
{:noreply,
socket
|> assign(:dpo_contact, dpo_contact)
|> put_flash(:info, gettext("Email imported from General Settings"))}
{:error, _} ->
{:noreply, put_flash(socket, :error, gettext("Failed to import email"))}
end
end
@impl true
def handle_event("import_dpo_address", _params, socket) do
company_address = socket.assigns.company_address_formatted
dpo_contact = Map.put(socket.assigns.dpo_contact, "address", company_address)
case Legal.update_dpo_contact(dpo_contact) do
{:ok, _} ->
{:noreply,
socket
|> assign(:dpo_contact, dpo_contact)
|> put_flash(:info, gettext("Address imported from Organization"))}
{:error, _} ->
{:noreply, put_flash(socket, :error, gettext("Failed to import address"))}
end
end
@impl true
def handle_event("save_dpo_contact", params, socket) do
dpo_contact = %{
"name" => params["dpo_name"] || "",
"email" => params["dpo_email"] || "",
"phone" => params["dpo_phone"] || "",
"address" => params["dpo_address"] || ""
}
case Legal.update_dpo_contact(dpo_contact) do
{:ok, _} ->
{:noreply,
socket
|> assign(:dpo_contact, dpo_contact)
|> put_flash(:info, gettext("DPO contact saved"))}
{:error, _} ->
{:noreply, put_flash(socket, :error, gettext("Failed to save DPO contact"))}
end
end
@impl true
def handle_event("generate_page", %{"page_type" => page_type}, socket) do
socket = assign(socket, :generating, true)
case Legal.generate_page(page_type, scope: socket.assigns[:current_scope]) do
{:ok, _post} ->
{:noreply,
socket
|> assign(:generating, false)
|> assign(:generated_pages, Legal.list_generated_pages())
|> assign(:unpublished_pages, Legal.get_unpublished_required_pages())
|> put_flash(:info, gettext("Page generated successfully"))}
{:error, reason} ->
{:noreply,
socket
|> assign(:generating, false)
|> put_flash(
:error,
gettext("Failed to generate page: %{reason}", reason: inspect(reason))
)}
end
end
@impl true
def handle_event("publish_page", %{"page_slug" => page_slug}, socket) do
case Legal.publish_page(page_slug, scope: socket.assigns[:current_scope]) do
{:ok, _post} ->
{:noreply,
socket
|> assign(:generated_pages, Legal.list_generated_pages())
|> assign(:unpublished_pages, Legal.get_unpublished_required_pages())
|> put_flash(:info, gettext("Page published successfully"))}
{:error, reason} ->
{:noreply,
put_flash(
socket,
:error,
gettext("Failed to publish page: %{reason}", reason: inspect(reason))
)}
end
end
@impl true
def handle_event("generate_all_pages", _params, socket) do
socket = assign(socket, :generating, true)
{:ok, results} = Legal.generate_all_pages(scope: socket.assigns[:current_scope])
success_count =
results
|> Enum.count(fn {_, result} -> match?({:ok, _}, result) end)
{:noreply,
socket
|> assign(:generating, false)
|> assign(:generated_pages, Legal.list_generated_pages())
|> assign(:unpublished_pages, Legal.get_unpublished_required_pages())
|> put_flash(:info, gettext("Generated %{count} pages", count: success_count))}
end
# ===================================
# CONSENT WIDGET EVENTS (Phase 2)
# ===================================
@impl true
def handle_event("toggle_consent_widget", _params, socket) do
if socket.assigns.consent_widget_enabled do
case Legal.disable_consent_widget() do
{:ok, _} ->
{:noreply,
socket
|> assign(:consent_widget_enabled, false)
|> assign(:show_consent_icon, false)
|> put_flash(:info, gettext("Cookie consent widget disabled"))}
{:error, _} ->
{:noreply, put_flash(socket, :error, gettext("Failed to update setting"))}
end
else
case Legal.enable_consent_widget() do
{:ok, _} ->
show_icon = Legal.has_opt_in_framework?()
{:noreply,
socket
|> assign(:consent_widget_enabled, true)
|> assign(:show_consent_icon, show_icon)
|> put_flash(:info, gettext("Cookie consent widget enabled"))}
{:error, _} ->
{:noreply, put_flash(socket, :error, gettext("Failed to update setting"))}
end
end
end
@impl true
def handle_event("save_consent_settings", params, socket) do
consent_mode = params["consent_mode"] || "strict"
hide_for_auth = params["hide_for_authenticated"] == "true"
with {:ok, _} <- Legal.update_consent_mode(consent_mode),
{:ok, _} <- Legal.update_hide_for_authenticated(hide_for_auth),
{:ok, _} <- Legal.update_icon_position(params["icon_position"] || "bottom-right"),
{:ok, _} <- update_google_consent_mode(params["google_consent_mode"]) do
# Recalculate show_icon based on new settings
show_icon = Legal.should_show_consent_icon?()
{:noreply,
socket
|> assign(:consent_mode, consent_mode)
|> assign(:hide_for_authenticated, hide_for_auth)
|> assign(:icon_position, params["icon_position"] || "bottom-right")
|> assign(:google_consent_mode, params["google_consent_mode"] == "true")
|> assign(:show_consent_icon, show_icon)
|> put_flash(:info, gettext("Consent widget settings saved"))}
else
{:error, _} ->
{:noreply, put_flash(socket, :error, gettext("Failed to save settings"))}
end
end
defp update_google_consent_mode("true"), do: Legal.enable_google_consent_mode()
defp update_google_consent_mode(_), do: Legal.disable_google_consent_mode()
# Helper to get edit URL for a legal page
defp get_edit_url(page_slug, generated_pages) do
case Enum.find(generated_pages, fn p -> p.slug == page_slug end) do
nil ->
Routes.path("/admin/publishing/legal")
page ->
Routes.path("/admin/publishing/legal/edit?path=#{URI.encode(page.path)}")
end
end
# Helper to check if a page is generated
defp page_generated?(page_slug, generated_pages) do
Enum.any?(generated_pages, fn p -> p.slug == page_slug end)
end
# Helper to get page status
defp get_page_status(page_slug, generated_pages) do
case Enum.find(generated_pages, fn p -> p.slug == page_slug end) do
nil -> nil
page -> page.status
end
end
# Helper to get country name from code
defp get_country_name(nil), do: ""
defp get_country_name(""), do: ""
defp get_country_name(country_code) do
case BeamLabCountries.get(country_code) do
nil -> country_code
country -> country.name
end
end
# Helper to format company address as one line (for DPO import)
defp format_company_address_oneline(company) do
parts =
[
company["address_line1"],
company["address_line2"],
company["city"],
company["state"],
company["postal_code"],
get_country_name(company["country"])
]
|> Enum.reject(&(&1 == "" or is_nil(&1)))
Enum.join(parts, ", ")
end
end