diff --git a/lib/modules/legal/legal.ex b/lib/modules/legal/legal.ex index 20121c2a1..e2169761d 100644 --- a/lib/modules/legal/legal.ex +++ b/lib/modules/legal/legal.ex @@ -38,6 +38,7 @@ defmodule PhoenixKit.Modules.Legal do alias PhoenixKit.Modules.Legal.PageType alias PhoenixKit.Modules.Legal.TemplateGenerator alias PhoenixKit.Settings + alias PhoenixKit.Utils.Routes @enabled_key "legal_enabled" @module_name "legal" @@ -551,12 +552,26 @@ defmodule PhoenixKit.Modules.Legal do - google_consent_mode: boolean - hide_for_authenticated: boolean - frameworks: list of framework IDs - - cookie_policy_url: string - - privacy_policy_url: string + - cookie_policy_url: string (backward compat, derived from published pages) + - privacy_policy_url: string (backward compat, derived from published pages) + - legal_links: list of %{title: string, url: string} for all published legal pages + - legal_index_url: string """ @spec get_consent_widget_config() :: map() def get_consent_widget_config do - prefix = PhoenixKit.Config.get_url_prefix() + legal_links = get_published_legal_links() + + cookie_policy_url = + case Enum.find(legal_links, &String.ends_with?(&1.url, "/cookie-policy")) do + %{url: url} -> url + nil -> Routes.path("/legal/cookie-policy") + end + + privacy_policy_url = + case Enum.find(legal_links, &String.ends_with?(&1.url, "/privacy-policy")) do + %{url: url} -> url + nil -> Routes.path("/legal/privacy-policy") + end %{ enabled: consent_widget_enabled?(), @@ -567,11 +582,26 @@ defmodule PhoenixKit.Modules.Legal do policy_version: get_auto_policy_version(), google_consent_mode: google_consent_mode_enabled?(), frameworks: get_selected_frameworks(), - cookie_policy_url: "#{prefix}/legal/cookie-policy", - privacy_policy_url: "#{prefix}/legal/privacy-policy" + cookie_policy_url: cookie_policy_url, + privacy_policy_url: privacy_policy_url, + legal_links: legal_links, + legal_index_url: Routes.path("/legal") } end + @doc """ + Returns a list of all published legal pages as link maps. + + Each map has `:title` and `:url` keys. Used by the cookie consent widget + to render dynamic links to all published legal pages. + """ + @spec get_published_legal_links() :: list(%{title: String.t(), url: String.t()}) + def get_published_legal_links do + list_generated_pages() + |> Enum.filter(&(&1.status == "published")) + |> Enum.map(&%{title: &1.title, url: Routes.path("/legal/#{&1.slug}")}) + end + @doc """ Check if there are unpublished legal pages that are required. diff --git a/lib/modules/sync/web/connections_live.ex b/lib/modules/sync/web/connections_live.ex index 1111486cd..caba1a401 100644 --- a/lib/modules/sync/web/connections_live.ex +++ b/lib/modules/sync/web/connections_live.ex @@ -1116,6 +1116,7 @@ defmodule PhoenixKit.Modules.Sync.Web.ConnectionsLive do end # Get all FK dependencies for a table (recursive) + @dialyzer [{:no_opaque, get_table_dependencies: 2}, {:no_opaque, get_table_dependencies: 3}] defp get_table_dependencies(table_name, tables) do get_table_dependencies(table_name, tables, MapSet.new()) |> MapSet.to_list() @@ -1165,6 +1166,7 @@ defmodule PhoenixKit.Modules.Sync.Web.ConnectionsLive do defp topo_sort(_graph, [], sorted, _visited), do: sorted + @dialyzer {:no_opaque, topo_sort: 4} defp topo_sort(graph, [node | rest], sorted, visited) do if MapSet.member?(visited, node) do topo_sort(graph, rest, sorted, visited) @@ -1174,6 +1176,7 @@ defmodule PhoenixKit.Modules.Sync.Web.ConnectionsLive do end end + @dialyzer {:no_opaque, visit_node: 5} defp visit_node(graph, node, sorted, visited, path) do if MapSet.member?(visited, node) do {sorted, visited} diff --git a/lib/phoenix_kit_web/components/core/cookie_consent.ex b/lib/phoenix_kit_web/components/core/cookie_consent.ex index ed04a93b4..1e9c5e2eb 100644 --- a/lib/phoenix_kit_web/components/core/cookie_consent.ex +++ b/lib/phoenix_kit_web/components/core/cookie_consent.ex @@ -75,6 +75,13 @@ defmodule PhoenixKitWeb.Components.Core.CookieConsent do attr :policy_version, :string, default: "1.0", doc: "Policy version for consent tracking" attr :cookie_policy_url, :string, default: "/legal/cookie-policy" attr :privacy_policy_url, :string, default: "/legal/privacy-policy" + + attr :legal_links, :list, + default: [], + doc: "Dynamic list of %{title, url} for published legal pages" + + attr :legal_index_url, :string, default: "/legal", doc: "URL to legal pages index" + attr :google_consent_mode, :boolean, default: false, doc: "Enable Google Consent Mode v2" attr :class, :string, default: "" @@ -173,7 +180,7 @@ defmodule PhoenixKitWeb.Components.Core.CookieConsent do } .pk-glass { - background: oklch(var(--b1) / 0.95); + background: oklch(var(--b1) / 0.98); backdrop-filter: blur(20px) saturate(180%); -webkit-backdrop-filter: blur(20px) saturate(180%); border: 1px solid var(--pk-border); @@ -190,25 +197,6 @@ defmodule PhoenixKitWeb.Components.Core.CookieConsent do transform: translateY(-2px); box-shadow: 0 4px 12px oklch(var(--bc) / 0.1); } - - .pk-toggle-track { - background: var(--pk-border); - transition: background-color 0.2s ease; - } - - .pk-toggle-track.active { - background: var(--pk-primary); - } - - .pk-toggle-thumb { - background: var(--pk-bg); - box-shadow: 0 1px 3px oklch(var(--bc) / 0.2); - transition: transform 0.2s cubic-bezier(0.34, 1.56, 0.64, 1); - } - - input:checked + .pk-toggle-track .pk-toggle-thumb { - transform: translateX(20px); - } <%!-- Floating Icon (only for opt-in frameworks) --%> @@ -254,17 +242,16 @@ defmodule PhoenixKitWeb.Components.Core.CookieConsent do

