Packages

phoenix_kit

1.7.113
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
phoenix_kit lib phoenix_kit_web live modules.html.heex
Raw

lib/phoenix_kit_web/live/modules.html.heex

<PhoenixKitWeb.Components.LayoutWrapper.app_layout
flash={@flash}
phoenix_kit_current_scope={assigns[:phoenix_kit_current_scope]}
page_title="{@project_title} Modules"
current_path={@url_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={PhoenixKit.Utils.Routes.path("/admin")}
title={gettext("Modules")}
subtitle={gettext("Manage modules")}
/>
<%!-- 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>
<div class="grid gap-6 md:grid-cols-2 lg:grid-cols-3">
<%!-- Storage Module --%>
<%= if "storage" in @accessible_modules do %>
<% cfg = @module_configs["storage"] || %{} %>
<PhoenixKitWeb.Components.Core.ModuleCard.module_card
title={gettext("Storage")}
description={
gettext(
"Distributed file storage system with multi-location redundancy and automatic failover"
)
}
icon="💾"
enabled={cfg[:module_enabled]}
toggle_event="toggle_module"
toggle_key="storage"
show_toggle={false}
>
<:status_badges>
<span class="badge badge-success z-0">{gettext("Always Enabled")}</span>
<span class="badge badge-info z-0">{gettext("Core System")}</span>
</:status_badges>
<:action_buttons>
<.link
navigate={PhoenixKit.Utils.Routes.path("/admin/settings/media")}
class="btn btn-primary btn-sm"
>
<.icon name="hero-cog-6-tooth" class="w-4 h-4 mr-1" /> {gettext("Configure")}
</.link>
</:action_buttons>
<:stats>
<div class="grid grid-cols-2 gap-2 text-xs">
<div>
<span class="text-base-content/70">{gettext("Total Buckets")}:</span>
<span class="font-medium">{cfg[:buckets_count]}</span>
</div>
<div>
<span class="text-base-content/70">{gettext("Active")}:</span>
<span class="font-medium">{cfg[:active_buckets_count]}</span>
</div>
</div>
</:stats>
</PhoenixKitWeb.Components.Core.ModuleCard.module_card>
<% end %>
<%!-- Referral Codes Module --%>
<%= if "referrals" in @accessible_modules do %>
<% cfg = @module_configs["referrals"] || %{} %>
<PhoenixKitWeb.Components.Core.ModuleCard.module_card
title={gettext("Referral Codes")}
description={gettext("Manage referral codes for user registration and monitoring")}
icon="🎫"
enabled={cfg[:enabled] || false}
toggle_event="toggle_module"
toggle_key="referrals"
>
<: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[:required], do: gettext("Required"), else: gettext("Optional")}
</span>
<% end %>
</:status_badges>
<:action_buttons>
<%= if cfg[:enabled] do %>
<.link
navigate={PhoenixKit.Utils.Routes.path("/admin/settings/referral-codes")}
class="btn btn-primary btn-sm"
>
<.icon name="hero-cog-6-tooth" class="w-4 h-4 mr-1" /> {gettext("Configure")}
</.link>
<.link
navigate={PhoenixKit.Utils.Routes.path("/admin/users/referral-codes")}
class="btn btn-outline btn-sm"
>
<.icon name="hero-users" class="w-4 h-4 mr-1" /> {gettext("Manage Codes")}
</.link>
<% else %>
<.link
navigate={PhoenixKit.Utils.Routes.path("/admin/settings/referral-codes")}
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("Max per user")}:</span>
<span class="font-medium">{cfg[:max_codes_per_user]}</span>
</div>
<div>
<span class="text-base-content/70">{gettext("Max uses")}:</span>
<span class="font-medium">{cfg[:max_uses_per_code]}</span>
</div>
</div>
</:stats>
</PhoenixKitWeb.Components.Core.ModuleCard.module_card>
<% end %>
<%!-- Languages Module --%>
<%= if "languages" in @accessible_modules 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 %>
<%!-- Comments Module --%>
<%= if "comments" in @accessible_modules do %>
<% cfg = @module_configs["comments"] || %{} %>
<PhoenixKitWeb.Components.Core.ModuleCard.module_card
title={gettext("Comments")}
description={
gettext(
"Threaded comments with likes, moderation, and reactions across all content types"
)
}
icon="💬"
enabled={cfg[:enabled] || false}
toggle_event="toggle_module"
toggle_key="comments"
>
<: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[:pending_comments] > 0 do %>
<span class="badge badge-warning ml-2 whitespace-nowrap">
{gettext("%{count} pending", count: cfg[:pending_comments])}
</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/comments")}
class="btn btn-primary btn-sm"
>
<.icon name="hero-chat-bubble-left-right" class="w-4 h-4 mr-1" />
{gettext("Manage Comments")}
</.link>
<.link
navigate={PhoenixKit.Utils.Routes.path("/admin/settings/comments")}
class="btn btn-outline btn-sm"
>
<.icon name="hero-cog-6-tooth" class="w-4 h-4 mr-1" /> {gettext("Settings")}
</.link>
<% else %>
<.link
navigate={PhoenixKit.Utils.Routes.path("/admin/settings/comments")}
class="btn btn-outline btn-sm"
>
<.icon name="hero-cog-6-tooth" class="w-4 h-4 mr-1" /> {gettext("Configure")}
</.link>
<% end %>
</div>
</:action_buttons>
<:stats>
<div class="grid grid-cols-3 gap-2 text-xs">
<div>
<span class="text-base-content/70">{gettext("Total")}:</span>
<span class="font-medium">{cfg[:total_comments]}</span>
</div>
<div>
<span class="text-base-content/70">{gettext("Published")}:</span>
<span class="font-medium">{cfg[:published_comments]}</span>
</div>
<div>
<span class="text-base-content/70">{gettext("Pending")}:</span>
<span class="font-medium">{cfg[:pending_comments]}</span>
</div>
</div>
</:stats>
</PhoenixKitWeb.Components.Core.ModuleCard.module_card>
<% end %>
<%!-- Customer Support Module --%>
<%= if "customer_support" in @accessible_modules do %>
<% cfg = @module_configs["customer_support"] || %{} %>
<PhoenixKitWeb.Components.Core.ModuleCard.module_card
title={gettext("Customer Support")}
description={
gettext("Customer support ticketing system with internal notes and status tracking")
}
icon="🎫"
enabled={cfg[:enabled] || false}
toggle_event="toggle_module"
toggle_key="customer_support"
>
<: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[:open_tickets] > 0 do %>
<span class="badge badge-warning ml-2 whitespace-nowrap">
{gettext("%{count} open", count: cfg[:open_tickets])}
</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/customer-support/tickets")}
class="btn btn-primary btn-sm"
>
<.icon name="hero-ticket" class="w-4 h-4 mr-1" /> {gettext("Manage Tickets")}
</.link>
<.link
navigate={PhoenixKit.Utils.Routes.path("/admin/settings/customer-support")}
class="btn btn-outline btn-sm"
>
<.icon name="hero-cog-6-tooth" class="w-4 h-4 mr-1" /> {gettext("Settings")}
</.link>
<% else %>
<.link
navigate={PhoenixKit.Utils.Routes.path("/admin/settings/customer-support")}
class="btn btn-outline btn-sm"
>
<.icon name="hero-cog-6-tooth" class="w-4 h-4 mr-1" /> {gettext("Configure")}
</.link>
<% end %>
</div>
</:action_buttons>
<:stats>
<div class="grid grid-cols-3 gap-2 text-xs">
<div>
<span class="text-base-content/70">{gettext("Total")}:</span>
<span class="font-medium">{cfg[:total_tickets]}</span>
</div>
<div>
<span class="text-base-content/70">{gettext("Open")}:</span>
<span class="font-medium">{cfg[:open_tickets]}</span>
</div>
<div>
<span class="text-base-content/70">{gettext("In Progress")}:</span>
<span class="font-medium">{cfg[:in_progress_tickets]}</span>
</div>
</div>
</:stats>
</PhoenixKitWeb.Components.Core.ModuleCard.module_card>
<% end %>
<%!-- Connections Module --%>
<%= if "connections" in @accessible_modules do %>
<% cfg = @module_configs["connections"] || %{} %>
<PhoenixKitWeb.Components.Core.ModuleCard.module_card
title={gettext("Connections")}
description={
gettext("Social relationships with one-way follows and mutual connections")
}
icon="🤝"
enabled={cfg[:enabled] || false}
toggle_event="toggle_module"
toggle_key="connections"
>
<:status_badges>
<span class={[
"badge",
if(cfg[:enabled], do: "badge-success", else: "badge-neutral")
]}>
{if cfg[:enabled], do: gettext("Enabled"), else: gettext("Disabled")}
</span>
</:status_badges>
<:action_buttons>
<div class="flex flex-col gap-2 w-full">
<%= if cfg[:enabled] do %>
<.link
navigate={PhoenixKit.Utils.Routes.path("/admin/connections")}
class="btn btn-primary btn-sm"
>
<.icon name="hero-users" class="w-4 h-4 mr-1" /> {gettext("Overview")}
</.link>
<% else %>
<span class="text-xs text-base-content/50 text-center">
{gettext("Enable module to manage connections")}
</span>
<% end %>
</div>
</:action_buttons>
<:stats>
<div class="grid grid-cols-3 gap-2 text-xs">
<div>
<span class="text-base-content/70">{gettext("Follows")}:</span>
<span class="font-medium">{cfg[:follows_count]}</span>
</div>
<div>
<span class="text-base-content/70">{gettext("Connections")}:</span>
<span class="font-medium">{cfg[:connections_count]}</span>
</div>
<div>
<span class="text-base-content/70">{gettext("Blocks")}:</span>
<span class="font-medium">{cfg[:blocks_count]}</span>
</div>
</div>
</:stats>
</PhoenixKitWeb.Components.Core.ModuleCard.module_card>
<% end %>
<%!-- SEO Module --%>
<%= if "seo" in @accessible_modules do %>
<% cfg = @module_configs["seo"] || %{} %>
<PhoenixKitWeb.Components.Core.ModuleCard.module_card
title={gettext("SEO")}
description={gettext("Manage search engine optimization controls for your application")}
icon="🔍"
enabled={cfg[:module_enabled]}
toggle_event="toggle_module"
toggle_key="seo"
>
<: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.path("/admin/settings/seo",
locale: @current_locale || "en"
)
}
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>
</:stats>
</PhoenixKitWeb.Components.Core.ModuleCard.module_card>
<% end %>
<%!-- Sitemap Module --%>
<%= if "sitemap" in @accessible_modules 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 --%>
<%!-- Under Construction (Maintenance Mode) Module --%>
<%= if "maintenance" in @accessible_modules do %>
<% cfg = @module_configs["maintenance"] || %{} %>
<PhoenixKitWeb.Components.Core.ModuleCard.module_card
title={gettext("Maintenance Mode")}
description={
gettext("Show maintenance page to non-admin users while you work on the site")
}
icon="🚧"
enabled={cfg[:module_enabled]}
toggle_event="toggle_module"
toggle_key="maintenance"
>
<: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>
<%= if cfg[:module_enabled] do %>
<span class={[
"badge badge-outline ml-2 whitespace-nowrap",
if(cfg[:enabled], do: "badge-warning", else: "badge-info")
]}>
{if cfg[:enabled],
do: gettext("Maintenance Active"),
else: gettext("Maintenance Off")}
</span>
<% end %>
</:status_badges>
<:action_buttons>
<div class="flex flex-col gap-2 w-full">
<%= if cfg[:module_enabled] do %>
<.link
navigate={PhoenixKit.Utils.Routes.path("/admin/settings/maintenance")}
class="btn btn-sm btn-primary"
>
<.icon name="hero-cog-6-tooth" class="w-4 h-4 mr-1" /> {gettext("Configure")}
</.link>
<div class="text-xs text-base-content/70 mt-2">
<strong>{gettext("Header")}:</strong>
{cfg[:header]}
</div>
<div class="text-xs text-base-content/70">
<strong>{gettext("Message")}:</strong>
{cfg[:subtext]}
</div>
<% else %>
<.link
navigate={PhoenixKit.Utils.Routes.path("/admin/settings/maintenance")}
class="btn btn-sm btn-outline"
>
<.icon name="hero-cog-6-tooth" class="w-4 h-4 mr-1" /> {gettext("Configure")}
</.link>
<% end %>
</div>
</:action_buttons>
<:stats>
<div class="text-xs text-base-content/70">
{if cfg[:module_enabled],
do: gettext("Activate maintenance mode in settings to block non-admin users"),
else: gettext("Enable module to access maintenance mode settings")}
</div>
</:stats>
</PhoenixKitWeb.Components.Core.ModuleCard.module_card>
<% end %>
<%!-- Jobs Module --%>
<%= if "jobs" in @accessible_modules 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 do %>
<PhoenixKitWeb.Components.Core.ModuleCard.module_card
title={ext.name}
description={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 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>
<%!-- Render stats from module_stats callback if available --%>
<%= if function_exported?(ext.module, :module_stats, 0) do %>
<div class="grid grid-cols-2 gap-2 text-xs">
<%= for stat <- ext.module.module_stats() do %>
<div>
<span class="text-base-content/70">{stat.label}:</span>
<span class="font-medium">{stat.value}</span>
</div>
<% end %>
</div>
<% end %>
</:stats>
</PhoenixKitWeb.Components.Core.ModuleCard.module_card>
<% end %>
</div>
<%!-- Available Packages (known but not installed) --%>
<%= if @not_installed_packages != [] do %>
<div class="mt-8">
<h2 class="text-lg font-semibold mb-3 text-base-content/70">
{gettext("Available Packages")}
</h2>
<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">{pkg.name}</h3>
<p class="text-sm text-base-content/50">{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>
</div>
<% end %>
<%!-- 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>