diff --git a/lib/modules/comments/web/index.html.heex b/lib/modules/comments/web/index.html.heex index 97ec82299..c867af9fb 100644 --- a/lib/modules/comments/web/index.html.heex +++ b/lib/modules/comments/web/index.html.heex @@ -1,309 +1,301 @@ - -
- <.admin_page_header - back={Routes.path("/admin")} - title="Comments" - subtitle="Moderate comments across all content" - > - <:actions> - <.link - navigate={Routes.path("/admin/settings/comments")} - class="btn btn-ghost btn-sm" - > - <.icon name="hero-cog-6-tooth" class="w-4 h-4" /> Settings - - - +
+ <.admin_page_header + back={Routes.path("/admin")} + title="Comments" + subtitle="Moderate comments across all content" + > + <:actions> + <.link + navigate={Routes.path("/admin/settings/comments")} + class="btn btn-ghost btn-sm" + > + <.icon name="hero-cog-6-tooth" class="w-4 h-4" /> Settings + + + - <%!-- Statistics --%> -
-
-
Total
-
{@stats.total}
-
-
-
Published
-
{@stats.published}
-
-
-
Pending
-
{@stats.pending}
-
-
-
Hidden
-
{@stats.hidden}
-
-
-
Deleted
-
{@stats.deleted}
-
+ <%!-- Statistics --%> +
+
+
Total
+
{@stats.total}
+
+
Published
+
{@stats.published}
+
+
+
Pending
+
{@stats.pending}
+
+
+
Hidden
+
{@stats.hidden}
+
+
+
Deleted
+
{@stats.deleted}
+
+
- <%!-- Filters --%> -
- <.form for={%{}} phx-change="filter" class="flex flex-wrap gap-4 items-end"> -
- - -
- -
- - -
- -
- - -
+ <%!-- Filters --%> +
+ <.form for={%{}} phx-change="filter" class="flex flex-wrap gap-4 items-end"> +
+ + +
- - -
+
+ + +
- <%!-- Bulk Actions --%> - <%= if length(@selected_uuids) > 0 do %> -
- {length(@selected_uuids)} selected - - - +
+ +
- <% end %> - <%!-- Comments Table --%> - <%= if length(@comments) > 0 do %> - <.table_default - id="comments-table" - variant="zebra" - size="sm" - class="w-full" - toggleable={true} - items={@comments} - card_title={fn comment -> String.slice(comment.content, 0..99) end} - card_fields={ - fn comment -> - [ - %{ - label: gettext("Author"), - value: if(comment.user, do: comment.user.email, else: "Deleted user") - }, - %{label: gettext("Resource"), value: comment.resource_type}, - %{label: gettext("Status"), value: comment.status}, - %{ - label: gettext("Date"), - value: Calendar.strftime(comment.inserted_at, "%b %d, %Y") - } - ] - end - } + + +
+ + <%!-- Bulk Actions --%> + <%= if length(@selected_uuids) > 0 do %> +
+ {length(@selected_uuids)} selected + + + - <% end %> - <%= if comment.status != "hidden" do %> - - <% end %> + Delete + +
+ <% end %> + + <%!-- Comments Table --%> + <%= if length(@comments) > 0 do %> + <.table_default + id="comments-table" + variant="zebra" + size="sm" + class="w-full" + toggleable={true} + items={@comments} + card_title={fn comment -> String.slice(comment.content, 0..99) end} + card_fields={ + fn comment -> + [ + %{ + label: gettext("Author"), + value: if(comment.user, do: comment.user.email, else: "Deleted user") + }, + %{label: gettext("Resource"), value: comment.resource_type}, + %{label: gettext("Status"), value: comment.status}, + %{ + label: gettext("Date"), + value: Calendar.strftime(comment.inserted_at, "%b %d, %Y") + } + ] + end + } + > + <:card_actions :let={comment}> + <%= if comment.status != "published" do %> - + <% end %> + <%= if comment.status != "hidden" do %> + + <% end %> + + - <.table_default_header> - <.table_default_row hover={false}> - <.table_default_header_cell class="w-8"> - <.table_default_header_cell>Content - <.table_default_header_cell>Author - <.table_default_header_cell>Resource - <.table_default_header_cell>Status - <.table_default_header_cell>Date - <.table_default_header_cell>Actions - - + <.table_default_header> + <.table_default_row hover={false}> + <.table_default_header_cell class="w-8"> + <.table_default_header_cell>Content + <.table_default_header_cell>Author + <.table_default_header_cell>Resource + <.table_default_header_cell>Status + <.table_default_header_cell>Date + <.table_default_header_cell>Actions + + - <.table_default_body> - <%= for comment <- @comments do %> - <.table_default_row> - <.table_default_cell> - - - <.table_default_cell class="max-w-xs"> - <%= if comment.depth > 0 do %> -
- <.icon name="hero-arrow-uturn-right-mini" class="size-3" /> - Reply - <%= if comment.parent do %> - - — Re: {String.slice(comment.parent.content, 0..39)} - - <% end %> -
- <% end %> -
{String.slice(comment.content, 0..99)}
- - <.table_default_cell class="text-sm"> - <%= if comment.user do %> - {comment.user.email} - <% else %> - Deleted user - <% end %> - - <.table_default_cell> - - {comment.resource_type} - - <%= case resource_info(@resource_context, comment) do %> - <% %{title: title, path: path} -> %> - <.link - navigate={Routes.path(path)} - class="link link-hover text-sm ml-1 truncate max-w-[200px] inline-block align-bottom" - title={title} - > - {String.slice(title, 0..49)} - - <% _ -> %> - - {String.slice(to_string(comment.resource_uuid), 0..7)} + <.table_default_body> + <%= for comment <- @comments do %> + <.table_default_row> + <.table_default_cell> + + + <.table_default_cell class="max-w-xs"> + <%= if comment.depth > 0 do %> +
+ <.icon name="hero-arrow-uturn-right-mini" class="size-3" /> + Reply + <%= if comment.parent do %> + + — Re: {String.slice(comment.parent.content, 0..39)} - <% end %> - - <.table_default_cell> - - {comment.status} - - - <.table_default_cell class="text-sm text-base-content/70"> - {Calendar.strftime(comment.inserted_at, "%b %d, %Y")} - - <.table_default_cell> -
- <%= if comment.status != "published" do %> - - <% end %> - <%= if comment.status != "hidden" do %> - <% end %> +
+ <% end %> +
{String.slice(comment.content, 0..99)}
+ + <.table_default_cell class="text-sm"> + <%= if comment.user do %> + {comment.user.email} + <% else %> + Deleted user + <% end %> + + <.table_default_cell> + + {comment.resource_type} + + <%= case resource_info(@resource_context, comment) do %> + <% %{title: title, path: path} -> %> + <.link + navigate={Routes.path(path)} + class="link link-hover text-sm ml-1 truncate max-w-[200px] inline-block align-bottom" + title={title} + > + {String.slice(title, 0..49)} + + <% _ -> %> + + {String.slice(to_string(comment.resource_uuid), 0..7)} + + <% end %> + + <.table_default_cell> + + {comment.status} + + + <.table_default_cell class="text-sm text-base-content/70"> + {Calendar.strftime(comment.inserted_at, "%b %d, %Y")} + + <.table_default_cell> +
+ <%= if comment.status != "published" do %> -
- - - <% end %> - - - <% else %> -
- <.icon name="hero-chat-bubble-left" class="w-12 h-12 mx-auto mb-2 opacity-50" /> -