{gettext("We value your privacy")}

-

+

{gettext( "We use cookies to enhance your browsing experience and analyze our traffic." )} {" "} - {gettext("Cookie Policy")} + {gettext("Legal")}

@@ -308,7 +295,7 @@ defmodule PhoenixKitWeb.Components.Core.CookieConsent do > <%!-- Backdrop --%>
@@ -328,7 +315,7 @@ defmodule PhoenixKitWeb.Components.Core.CookieConsent do

{gettext("Privacy Preferences")}

-

+

{gettext("Manage your cookie settings")}

@@ -356,7 +343,7 @@ defmodule PhoenixKitWeb.Components.Core.CookieConsent do <%= for category <- @categories do %>
@@ -374,34 +361,21 @@ defmodule PhoenixKitWeb.Components.Core.CookieConsent do <% end %>
-

+

{category.description}

- <%!-- Custom Toggle --%> - + <%!-- Toggle --%> + <% end %> @@ -411,21 +385,12 @@ defmodule PhoenixKitWeb.Components.Core.CookieConsent do
<%!-- Policy Links --%> -
- - {gettext("Privacy Policy")} - - + diff --git a/lib/phoenix_kit_web/components/layout_wrapper.ex b/lib/phoenix_kit_web/components/layout_wrapper.ex index 5f77299a7..37b6e7d57 100644 --- a/lib/phoenix_kit_web/components/layout_wrapper.ex +++ b/lib/phoenix_kit_web/components/layout_wrapper.ex @@ -44,6 +44,7 @@ defmodule PhoenixKitWeb.Components.LayoutWrapper do alias PhoenixKit.Modules.Languages.DialectMapper alias PhoenixKit.Modules.Legal alias PhoenixKit.Modules.SEO + alias PhoenixKit.Modules.Storage.URLSigner alias PhoenixKit.ThemeConfig alias PhoenixKit.Users.Auth.Scope alias PhoenixKit.Utils.PhoenixVersion @@ -236,7 +237,12 @@ defmodule PhoenixKitWeb.Components.LayoutWrapper do current_locale: assigns[:current_locale], current_locale_base: assigns[:current_locale] && DialectMapper.extract_base(assigns[:current_locale]), - scope: assigns[:phoenix_kit_current_scope] + scope: assigns[:phoenix_kit_current_scope], + auth_logo_url: + case PhoenixKit.Settings.get_setting("auth_logo_file_uuid", "") do + uuid when is_binary(uuid) and uuid != "" -> URLSigner.signed_url(uuid, "medium") + _ -> nil + end } assigns = template_assigns @@ -281,9 +287,9 @@ defmodule PhoenixKitWeb.Components.LayoutWrapper do <%!-- Logo --%> -
- -
+ <%= if @auth_logo_url do %> + {@project_title} + <% end %> <%!-- Project title and Admin label grouped together --%>
@@ -700,6 +706,8 @@ defmodule PhoenixKitWeb.Components.LayoutWrapper do policy_version={config.policy_version} cookie_policy_url={config.cookie_policy_url} privacy_policy_url={config.privacy_policy_url} + legal_links={config.legal_links} + legal_index_url={config.legal_index_url} google_consent_mode={config.google_consent_mode} /> <% end %>