Packages
phoenix_kit
1.7.34
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/shop/web/settings.ex
defmodule PhoenixKit.Modules.Shop.Web.Settings do
@moduledoc """
E-Commerce module settings LiveView.
Allows configuration of e-commerce settings including inventory tracking.
"""
use PhoenixKitWeb, :live_view
alias PhoenixKit.Modules.Billing
alias PhoenixKit.Modules.Shop
alias PhoenixKit.Settings
alias PhoenixKit.Utils.Routes
# Test comment for live reload
@impl true
def mount(_params, _session, socket) do
config = Shop.get_config()
socket =
socket
|> assign(:page_title, "E-Commerce Settings")
|> assign(:enabled, config.enabled)
|> assign(:inventory_tracking, config.inventory_tracking)
|> assign(:billing_enabled, billing_enabled?())
|> assign(:category_name_display, get_category_name_display())
|> assign(:category_icon_mode, get_category_icon_mode())
{:ok, socket}
end
defp get_category_name_display do
Settings.get_setting_cached("shop_category_name_display", "truncate")
end
defp get_category_icon_mode do
Settings.get_setting_cached("shop_category_icon_mode", "none")
end
@impl true
def handle_event("toggle_enabled", _params, socket) do
new_enabled = !socket.assigns.enabled
if new_enabled and not billing_enabled?() do
# Cannot enable Shop without Billing
{:noreply,
socket
|> assign(:enabled, false)
|> put_flash(:error, "Please enable Billing module first")}
else
result =
if new_enabled do
Shop.enable_system()
else
Shop.disable_system()
end
case result do
:ok ->
socket =
socket
|> assign(:enabled, new_enabled)
|> put_flash(
:info,
if(new_enabled, do: "E-Commerce module enabled", else: "E-Commerce module disabled")
)
{:noreply, socket}
_ ->
{:noreply,
socket
|> assign(:enabled, false)
|> put_flash(:error, "Failed to update E-Commerce status")}
end
end
end
@impl true
def handle_event("toggle_inventory_tracking", _params, socket) do
new_value = !socket.assigns.inventory_tracking
value_str = if(new_value, do: "true", else: "false")
case Settings.update_setting("shop_inventory_tracking", value_str) do
{:ok, _} ->
{:noreply,
socket
|> assign(:inventory_tracking, new_value)
|> put_flash(
:info,
if(new_value, do: "Inventory tracking enabled", else: "Inventory tracking disabled")
)}
{:error, _} ->
{:noreply, put_flash(socket, :error, "Failed to update inventory setting")}
end
end
@impl true
def handle_event("update_category_display", %{"display" => display}, socket) do
case Settings.update_setting("shop_category_name_display", display) do
{:ok, _} ->
{:noreply,
socket
|> assign(:category_name_display, display)
|> put_flash(:info, "Category display setting updated")}
{:error, _} ->
{:noreply, put_flash(socket, :error, "Failed to update category display setting")}
end
end
@impl true
def handle_event("update_category_icon", %{"mode" => mode}, socket) do
case Settings.update_setting("shop_category_icon_mode", mode) do
{:ok, _} ->
{:noreply,
socket
|> assign(:category_icon_mode, mode)
|> put_flash(:info, "Category icon setting updated")}
{:error, _} ->
{:noreply, put_flash(socket, :error, "Failed to update category icon setting")}
end
end
@impl true
def render(assigns) do
~H"""
<PhoenixKitWeb.Components.LayoutWrapper.app_layout
flash={@flash}
phoenix_kit_current_scope={@phoenix_kit_current_scope}
current_path={@url_path}
current_locale={@current_locale}
page_title={@page_title}
>
<div class="container flex-col mx-auto px-4 py-6 max-w-5xl">
<%!-- Header --%>
<header class="mb-6">
<div class="flex items-start gap-4">
<.link
navigate={Routes.path("/admin/shop")}
class="btn btn-outline btn-primary btn-sm shrink-0"
>
<.icon name="hero-arrow-left" class="w-4 h-4 mr-2" /> Back
</.link>
<div class="flex-1 min-w-0">
<h1 class="text-3xl font-bold text-base-content">E-Commerce Settings</h1>
<p class="text-base-content/70 mt-1">Configure your e-commerce store</p>
</div>
</div>
</header>
<%!-- Module Status Card --%>
<div class="card bg-base-100 shadow-xl mb-6">
<div class="card-body">
<div class="flex items-center justify-between">
<div>
<h2 class="card-title text-xl">Module Status</h2>
<p class="text-base-content/70">Enable or disable the E-Commerce module</p>
</div>
<div class="flex items-center gap-4">
<span class={[
"badge badge-lg",
if(@enabled, do: "badge-success", else: "badge-neutral")
]}>
{if @enabled, do: "Enabled", else: "Disabled"}
</span>
<div class="flex flex-col items-end gap-1">
<input
type="checkbox"
class="toggle toggle-primary toggle-lg"
checked={@enabled}
disabled={not @billing_enabled}
phx-click="toggle_enabled"
/>
<%= if not @billing_enabled do %>
<.link
navigate={Routes.path("/admin/modules")}
class="badge badge-warning badge-sm hover:badge-error"
>
Billing Required
</.link>
<% end %>
</div>
</div>
</div>
</div>
</div>
<%!-- Inventory Settings (toggle pattern) --%>
<div class="card bg-base-100 shadow-xl mb-6">
<div class="card-body">
<h2 class="card-title text-xl mb-6">
<.icon name="hero-archive-box" class="w-6 h-6" /> Inventory
</h2>
<div class="form-control">
<label class="label cursor-pointer justify-between">
<span class="label-text text-lg">
<span class="font-semibold">Track Inventory</span>
<div class="text-sm text-base-content/70 mt-1">
Enable stock tracking for products (Phase 2)
</div>
</span>
<input
type="checkbox"
class="toggle toggle-secondary"
checked={@inventory_tracking}
phx-click="toggle_inventory_tracking"
/>
</label>
</div>
</div>
</div>
<%!-- Info about Billing --%>
<div class="alert alert-info mb-6">
<.icon name="hero-information-circle" class="w-6 h-6" />
<div>
<h3 class="font-bold">Currency & Tax Settings</h3>
<p class="text-sm">
Currency and tax configuration is managed in the
<.link navigate={Routes.path("/admin/settings/billing")} class="link font-medium">
Billing module settings
</.link>
</p>
</div>
</div>
<%!-- Product Options --%>
<div class="card bg-base-100 shadow-xl mb-6">
<div class="card-body">
<h2 class="card-title text-xl mb-6">
<.icon name="hero-tag" class="w-6 h-6" /> Product Options
</h2>
<div class="form-control">
<label class="label cursor-pointer justify-between">
<span class="label-text text-lg">
<span class="font-semibold">Global Product Options</span>
<div class="text-sm text-base-content/70 mt-1">
Define options that apply to all products (size, color, material, etc.)
</div>
<div class="text-xs text-base-content/50 mt-1">
Price override is configured per-option in the options settings.
</div>
</span>
<.link
navigate={Routes.path("/admin/shop/settings/options")}
class="btn btn-primary"
>
<.icon name="hero-cog-6-tooth" class="w-4 h-4 mr-2" /> Configure
</.link>
</label>
</div>
</div>
</div>
<%!-- Import Configurations --%>
<div class="card bg-base-100 shadow-xl mb-6">
<div class="card-body">
<h2 class="card-title text-xl mb-6">
<.icon name="hero-funnel" class="w-6 h-6" /> Import Configurations
</h2>
<div class="form-control">
<label class="label cursor-pointer justify-between">
<span class="label-text text-lg">
<span class="font-semibold">CSV Import Filters</span>
<div class="text-sm text-base-content/70 mt-1">
Configure keyword filters and category rules for CSV product imports
</div>
</span>
<.link
navigate={Routes.path("/admin/shop/settings/import-configs")}
class="btn btn-primary"
>
<.icon name="hero-cog-6-tooth" class="w-4 h-4 mr-2" /> Configure
</.link>
</label>
</div>
</div>
</div>
<%!-- Sidebar Display Settings --%>
<div class="card bg-base-100 shadow-xl mb-6">
<div class="card-body">
<h2 class="card-title text-xl mb-6">
<.icon name="hero-bars-3" class="w-6 h-6" /> Sidebar Display
</h2>
<%!-- Category Name Display --%>
<div class="form-control mb-6">
<label class="label">
<span class="label-text text-lg font-semibold">Category Name Display</span>
</label>
<p class="text-sm text-base-content/70 mb-3">
How category names should be displayed in the sidebar
</p>
<div class="flex gap-4">
<label class="label cursor-pointer gap-2">
<input
type="radio"
name="category_display"
class="radio radio-primary"
value="truncate"
checked={@category_name_display == "truncate"}
phx-click="update_category_display"
phx-value-display="truncate"
/>
<span class="label-text">Truncate (single line)</span>
</label>
<label class="label cursor-pointer gap-2">
<input
type="radio"
name="category_display"
class="radio radio-primary"
value="wrap"
checked={@category_name_display == "wrap"}
phx-click="update_category_display"
phx-value-display="wrap"
/>
<span class="label-text">Wrap (multi-line)</span>
</label>
</div>
</div>
<div class="divider"></div>
<%!-- Category Icon Mode --%>
<div class="form-control">
<label class="label">
<span class="label-text text-lg font-semibold">Category Icons</span>
</label>
<p class="text-sm text-base-content/70 mb-3">
Show icons next to category names in sidebar
</p>
<div class="flex gap-4">
<label class="label cursor-pointer gap-2">
<input
type="radio"
name="category_icon"
class="radio radio-primary"
value="none"
checked={@category_icon_mode == "none"}
phx-click="update_category_icon"
phx-value-mode="none"
/>
<span class="label-text">No icons</span>
</label>
<label class="label cursor-pointer gap-2">
<input
type="radio"
name="category_icon"
class="radio radio-primary"
value="folder"
checked={@category_icon_mode == "folder"}
phx-click="update_category_icon"
phx-value-mode="folder"
/>
<span class="label-text">Folder icon</span>
</label>
<label class="label cursor-pointer gap-2">
<input
type="radio"
name="category_icon"
class="radio radio-primary"
value="category"
checked={@category_icon_mode == "category"}
phx-click="update_category_icon"
phx-value-mode="category"
/>
<span class="label-text">Category image</span>
</label>
</div>
</div>
</div>
</div>
</div>
</PhoenixKitWeb.Components.LayoutWrapper.app_layout>
"""
end
defp billing_enabled? do
Code.ensure_loaded?(Billing) and
function_exported?(Billing, :enabled?, 0) and
Billing.enabled?()
rescue
_ -> false
end
end