From 26b81bf536a74f6c6ee683151967c44e6b72611d Mon Sep 17 00:00:00 2001 From: timujeen Date: Tue, 24 Mar 2026 16:26:38 +0000 Subject: [PATCH 1/2] Fix cookie consent: dynamic legal links, theme-aware backdrop, daisyUI toggle MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Replace hardcoded cookie/privacy URLs with Routes.path() to fix double-slash bug - Add dynamic legal_links from published pages + single /legal index link - Use bg-base-100/70 backdrop instead of bg-black for light/dark theme compatibility - Improve glass opacity (0.95→0.98), card bg (50→80%), text contrast - Replace custom toggle with standard daisyUI toggle toggle-primary --- lib/modules/legal/legal.ex | 40 +++++++-- .../components/core/cookie_consent.ex | 89 ++++++------------- .../components/layout_wrapper.ex | 2 + 3 files changed, 64 insertions(+), 67 deletions(-) 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/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..e557c0c41 100644 --- a/lib/phoenix_kit_web/components/layout_wrapper.ex +++ b/lib/phoenix_kit_web/components/layout_wrapper.ex @@ -700,6 +700,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 %> From 63018d3b46a28dbbdc4b7c7b9c7bd614452a4be6 Mon Sep 17 00:00:00 2001 From: timujeen Date: Mon, 30 Mar 2026 21:03:12 +0000 Subject: [PATCH 2/2] Use auth logo image in admin header instead of shield icon Read auth_logo_file_uuid setting and display the uploaded logo in the admin top bar. When no logo is configured, the logo area is hidden entirely instead of showing the default shield icon. Also suppress pre-existing dialyzer opaque type warnings in ConnectionsLive MapSet-related functions (dialyxir bug with newer Elixir/OTP versions). Co-Authored-By: Claude Opus 4.6 (1M context) --- lib/modules/sync/web/connections_live.ex | 3 +++ lib/phoenix_kit_web/components/layout_wrapper.ex | 14 ++++++++++---- 2 files changed, 13 insertions(+), 4 deletions(-) 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/layout_wrapper.ex b/lib/phoenix_kit_web/components/layout_wrapper.ex index e557c0c41..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 --%>