No comments found

-
- <% end %> + <% end %> + <%= if comment.status != "hidden" do %> + + <% end %> + +
+ + + <% end %> + + + <% else %> +
+ <.icon name="hero-chat-bubble-left" class="w-12 h-12 mx-auto mb-2 opacity-50" /> +

No comments found

+
+ <% end %> - <%!-- Pagination --%> - <%= if @total_pages > 1 do %> -
-
- <%= for page <- max(1, @page - 2)..min(@total_pages, @page + 2) do %> - <.link - patch={ - Routes.path( - "/admin/comments?page=#{page}&search=#{@search}&resource_type=#{@filter_resource_type || ""}&status=#{@filter_status || ""}" - ) - } - class={["join-item btn btn-sm", if(page == @page, do: "btn-active", else: "")]} - > - {page} - - <% end %> -
+ <%!-- Pagination --%> + <%= if @total_pages > 1 do %> +
+
+ <%= for page <- max(1, @page - 2)..min(@total_pages, @page + 2) do %> + <.link + patch={ + Routes.path( + "/admin/comments?page=#{page}&search=#{@search}&resource_type=#{@filter_resource_type || ""}&status=#{@filter_status || ""}" + ) + } + class={["join-item btn btn-sm", if(page == @page, do: "btn-active", else: "")]} + > + {page} + + <% end %>
- <% end %> -
- +
+ <% end %> +
diff --git a/lib/modules/comments/web/settings.html.heex b/lib/modules/comments/web/settings.html.heex index f3df4e4cf..ebc7bbedf 100644 --- a/lib/modules/comments/web/settings.html.heex +++ b/lib/modules/comments/web/settings.html.heex @@ -1,143 +1,135 @@ - -
- <.admin_page_header - back={PhoenixKit.Utils.Routes.path("/admin/settings")} - title="Comments Settings" - subtitle="Configure the standalone comments module" - /> +
+ <.admin_page_header + back={PhoenixKit.Utils.Routes.path("/admin/settings")} + title="Comments Settings" + subtitle="Configure the standalone comments module" + /> - <%!-- Settings Form --%> -
- <.form for={%{}} phx-submit="save"> - <%!-- Module Status --%> -
-
-

