Skip to content
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
40 changes: 5 additions & 35 deletions lib/modules/legal/legal.ex
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ 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"
Expand Down Expand Up @@ -552,26 +551,12 @@ defmodule PhoenixKit.Modules.Legal do
- google_consent_mode: boolean
- hide_for_authenticated: boolean
- frameworks: list of framework IDs
- 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
- cookie_policy_url: string
- privacy_policy_url: string
"""
@spec get_consent_widget_config() :: map()
def get_consent_widget_config do
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
prefix = PhoenixKit.Config.get_url_prefix()

%{
enabled: consent_widget_enabled?(),
Expand All @@ -582,26 +567,11 @@ 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: cookie_policy_url,
privacy_policy_url: privacy_policy_url,
legal_links: legal_links,
legal_index_url: Routes.path("/legal")
cookie_policy_url: "#{prefix}/legal/cookie-policy",
privacy_policy_url: "#{prefix}/legal/privacy-policy"
}
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.

Expand Down
16 changes: 1 addition & 15 deletions lib/modules/sitemap/sources/publishing.ex
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ defmodule PhoenixKit.Modules.Sitemap.Sources.Publishing do

UrlEntry.new(%{
loc: url,
lastmod: latest_post_date(slug, language),
lastmod: nil,
changefreq: "daily",
priority: 0.7,
title: name,
Expand Down Expand Up @@ -384,20 +384,6 @@ defmodule PhoenixKit.Modules.Sitemap.Sources.Publishing do
end
end

# Latest lastmod among published posts in a group (for group listing pages)
defp latest_post_date(group_slug, language) do
post_language = language || get_default_language()

Publishing.list_posts(group_slug, post_language)
|> Enum.filter(&published?/1)
|> Enum.reject(&excluded?/1)
|> Enum.map(&get_post_lastmod/1)
|> Enum.reject(&is_nil/1)
|> Enum.max(Date, fn -> nil end)
rescue
_ -> nil
end

defp get_post_lastmod(post) do
case post do
# Check metadata fields first (PhoenixKit Publishing uses published_at)
Expand Down
23 changes: 2 additions & 21 deletions lib/modules/sitemap/sources/static.ex
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ defmodule PhoenixKit.Modules.Sitemap.Sources.Static do

UrlEntry.new(%{
loc: url,
lastmod: static_lastmod(path),
lastmod: Date.utc_today(),
changefreq: Map.get(config, "changefreq", "weekly"),
priority: Map.get(config, "priority", 0.5),
title: Map.get(config, "title", path),
Expand All @@ -224,7 +224,7 @@ defmodule PhoenixKit.Modules.Sitemap.Sources.Static do

UrlEntry.new(%{
loc: url,
lastmod: static_lastmod(path),
lastmod: Date.utc_today(),
changefreq: Map.get(config, "changefreq", "weekly"),
priority: Map.get(config, "priority", 0.5),
title: Map.get(config, "title", path),
Expand All @@ -237,25 +237,6 @@ defmodule PhoenixKit.Modules.Sitemap.Sources.Static do
end
end

# For homepage, use the latest published content date across all publishing groups.
# For other static pages, use today's date as a reasonable approximation.
defp static_lastmod("/") do
alias PhoenixKit.Modules.Sitemap.Sources.Publishing

if Code.ensure_loaded?(Publishing) and function_exported?(Publishing, :collect, 1) do
Publishing.collect([])
|> Enum.map(& &1.lastmod)
|> Enum.reject(&is_nil/1)
|> Enum.max(Date, fn -> Date.utc_today() end)
else
Date.utc_today()
end
rescue
_ -> Date.utc_today()
end

defp static_lastmod(_path), do: Date.utc_today()

# Resolve path from config: explicit path OR via RouteResolver
defp resolve_path(%{"path" => path}) when is_binary(path) and path != "" do
path
Expand Down
89 changes: 62 additions & 27 deletions lib/phoenix_kit_web/components/core/cookie_consent.ex
Original file line number Diff line number Diff line change
Expand Up @@ -75,13 +75,6 @@ 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: ""

Expand Down Expand Up @@ -180,7 +173,7 @@ defmodule PhoenixKitWeb.Components.Core.CookieConsent do
}

.pk-glass {
background: oklch(var(--b1) / 0.98);
background: oklch(var(--b1) / 0.95);
backdrop-filter: blur(20px) saturate(180%);
-webkit-backdrop-filter: blur(20px) saturate(180%);
border: 1px solid var(--pk-border);
Expand All @@ -197,6 +190,25 @@ 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);
}
</style>

<%!-- Floating Icon (only for opt-in frameworks) --%>
Expand Down Expand Up @@ -242,16 +254,17 @@ defmodule PhoenixKitWeb.Components.Core.CookieConsent do
<h3 class="font-semibold text-base-content text-sm sm:text-base">
{gettext("We value your privacy")}
</h3>
<p class="text-base-content/80 text-xs sm:text-sm mt-0.5 leading-relaxed max-w-xl">
<p class="text-base-content/70 text-xs sm:text-sm mt-0.5 leading-relaxed max-w-xl">
{gettext(
"We use cookies to enhance your browsing experience and analyze our traffic."
)}
{" "}
<a
href={@legal_index_url}
href={@cookie_policy_url}
class="link link-primary text-xs sm:text-sm"
target="_blank"
>
{gettext("Legal")}
{gettext("Cookie Policy")}
</a>
</p>
</div>
Expand Down Expand Up @@ -295,7 +308,7 @@ defmodule PhoenixKitWeb.Components.Core.CookieConsent do
>
<%!-- Backdrop --%>
<div
class="pk-modal-backdrop absolute inset-0 bg-base-100/70 backdrop-blur-sm"
class="pk-modal-backdrop absolute inset-0 bg-black/40 backdrop-blur-sm"
onclick="window.PhoenixKitConsent?.closePreferences()"
>
</div>
Expand All @@ -315,7 +328,7 @@ defmodule PhoenixKitWeb.Components.Core.CookieConsent do
<h2 class="font-semibold text-lg text-base-content">
{gettext("Privacy Preferences")}
</h2>
<p class="text-xs text-base-content/70">
<p class="text-xs text-base-content/60">
{gettext("Manage your cookie settings")}
</p>
</div>
Expand Down Expand Up @@ -343,7 +356,7 @@ defmodule PhoenixKitWeb.Components.Core.CookieConsent do
<%= for category <- @categories do %>
<div class={[
"pk-category-card rounded-xl p-4",
"bg-base-200/80 border border-base-300/30"
"bg-base-200/50 border border-base-300/30"
]}>
<div class="flex items-start justify-between gap-3">
<div class="flex items-start gap-3 flex-1 min-w-0">
Expand All @@ -361,21 +374,34 @@ defmodule PhoenixKitWeb.Components.Core.CookieConsent do
</span>
<% end %>
</div>
<p class="text-xs text-base-content/70 mt-1 leading-relaxed">
<p class="text-xs text-base-content/60 mt-1 leading-relaxed">
{category.description}
</p>
</div>
</div>

<%!-- Toggle --%>
<input
type="checkbox"
id={"pk-consent-#{category.id}"}
class="toggle toggle-primary"
checked={Map.get(category, :always_enabled, false)}
disabled={Map.get(category, :always_enabled, false)}
data-category={category.id}
/>
<%!-- Custom Toggle --%>
<label class="relative inline-flex items-center cursor-pointer flex-shrink-0">
<input
type="checkbox"
id={"pk-consent-#{category.id}"}
class="sr-only peer"
checked={Map.get(category, :always_enabled, false)}
disabled={Map.get(category, :always_enabled, false)}
data-category={category.id}
/>
<div class={[
"pk-toggle-track relative w-11 h-6 rounded-full",
"bg-base-300 peer-checked:bg-primary",
"peer-disabled:opacity-60 peer-disabled:cursor-not-allowed",
"after:content-[''] after:absolute after:top-0.5 after:left-0.5",
"after:bg-white after:rounded-full after:h-5 after:w-5",
"after:shadow-sm after:transition-transform after:duration-200",
"peer-checked:after:translate-x-5",
"peer-focus:ring-2 peer-focus:ring-primary/30"
]}>
</div>
</label>
</div>
</div>
<% end %>
Expand All @@ -385,12 +411,21 @@ defmodule PhoenixKitWeb.Components.Core.CookieConsent do
<div class="px-6 py-4 border-t border-base-300/50 bg-base-200/30">
<div class="flex flex-col sm:flex-row items-stretch sm:items-center gap-3">
<%!-- Policy Links --%>
<div class="flex items-center gap-3 text-xs text-base-content/70">
<div class="flex items-center gap-3 text-xs text-base-content/60">
<a
href={@privacy_policy_url}
class="link hover:text-primary transition-colors"
target="_blank"
>
{gettext("Privacy Policy")}
</a>
<span class="text-base-300">•</span>
<a
href={@legal_index_url}
href={@cookie_policy_url}
class="link hover:text-primary transition-colors"
target="_blank"
>
{gettext("Legal")}
{gettext("Cookie Policy")}
</a>
</div>

Expand Down
2 changes: 0 additions & 2 deletions lib/phoenix_kit_web/components/layout_wrapper.ex
Original file line number Diff line number Diff line change
Expand Up @@ -700,8 +700,6 @@ 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 %>
Expand Down
Loading