Packages
phoenix_kit
1.7.206
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/sitemap/web/settings.html.heex
<PhoenixKitWeb.Components.LayoutWrapper.app_layout
flash={@flash}
phoenix_kit_current_scope={assigns[:phoenix_kit_current_scope]}
page_title={gettext("Sitemap Settings")}
current_path={@current_path}
project_title={@project_title}
current_locale={@current_locale}
>
<div class="container flex flex-col mx-auto px-4 py-6">
<.admin_page_header
back={Routes.path("/admin/modules", locale: @current_locale)}
title={gettext("Sitemap Settings")}
subtitle={gettext("Configure sitemap generation and scheduling")}
>
<:actions>
<label class="label cursor-pointer gap-2">
<span class="label-text font-medium">
{if @config.enabled, do: gettext("Enabled"), else: gettext("Disabled")}
</span>
<input
type="checkbox"
class="toggle toggle-primary"
checked={@config.enabled}
phx-click="toggle_sitemap"
/>
</label>
</:actions>
</.admin_page_header>
<div class="space-y-6">
<%!-- Stats Cards --%>
<div class="stats shadow w-full">
<div class="stat">
<div class="stat-figure text-primary">
<.icon name="hero-link" class="w-8 h-8" />
</div>
<div class="stat-title">{gettext("Total URLs")}</div>
<div class="stat-value text-primary">{@config.url_count || 0}</div>
</div>
<div class="stat">
<div class="stat-figure text-secondary">
<.icon name="hero-document-duplicate" class="w-8 h-8" />
</div>
<div class="stat-title">{gettext("Sitemap Files")}</div>
<div class="stat-value text-secondary">{length(@module_files)}</div>
</div>
<div class="stat">
<div class="stat-figure text-info">
<.icon name="hero-clock" class="w-8 h-8" />
</div>
<div class="stat-title">{gettext("Last Generated")}</div>
<div class="stat-value text-info text-lg">
{format_timestamp(@config.last_generated)}
</div>
</div>
<div class="stat">
<div class="stat-figure text-accent">
<button
class={"btn btn-circle btn-accent #{if @generating, do: "loading"}"}
phx-click="regenerate"
disabled={@generating || !@config.enabled}
>
<.icon :if={!@generating} name="hero-arrow-path" class="w-6 h-6" />
</button>
</div>
<div class="stat-title">{gettext("Actions")}</div>
<div class="stat-value text-lg">
<button class="btn btn-sm btn-ghost" phx-click="regenerate" disabled={@generating}>
{gettext("Regenerate All")}
</button>
</div>
</div>
</div>
<%!-- Router Discovery / Mode Switch --%>
<div class="card bg-base-200 shadow-md">
<div class="card-body">
<div class="flex items-center justify-between">
<div>
<h2 class="card-title">
<.icon name="hero-map" class="w-5 h-5" /> {gettext("Router Discovery")}
</h2>
<p class="text-sm text-base-content/60 mt-1">
{gettext(
"When enabled, scans all routes and builds a single flat sitemap.xml with URLs from all content sources (shop, entities, posts, publishing). When disabled, each source generates its own sitemap file."
)}
</p>
</div>
<input
type="checkbox"
class="toggle toggle-primary"
checked={@config.router_discovery_enabled}
phx-click="toggle_source"
phx-value-source="router_discovery"
/>
</div>
<%!-- Flat mode info (when Router Discovery ON) --%>
<div :if={@config.router_discovery_enabled} class="mt-4">
<div class="alert alert-info">
<.icon name="hero-information-circle" class="w-5 h-5" />
<span>
{gettext("Flat mode active — /sitemap.xml contains all URLs in a single <urlset>")}
</span>
</div>
<div class="flex flex-wrap gap-2 mt-3">
<span class="badge badge-primary">{gettext("Routes")}</span>
<span class="badge badge-primary">{gettext("Static")}</span>
<span class={
if @module_enabled.publishing,
do: "badge badge-primary",
else: "badge badge-ghost"
}>
{gettext("Publishing")}
</span>
<span class={
if @module_enabled.entities, do: "badge badge-primary", else: "badge badge-ghost"
}>
{gettext("Entities")}
</span>
<span class={
if @module_enabled.posts, do: "badge badge-primary", else: "badge badge-ghost"
}>
{gettext("Posts")}
</span>
<span class={
if @module_enabled.shop, do: "badge badge-primary", else: "badge badge-ghost"
}>
{gettext("Shop")}
</span>
</div>
</div>
</div>
</div>
<%!-- Module Sitemaps (only when Router Discovery OFF = index mode) --%>
<div :if={!@config.router_discovery_enabled} class="card bg-base-200 shadow-md">
<div class="card-body">
<h2 class="card-title">
<.icon name="hero-circle-stack" class="w-5 h-5" /> {gettext("Module Sitemaps")}
</h2>
<p class="text-sm text-base-content/60 mb-4">
{gettext(
"Each source generates its own sitemap file, referenced by the main sitemap index"
)}
</p>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<%!-- Static Pages --%>
<div class="rounded-lg bg-base-100 p-4">
<div class="flex items-center justify-between mb-2">
<div class="flex items-center gap-2">
<.icon name="hero-document-text" class="w-4 h-4 text-primary" />
<span class="font-medium">{gettext("Static Pages")}</span>
</div>
<input
type="checkbox"
class="toggle toggle-sm toggle-primary"
checked={@config.include_static}
phx-click="toggle_source"
phx-value-source="static"
/>
</div>
<p class="text-xs text-base-content/60 mb-2">
{gettext("Homepage and custom static URLs")}
</p>
<div class="flex items-center justify-between text-xs">
<div class="flex items-center gap-2">
<span class="badge badge-sm badge-outline">
{count_source_urls(@module_stats, "sitemap-static")} URLs
</span>
<span
:for={f <- source_files(@module_files, "sitemap-static")}
class="badge badge-sm badge-ghost"
>
{f}.xml
</span>
</div>
<div class="flex items-center gap-1">
<.link
:for={f <- source_files(@module_files, "sitemap-static")}
href={Routes.path("/sitemaps/#{f}.xml", locale: :none)}
target="_blank"
class="btn btn-ghost btn-xs"
title={gettext("View sitemap")}
>
<.icon name="hero-arrow-top-right-on-square" class="w-3 h-3" />
</.link>
</div>
</div>
</div>
<%!-- Publishing / Blogs --%>
<div class={"rounded-lg bg-base-100 p-4 #{unless @module_enabled.publishing, do: "opacity-50"}"}>
<div class="flex items-center justify-between mb-2">
<div class="flex items-center gap-2">
<.icon name="hero-newspaper" class="w-4 h-4 text-primary" />
<span class="font-medium">{gettext("Publishing")}</span>
<span :if={!@module_enabled.publishing} class="badge badge-xs badge-warning">
{gettext("module off")}
</span>
</div>
<input
type="checkbox"
class="toggle toggle-sm toggle-primary"
checked={@config.include_blogs}
phx-click="toggle_source"
phx-value-source="blogs"
disabled={!@module_enabled.publishing}
/>
</div>
<p class="text-xs text-base-content/60 mb-2">
{gettext("Blog groups and published posts")}
</p>
<div class="flex items-center justify-between text-xs">
<div class="flex items-center gap-2 flex-wrap">
<span class="badge badge-sm badge-outline">
{count_source_urls(@module_stats, "sitemap-publishing")} URLs
</span>
<span
:for={f <- source_files(@module_files, "sitemap-publishing")}
class="badge badge-sm badge-ghost"
>
{f}.xml
</span>
</div>
<div class="flex items-center gap-1">
<.link
:for={f <- source_files(@module_files, "sitemap-publishing")}
href={Routes.path("/sitemaps/#{f}.xml", locale: :none)}
target="_blank"
class="btn btn-ghost btn-xs"
title={gettext("View sitemap")}
>
<.icon name="hero-arrow-top-right-on-square" class="w-3 h-3" />
</.link>
</div>
</div>
<%!-- Split by blog toggle --%>
<label class="flex items-center justify-between cursor-pointer mt-3 pt-3 border-t border-base-300">
<span class="text-xs">{gettext("Split by blog group")}</span>
<input
type="checkbox"
class="toggle toggle-xs toggle-secondary"
checked={@publishing_split_by_group}
phx-click="toggle_publishing_split"
/>
</label>
</div>
<%!-- Entities --%>
<div class={"rounded-lg bg-base-100 p-4 #{unless @module_enabled.entities, do: "opacity-50"}"}>
<div class="flex items-center justify-between mb-2">
<div class="flex items-center gap-2">
<.icon name="hero-cube" class="w-4 h-4 text-primary" />
<span class="font-medium">{gettext("Entities")}</span>
<span :if={!@module_enabled.entities} class="badge badge-xs badge-warning">
{gettext("module off")}
</span>
</div>
<input
type="checkbox"
class="toggle toggle-sm toggle-primary"
checked={@config.include_entities}
phx-click="toggle_source"
phx-value-source="entities"
disabled={!@module_enabled.entities}
/>
</div>
<p class="text-xs text-base-content/60 mb-2">
{gettext("Dynamic content types (per-type files)")}
</p>
<div class="flex items-center justify-between text-xs">
<div class="flex items-center gap-2 flex-wrap">
<span class="badge badge-sm badge-outline">
{count_source_urls(@module_stats, "sitemap-entities")} URLs
</span>
<span
:for={f <- source_files(@module_files, "sitemap-entities")}
class="badge badge-sm badge-ghost"
>
{f}.xml
</span>
</div>
<div class="flex items-center gap-1">
<.link
:for={f <- source_files(@module_files, "sitemap-entities")}
href={Routes.path("/sitemaps/#{f}.xml", locale: :none)}
target="_blank"
class="btn btn-ghost btn-xs"
title={gettext("View sitemap")}
>
<.icon name="hero-arrow-top-right-on-square" class="w-3 h-3" />
</.link>
</div>
</div>
</div>
<%!-- Posts --%>
<div class={"rounded-lg bg-base-100 p-4 #{unless @module_enabled.posts, do: "opacity-50"}"}>
<div class="flex items-center justify-between mb-2">
<div class="flex items-center gap-2">
<.icon name="hero-pencil-square" class="w-4 h-4 text-primary" />
<span class="font-medium">{gettext("Posts")}</span>
<span :if={!@module_enabled.posts} class="badge badge-xs badge-warning">
{gettext("module off")}
</span>
</div>
</div>
<p class="text-xs text-base-content/60 mb-2">
{gettext("Public posts from Posts module")}
</p>
<div class="flex items-center justify-between text-xs">
<div class="flex items-center gap-2">
<span class="badge badge-sm badge-outline">
{count_source_urls(@module_stats, "sitemap-posts")} URLs
</span>
<span
:for={f <- source_files(@module_files, "sitemap-posts")}
class="badge badge-sm badge-ghost"
>
{f}.xml
</span>
</div>
<div class="flex items-center gap-1">
<.link
:for={f <- source_files(@module_files, "sitemap-posts")}
href={Routes.path("/sitemaps/#{f}.xml", locale: :none)}
target="_blank"
class="btn btn-ghost btn-xs"
title={gettext("View sitemap")}
>
<.icon name="hero-arrow-top-right-on-square" class="w-3 h-3" />
</.link>
</div>
</div>
</div>
<%!-- Shop --%>
<div class={"rounded-lg bg-base-100 p-4 #{unless @module_enabled.shop, do: "opacity-50"}"}>
<div class="flex items-center justify-between mb-2">
<div class="flex items-center gap-2">
<.icon name="hero-shopping-bag" class="w-4 h-4 text-primary" />
<span class="font-medium">{gettext("Shop")}</span>
<span :if={!@module_enabled.shop} class="badge badge-xs badge-warning">
{gettext("module off")}
</span>
</div>
<input
type="checkbox"
class="toggle toggle-sm toggle-primary"
checked={@config.include_shop}
phx-click="toggle_source"
phx-value-source="shop"
disabled={!@module_enabled.shop}
/>
</div>
<p class="text-xs text-base-content/60 mb-2">
{gettext("Products, categories, catalog")}
</p>
<div class="flex items-center justify-between text-xs">
<div class="flex items-center gap-2">
<span class="badge badge-sm badge-outline">
{count_source_urls(@module_stats, "sitemap-shop")} URLs
</span>
<span
:for={f <- source_files(@module_files, "sitemap-shop")}
class="badge badge-sm badge-ghost"
>
{f}.xml
</span>
</div>
<div class="flex items-center gap-1">
<.link
:for={f <- source_files(@module_files, "sitemap-shop")}
href={Routes.path("/sitemaps/#{f}.xml", locale: :none)}
target="_blank"
class="btn btn-ghost btn-xs"
title={gettext("View sitemap")}
>
<.icon name="hero-arrow-top-right-on-square" class="w-3 h-3" />
</.link>
</div>
</div>
</div>
</div>
</div>
</div>
<%!-- Auth Pages Section --%>
<div class="card bg-base-200 shadow-md">
<div class="card-body">
<h2 class="card-title">
<.icon name="hero-lock-closed" class="w-5 h-5" /> {gettext("Auth Pages")}
</h2>
<div class="grid grid-cols-1 sm:grid-cols-3 gap-3 mt-2">
<%!-- Login - always excluded --%>
<div class="flex items-center justify-between p-3 rounded-lg bg-base-100">
<div>
<span class="label-text font-medium">{gettext("Login")}</span>
<span class="text-xs text-base-content/60 block">
{gettext("Always excluded from sitemap")}
</span>
</div>
<span class="badge badge-sm badge-error badge-outline">{gettext("Excluded")}</span>
</div>
<%!-- Logout - always excluded --%>
<div class="flex items-center justify-between p-3 rounded-lg bg-base-100">
<div>
<span class="label-text font-medium">{gettext("Logout")}</span>
<span class="text-xs text-base-content/60 block">
{gettext("Always excluded from sitemap")}
</span>
</div>
<span class="badge badge-sm badge-error badge-outline">{gettext("Excluded")}</span>
</div>
<%!-- Registration - toggleable --%>
<label class="flex items-center justify-between cursor-pointer p-3 rounded-lg bg-base-100">
<div>
<span class="label-text font-medium">{gettext("Registration")}</span>
<span class="text-xs text-base-content/60 block">
{gettext("Include in static sitemap")}
</span>
</div>
<input
type="checkbox"
class="toggle toggle-sm toggle-primary"
checked={@include_registration}
phx-click="toggle_registration"
/>
</label>
</div>
</div>
</div>
<%!-- Source-Contributed Settings (extension point: Source.sitemap_settings_schema/0) --%>
<div :for={{mod, fields} <- @extension_sources} class="card bg-base-200 shadow-md">
<div class="card-body">
<h2 class="card-title">
<.icon name="hero-puzzle-piece" class="w-5 h-5" /> {source_display_name(mod)}
</h2>
<div class="space-y-3 mt-2">
<div :for={field <- fields}>
<label
:if={field.type == :boolean}
class="flex items-center justify-between cursor-pointer"
>
<div>
<span class="label-text font-medium">{field.label}</span>
<span :if={field.help} class="text-xs text-base-content/60 block">
{field.help}
</span>
</div>
<input
type="checkbox"
class="toggle toggle-sm toggle-primary"
checked={field.value}
phx-click="toggle_extension_setting"
phx-value-key={field.key}
/>
</label>
<form
:if={field.type != :boolean}
phx-submit="save_extension_setting"
phx-value-key={field.key}
class="form-control"
>
<label class="label">
<span class="label-text font-medium">{field.label}</span>
</label>
<p :if={field.help} class="text-xs text-base-content/60 mb-1">{field.help}</p>
<div class="flex gap-2">
<input
type={if field.type == :integer, do: "number", else: "text"}
name="value"
value={field.value}
class="input input-bordered input-sm w-full"
/>
<button type="submit" class="btn btn-sm btn-primary">{gettext("Save")}</button>
</div>
</form>
</div>
</div>
</div>
</div>
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
<%!-- Configuration Card --%>
<div class="card bg-base-200 shadow-md">
<div class="card-body">
<h2 class="card-title">
<.icon name="hero-cog-6-tooth" class="w-5 h-5" /> {gettext("Configuration")}
</h2>
<div class="space-y-4 mt-4">
<%!-- Site URL (read-only) --%>
<div class="form-control">
<label class="label">
<span class="label-text font-medium">{gettext("Site URL")}</span>
</label>
<div class="flex gap-2">
<input
type="url"
class="input input-bordered w-full bg-base-200"
value={@site_url || ""}
disabled
readonly
/>
<.link
navigate={Routes.path("/admin/settings", locale: @current_locale)}
class="btn btn-outline btn-sm"
title={gettext("Edit in Settings")}
>
<.icon name="hero-pencil" class="w-4 h-4" />
</.link>
</div>
</div>
<%!-- XSL Styling --%>
<label class="flex items-center justify-between cursor-pointer">
<div>
<span class="label-text font-medium">{gettext("Browser Display Styling")}</span>
<span class="text-xs text-base-content/60 block">
{gettext("Style XML with XSL for beautiful browser display")}
</span>
</div>
<input
type="checkbox"
class="toggle toggle-sm toggle-primary"
checked={@config.html_enabled}
phx-click="toggle_html"
/>
</label>
<%!-- Display Style --%>
<form phx-change="update_style" class="form-control">
<label class="label">
<span class="label-text font-medium">{gettext("Display Style")}</span>
</label>
<label class="select w-full">
<select
name="style"
disabled={!@config.html_enabled}
>
<option value="table" selected={@config.html_style in ["table", "grouped"]}>
{gettext("Table (Clean table layout)")}
</option>
<option value="minimal" selected={@config.html_style in ["minimal", "flat"]}>
{gettext("Minimal (Simple list)")}
</option>
</select>
</label>
</form>
</div>
</div>
</div>
<%!-- Scheduling Card --%>
<div class="card bg-base-200 shadow-md">
<div class="card-body">
<div class="flex items-center justify-between">
<h2 class="card-title">
<.icon name="hero-clock" class="w-5 h-5" /> {gettext("Scheduled Generation")}
</h2>
<input
type="checkbox"
class="toggle toggle-primary"
checked={@config.schedule_enabled}
phx-click="toggle_schedule"
/>
</div>
<form phx-change="update_interval" class="flex items-center gap-4 mt-4">
<span class="text-sm">{gettext("Regenerate every")}</span>
<label class="select select-sm">
<select
name="interval"
disabled={!@config.schedule_enabled}
>
<option value="1" selected={@config.schedule_interval_hours == 1}>
{gettext("1 hour")}
</option>
<option value="6" selected={@config.schedule_interval_hours == 6}>
{gettext("6 hours")}
</option>
<option value="12" selected={@config.schedule_interval_hours == 12}>
{gettext("12 hours")}
</option>
<option value="24" selected={@config.schedule_interval_hours == 24}>
{gettext("24 hours")}
</option>
<option value="48" selected={@config.schedule_interval_hours == 48}>
{gettext("48 hours")}
</option>
<option value="168" selected={@config.schedule_interval_hours == 168}>
{gettext("Weekly")}
</option>
</select>
</label>
</form>
</div>
</div>
</div>
<%!-- Quick Actions Card --%>
<div class="card bg-base-200 shadow-md">
<div class="card-body">
<h2 class="card-title">
<.icon name="hero-bolt" class="w-5 h-5" /> {gettext("Quick Actions")}
</h2>
<div class="flex flex-wrap gap-3 mt-4">
<button class="btn btn-outline btn-sm" phx-click="preview" phx-value-type="xml">
<.icon name="hero-code-bracket" class="w-4 h-4 mr-1" /> {gettext(
"Preview Sitemap Source"
)}
</button>
<button class="btn btn-outline btn-sm btn-warning" phx-click="invalidate_cache">
<.icon name="hero-trash" class="w-4 h-4 mr-1" /> {gettext("Clear All Sitemaps")}
</button>
</div>
<%!-- File links: index mode shows all module files --%>
<div
:if={!@config.router_discovery_enabled && @module_files != []}
class="mt-4 pt-4 border-t border-base-300"
>
<p class="text-sm font-medium text-base-content mb-3">
{gettext("Generated sitemap files:")}
</p>
<div class="flex flex-wrap gap-2">
<.link
href={Routes.path("/sitemap.xml", locale: :none)}
target="_blank"
class="btn btn-sm btn-primary btn-outline gap-1"
>
<.icon name="hero-globe-alt" class="w-4 h-4" /> {gettext("sitemap.xml (index)")}
</.link>
<.link
:for={f <- @module_files}
href={Routes.path("/sitemaps/#{f}.xml", locale: :none)}
target="_blank"
class="btn btn-sm btn-outline gap-1"
>
<.icon name="hero-document" class="w-4 h-4" /> {f}.xml
</.link>
</div>
</div>
<%!-- File links: flat mode shows only main sitemap.xml --%>
<div
:if={@config.router_discovery_enabled}
class="mt-4 pt-4 border-t border-base-300"
>
<div class="flex flex-wrap gap-2">
<.link
href={Routes.path("/sitemap.xml", locale: :none)}
target="_blank"
class="btn btn-sm btn-primary btn-outline gap-1"
>
<.icon name="hero-globe-alt" class="w-4 h-4" /> sitemap.xml
</.link>
</div>
</div>
<p class="text-xs text-base-content/60 mt-3">
<.icon name="hero-information-circle" class="w-3 h-3 inline" />
{if @config.router_discovery_enabled,
do: gettext("/sitemap.xml contains all URLs in a single <urlset>"),
else: gettext("/sitemap.xml is a sitemapindex referencing per-module sitemap files")}
</p>
</div>
</div>
<%!-- Advanced Settings --%>
<div class="card bg-base-200 shadow-md">
<div class="card-body">
<h2 class="card-title">
<.icon name="hero-wrench-screwdriver" class="w-5 h-5" /> {gettext("Advanced")}
</h2>
<p class="text-sm text-base-content/60 mb-2">
{gettext(
"Low-level Router Discovery and static-page settings. Most sites won't need these — they were previously only reachable by editing the database directly."
)}
</p>
<div class="grid grid-cols-1 lg:grid-cols-2 gap-4 mt-2">
<%!-- Exclude Patterns --%>
<form phx-submit="save_exclude_patterns" class="form-control">
<label class="label">
<span class="label-text font-medium">
{gettext("Router Discovery: Exclude Patterns")}
</span>
</label>
<p class="text-xs text-base-content/60 mb-1">
{gettext(
"One regular expression per line. Replaces the built-in defaults entirely when saved — an empty list means nothing is excluded."
)}
</p>
<textarea
name="patterns"
rows="6"
class="textarea textarea-bordered font-mono text-xs w-full"
>{@exclude_patterns_text}</textarea>
<div :if={@exclude_patterns_error} class="alert alert-error mt-2 py-2 text-xs">
{@exclude_patterns_error}
</div>
<button type="submit" class="btn btn-sm btn-primary mt-2 self-start">
{gettext("Save Exclude Patterns")}
</button>
</form>
<%!-- Protected Pipelines --%>
<form phx-submit="save_protected_pipelines" class="form-control">
<label class="label">
<span class="label-text font-medium">{gettext("Protected Pipelines")}</span>
</label>
<p class="text-xs text-base-content/60 mb-1">
{gettext("One pipeline name per line. Added on top of the built-in defaults:")}
<span class="font-mono">{@protected_pipelines_defaults_text}</span>
</p>
<textarea
name="pipelines"
rows="6"
class="textarea textarea-bordered font-mono text-xs w-full"
>{@protected_pipelines_text}</textarea>
<div :if={@protected_pipelines_error} class="alert alert-error mt-2 py-2 text-xs">
{@protected_pipelines_error}
</div>
<button type="submit" class="btn btn-sm btn-primary mt-2 self-start">
{gettext("Save Protected Pipelines")}
</button>
</form>
<%!-- Custom URLs --%>
<form phx-submit="save_custom_urls" class="form-control">
<label class="label">
<span class="label-text font-medium">{gettext("Custom URLs")}</span>
</label>
<p class="text-xs text-base-content/60 mb-1">
{gettext(
"JSON array of extra static URL entries (path, priority, changefreq, title, category)."
)}
</p>
<textarea
name="json"
rows="8"
class="textarea textarea-bordered font-mono text-xs w-full"
>{@custom_urls_text}</textarea>
<div :if={@custom_urls_error} class="alert alert-error mt-2 py-2 text-xs">
{@custom_urls_error}
</div>
<button type="submit" class="btn btn-sm btn-primary mt-2 self-start">
{gettext("Save Custom URLs")}
</button>
</form>
<%!-- Static Routes --%>
<form phx-submit="save_static_routes" class="form-control">
<label class="label">
<span class="label-text font-medium">{gettext("Static Routes")}</span>
</label>
<p class="text-xs text-base-content/60 mb-1">
{gettext(
"JSON array of route configurations (plug or path, priority, changefreq, title, category). Replaces the built-in defaults entirely when saved — an empty list removes the homepage entry too."
)}
</p>
<textarea
name="json"
rows="8"
class="textarea textarea-bordered font-mono text-xs w-full"
>{@static_routes_text}</textarea>
<div :if={@static_routes_error} class="alert alert-error mt-2 py-2 text-xs">
{@static_routes_error}
</div>
<button type="submit" class="btn btn-sm btn-primary mt-2 self-start">
{gettext("Save Static Routes")}
</button>
</form>
</div>
</div>
</div>
</div>
<%!-- Preview Modal --%>
<div :if={@show_preview} class="modal modal-open">
<div class="modal-box max-w-4xl max-h-[80vh]">
<div class="flex justify-between items-center mb-4">
<h3 class="font-bold text-lg">{gettext("Sitemap Source")}</h3>
<button class="btn btn-ghost btn-sm btn-circle" phx-click="close_preview">
<.icon name="hero-x-mark" class="w-4 h-4" />
</button>
</div>
<div class="overflow-auto max-h-[60vh] bg-base-300 rounded-lg p-4">
<pre class="text-xs whitespace-pre-wrap break-all"><code>{@preview_content}</code></pre>
</div>
<div class="modal-action">
<.link
href={Routes.path("/sitemap.xml", locale: :none)}
target="_blank"
class="btn btn-primary"
>
<.icon name="hero-arrow-top-right-on-square" class="w-4 h-4 mr-1" /> {gettext(
"Open in Browser"
)}
</.link>
<button class="btn" phx-click="close_preview">{gettext("Close")}</button>
</div>
</div>
<div class="modal-backdrop" phx-click="close_preview"></div>
</div>
</div>
</PhoenixKitWeb.Components.LayoutWrapper.app_layout>