Packages
phoenix_kit
2.22.5
2.22.7
2.22.6
2.22.5
2.22.4
2.22.3
2.22.2
2.22.1
2.22.0
2.21.5
2.21.4
2.21.3
2.21.2
2.21.1
2.21.0
2.20.0
2.19.0
2.18.1
2.18.0
2.17.0
2.16.0
2.15.1
2.15.0
2.14.2
2.14.1
2.14.0
2.13.19
2.13.18
2.13.17
2.13.16
2.13.15
2.13.13
2.13.12
2.13.11
2.13.10
2.13.9
2.13.8
2.13.7
2.13.6
2.13.5
2.13.4
2.13.3
2.13.2
2.13.1
2.13.0
2.12.1
2.12.0
2.11.0
2.10.0
2.9.0
2.8.1
2.8.0
2.7.0
2.6.0
2.5.0
2.4.0
2.3.0
2.2.0
2.1.0
2.0.1
2.0.0
1.7.236
1.7.235
1.7.234
1.7.233
1.7.232
1.7.231
1.7.230
1.7.229
1.7.228
1.7.227
1.7.226
1.7.225
1.7.224
1.7.223
1.7.222
1.7.221
1.7.220
1.7.219
1.7.218
1.7.217
1.7.216
1.7.215
1.7.214
1.7.213
1.7.212
1.7.211
1.7.210
1.7.209
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/phoenix_kit_web/live/modules.html.heex
<PhoenixKitWeb.Components.LayoutWrapper.app_layout
socket={@socket}
flash={@flash}
phoenix_kit_current_scope={assigns[:phoenix_kit_current_scope]}
page_title={gettext("Modules")}
page_subtitle={gettext("Manage modules")}
current_path={@url_path}
project_title={@project_title}
current_locale={@current_locale}
>
<div class="flex flex-col px-4 py-6">
<%!-- Dependency warnings --%>
<div :if={@dep_warnings != []} class="mb-4 space-y-2">
<div
:for={w <- @dep_warnings}
class="alert alert-warning flex items-center gap-2 py-2 text-sm"
>
<.icon name="hero-exclamation-triangle" class="w-4 h-4 shrink-0" />
<span>
<strong>{w.module_name}</strong>
{gettext("requires the")} <strong>{w.requires_key}</strong>
{gettext("module to be enabled. Some features may not work correctly.")}
</span>
</div>
</div>
<.nav_tabs
active_tab={@modules_tab}
on_change="switch_modules_tab"
variant={:border}
class="mb-6"
tabs={[
%{
id: "active",
label: gettext("Active"),
icon: "hero-check-circle",
badge: @modules_tab_counts.active
},
%{
id: "disabled",
label: gettext("Disabled"),
icon: "hero-pause-circle",
badge: @modules_tab_counts.disabled
},
%{
id: "not_installed",
label: gettext("Not Installed"),
icon: "hero-cube-transparent",
badge: @modules_tab_counts.not_installed
}
]}
/>
<div
:if={
@modules_tab in ["active", "disabled"] and
Map.get(@modules_tab_counts, String.to_existing_atom(@modules_tab)) == 0
}
class="alert alert-info"
>
<.icon name="hero-information-circle" class="w-5 h-5" />
<span :if={@modules_tab == "active"}>{gettext("No modules are currently enabled.")}</span>
<span :if={@modules_tab == "disabled"}>{gettext("No modules are currently disabled.")}</span>
</div>
<div
:if={@modules_tab in ["active", "disabled"]}
class="grid gap-6 md:grid-cols-2 lg:grid-cols-3"
>
<%!-- Languages Module --%>
<%= if "languages" in @accessible_modules and modules_tab_match?(@modules_tab, module_enabled?(@module_configs["languages"] || %{})) do %>
<% cfg = @module_configs["languages"] || %{} %>
<PhoenixKitWeb.Components.Core.ModuleCard.module_card
title={gettext("Languages")}
description={gettext("Manage language module and translations")}
icon="🌐"
enabled={cfg[:enabled] || false}
toggle_event="toggle_module"
toggle_key="languages"
>
<:status_badges>
<span class={[
"badge",
if(cfg[:enabled], do: "badge-success", else: "badge-neutral")
]}>
{if cfg[:enabled], do: gettext("Enabled"), else: gettext("Disabled")}
</span>
<%= if cfg[:enabled] do %>
<span class="badge badge-outline ml-2 whitespace-nowrap">
{if cfg[:default_language],
do: cfg[:default_language].name,
else: gettext("No Default")}
</span>
<% end %>
</:status_badges>
<:action_buttons>
<%= if cfg[:enabled] do %>
<.link
navigate={PhoenixKit.Utils.Routes.path("/admin/settings/languages")}
class="btn btn-primary btn-sm"
>
<.icon name="hero-cog-6-tooth" class="w-4 h-4 mr-1" /> {gettext("Configure")}
</.link>
<% else %>
<.link
navigate={PhoenixKit.Utils.Routes.path("/admin/settings/languages")}
class="btn btn-outline btn-sm"
>
<.icon name="hero-cog-6-tooth" class="w-4 h-4 mr-1" /> {gettext("Configure")}
</.link>
<% end %>
</:action_buttons>
<:stats>
<div class="grid grid-cols-2 gap-2 text-xs">
<div>
<span class="text-base-content/70">{gettext("Total languages")}:</span>
<span class="font-medium">{cfg[:language_count]}</span>
</div>
<div>
<span class="text-base-content/70">{gettext("Enabled")}:</span>
<span class="font-medium">{cfg[:enabled_count]}</span>
</div>
</div>
</:stats>
</PhoenixKitWeb.Components.Core.ModuleCard.module_card>
<% end %>
<%!-- Crawlers Module --%>
<%= if "crawlers" in @accessible_modules and modules_tab_match?(@modules_tab, module_enabled?(@module_configs["crawlers"] || %{})) do %>
<% cfg = @module_configs["crawlers"] || %{} %>
<PhoenixKitWeb.Components.Core.ModuleCard.module_card
title={gettext("Crawlers")}
description={
gettext("Control how search engines and AI bots crawl and index this site")
}
icon="🕷️"
enabled={cfg[:module_enabled]}
toggle_event="toggle_module"
toggle_key="crawlers"
>
<:status_badges>
<span class={[
"badge",
if(cfg[:module_enabled], do: "badge-success", else: "badge-neutral")
]}>
{if cfg[:module_enabled], do: gettext("Enabled"), else: gettext("Disabled")}
</span>
</:status_badges>
<:action_buttons>
<%= if cfg[:module_enabled] do %>
<.link
navigate={
PhoenixKit.Utils.Routes.locale_aware_path(
assigns,
"/admin/settings/crawlers"
)
}
class="btn btn-sm w-full btn-primary"
>
<.icon name="hero-cog-6-tooth" class="w-4 h-4 mr-1" /> {gettext("Configure")}
</.link>
<% else %>
<span class="text-xs text-base-content/50 text-center">
{gettext("Enable module to access settings")}
</span>
<% end %>
</:action_buttons>
<:stats>
<div class="text-sm">
<span class="text-base-content/70">{gettext("Noindex")}:</span>
<span class="ml-2 font-medium">
{if cfg[:no_index_enabled], do: gettext("On"), else: gettext("Off")}
</span>
</div>
<div class="text-sm">
<span class="text-base-content/70">{gettext("Blocked groups")}:</span>
<span class="ml-2 font-medium">
{length(cfg[:blocked_groups] || [])}
</span>
</div>
</:stats>
</PhoenixKitWeb.Components.Core.ModuleCard.module_card>
<% end %>
<%!-- Sitemap Module --%>
<%= if "sitemap" in @accessible_modules and modules_tab_match?(@modules_tab, module_enabled?(@module_configs["sitemap"] || %{})) do %>
<% cfg = @module_configs["sitemap"] || %{} %>
<PhoenixKitWeb.Components.Core.ModuleCard.module_card
title={gettext("Sitemap")}
description={
gettext("Generate XML sitemaps for search engines with styled browser display")
}
icon="🗺️"
enabled={cfg[:enabled] || false}
toggle_event="toggle_module"
toggle_key="sitemap"
>
<:status_badges>
<span class={[
"badge",
if(cfg[:enabled], do: "badge-success", else: "badge-neutral")
]}>
{if cfg[:enabled], do: gettext("Enabled"), else: gettext("Disabled")}
</span>
<%= if cfg[:enabled] && cfg[:schedule_enabled] do %>
<span class="badge badge-outline ml-2 whitespace-nowrap">
{gettext("Auto-regenerate")}
</span>
<% end %>
</:status_badges>
<:action_buttons>
<div class="flex flex-col gap-2 w-full">
<%= if cfg[:enabled] do %>
<.link
navigate={
PhoenixKit.Utils.Routes.path("/admin/settings/sitemap",
locale: :none
)
}
class="btn btn-sm btn-primary"
>
<.icon name="hero-cog-6-tooth" class="w-4 h-4 mr-1" /> {gettext("Configure")}
</.link>
<.link
href={PhoenixKit.Utils.Routes.path("/sitemap.xml", locale: :none)}
target="_blank"
class="btn btn-outline btn-sm"
>
<.icon name="hero-arrow-top-right-on-square" class="w-4 h-4 mr-1" /> {gettext(
"Sitemap"
)}
</.link>
<% else %>
<span class="text-xs text-base-content/50 text-center">
{gettext("Enable module to access settings")}
</span>
<% end %>
</div>
</:action_buttons>
<:stats>
<div class="grid grid-cols-2 gap-2 text-xs">
<div>
<span class="text-base-content/70">{gettext("URLs")}:</span>
<span class="font-medium">{cfg[:url_count]}</span>
</div>
<div>
<span class="text-base-content/70">{gettext("Last gen")}:</span>
<span class="font-medium">
{format_timestamp(cfg[:last_generated])}
</span>
</div>
</div>
</:stats>
</PhoenixKitWeb.Components.Core.ModuleCard.module_card>
<% end %>
<%!-- Pages module remains disabled but code retained for future use --%>
<%!-- Jobs Module --%>
<%= if "jobs" in @accessible_modules and modules_tab_match?(@modules_tab, module_enabled?(@module_configs["jobs"] || %{})) do %>
<% cfg = @module_configs["jobs"] || %{} %>
<PhoenixKitWeb.Components.Core.ModuleCard.module_card
title={gettext("Jobs")}
description={gettext("View-only dashboard for background job status and history")}
icon="⚙️"
enabled={cfg[:enabled] || false}
toggle_event="toggle_module"
toggle_key="jobs"
>
<:status_badges>
<span class={[
"badge",
if(cfg[:enabled], do: "badge-success", else: "badge-neutral")
]}>
{if cfg[:enabled], do: gettext("Enabled"), else: gettext("Disabled")}
</span>
<%= if cfg[:enabled] && cfg[:stats].executing > 0 do %>
<span class="badge badge-outline badge-warning ml-2 whitespace-nowrap">
{gettext("%{count} running", count: cfg[:stats].executing)}
</span>
<% end %>
</:status_badges>
<:action_buttons>
<div class="flex flex-col gap-2 w-full">
<%= if cfg[:enabled] do %>
<.link
navigate={PhoenixKit.Utils.Routes.path("/admin/jobs")}
class="btn btn-primary btn-sm"
>
<.icon name="hero-queue-list" class="w-4 h-4 mr-1" /> {gettext("View Jobs")}
</.link>
<% else %>
<span class="text-xs text-base-content/50 text-center">
{gettext("Enable module to view background jobs")}
</span>
<% end %>
</div>
</:action_buttons>
<:stats>
<div class="grid grid-cols-4 gap-2 text-xs">
<div>
<span class="text-base-content/70">{gettext("Available")}:</span>
<span class="font-medium">{cfg[:stats].available}</span>
</div>
<div>
<span class="text-base-content/70">{gettext("Completed")}:</span>
<span class="font-medium">{cfg[:stats].completed}</span>
</div>
<div>
<span class="text-base-content/70">{gettext("Retryable")}:</span>
<span class="font-medium">{cfg[:stats].retryable}</span>
</div>
<div>
<span class="text-base-content/70">{gettext("Discarded")}:</span>
<span class="font-medium">{cfg[:stats].discarded}</span>
</div>
</div>
</:stats>
</PhoenixKitWeb.Components.Core.ModuleCard.module_card>
<% end %>
<%!-- Legal Module --%>
<%!-- External Modules (auto-discovered) --%>
<%= for ext <- @external_modules,
ext.key in @accessible_modules,
modules_tab_match?(@modules_tab, ext.enabled) do %>
<PhoenixKitWeb.Components.Core.ModuleCard.module_card
title={Gettext.gettext(PhoenixKitWeb.Gettext, ext.name)}
description={Gettext.gettext(PhoenixKitWeb.Gettext, ext.description)}
icon={ext.icon}
enabled={ext.enabled}
toggle_event="toggle_module"
toggle_key={ext.key}
>
<:status_badges>
<span class={[
"badge",
if(ext.enabled, do: "badge-success", else: "badge-neutral")
]}>
{if ext.enabled, do: gettext("Enabled"), else: gettext("Disabled")}
</span>
<span class="badge badge-outline">v{ext.version}</span>
<span class="badge badge-info badge-outline">{gettext("External")}</span>
<%= for req_mod <- ext.required_modules do %>
<%= unless mcfg(@module_configs, req_mod, :enabled, false) do %>
<span class="badge badge-warning">
{gettext("Requires %{module}", module: String.capitalize(req_mod))}
</span>
<% end %>
<% end %>
<%!-- Module-specific badges from config --%>
<%= if ext.enabled do %>
<%= if Map.has_key?(ext.config, :save_body) do %>
<span class="badge badge-outline whitespace-nowrap">
{if ext.config[:save_body],
do: gettext("Body Saved"),
else: gettext("Headers Only")}
</span>
<% end %>
<% end %>
</:status_badges>
<:action_buttons>
<div class="flex flex-col items-stretch space-y-2 w-full">
<%= if ext.enabled do %>
<%= if ext.settings_path do %>
<.link
navigate={PhoenixKit.Utils.Routes.path(ext.settings_path)}
class="btn btn-primary btn-sm"
>
<.icon name="hero-cog-6-tooth" class="w-4 h-4 mr-1" /> {gettext("Configure")}
</.link>
<% end %>
<%= if ext.admin_links != [] do %>
<div class="flex flex-wrap gap-2">
<%= for link <- ext.admin_links do %>
<.link
navigate={PhoenixKit.Utils.Routes.path(link.path)}
class="btn btn-outline btn-sm flex-1"
>
<%= if link.icon do %>
<.icon name={link.icon} class="w-4 h-4 mr-1" />
<% end %>
{link.label}
</.link>
<% end %>
</div>
<% end %>
<% else %>
<%= if ext.settings_path do %>
<.link
navigate={PhoenixKit.Utils.Routes.path(ext.settings_path)}
class="btn btn-outline btn-sm"
>
<.icon name="hero-cog-6-tooth" class="w-4 h-4 mr-1" /> {gettext("Configure")}
</.link>
<% end %>
<% end %>
</div>
</:action_buttons>
<:stats :if={function_exported?(ext.module, :module_stats, 0)}>
<div class="grid grid-cols-2 gap-2 text-xs">
<%= for stat <- ext.module.module_stats() do %>
<div>
<span class="text-base-content/70">
{if is_binary(stat.label),
do: Gettext.gettext(PhoenixKitWeb.Gettext, stat.label),
else: to_string(stat.label)}:
</span>
<span class="font-medium">{stat.value}</span>
</div>
<% end %>
</div>
</:stats>
</PhoenixKitWeb.Components.Core.ModuleCard.module_card>
<% end %>
</div>
<div :if={@modules_tab == "not_installed"}>
<%!-- Hex was unreachable. Rendered separately from the list below, because an
empty list and "we could not ask" look identical to an operator and
only one of them means there is nothing to install. --%>
<div :if={@catalog_status == :unavailable} class="mb-8">
<div class="alert alert-warning">
<.icon name="hero-exclamation-triangle" class="w-5 h-5" />
<span>
{gettext("Couldn't reach hex.pm, so the package list is unavailable right now.")}
<a href={@hex_browse_url} target="_blank" rel="noopener" class="link">
{gettext("Browse PhoenixKit packages on hex.pm")} →
</a>
</span>
</div>
</div>
<%= if @not_installed_packages == [] do %>
<div class="alert alert-info">
<.icon name="hero-information-circle" class="w-5 h-5" />
<span>{gettext("No additional PhoenixKit packages available to install right now.")}</span>
</div>
<% else %>
<div class="grid gap-6 md:grid-cols-2 lg:grid-cols-3">
<%= for pkg <- @not_installed_packages do %>
<div class="card bg-base-100 shadow border-2 border-dashed border-base-300">
<div class="card-body">
<div class="flex items-center gap-3">
<.icon name={pkg.icon} class="w-8 h-8 opacity-60" />
<div class="flex-1">
<h3 class="card-title text-base-content/70">
{Gettext.gettext(PhoenixKitWeb.Gettext, pkg.name)}
</h3>
<p class="text-sm text-base-content/50">
{Gettext.gettext(PhoenixKitWeb.Gettext, pkg.description)}
</p>
</div>
</div>
<div class="divider my-2"></div>
<div class="flex items-center justify-between gap-2 flex-wrap">
<span class="badge badge-ghost badge-outline">{gettext("Not installed")}</span>
<a
:if={pkg.hex_url}
href={pkg.hex_url}
target="_blank"
class="btn btn-xs btn-outline"
>
<.icon name="hero-arrow-top-right-on-square" class="w-3 h-3" /> Hex.pm
</a>
</div>
<div class="mt-2">
<p class="text-xs text-base-content/40 mb-1">{gettext("Add to mix.exs")}:</p>
<code class="block text-xs bg-base-200 rounded p-2 font-mono">
{~s|{:#{pkg.package}, "~> 0.1"}|}
</code>
</div>
</div>
</div>
<% end %>
</div>
<% end %>
</div>
<%!-- Module Management Help --%>
<div class="alert alert-info mt-6">
<.icon name="hero-information-circle" class="w-5 h-5" />
<div>
<h3 class="font-bold">{gettext("Module Management")}</h3>
<p class="text-sm">
{gettext(
"Each module can be enabled or disabled using the toggle switches above. Click \"Configure\" to access detailed settings for each module on dedicated pages."
)}
</p>
</div>
</div>
</div>
</PhoenixKitWeb.Components.LayoutWrapper.app_layout>