- <.icon name="hero-cog-6-tooth" class="w-6 h-6" /> Module Status -

+ <%!-- Settings Form --%> +
+ <.form for={%{}} phx-submit="save"> + <%!-- Module Status --%> +
+
+

+ <.icon name="hero-cog-6-tooth" class="w-6 h-6" /> Module Status +

-
- -
+
+
+
- <%!-- Moderation --%> -
-
-

- <.icon name="hero-shield-check" class="w-6 h-6" /> Moderation -

+ <%!-- Moderation --%> +
+
+

+ <.icon name="hero-shield-check" class="w-6 h-6" /> Moderation +

-
- -
+
+
+
- <%!-- Limits --%> -
-
-

- <.icon name="hero-adjustments-horizontal" class="w-6 h-6" /> Limits -

+ <%!-- Limits --%> +
+
+

+ <.icon name="hero-adjustments-horizontal" class="w-6 h-6" /> Limits +

-
-
- - - -
+
+
+ + + +
-
- - - -
+
+ + +
+
- <%!-- Action Buttons --%> -
- - -
- -
+ <%!-- Action Buttons --%> +
+ + +
+
- +
diff --git a/lib/phoenix_kit_web/components/auth_page_wrapper.ex b/lib/phoenix_kit_web/components/auth_page_wrapper.ex index 2ad96854e..5f7fec1a9 100644 --- a/lib/phoenix_kit_web/components/auth_page_wrapper.ex +++ b/lib/phoenix_kit_web/components/auth_page_wrapper.ex @@ -52,8 +52,7 @@ defmodule PhoenixKitWeb.Components.AuthPageWrapper do page_title={@page_title} > {raw(@bg_style_tag)} - <%!-- z-10: above footer (z-auto/0) but below sticky header (z-50) in parent layouts --%> -
+
<%= if @auth_logo_url != "" do %> diff --git a/lib/phoenix_kit_web/live/components/user_settings.ex b/lib/phoenix_kit_web/live/components/user_settings.ex index a169a68aa..47ebfe7e5 100644 --- a/lib/phoenix_kit_web/live/components/user_settings.ex +++ b/lib/phoenix_kit_web/live/components/user_settings.ex @@ -20,8 +20,8 @@ defmodule PhoenixKitWeb.Live.Components.UserSettings do ## Optional assigns - * `sections` — list of sections to display: `:profile`, `:email`, `:password`, `:oauth` - (default: all four) + * `sections` — list of sections to display: `:identity`, `:custom_fields`, `:email`, `:password`, `:oauth` + (default: all five). `:profile` is accepted as a legacy alias that expands to `[:identity, :custom_fields]` * `email_confirm_url_fn` — `(token -> url)` for email confirmation links (default: `&Routes.url("/dashboard/settings/confirm-email/\#{&1}")`) * `return_to` — where OAuth redirect returns to (default: `"/dashboard/settings"`) @@ -43,7 +43,7 @@ defmodule PhoenixKitWeb.Live.Components.UserSettings do alias PhoenixKit.Users.OAuthAvailability alias PhoenixKit.Utils.Routes - @default_sections [:profile, :email, :password, :oauth] + @default_sections [:identity, :custom_fields, :email, :password, :oauth] @impl true def update(%{action: :check_avatar_uploads_complete}, socket) do @@ -73,6 +73,13 @@ defmodule PhoenixKitWeb.Live.Components.UserSettings do user = assigns[:user] || socket.assigns[:user] sections = assigns[:sections] || socket.assigns[:sections] || @default_sections + # Expand legacy :profile into fine-grained sections for backward compatibility + sections = + Enum.flat_map(sections, fn + :profile -> [:identity, :custom_fields] + other -> [other] + end) + email_confirm_url_fn = assigns[:email_confirm_url_fn] || socket.assigns[:email_confirm_url_fn] || (&Routes.url("/dashboard/settings/confirm-email/#{&1}")) @@ -653,8 +660,8 @@ defmodule PhoenixKitWeb.Live.Components.UserSettings do ~H"""
- <%!-- Profile Section --%> - <%= if :profile in @sections do %> + <%!-- Identity Section (avatar, name, username, timezone) --%> + <%= if :identity in @sections do %>
<%!-- Success Message --%> <%= if @profile_success_message do %> @@ -678,7 +685,7 @@ defmodule PhoenixKitWeb.Live.Components.UserSettings do
<% end %> - <%!-- Profile Form with Avatar --%> + <%!-- Identity Form with Avatar --%> <.simple_form for={@profile_form} id={"#{@id}-profile-form"} @@ -733,84 +740,6 @@ defmodule PhoenixKitWeb.Live.Components.UserSettings do label="Username" />
- - <%!-- Custom Fields --%> - <%= if length(@custom_field_definitions) > 0 do %> -
-
Additional Information
-
- - <%= for field <- @custom_field_definitions do %> - <% field_name = "profile_form[user][custom_fields][#{field["key"]}]" %> - <% field_value = - get_in(@user.custom_fields, [field["key"]]) || field["default"] || "" %> -
- <%= case field["type"] do %> - <% "select" -> %> - <.select - name={field_name} - label={field["label"]} - options={ - Enum.map(field["options"] || [], fn opt -> - if is_binary(opt), - do: {opt, opt}, - else: {opt["label"], opt["value"]} - end) - } - value={field_value} - required={field["required"]} - /> - <% "textarea" -> %> - <.textarea - name={field_name} - label={field["label"]} - value={field_value} - required={field["required"]} - /> - <% "number" -> %> - <.input - name={field_name} - type="number" - label={field["label"]} - value={field_value} - required={field["required"]} - /> - <% "email" -> %> - <.input - name={field_name} - type="email" - label={field["label"]} - value={field_value} - required={field["required"]} - /> - <% "url" -> %> - <.input - name={field_name} - type="url" - label={field["label"]} - value={field_value} - required={field["required"]} - /> - <% "date" -> %> - <.input - name={field_name} - type="date" - label={field["label"]} - value={field_value} - required={field["required"]} - /> - <% _ -> %> - <.input - name={field_name} - type="text" - label={field["label"]} - value={field_value} - required={field["required"]} - /> - <% end %> -
- <% end %> - <% end %>
@@ -852,7 +781,112 @@ defmodule PhoenixKitWeb.Live.Components.UserSettings do
-
+ <%= if Enum.any?([:custom_fields, :email, :password, :oauth], & &1 in @sections) do %> +
+ <% end %> +
+ <% end %> + + <%!-- Custom Fields Section --%> + <%= if :custom_fields in @sections and length(@custom_field_definitions) > 0 do %> +
+ <.simple_form + for={@profile_form} + id={"#{@id}-custom-fields-form"} + phx-submit="update_profile" + phx-change="validate_profile" + phx-target={@myself} + > + <%= if :identity in @sections do %> +
Additional Information
+ <% else %> +

