Packages
phoenix_kit
1.7.41
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
Current section
Files
lib/modules/legal/web/settings.html.heex
<PhoenixKitWeb.Components.LayoutWrapper.app_layout
flash={@flash}
phoenix_kit_current_scope={assigns[:phoenix_kit_current_scope]}
page_title={@page_title}
current_path={@current_path}
project_title={@project_title}
current_locale={@current_locale}
>
<div class="container flex flex-col mx-auto px-4 py-6">
<%!-- Header Section --%>
<header class="w-full mb-6">
<%!-- Back Button (Left aligned) --%>
<.link
navigate={PhoenixKit.Utils.Routes.path("/admin/settings", locale: @current_locale_base)}
class="btn btn-outline btn-primary btn-sm"
>
<.icon name="hero-arrow-left" class="w-4 h-4" /> Back to Settings
</.link>
<%!-- Title Section --%>
<div class="text-center">
<h1 class="text-2xl sm:text-4xl font-bold text-base-content mb-3">
{gettext("Legal Settings")}
</h1>
<p class="text-base sm:text-lg text-base-content">
{gettext(
"Configure compliance frameworks, company information, and generate legal pages."
)}
</p>
</div>
</header>
<div class="max-w-4xl mx-auto space-y-6">
<%!-- Module Toggle --%>
<div class="flex items-center justify-between bg-base-200 p-4 rounded-lg">
<div>
<h2 class="font-semibold">{gettext("Legal Module")}</h2>
<p class="text-sm text-base-content/60">
{gettext("Enable or disable the Legal module")}
</p>
</div>
<div class="form-control">
<label class="label cursor-pointer gap-4">
<input
type="checkbox"
class="toggle toggle-primary"
checked={@legal_enabled}
phx-click="toggle_legal"
disabled={not @publishing_enabled}
/>
</label>
</div>
</div>
<%!-- Publishing Dependency Warning --%>
<%= if not @publishing_enabled do %>
<div class="alert alert-warning">
<svg
xmlns="http://www.w3.org/2000/svg"
class="stroke-current shrink-0 h-6 w-6"
fill="none"
viewBox="0 0 24 24"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"
/>
</svg>
<span>
{gettext(
"Publishing module is required for Legal pages. Please enable Publishing first."
)}
</span>
<.link
navigate={PhoenixKit.Utils.Routes.path("/admin/settings/publishing")}
class="btn btn-sm"
>
{gettext("Enable Publishing")}
</.link>
</div>
<% end %>
<%= if @legal_enabled do %>
<%!-- Section 1: Compliance Frameworks --%>
<div class="card bg-base-100 shadow">
<div class="card-body">
<h2 class="card-title">{gettext("Compliance Frameworks")}</h2>
<p class="text-sm text-base-content/60 mb-4">
{gettext("Select the privacy regulations your application must comply with.")}
</p>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<%= for {id, framework} <- @available_frameworks do %>
<label class={"flex items-start gap-3 p-4 border rounded-lg cursor-pointer hover:bg-base-200 transition-colors #{if id in @selected_frameworks, do: "border-primary bg-primary/5", else: "border-base-300"}"}>
<input
type="checkbox"
class="checkbox checkbox-primary checkbox-sm mt-0.5 shrink-0"
checked={id in @selected_frameworks}
phx-click="toggle_framework"
phx-value-id={id}
/>
<div class="min-w-0 flex-1">
<div class="font-medium text-sm">{framework.name}</div>
<div class="text-xs text-base-content/60 mt-1">
{framework.description}
</div>
</div>
</label>
<% end %>
</div>
</div>
</div>
<%!-- Section 2: Company Information (Preview) --%>
<div class="card bg-base-100 shadow">
<div class="card-body">
<div class="flex items-center justify-between">
<h2 class="card-title">{gettext("Company Information")}</h2>
<div class="flex gap-2">
<.link
navigate={
PhoenixKit.Utils.Routes.path("/admin/settings/organization",
locale: @current_locale_base
)
}
class="btn btn-sm btn-primary"
>
<.icon name="hero-pencil-square" class="w-4 h-4" />
{gettext("Edit Organization")}
</.link>
<.link
navigate={
PhoenixKit.Utils.Routes.path("/admin/settings", locale: @current_locale_base)
}
class="btn btn-sm btn-outline"
>
<.icon name="hero-globe-alt" class="w-4 h-4" />
{gettext("Edit Site URL")}
</.link>
</div>
</div>
<p class="text-sm text-base-content/60 mb-4">
{gettext("This information will appear in your legal documents.")}
</p>
<%= if @company_info["name"] == "" do %>
<%!-- Not configured warning --%>
<div class="alert alert-warning">
<.icon name="hero-exclamation-triangle" class="w-5 h-5" />
<div>
<p class="font-medium">{gettext("Company information not configured")}</p>
<p class="text-sm">
{gettext("Go to Settings → Organization to set up your company details.")}
</p>
</div>
</div>
<% else %>
<%!-- Company info preview --%>
<div class="bg-base-200 rounded-lg p-4 space-y-3">
<div class="flex items-start gap-2">
<.icon name="hero-building-office" class="w-5 h-5 text-primary shrink-0 mt-0.5" />
<div>
<p class="font-semibold">{@company_info["name"]}</p>
<%= if @company_info["registration_number"] != "" do %>
<p class="text-xs text-base-content/60">
{gettext("Reg. No:")} {@company_info["registration_number"]}
</p>
<% end %>
</div>
</div>
<%= if @company_info["address_line1"] != "" do %>
<div class="flex items-start gap-2">
<.icon name="hero-map-pin" class="w-5 h-5 text-primary shrink-0 mt-0.5" />
<div class="text-sm">
<p>{@company_info["address_line1"]}</p>
<%= if @company_info["address_line2"] != "" do %>
<p>{@company_info["address_line2"]}</p>
<% end %>
<p>
{@company_info["city"]}
<%= if @company_info["state"] != "" do %>
, {@company_info["state"]}
<% end %>
{@company_info["postal_code"]}
</p>
<%= if @company_info["country"] != "" do %>
<p>{@company_country_name}</p>
<% end %>
</div>
</div>
<% end %>
<%= if @company_info["vat_number"] != "" do %>
<div class="flex items-start gap-2">
<.icon name="hero-document-text" class="w-5 h-5 text-primary shrink-0 mt-0.5" />
<div class="text-sm">
<span class="text-base-content/60">{gettext("VAT:")}</span> {@company_info[
"vat_number"
]}
</div>
</div>
<% end %>
<%= if @site_url != "" do %>
<div class="flex items-start gap-2">
<.icon name="hero-globe-alt" class="w-5 h-5 text-primary shrink-0 mt-0.5" />
<a href={@site_url} target="_blank" class="text-sm link link-primary">
{@site_url}
</a>
</div>
<% else %>
<div class="flex items-start gap-2">
<.icon
name="hero-globe-alt"
class="w-5 h-5 text-base-content/40 shrink-0 mt-0.5"
/>
<span class="text-sm text-base-content/60 italic">
{gettext("Site URL not configured")}
</span>
</div>
<% end %>
</div>
<% end %>
</div>
</div>
<%!-- Section 3: DPO Contact --%>
<div class="card bg-base-100 shadow">
<div class="card-body">
<h2 class="card-title">{gettext("Data Protection Officer (DPO)")}</h2>
<p class="text-sm text-base-content/60 mb-4">
{gettext("Required for GDPR compliance. Leave empty if not applicable.")}
</p>
<form phx-submit="save_dpo_contact">
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div class="form-control">
<label class="label">
<span class="label-text">{gettext("DPO Name")}</span>
</label>
<input
type="text"
name="dpo_name"
value={@dpo_contact["name"]}
class="input input-bordered"
/>
</div>
<div class="form-control">
<label class="label">
<span class="label-text">{gettext("DPO Email")}</span>
</label>
<div class="join w-full">
<input
type="email"
name="dpo_email"
value={@dpo_contact["email"]}
class="input input-bordered join-item flex-1"
placeholder="dpo@example.com"
/>
<%= if @from_email != "" do %>
<button
type="button"
class="btn btn-outline join-item"
phx-click="import_dpo_email"
title={gettext("Import from General Settings")}
>
<.icon name="hero-arrow-down-tray" class="w-4 h-4" />
</button>
<% end %>
</div>
<%= if @from_email != "" do %>
<label class="label">
<span class="label-text-alt text-base-content/50">
{gettext("Import available:")} {@from_email}
</span>
</label>
<% end %>
</div>
<div class="form-control">
<label class="label">
<span class="label-text">{gettext("DPO Phone")}</span>
</label>
<input
type="tel"
name="dpo_phone"
value={@dpo_contact["phone"]}
class="input input-bordered"
/>
</div>
<div class="form-control">
<label class="label">
<span class="label-text">{gettext("DPO Address")}</span>
</label>
<div class="join w-full">
<input
type="text"
name="dpo_address"
value={@dpo_contact["address"]}
class="input input-bordered join-item flex-1"
/>
<%= if @company_address_formatted != "" do %>
<button
type="button"
class="btn btn-outline join-item"
phx-click="import_dpo_address"
title={gettext("Import from Company Address")}
>
<.icon name="hero-arrow-down-tray" class="w-4 h-4" />
</button>
<% end %>
</div>
<%= if @company_address_formatted != "" do %>
<label class="label">
<span class="label-text-alt text-base-content/50">
{gettext("Import available from Organization")}
</span>
</label>
<% end %>
</div>
</div>
<div class="card-actions justify-end mt-4">
<button type="submit" class="btn btn-primary">
{gettext("Save DPO Contact")}
</button>
</div>
</form>
</div>
</div>
<%!-- Section 4: Page Generation --%>
<div class="card bg-base-100 shadow">
<div class="card-body">
<div class="flex items-center justify-between">
<div>
<h2 class="card-title">{gettext("Legal Pages")}</h2>
<p class="text-sm text-base-content/60">
{gettext("Generate legal pages based on selected frameworks.")}
</p>
</div>
<button
phx-click="generate_all_pages"
class="btn btn-primary"
disabled={@generating or Enum.empty?(@selected_frameworks)}
>
<%= if @generating do %>
<span class="loading loading-spinner loading-sm"></span>
<% end %>
{gettext("Generate All Required Pages")}
</button>
</div>
<div class="divider"></div>
<%= if Enum.empty?(@selected_frameworks) do %>
<div class="alert alert-info">
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
class="stroke-current shrink-0 w-6 h-6"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
>
</path>
</svg>
<span>
{gettext("Select compliance frameworks above to see required pages.")}
</span>
</div>
<% else %>
<div class="overflow-x-auto">
<table class="table">
<thead>
<tr>
<th>{gettext("Page")}</th>
<th>{gettext("Required By")}</th>
<th>{gettext("Status")}</th>
<th class="text-right">{gettext("Actions")}</th>
</tr>
</thead>
<tbody>
<%= for {page_type, page_config} <- @available_page_types do %>
<% required_by =
Enum.filter(@selected_frameworks, fn fw_id ->
fw = @available_frameworks[fw_id]
fw && page_type in (fw.required_pages ++ fw.optional_pages)
end) %>
<%= if not Enum.empty?(required_by) do %>
<tr>
<td>
<div class="font-medium">{page_config.title}</div>
<div class="text-xs text-base-content/60">
{page_config.description}
</div>
</td>
<td>
<div class="flex flex-wrap gap-1">
<%= for fw_id <- required_by do %>
<% fw = @available_frameworks[fw_id] %>
<span class={"badge badge-sm #{if page_type in fw.required_pages, do: "badge-primary", else: "badge-ghost"}"}>
{fw_id}
</span>
<% end %>
</div>
</td>
<td>
<%= if page_generated?(page_type, @generated_pages) do %>
<% status = get_page_status(page_type, @generated_pages) %>
<span class={"badge #{if status == "published", do: "badge-success", else: "badge-warning"}"}>
{status}
</span>
<% else %>
<span class="badge badge-ghost">{gettext("Not generated")}</span>
<% end %>
</td>
<td class="text-right">
<div class="flex justify-end gap-2">
<%= if page_generated?(page_type, @generated_pages) do %>
<.link
navigate={get_edit_url(page_type, @generated_pages)}
class="btn btn-sm btn-ghost"
>
{gettext("Edit")}
</.link>
<%= if get_page_status(page_type, @generated_pages) != "published" do %>
<button
phx-click="publish_page"
phx-value-page_slug={page_type}
class="btn btn-sm btn-success"
>
{gettext("Publish")}
</button>
<% end %>
<% end %>
<button
phx-click="generate_page"
phx-value-page_type={page_type}
class="btn btn-sm btn-outline"
disabled={@generating}
>
{if page_generated?(page_type, @generated_pages),
do: gettext("Regenerate"),
else: gettext("Generate")}
</button>
</div>
</td>
</tr>
<% end %>
<% end %>
</tbody>
</table>
</div>
<div class="alert alert-warning mt-4">
<svg
xmlns="http://www.w3.org/2000/svg"
class="stroke-current shrink-0 h-6 w-6"
fill="none"
viewBox="0 0 24 24"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"
/>
</svg>
<div>
<h3 class="font-bold">{gettext("Legal Review Required")}</h3>
<div class="text-sm">
{gettext(
"Generated pages are drafts. Have them reviewed by a legal professional before publishing."
)}
</div>
</div>
</div>
<% end %>
</div>
</div>
<%!-- Section 5: Cookie Consent Widget (Phase 2) --%>
<div class="card bg-base-100 shadow">
<div class="card-body">
<h2 class="card-title flex items-center gap-2">
<svg class="w-5 h-5 text-primary" viewBox="0 0 24 24" fill="currentColor">
<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-1 17.93c-3.95-.49-7-3.85-7-7.93 0-.62.08-1.21.21-1.79L9 15v1c0 1.1.9 2 2 2v1.93zm6.9-2.54c-.26-.81-1-1.39-1.9-1.39h-1v-3c0-.55-.45-1-1-1H8v-2h2c.55 0 1-.45 1-1V7h2c1.1 0 2-.9 2-2v-.41c2.93 1.19 5 4.06 5 7.41 0 2.08-.8 3.97-2.1 5.39z" />
</svg>
{gettext("Cookie Consent Widget")}
</h2>
<p class="text-sm text-base-content/60 mb-4">
{gettext("Configure the cookie consent banner for your website visitors.")}
</p>
<%!-- Widget Enable Toggle --%>
<div class="flex items-center justify-between p-4 bg-base-200 rounded-lg mb-4">
<div>
<span class="font-medium">{gettext("Enable Cookie Banner")}</span>
<p class="text-sm text-base-content/60">
{gettext("Show cookie consent banner to visitors")}
</p>
</div>
<input
type="checkbox"
class="toggle toggle-primary"
checked={@consent_widget_enabled}
phx-click="toggle_consent_widget"
/>
</div>
<%!-- Warning about unpublished pages --%>
<%= if @consent_widget_enabled and @unpublished_pages != [] do %>
<div role="alert" class="alert alert-warning mb-4">
<svg
xmlns="http://www.w3.org/2000/svg"
class="stroke-current shrink-0 h-6 w-6"
fill="none"
viewBox="0 0 24 24"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"
/>
</svg>
<div>
<h3 class="font-bold">{gettext("Legal pages not published")}</h3>
<div class="text-sm">
{gettext(
"The following pages need to be published for the consent widget to work properly:"
)}
<strong>{Enum.join(@unpublished_pages, ", ")}</strong>. {gettext(
"Go to the Legal Pages section below to generate and publish them."
)}
</div>
</div>
</div>
<% end %>
<%!-- Consent widget settings form --%>
<%= if @consent_widget_enabled do %>
<form phx-submit="save_consent_settings" class="space-y-6">
<%!-- Consent Mode --%>
<div class="form-control">
<label class="label">
<span class="label-text font-medium">{gettext("Consent Mode")}</span>
</label>
<div class="grid grid-cols-1 md:grid-cols-2 gap-3">
<%!-- Strict Mode --%>
<label class={"flex items-start gap-3 p-4 border rounded-lg cursor-pointer hover:bg-base-200 transition-colors #{if @consent_mode == "strict", do: "border-primary bg-primary/5", else: "border-base-300"}"}>
<input
type="radio"
name="consent_mode"
value="strict"
checked={@consent_mode == "strict"}
class="radio radio-primary mt-1"
/>
<div>
<span class="font-medium">{gettext("Strict")}</span>
<p class="text-sm text-base-content/60 mt-1">
{gettext(
"Full GDPR compliance: floating icon, script blocking, re-consent on policy updates"
)}
</p>
</div>
</label>
<%!-- Notice Mode --%>
<label class={"flex items-start gap-3 p-4 border rounded-lg cursor-pointer hover:bg-base-200 transition-colors #{if @consent_mode == "notice", do: "border-primary bg-primary/5", else: "border-base-300"}"}>
<input
type="radio"
name="consent_mode"
value="notice"
checked={@consent_mode == "notice"}
class="radio radio-primary mt-1"
/>
<div>
<span class="font-medium">{gettext("Notice")}</span>
<p class="text-sm text-base-content/60 mt-1">
{gettext(
"Simple notice: banner shown once, no blocking, no floating icon"
)}
</p>
</div>
</label>
</div>
</div>
<%!-- Mode-specific options --%>
<%= if @consent_mode == "strict" do %>
<%!-- Icon visibility info for strict mode --%>
<div class={"alert #{if @show_consent_icon, do: "alert-success", else: "alert-info"}"}>
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
class="stroke-current shrink-0 w-6 h-6"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
/>
</svg>
<div>
<%= if @show_consent_icon do %>
<span class="font-medium">{gettext("Floating icon will be shown")}</span>
<p class="text-sm">
{gettext("Opt-in framework selected (GDPR, UK GDPR, LGPD, or PIPEDA)")}
</p>
<% else %>
<span class="font-medium">
{gettext("Floating icon will NOT be shown")}
</span>
<p class="text-sm">
{gettext("Select an opt-in framework to enable the floating icon")}
</p>
<% end %>
</div>
</div>
<%!-- Icon Position (only for strict mode) --%>
<div class="form-control">
<label class="label">
<span class="label-text">{gettext("Icon Position")}</span>
</label>
<div class="grid grid-cols-2 md:grid-cols-4 gap-2">
<%= for {position, label} <- [
{"top-left", gettext("Top Left")},
{"top-right", gettext("Top Right")},
{"bottom-left", gettext("Bottom Left")},
{"bottom-right", gettext("Bottom Right")}
] do %>
<label class={"flex items-center justify-center gap-2 p-3 border rounded-lg cursor-pointer hover:bg-base-200 transition-colors #{if @icon_position == position, do: "border-primary bg-primary/5", else: "border-base-300"}"}>
<input
type="radio"
name="icon_position"
value={position}
checked={@icon_position == position}
class="radio radio-primary radio-sm"
/>
<span class="text-sm">{label}</span>
</label>
<% end %>
</div>
</div>
<% else %>
<%!-- Notice mode options --%>
<div class="form-control">
<label class="label cursor-pointer justify-start gap-3">
<input
type="checkbox"
name="hide_for_authenticated"
value="true"
class="checkbox checkbox-primary"
checked={@hide_for_authenticated}
/>
<div>
<span class="label-text font-medium">
{gettext("Hide for authenticated users")}
</span>
<p class="text-sm text-base-content/60">
{gettext(
"Don't show consent banner to logged-in users (they agreed via Terms)"
)}
</p>
</div>
</label>
</div>
<%!-- Hidden icon_position for notice mode (keep current value) --%>
<input type="hidden" name="icon_position" value={@icon_position} />
<% end %>
<%!-- Policy Version Info --%>
<div class="form-control">
<label class="label">
<span class="label-text">{gettext("Policy Version")}</span>
</label>
<div class="flex items-center gap-2">
<span class="badge badge-lg badge-outline">{@policy_version}</span>
<span class="text-sm text-base-content/60">
{gettext("Auto-calculated from Cookie/Privacy Policy update dates")}
</span>
</div>
</div>
<%!-- Google Consent Mode --%>
<div class="form-control">
<label class="label cursor-pointer justify-start gap-3">
<input
type="checkbox"
name="google_consent_mode"
value="true"
class="checkbox checkbox-primary"
checked={@google_consent_mode}
/>
<div>
<span class="label-text font-medium">
{gettext("Google Consent Mode v2")}
</span>
<p class="text-sm text-base-content/60">
{gettext("Integrate with Google Analytics/Ads consent signals")}
</p>
</div>
</label>
</div>
<div class="card-actions justify-end">
<button type="submit" class="btn btn-primary">
{gettext("Save Settings")}
</button>
</div>
</form>
<%!-- Preview Section --%>
<div class="divider">{gettext("Preview")}</div>
<div class="mockup-browser border border-base-300 bg-base-100">
<div class="mockup-browser-toolbar">
<div class="input">{gettext("your-website.com")}</div>
</div>
<div class="bg-base-200 relative min-h-[320px]">
<%!-- Website placeholder content --%>
<div class="absolute inset-0 flex items-center justify-center p-8">
<div class="text-center text-base-content/30">
<svg
class="w-16 h-16 mx-auto mb-2 opacity-50"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="1.5"
d="M3.055 11H5a2 2 0 012 2v1a2 2 0 002 2 2 2 0 012 2v2.945M8 3.935V5.5A2.5 2.5 0 0010.5 8h.5a2 2 0 012 2 2 2 0 104 0 2 2 0 012-2h1.064M15 20.488V18a2 2 0 012-2h3.064M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
/>
</svg>
<p class="text-sm">{gettext("Your website content")}</p>
</div>
</div>
<%!-- Preview Floating Icon --%>
<%= if @show_consent_icon do %>
<div
class={[
"absolute z-20 w-12 h-12 rounded-full bg-primary shadow-lg flex items-center justify-center cursor-pointer hover:scale-110 transition-transform",
case @icon_position do
"top-left" -> "top-4 left-4"
"top-right" -> "top-4 right-4"
"bottom-left" -> "bottom-20 left-4"
_ -> "bottom-20 right-4"
end
]}
title={gettext("Cookie preferences")}
>
<svg
class="w-6 h-6 text-primary-content"
viewBox="0 0 24 24"
fill="currentColor"
>
<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-1 17.93c-3.95-.49-7-3.85-7-7.93 0-.62.08-1.21.21-1.79L9 15v1c0 1.1.9 2 2 2v1.93zm6.9-2.54c-.26-.81-1-1.39-1.9-1.39h-1v-3c0-.55-.45-1-1-1H8v-2h2c.55 0 1-.45 1-1V7h2c1.1 0 2-.9 2-2v-.41c2.93 1.19 5 4.06 5 7.41 0 2.08-.8 3.97-2.1 5.39z" />
</svg>
</div>
<% end %>
<%!-- Preview Banner (matches real widget) --%>
<div class="absolute z-10 bottom-0 left-0 right-0 bg-base-100/95 backdrop-blur-sm border-t border-base-300 shadow-lg">
<div class="max-w-4xl mx-auto px-4 py-4">
<div class="flex flex-wrap items-center gap-4">
<div class="flex items-start gap-3 flex-1 min-w-[200px]">
<div class="w-10 h-10 rounded-full bg-primary/10 flex items-center justify-center shrink-0">
<svg
class="w-5 h-5 text-primary"
viewBox="0 0 24 24"
fill="currentColor"
>
<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-1 17.93c-3.95-.49-7-3.85-7-7.93 0-.62.08-1.21.21-1.79L9 15v1c0 1.1.9 2 2 2v1.93z" />
</svg>
</div>
<div>
<h3 class="font-semibold text-sm text-base-content">
{gettext("We value your privacy")}
</h3>
<p class="text-xs text-base-content/60 mt-0.5">
{gettext("We use cookies to enhance your experience.")}
<a href="#" class="link link-primary text-xs ml-1">
{gettext("Cookie Policy")}
</a>
</p>
</div>
</div>
<div class="flex items-center gap-2 flex-wrap">
<button class="btn btn-ghost btn-sm text-xs">
{gettext("Customize")}
</button>
<button class="btn btn-outline btn-sm text-xs">
{gettext("Reject")}
</button>
<button class="btn btn-primary btn-sm text-xs">
{gettext("Accept All")}
</button>
</div>
</div>
</div>
</div>
</div>
</div>
<% end %>
</div>
</div>
<% end %>
</div>
</div>
</PhoenixKitWeb.Components.LayoutWrapper.app_layout>