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 settings.html.heex
Raw

lib/phoenix_kit_web/live/settings.html.heex

<PhoenixKitWeb.Components.LayoutWrapper.app_layout
flash={@flash}
phoenix_kit_current_scope={assigns[:phoenix_kit_current_scope]}
page_title="{@project_title} Settings"
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("Settings")}
subtitle={gettext("Configure system preferences and options")}
/>
<%!-- Settings Form --%>
<div class="card bg-base-100 shadow-xl">
<div class="card-body">
<h2 class="card-title text-xl mb-6">{gettext("System Configuration")}</h2>
<.form
:let={_form}
for={@changeset}
id="settings_form"
phx-submit="save_settings"
phx-change="validate_settings"
class="space-y-6"
>
<%!-- Hidden fields for required settings from Users page --%>
<%!-- Use @saved_settings to preserve database values, not @settings which may have incorrect form state --%>
<input
type="hidden"
name="settings[allow_registration]"
value={@saved_settings["allow_registration"]}
/>
<input
type="hidden"
name="settings[new_user_default_role]"
value={@saved_settings["new_user_default_role"]}
/>
<input
type="hidden"
name="settings[new_user_default_status]"
value={@saved_settings["new_user_default_status"]}
/>
<input
type="hidden"
name="settings[track_registration_geolocation]"
value={@saved_settings["track_registration_geolocation"]}
/>
<%!-- Project Title Setting --%>
<div class="form-control w-full">
<label class="label">
<span class="label-text text-base font-medium">{gettext("Project Title")}</span>
<span class="label-text-alt text-sm text-base-content/60">
{gettext("Application name display")}
</span>
</label>
<input
name="settings[project_title]"
type="text"
value={@settings["project_title"]}
class="input input-bordered w-full focus:input-primary"
placeholder={gettext("Enter project title")}
phx-debounce="500"
/>
<div class="label">
<span class="label-text-alt text-xs text-base-content/50">
<%= if @settings["project_title"] != @saved_settings["project_title"] do %>
<span class="text-warning">
{gettext("Selected:")} {@settings["project_title"]}
</span>
<span class="text-base-content/40">
| {gettext("Saved:")} {@saved_settings["project_title"]}
</span>
<% else %>
{gettext("Saved:")} {@settings["project_title"]}
<% end %>
</span>
</div>
</div>
<%!-- Project Logo --%>
<div class="form-control w-full">
<label class="label">
<span class="label-text text-base font-medium">{gettext("Project Logo")}</span>
</label>
<input
type="hidden"
name="settings[auth_logo_file_uuid]"
value={@settings["auth_logo_file_uuid"]}
/>
<%= if (@settings["auth_logo_file_uuid"] || "") != "" do %>
<div class="flex items-center gap-3 p-2 bg-base-200 rounded-lg">
<img
src={signed_preview_url(@settings["auth_logo_file_uuid"], "medium")}
alt="Logo preview"
class="h-20 object-contain"
/>
<button
type="button"
class="btn btn-ghost btn-sm"
phx-click="clear_image"
phx-value-target="logo"
>
{gettext("Remove")}
</button>
</div>
<% else %>
<button
type="button"
class="btn btn-outline btn-sm w-fit"
phx-click="open_media_selector"
phx-value-target="logo"
>
{gettext("Select Image")}
</button>
<% end %>
<div class="label">
<span class="label-text-alt text-xs text-base-content/50">
{gettext(
"Logo displayed across the application. Leave empty to show project title text."
)}
</span>
</div>
</div>
<%!-- Site Icon (Favicon) --%>
<div class="form-control w-full">
<label class="label">
<span class="label-text text-base font-medium">{gettext("Site Icon")}</span>
</label>
<input
type="hidden"
name="settings[site_icon_file_uuid]"
value={@settings["site_icon_file_uuid"]}
/>
<%!-- Browser Tab Preview --%>
<div class="mb-3">
<div class="inline-flex items-center bg-base-200 rounded-t-lg border border-base-300 px-3 py-1.5 gap-2 max-w-xs">
<%= if (@settings["site_icon_file_uuid"] || "") != "" do %>
<img
src={signed_preview_url(@settings["site_icon_file_uuid"], "thumbnail")}
alt=""
class="w-4 h-4 rounded-sm object-cover"
/>
<% else %>
<div class="w-4 h-4 rounded-sm bg-base-300"></div>
<% end %>
<span class="text-xs truncate max-w-[180px]">
{if (@settings["default_tab_title"] || "") != "",
do: @settings["default_tab_title"],
else: @settings["project_title"] || "My Site"}
</span>
<span class="text-base-content/30 text-xs">×</span>
</div>
</div>
<%= if (@settings["site_icon_file_uuid"] || "") != "" do %>
<div class="flex items-center gap-3 p-2 bg-base-200 rounded-lg">
<img
src={signed_preview_url(@settings["site_icon_file_uuid"], "thumbnail")}
alt="Site icon preview"
class="h-12 w-12 rounded object-cover"
/>
<button
type="button"
class="btn btn-ghost btn-sm"
phx-click="clear_image"
phx-value-target="site_icon"
>
{gettext("Remove")}
</button>
</div>
<% else %>
<button
type="button"
class="btn btn-outline btn-sm w-fit"
phx-click="open_media_selector"
phx-value-target="site_icon"
>
{gettext("Select Image")}
</button>
<% end %>
<div class="label">
<span class="label-text-alt text-xs text-base-content/50">
{gettext(
"Shown in browser tabs and bookmarks. Should be square, at least 512×512 pixels."
)}
</span>
</div>
</div>
<%!-- Default Tab Title --%>
<div class="form-control w-full">
<label class="label">
<span class="label-text text-base font-medium">
{gettext("Default Tab Title")}
</span>
<span class="label-text-alt text-sm text-base-content/60">
{gettext("Browser tab fallback")}
</span>
</label>
<input
name="settings[default_tab_title]"
type="text"
value={@settings["default_tab_title"]}
class="input input-bordered w-full focus:input-primary"
placeholder={gettext("e.g. Welcome to My Site")}
phx-debounce="500"
/>
<div class="label">
<span class="label-text-alt text-xs text-base-content/50">
{gettext("Text shown in the browser tab when a page doesn't set its own title.")}
</span>
</div>
</div>
<%!-- Notifications --%>
<div class="form-control w-full">
<label class="label">
<span class="label-text text-base font-medium">
{gettext("Notifications")}
</span>
<span class="label-text-alt text-sm text-base-content/60">
{gettext("Per-user inbox driven by the activity feed")}
</span>
</label>
<div class="flex items-center gap-3">
<input type="hidden" name="settings[notifications_enabled]" value="false" />
<input
name="settings[notifications_enabled]"
type="checkbox"
value="true"
checked={@settings["notifications_enabled"] == "true"}
class="checkbox checkbox-primary"
/>
<span class="label-text">
{gettext("Enable user notifications")}
</span>
</div>
<div class="label">
<span class="label-text-alt text-xs text-base-content/50">
{gettext(
"When on, users receive a bell notification whenever an activity targets them (e.g., their post is liked)."
)}
</span>
</div>
</div>
<%!-- Site Address URL Setting --%>
<div class="form-control w-full">
<label class="label">
<span class="label-text text-base font-medium">
{gettext("Site Address (URL)")}
</span>
</label>
<input
name="settings[site_url]"
type="url"
value={@settings["site_url"]}
class="input input-bordered w-full focus:input-primary"
placeholder="https://site.com"
phx-debounce="500"
/>
<div class="label">
<span class="label-text-alt text-xs text-base-content/50">
<%= if @settings["site_url"] != @saved_settings["site_url"] do %>
<span class="text-warning">
{gettext("Selected:")} {if @settings["site_url"] != "",
do: @settings["site_url"],
else: gettext("(empty)")}
</span>
<span class="text-base-content/40">
| {gettext("Saved:")} {if @saved_settings["site_url"] != "",
do: @saved_settings["site_url"],
else: gettext("(empty)")}
</span>
<% else %>
{gettext("Saved:")} {if @settings["site_url"] != "",
do: @settings["site_url"],
else: gettext("(empty)")}
<% end %>
</span>
</div>
</div>
<%!-- Week Start Day Setting --%>
<div class="form-control w-full">
<label class="label">
<span class="label-text text-base font-medium">{gettext("Week Start On")}</span>
<span class="label-text-alt text-sm text-base-content/60">
{gettext("First day of the week")}
</span>
</label>
<label class="select w-full focus:select-primary">
<select name="settings[week_start_day]">
<%= for {label, value} <- @setting_options["week_start_day"] do %>
<option value={value} selected={value == @settings["week_start_day"]}>
{label}
</option>
<% end %>
</select>
</label>
<div class="label">
<span class="label-text-alt text-xs text-base-content/50">
<%= if @settings["week_start_day"] != @saved_settings["week_start_day"] do %>
<span class="text-warning">
{gettext("Selected:")} {get_option_label(
@settings["week_start_day"],
@setting_options["week_start_day"]
)}
</span>
<span class="text-base-content/40">
| {gettext("Saved:")} {get_option_label(
@saved_settings["week_start_day"],
@setting_options["week_start_day"]
)}
</span>
<% else %>
{gettext("Saved:")} {get_option_label(
@settings["week_start_day"],
@setting_options["week_start_day"]
)}
<% end %>
</span>
</div>
</div>
<%!-- Time Zone Setting --%>
<div class="form-control w-full">
<label class="label">
<span class="label-text text-base font-medium">{gettext("Time Zone")}</span>
<span class="label-text-alt text-sm text-base-content/60">
{gettext("System default timezone")}
</span>
</label>
<label class="select w-full focus:select-primary">
<select name="settings[time_zone]">
<%= for {label, value} <- @setting_options["time_zone"] do %>
<option value={value} selected={value == @settings["time_zone"]}>
{label}
</option>
<% end %>
</select>
</label>
<div class="label">
<span class="label-text-alt text-xs text-base-content/50">
<%= if @settings["time_zone"] != @saved_settings["time_zone"] do %>
<span class="text-warning">
{gettext("Selected:")} {get_timezone_label(
@settings["time_zone"],
@setting_options
)}
</span>
<span class="text-base-content/40">
| {gettext("Saved:")} {get_timezone_label(
@saved_settings["time_zone"],
@setting_options
)}
</span>
<% else %>
{gettext("Saved:")} {get_timezone_label(
@settings["time_zone"],
@setting_options
)}
<% end %>
</span>
</div>
</div>
<%!-- Date Format Setting --%>
<div class="form-control w-full">
<label class="label">
<span class="label-text text-base font-medium">{gettext("Date Format")}</span>
<span class="label-text-alt text-sm text-base-content/60">
{gettext("How dates are displayed")}
</span>
</label>
<label class="select w-full focus:select-primary">
<select name="settings[date_format]">
<%= for {label, value} <- @setting_options["date_format"] do %>
<option value={value} selected={value == @settings["date_format"]}>
{label}
</option>
<% end %>
</select>
</label>
<div class="label">
<span class="label-text-alt text-xs text-base-content/50">
<%= if @settings["date_format"] != @saved_settings["date_format"] do %>
<span class="text-warning">
{gettext("Selected:")} {get_option_label(
@settings["date_format"],
@setting_options["date_format"]
)}
</span>
<span class="text-base-content/40">
| {gettext("Saved:")} {get_option_label(
@saved_settings["date_format"],
@setting_options["date_format"]
)}
</span>
<% else %>
{gettext("Saved:")} {get_option_label(
@settings["date_format"],
@setting_options["date_format"]
)}
<% end %>
</span>
</div>
</div>
<%!-- Time Format Setting --%>
<div class="form-control w-full">
<label class="label">
<span class="label-text text-base font-medium">{gettext("Time Format")}</span>
<span class="label-text-alt text-sm text-base-content/60">
{gettext("How times are displayed")}
</span>
</label>
<label class="select w-full focus:select-primary">
<select name="settings[time_format]">
<%= for {label, value} <- @setting_options["time_format"] do %>
<option value={value} selected={value == @settings["time_format"]}>
{label}
</option>
<% end %>
</select>
</label>
<div class="label">
<span class="label-text-alt text-xs text-base-content/50">
<%= if @settings["time_format"] != @saved_settings["time_format"] do %>
<span class="text-warning">
{gettext("Selected:")} {get_option_label(
@settings["time_format"],
@setting_options["time_format"]
)}
</span>
<span class="text-base-content/40">
| {gettext("Saved:")} {get_option_label(
@saved_settings["time_format"],
@setting_options["time_format"]
)}
</span>
<% else %>
{gettext("Saved:")} {get_option_label(
@settings["time_format"],
@setting_options["time_format"]
)}
<% end %>
</span>
</div>
</div>
<%!-- Action Buttons --%>
<div class="flex gap-3 pt-4">
<button
type="submit"
class={[
"btn btn-primary flex-1",
if(@saving, do: "loading", else: "")
]}
disabled={@saving}
>
<%= if @saving do %>
{gettext("Saving...")}
<% else %>
{gettext("Save All Settings")}
<% end %>
</button>
<button
type="button"
class="btn btn-outline"
phx-click="reset_to_defaults"
>
<PhoenixKitWeb.Components.Core.Icons.icon_refresh class="w-4 h-4 mr-2" /> {gettext(
"Reset"
)}
</button>
</div>
</.form>
<%!-- Information Panel --%>
<div class="divider">{gettext("Information")}</div>
<div class="bg-base-200 rounded-lg p-4 mt-6">
<div class="flex items-start gap-3">
<div class="text-2xl">💡</div>
<div class="flex-1">
<h3 class="font-semibold text-base-content mb-2">
{gettext("About System Settings")}
</h3>
<ul class="text-sm text-base-content/70 space-y-1">
<li>
{gettext("• Make changes to dropdowns and click \"Save All Settings\" to apply")}
</li>
<li>
{gettext("• Changes show \"Selected\" vs \"Saved\" values until you click Save")}
</li>
<li>
{gettext(
"• User settings (registration, defaults, custom fields) are managed separately"
)}
</li>
<li>
{gettext(
"• Week start day controls which day begins the week (useful for calendar features)"
)}
</li>
<li>
{gettext("• Time zone affects date/time display throughout the system")}
</li>
<li>
{gettext(
"• Date formats control how dates appear (e.g. Users dashboard \"Registered\" field)"
)}
</li>
<li>{gettext("• Time formats control 12-hour vs 24-hour time display")}</li>
<li>
{gettext(
"• Settings are applied system-wide and use Timex for robust formatting"
)}
</li>
<li>
{gettext(
"• Currently supports 6 date formats including \"September 2, 2025\" style"
)}
</li>
</ul>
</div>
</div>
</div>
</div>
</div>
</div>
<.live_component
:if={@show_media_selector}
module={PhoenixKitWeb.Live.Components.MediaSelectorModal}
id="settings-media-selector"
show={@show_media_selector}
mode={:single}
selected_uuids={[]}
phoenix_kit_current_user={assigns[:phoenix_kit_current_user]}
/>
</PhoenixKitWeb.Components.LayoutWrapper.app_layout>