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
phoenix_kit lib modules comments web index.html.heex
Raw

lib/modules/comments/web/index.html.heex

<PhoenixKitWeb.Components.LayoutWrapper.app_layout
flash={@flash}
phoenix_kit_current_scope={assigns[:phoenix_kit_current_scope]}
page_title="Comments"
current_path={@url_path}
project_title={@project_title}
>
<div class="container flex flex-col mx-auto px-4 py-6">
<%!-- Header --%>
<header class="w-full relative mb-6">
<.link
navigate={Routes.path("/admin")}
class="btn btn-outline btn-primary btn-sm absolute left-0 top-0"
>
<.icon name="hero-arrow-left" class="w-4 h-4" /> Back
</.link>
<div class="text-center">
<h1 class="text-4xl font-bold">Comments</h1>
<p class="text-lg text-base-content/70">Moderate comments across all content</p>
</div>
<div class="absolute right-0 top-0">
<.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
</.link>
</div>
</header>
<%!-- Statistics --%>
<div class="grid grid-cols-2 md:grid-cols-5 gap-4 mb-6">
<div class="stat bg-base-200 rounded-xl">
<div class="stat-title">Total</div>
<div class="stat-value text-primary text-2xl">{@stats.total}</div>
</div>
<div class="stat bg-base-200 rounded-xl">
<div class="stat-title">Published</div>
<div class="stat-value text-success text-2xl">{@stats.published}</div>
</div>
<div class="stat bg-base-200 rounded-xl">
<div class="stat-title">Pending</div>
<div class="stat-value text-warning text-2xl">{@stats.pending}</div>
</div>
<div class="stat bg-base-200 rounded-xl">
<div class="stat-title">Hidden</div>
<div class="stat-value text-info text-2xl">{@stats.hidden}</div>
</div>
<div class="stat bg-base-200 rounded-xl">
<div class="stat-title">Deleted</div>
<div class="stat-value text-error text-2xl">{@stats.deleted}</div>
</div>
</div>
<%!-- Filters --%>
<div class="bg-base-200 rounded-lg p-4 mb-6">
<.form for={%{}} phx-change="filter" class="flex flex-wrap gap-4 items-end">
<div class="form-control flex-1 min-w-48">
<label class="label"><span class="label-text">Search</span></label>
<input
type="text"
name="search[query]"
value={@search}
placeholder="Search comment content..."
class="input input-bordered input-sm w-full"
phx-debounce="300"
/>
</div>
<div class="form-control">
<label class="label"><span class="label-text">Resource Type</span></label>
<select name="filter[resource_type]" class="select select-bordered select-sm">
<option value="">All Types</option>
<option value="post" selected={@filter_resource_type == "post"}>Post</option>
<option value="entity" selected={@filter_resource_type == "entity"}>Entity</option>
<option value="ticket" selected={@filter_resource_type == "ticket"}>Ticket</option>
</select>
</div>
<div class="form-control">
<label class="label"><span class="label-text">Status</span></label>
<select name="filter[status]" class="select select-bordered select-sm">
<option value="">All Statuses</option>
<option value="published" selected={@filter_status == "published"}>Published</option>
<option value="pending" selected={@filter_status == "pending"}>Pending</option>
<option value="hidden" selected={@filter_status == "hidden"}>Hidden</option>
<option value="deleted" selected={@filter_status == "deleted"}>Deleted</option>
</select>
</div>
<button type="button" phx-click="clear_filters" class="btn btn-ghost btn-sm">
Clear
</button>
</.form>
</div>
<%!-- Bulk Actions --%>
<%= if length(@selected_ids) > 0 do %>
<div class="flex gap-2 mb-4 items-center">
<span class="text-sm text-base-content/70">{length(@selected_ids)} selected</span>
<button phx-click="bulk_action" phx-value-action="approve" class="btn btn-success btn-xs">
Approve
</button>
<button phx-click="bulk_action" phx-value-action="hide" class="btn btn-warning btn-xs">
Hide
</button>
<button
phx-click="bulk_action"
phx-value-action="delete"
class="btn btn-error btn-xs"
data-confirm="Delete selected comments?"
>
Delete
</button>
</div>
<% end %>
<%!-- Comments Table --%>
<div class="card bg-base-100 shadow-xl">
<div class="card-body p-0">
<%= if length(@comments) > 0 do %>
<div class="overflow-x-auto">
<table class="table table-sm">
<thead>
<tr>
<th class="w-8"></th>
<th>Content</th>
<th>Author</th>
<th>Resource</th>
<th>Status</th>
<th>Date</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<%= for comment <- @comments do %>
<tr class="hover">
<td>
<input
type="checkbox"
class="checkbox checkbox-xs"
phx-click="toggle_select"
phx-value-id={comment.id}
checked={comment.id in @selected_ids}
/>
</td>
<td class="max-w-xs">
<div class="truncate text-sm">{String.slice(comment.content, 0..99)}</div>
</td>
<td class="text-sm">
<%= if comment.user do %>
{comment.user.email}
<% else %>
<span class="text-base-content/50">Deleted user</span>
<% end %>
</td>
<td>
<span class="badge badge-ghost badge-sm">{comment.resource_type}</span>
</td>
<td>
<span class={status_badge_class(comment.status)}>
{comment.status}
</span>
</td>
<td class="text-sm text-base-content/70">
{Calendar.strftime(comment.inserted_at, "%b %d, %Y")}
</td>
<td>
<div class="flex gap-1">
<%= if comment.status != "published" do %>
<button
phx-click="approve"
phx-value-id={comment.id}
class="btn btn-ghost btn-xs text-success"
title="Approve"
>
<.icon name="hero-check" class="w-4 h-4" />
</button>
<% end %>
<%= if comment.status != "hidden" do %>
<button
phx-click="hide"
phx-value-id={comment.id}
class="btn btn-ghost btn-xs text-warning"
title="Hide"
>
<.icon name="hero-eye-slash" class="w-4 h-4" />
</button>
<% end %>
<button
phx-click="delete"
phx-value-id={comment.id}
class="btn btn-ghost btn-xs text-error"
title="Delete"
data-confirm="Delete this comment?"
>
<.icon name="hero-trash" class="w-4 h-4" />
</button>
</div>
</td>
</tr>
<% end %>
</tbody>
</table>
</div>
<% else %>
<div class="text-center py-12 text-base-content/60">
<.icon name="hero-chat-bubble-left" class="w-12 h-12 mx-auto mb-2 opacity-50" />
<p>No comments found</p>
</div>
<% end %>
</div>
</div>
<%!-- Pagination --%>
<%= if @total_pages > 1 do %>
<div class="flex justify-center mt-6">
<div class="join">
<%= 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}
</.link>
<% end %>
</div>
</div>
<% end %>
</div>
</PhoenixKitWeb.Components.LayoutWrapper.app_layout>