Packages

phoenix_kit

2.63.0
3.0.0 2.65.0 2.64.1 2.64.0 2.63.0 2.62.2 2.62.1 2.62.0 2.61.0 2.60.3 2.60.2 2.60.1 2.60.0 2.59.0 2.58.0 2.57.1 2.57.0 2.56.1 2.56.0 2.55.1 2.55.0 2.54.2 2.54.1 2.54.0 2.53.0 2.52.2 2.52.1 2.52.0 2.51.0 2.50.0 2.49.1 2.49.0 2.48.0 2.47.0 2.46.0 2.45.0 2.44.0 2.43.1 2.43.0 2.42.1 2.42.0 2.41.6 2.41.4 2.41.3 2.41.2 2.41.1 2.41.0 2.40.1 2.40.0 2.39.0 2.38.1 2.38.0 2.37.5 2.37.4 2.37.3 2.37.2 2.37.1 2.37.0 2.36.1 2.36.0 2.35.0 2.34.0 2.33.0 2.32.1 2.32.0 2.31.1 2.31.0 2.30.0 2.29.1 2.29.0 2.28.2 2.28.1 2.28.0 2.27.2 2.27.1 2.27.0 2.26.1 2.26.0 2.25.0 2.24.0 2.23.3 2.23.2 2.23.1 2.23.0 2.22.24 2.22.23 2.22.22 2.22.21 2.22.20 2.22.19 2.22.18 2.22.17 2.22.16 2.22.15 2.22.14 2.22.13 2.22.12 2.22.11 2.22.10 2.22.9 2.22.8 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
phoenix_kit lib phoenix_kit_web live settings.html.heex
Raw

lib/phoenix_kit_web/live/settings.html.heex

<PhoenixKitWeb.Components.LayoutWrapper.app_layout
  socket={@socket}
  flash={@flash}
  phoenix_kit_current_scope={assigns[:phoenix_kit_current_scope]}
  page_title={@page_title}
  page_subtitle={@page_subtitle}
  page_section={@page_section}
  page_section_path={@page_section_path}
  current_path={@url_path}
  project_title={@project_title}
  current_locale={@current_locale}