Additional Information

+ <% end %> + +
+ <%= for field <- @custom_field_definitions do %> + <% field_name = "profile_form[user][custom_fields][#{field["key"]}]" %> + <% field_value = + get_in(@user.custom_fields, [field["key"]]) || field["default"] || "" %> +
+ <%= case field["type"] do %> + <% "select" -> %> + <.select + name={field_name} + label={field["label"]} + options={ + Enum.map(field["options"] || [], fn opt -> + if is_binary(opt), + do: {opt, opt}, + else: {opt["label"], opt["value"]} + end) + } + value={field_value} + required={field["required"]} + /> + <% "textarea" -> %> + <.textarea + name={field_name} + label={field["label"]} + value={field_value} + required={field["required"]} + /> + <% "number" -> %> + <.input + name={field_name} + type="number" + label={field["label"]} + value={field_value} + required={field["required"]} + /> + <% "email" -> %> + <.input + name={field_name} + type="email" + label={field["label"]} + value={field_value} + required={field["required"]} + /> + <% "url" -> %> + <.input + name={field_name} + type="url" + label={field["label"]} + value={field_value} + required={field["required"]} + /> + <% "date" -> %> + <.input + name={field_name} + type="date" + label={field["label"]} + value={field_value} + required={field["required"]} + /> + <% _ -> %> + <.input + name={field_name} + type="text" + label={field["label"]} + value={field_value} + required={field["required"]} + /> + <% end %> +
+ <% end %> +
+ + <:actions> +
+ <.button phx-disable-with="Updating..." class="btn-primary"> + Update Custom Fields + +
+ + + <%= if Enum.any?([:email, :password, :oauth], & &1 in @sections) do %> +
+ <% end %>
<% end %> @@ -927,7 +961,9 @@ defmodule PhoenixKitWeb.Live.Components.UserSettings do
<% end %> -
+ <%= if :email in @sections and Enum.any?([:password, :oauth], & &1 in @sections) do %> +
+ <% end %> <%!-- Password Section --%> <%= if :password in @sections do %>