>
  <div class="flex flex-col px-4 py-6">
    <%!-- Settings Form --%>
    <div class="card bg-base-100 shadow-xl">
      <div class="card-body">
        <.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"]}
          />

          <.nav_tabs
            active_tab={@active_tab}
            variant={:border}
            tabs={UrlTabs.patch_links(tabs(), "/admin/settings")}
          />

          <div class={if @active_tab != "identity", do: "hidden"}>
            <%!-- Project Title Setting --%>
            <div class="fieldset w-full min-w-0 grid-cols-[minmax(0,1fr)]">
              <label class="label flex-wrap whitespace-normal">
                <span class="fieldset-legend text-base font-medium">{gettext("Project Title")}</span>
                <span class="fieldset-label text-sm text-base-content/60">
                  {gettext("Application name display")}
                </span>
              </label>
              <.input
                name="settings[project_title]"
                type="text"
                value={@settings["project_title"]}
                class="focus:input-primary"
                placeholder={gettext("Enter project title")}
                phx-debounce="500"
              />
              <.unsaved_hint
                dirty={@settings["project_title"] != @saved_settings["project_title"]}
                saved={@saved_settings["project_title"]}
              />
            </div>

            <%!-- Primary language lives on Settings → Languages; this line is
                 how the admin of a single-language site finds it. --%>
            <div
              id="primary-language-link"
              class="fieldset w-full min-w-0 grid-cols-[minmax(0,1fr)]"
            >
              <span class="fieldset-legend text-base font-medium">
                {gettext("Language & URLs")}
              </span>
              <p class="text-sm text-base-content/70">
                {gettext("Primary language: %{language}.", language: @primary_language_name)}
                <.pk_link navigate="/admin/settings/languages" class="link link-primary">
                  {gettext("Set it, and how languages appear in URLs")}
                </.pk_link>
              </p>
            </div>

            <%!-- "Admin Panel" chip in the admin header --%>
            <div class="fieldset w-full min-w-0 grid-cols-[minmax(0,1fr)]">
              <.checkbox
                name="settings[show_admin_panel_label]"
                checked={@settings["show_admin_panel_label"] != "false"}
                label={gettext("Show the \"Admin Panel\" label in the admin header")}
              >
                <:description>
                  {gettext(
                    "Shown next to the project name. Turn it off to show just the project name. The wording is not editable here because it is translated — each visitor reads it in their own language. To call your admin area something else and keep that, a developer can set `admin_panel_label` in config.exs, where the available names are listed as comments. The browser tab title is unaffected."
                  )}
                </:description>
              </.checkbox>
            </div>

            <%!-- Page descriptions in the admin header --%>
            <div class="fieldset w-full min-w-0 grid-cols-[minmax(0,1fr)]">
              <.checkbox
                name="settings[show_page_descriptions]"
                checked={@settings["show_page_descriptions"] == "true"}
                label={gettext("Show page descriptions in the admin header")}
              >
                <:description>
                  {gettext(
                    "A page can carry a one-line description; when this is on it appears after the page title in the admin header and under the in-page title. Off by default: the breadcrumb and the title say where you are."
                  )}
                </:description>
              </.checkbox>
            </div>

            <%!-- Project Logo --%>
            <div class="fieldset w-full min-w-0 grid-cols-[minmax(0,1fr)]">
              <label class="label flex-wrap whitespace-normal">
                <span class="fieldset-legend 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>
                <span class="block text-xs text-base-content/50">
                  {gettext(
                    "Logo displayed across the application. Falls back to the site icon, then the project title text."
                  )}
                  <%= if (@settings["auth_logo_file_uuid"] || "") == "" &&
                      (@settings["site_icon_file_uuid"] || "") != "" do %>
                    <span class="text-info">{gettext("Currently using the site icon.")}</span>
                  <% end %>
                </span>
              </div>
            </div>

            <%!-- Browser Tab: site icon + default tab title as one identity group,
               with a live browser-chrome preview (WordPress site-icon style). --%>
            <div class="fieldset w-full min-w-0 grid-cols-[minmax(0,1fr)]">
              <label class="label flex-wrap whitespace-normal">
                <span class="fieldset-legend text-base font-medium">{gettext("Browser Tab")}</span>
                <span class="fieldset-label text-sm text-base-content/60">
                  {gettext("Site icon and tab title")}
                </span>
              </label>
              <input
                type="hidden"
                name="settings[site_icon_file_uuid]"
                value={@settings["site_icon_file_uuid"]}
              />

              <%!-- The favicon defaults to the project logo when unset (and vice
                 versa) — mirror that fallback so the preview shows what the
                 browser will actually render. --%>
              <% preview_icon_uuid =
                if (@settings["site_icon_file_uuid"] || "") != "",
                  do: @settings["site_icon_file_uuid"],
                  else: @settings["auth_logo_file_uuid"] %>

              <div class="rounded-box border border-base-300 overflow-hidden">
                <%!-- Live preview: tab strip. The title updates as you type
                   (the form's phx-change refreshes @settings). --%>
                <div class="bg-base-200 px-4 pt-3">
                  <div class="flex items-end gap-2">
                    <div class="hidden sm:flex items-center gap-1.5 mb-2.5 mr-1">
                      <span class="w-2.5 h-2.5 rounded-full bg-error/60"></span>
                      <span class="w-2.5 h-2.5 rounded-full bg-warning/60"></span>
                      <span class="w-2.5 h-2.5 rounded-full bg-success/60"></span>
                    </div>
                    <div class="flex items-center gap-2 bg-base-100 rounded-t-lg px-3 py-2 min-w-0 max-w-[260px] shadow-sm">
                      <%= if (preview_icon_uuid || "") != "" do %>
                        <img
                          src={signed_preview_url(preview_icon_uuid, "thumbnail")}
                          alt=""
                          class="w-4 h-4 rounded-sm object-cover shrink-0"
                        />
                      <% else %>
                        <.icon
                          name="hero-globe-alt"
                          class="w-4 h-4 text-base-content/30 shrink-0"
                        />
                      <% end %>
                      <span class="text-xs font-medium truncate">
                        {if (@settings["default_tab_title"] || "") != "",
                          do: @settings["default_tab_title"],
                          else: @settings["project_title"] || "My Site"}
                      </span>
                      <.icon
                        name="hero-x-mark"
                        class="w-3 h-3 text-base-content/40 shrink-0 ml-auto"
                      />
                    </div>
                    <div class="mb-2.5 ml-1 text-base-content/40">
                      <.icon name="hero-plus" class="w-3.5 h-3.5" />
                    </div>
                  </div>
                </div>
                <%!-- Address bar --%>
                <div class="bg-base-100 border-b border-base-300 px-4 py-2 flex items-center gap-3">
                  <div class="hidden sm:flex items-center gap-2 text-base-content/25">
                    <.icon name="hero-arrow-left" class="w-3.5 h-3.5" />
                    <.icon name="hero-arrow-right" class="w-3.5 h-3.5" />
                    <.icon name="hero-arrow-path" class="w-3.5 h-3.5" />
                  </div>
                  <div class="flex items-center gap-1.5 bg-base-200 rounded-full px-3 py-1 flex-1 max-w-xs min-w-0">
                    <.icon name="hero-lock-closed" class="w-3 h-3 text-base-content/40 shrink-0" />
                    <span class="text-xs text-base-content/50 truncate">{site_host()}</span>
                  </div>
                </div>

                <%!-- Controls --%>
                <div class="p-4 sm:p-5 bg-base-100 flex flex-col sm:flex-row gap-5">
                  <div class="flex items-start gap-4">
                    <%= if (@settings["site_icon_file_uuid"] || "") != "" do %>
                      <img
                        src={signed_preview_url(@settings["site_icon_file_uuid"], "thumbnail")}
                        alt={gettext("Site icon preview")}
                        class="w-16 h-16 rounded-2xl object-cover ring-1 ring-base-300 shadow-sm shrink-0"
                      />
                      <div class="flex flex-col gap-2">
                        <button
                          type="button"
                          class="btn btn-outline btn-sm"
                          phx-click="open_media_selector"
                          phx-value-target="site_icon"
                        >
                          {gettext("Change icon")}
                        </button>
                        <button
                          type="button"
                          class="btn btn-ghost btn-sm text-error"
                          phx-click="clear_image"
                          phx-value-target="site_icon"
                        >
                          {gettext("Remove")}
                        </button>
                      </div>
                    <% else %>
                      <button
                        type="button"
                        class="w-16 h-16 rounded-2xl border-2 border-dashed border-base-300 hover:border-primary hover:text-primary text-base-content/40 flex items-center justify-center transition-colors shrink-0"
                        title={gettext("Select site icon")}
                        phx-click="open_media_selector"
                        phx-value-target="site_icon"
                      >
                        <.icon name="hero-photo" class="w-6 h-6" />
                      </button>
                      <div class="flex flex-col gap-2">
                        <button
                          type="button"
                          class="btn btn-outline btn-sm"
                          phx-click="open_media_selector"
                          phx-value-target="site_icon"
                        >
                          {gettext("Select site icon")}
                        </button>
                        <p class="text-xs text-base-content/50 max-w-[16rem]">
                          {gettext("Square, at least 512×512 pixels.")}
                          <%= if (@settings["auth_logo_file_uuid"] || "") != "" do %>
                            {gettext("Currently using the project logo.")}
                          <% end %>
                        </p>
                      </div>
                    <% end %>
                  </div>

                  <div class="divider divider-horizontal m-0 hidden sm:flex"></div>

                  <div class="flex-1 min-w-0">
                    <label class="label pt-0 pb-1">
                      <span class="fieldset-legend text-sm font-medium">
                        {gettext("Default Tab Title")}
                      </span>
                    </label>
                    <.input
                      name="settings[default_tab_title]"
                      type="text"
                      value={@settings["default_tab_title"]}
                      class="focus:input-primary"
                      placeholder={
                        @settings["project_title"] || gettext("e.g. Welcome to My Site")
                      }
                      phx-debounce="300"
                    />
                    <p class="text-xs text-base-content/50 mt-2">
                      {gettext(
                        "Shown when a page doesn't set its own title. Leave empty to use the project title."
                      )}
                    </p>
                  </div>
                </div>
              </div>

              <div>
                <span class="block text-xs text-base-content/50">
                  {gettext(
                    "The icon appears in browser tabs, bookmarks, and on phone home screens."
                  )}
                </span>
              </div>
            </div>
          </div>

          <div class={if @active_tab != "address", do: "hidden"}>
            <%!-- Site Address URL Setting --%>
            <div class="fieldset w-full min-w-0 grid-cols-[minmax(0,1fr)]">
              <label class="label flex-wrap whitespace-normal">
                <span class="fieldset-legend text-base font-medium">
                  {gettext("Site Address (URL)")}
                </span>
                <span class="fieldset-label text-sm text-base-content/60">
                  {gettext("Public base URL of this application")}
                </span>
              </label>
              <.input
                name="settings[site_url]"
                type="url"
                value={@settings["site_url"]}
                class="focus:input-primary"
                placeholder="https://site.com"
                phx-debounce="500"
              />
              <.unsaved_hint
                dirty={@settings["site_url"] != @saved_settings["site_url"]}
                saved={
                  if @saved_settings["site_url"] != "",
                    do: @saved_settings["site_url"],
                    else: gettext("(empty)")
                }
              />
            </div>

            <%!-- Main Page: where core sends a visitor who is not signed in.
                Empty = core's own /users/log-in, which exists in every install
                and every locale. --%>
            <div class="fieldset w-full min-w-0 grid-cols-[minmax(0,1fr)]">
              <label class="label flex-wrap whitespace-normal">
                <span class="fieldset-legend text-base font-medium">
                  {gettext("Signed-Out Landing Page")}
                </span>
              </label>
              <.input
                name="settings[main_page_path]"
                type="text"
                value={@settings["main_page_path"]}
                class="focus:input-primary"
                placeholder="/"
                phx-debounce="500"
              />
              <div class="text-xs text-base-content/60 mt-1">
                {gettext(
                  "Where a visitor who isn't signed in lands — including right after logging out. Empty = the sign-in page."
                )}
              </div>
              <.unsaved_hint
                dirty={@settings["main_page_path"] != @saved_settings["main_page_path"]}
                saved={
                  if @saved_settings["main_page_path"] != "",
                    do: @saved_settings["main_page_path"],
                    else: gettext("(empty)")
                }
              />
            </div>
          </div>

          <div class={if @active_tab != "features", do: "hidden"}>
            <%!-- Notifications --%>
            <div class="fieldset w-full min-w-0 grid-cols-[minmax(0,1fr)]">
              <label class="label flex-wrap whitespace-normal">
                <span class="fieldset-legend text-base font-medium">
                  {gettext("Notifications")}
                </span>
                <span class="fieldset-label text-sm text-base-content/60">
                  {gettext("Per-user inbox driven by the activity feed")}
                </span>
              </label>
              <.checkbox
                name="settings[notifications_enabled]"
                checked={@settings["notifications_enabled"] == "true"}
                label={gettext("Enable user notifications")}
              >
                <:description>
                  {gettext(
                    "When on, users receive a bell notification whenever an activity targets them (e.g., their post is liked)."
                  )}
                </:description>
              </.checkbox>

              <%!-- Default notification destination (catch-all click target) --%>
              <label class="label pt-3">
                <span class="fieldset-legend">{gettext("Default notification link")}</span>
              </label>
              <.input
                type="text"
                name="settings[notification_default_link]"
                value={@settings["notification_default_link"] || "/admin"}
                placeholder="/admin"
                class="input-sm max-w-md"
              />
              <div>
                <span class="block text-xs text-base-content/50">
                  {gettext(
                    "Where a notification opens when it has no link of its own. Defaults to /admin (every signed-in user can open it). Clear the field to make such notifications non-clickable instead. The URL prefix and locale are added automatically."
                  )}
                </span>
              </div>
            </div>
          </div>

          <div class={if @active_tab != "editor", do: "hidden"}>
            <div class="grid grid-cols-1 md:grid-cols-2 gap-x-6 gap-y-2">
              <%!-- Default Editor Mode Setting --%>
              <div class="fieldset w-full min-w-0 grid-cols-[minmax(0,1fr)]">
                <label class="label flex-wrap whitespace-normal">
                  <span class="fieldset-legend text-base font-medium">
                    {gettext("Default Editor Mode")}
                  </span>
                  <span class="fieldset-label text-sm text-base-content/60">
                    {gettext("Mode every content editor opens in")}
                  </span>
                </label>
                <.select
                  name="settings[editor_default_mode]"
                  value={@settings["editor_default_mode"]}
                  options={@setting_options["editor_default_mode"]}
                  class="min-w-0 max-w-full focus-within:select-primary [&_select]:min-w-0 [&_select]:truncate"
                />
                <div>
                  <span class="block text-xs text-base-content/50">
                    {gettext(
                      "Applies site-wide wherever the rich-text editor is rendered (posts, comments). Users can still switch modes inside the editor."
                    )}
                  </span>
                </div>
                <.unsaved_hint
                  dirty={
                    @settings["editor_default_mode"] != @saved_settings["editor_default_mode"]
                  }
                  saved={
                    get_option_label(
                      @saved_settings["editor_default_mode"],
                      @setting_options["editor_default_mode"]
                    )
                  }
                />
              </div>

              <%!-- Mentions --%>
              <div class="fieldset w-full min-w-0 grid-cols-[minmax(0,1fr)] gap-3">
                <.checkbox
                  name="settings[mentions_enabled]"
                  checked={@settings["mentions_enabled"] == "true"}
                  label={gettext("Mentions and record links")}
                >
                  <:description>
                    {gettext(
                      "Lets people type @ to mention someone and # to link a record, anywhere they can write free text. Turning it off stops the suggestions; mentions already written stay readable as plain text."
                    )}
                  </:description>
                </.checkbox>
                <.checkbox
                  name="settings[mentions_redact_titles]"
                  checked={@settings["mentions_redact_titles"] == "true"}
                  label={gettext("Hide titles of records the reader can't open")}
                >
                  <:description>
                    {gettext(
                      "By default a mention shows what it names even to someone without access, with a lock icon and no link. Turn this on where the name itself is sensitive; they then see the name as it was when it was written, not its current one."
                    )}
                  </:description>
                </.checkbox>
                <.unsaved_hint
                  dirty={
                    @settings["mentions_enabled"] != @saved_settings["mentions_enabled"] or
                      @settings["mentions_redact_titles"] !=
                        @saved_settings["mentions_redact_titles"]
                  }
                  saved={
                    if(@saved_settings["mentions_enabled"] == "true",
                      do: gettext("On"),
                      else: gettext("Off")
                    )
                  }
                />
              </div>
            </div>
          </div>

          <div class={if @active_tab != "datetime", do: "hidden"}>
            <div class="grid grid-cols-1 md:grid-cols-2 gap-x-6 gap-y-2">
              <%!-- Week Start Day Setting --%>
              <div class="fieldset w-full min-w-0 grid-cols-[minmax(0,1fr)]">
                <label class="label flex-wrap whitespace-normal">
                  <span class="fieldset-legend text-base font-medium">{gettext("Week Start On")}</span>
                  <span class="fieldset-label text-sm text-base-content/60">
                    {gettext("First day of the week")}
                  </span>
                </label>
                <.select
                  name="settings[week_start_day]"
                  value={@settings["week_start_day"]}
                  options={@setting_options["week_start_day"]}
                  class="min-w-0 max-w-full focus-within:select-primary [&_select]:min-w-0 [&_select]:truncate"
                />
                <.unsaved_hint
                  dirty={@settings["week_start_day"] != @saved_settings["week_start_day"]}
                  saved={
                    get_option_label(
                      @saved_settings["week_start_day"],
                      @setting_options["week_start_day"]
                    )
                  }
                />
              </div>

              <%!-- Time Zone Setting --%>
              <div class="fieldset w-full min-w-0 grid-cols-[minmax(0,1fr)]">
                <label class="label flex-wrap whitespace-normal">
                  <span class="fieldset-legend text-base font-medium">{gettext("Time Zone")}</span>
                  <span class="fieldset-label text-sm text-base-content/60">
                    {gettext("System default timezone")}
                  </span>
                </label>
                <.select
                  name="settings[time_zone]"
                  value={@settings["time_zone"]}
                  options={@setting_options["time_zone"]}
                  class="min-w-0 max-w-full focus-within:select-primary [&_select]:min-w-0 [&_select]:truncate"
                />
                <.unsaved_hint
                  dirty={@settings["time_zone"] != @saved_settings["time_zone"]}
                  saved={get_timezone_label(@saved_settings["time_zone"], @setting_options)}
                />
              </div>

              <%!-- Date Format Setting --%>
              <div class="fieldset w-full min-w-0 grid-cols-[minmax(0,1fr)]">
                <label class="label flex-wrap whitespace-normal">
                  <span class="fieldset-legend text-base font-medium">{gettext("Date Format")}</span>
                  <span class="fieldset-label text-sm text-base-content/60">
                    {gettext("How dates are displayed")}
                  </span>
                </label>
                <.select
                  name="settings[date_format]"
                  value={@settings["date_format"]}
                  options={@setting_options["date_format"]}
                  class="min-w-0 max-w-full focus-within:select-primary [&_select]:min-w-0 [&_select]:truncate"
                />
                <div>
                  <span class="block text-xs text-base-content/50">
                    {gettext("Example:")} {get_current_date_example(@settings["date_format"])}
                  </span>
                </div>
                <.unsaved_hint
                  dirty={@settings["date_format"] != @saved_settings["date_format"]}
                  saved={
                    get_option_label(
                      @saved_settings["date_format"],
                      @setting_options["date_format"]
                    )
                  }
                />
              </div>

              <%!-- Time Format Setting --%>
              <div class="fieldset w-full min-w-0 grid-cols-[minmax(0,1fr)]">
                <label class="label flex-wrap whitespace-normal">
                  <span class="fieldset-legend text-base font-medium">{gettext("Time Format")}</span>
                  <span class="fieldset-label text-sm text-base-content/60">
                    {gettext("How times are displayed")}
                  </span>
                </label>
                <.select
                  name="settings[time_format]"
                  value={@settings["time_format"]}
                  options={@setting_options["time_format"]}
                  class="min-w-0 max-w-full focus-within:select-primary [&_select]:min-w-0 [&_select]:truncate"
                />
                <div>
                  <span class="block text-xs text-base-content/50">
                    {gettext("Example:")} {get_current_time_example(@settings["time_format"])}
                  </span>
                </div>
                <.unsaved_hint
                  dirty={@settings["time_format"] != @saved_settings["time_format"]}
                  saved={
                    get_option_label(
                      @saved_settings["time_format"],
                      @setting_options["time_format"]
                    )
                  }
                />
              </div>
            </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"
              data-confirm={
                gettext(
                  "Reset ALL settings to their defaults? Your title, logo, formats and feature toggles will be replaced. This cannot be undone."
                )
              }
            >
              <PhoenixKitWeb.Components.Core.Icons.icon_refresh class="w-4 h-4 mr-2" /> {gettext(
                "Reset"
              )}
            </button>
          </div>
        </.form>

        <%!-- Footnote --%>
        <div class="flex items-start gap-2 mt-6 text-xs text-base-content/50">
          <.icon name="hero-information-circle" class="w-4 h-4 shrink-0 mt-0.5" />
          <p>
            {gettext(
              "Changes apply system-wide after \"Save All Settings\". Registration and user defaults are managed on the Users page."
            )}
          </p>
        </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}
    title={
      if @media_selector_target == "site_icon",
        do: gettext("Select Site Icon"),
        else: gettext("Select Logo")
    }
    selected_uuids={[]}
    phoenix_kit_current_user={assigns[:phoenix_kit_current_user]}
    scope_folder_id={@branding_scope_folder}
  />
</PhoenixKitWeb.Components.LayoutWrapper.app_layout>