Packages

phoenix_kit

1.7.26
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 tickets web list.ex
Raw

lib/modules/tickets/web/list.ex

defmodule PhoenixKit.Modules.Tickets.Web.List do
@moduledoc """
LiveView for displaying and managing support tickets in PhoenixKit admin panel.
Provides comprehensive ticket management interface with filtering, searching,
and quick actions for the support ticketing system.
## Features
- **Real-time Ticket List**: Live updates of tickets
- **Status Filtering**: By open, in_progress, resolved, closed
- **Assignment Filtering**: By handler, unassigned
- **Search Functionality**: Search across titles and descriptions
- **Pagination**: Handle large volumes of tickets
- **Quick Actions**: View, assign, change status
- **Statistics Summary**: Key metrics (open, in_progress, resolved, closed)
## Route
This LiveView is mounted at `{prefix}/admin/tickets` and requires
appropriate admin or SupportAgent permissions.
## Permissions
Access is restricted to users with admin, owner, or SupportAgent roles.
"""
use PhoenixKitWeb, :live_view
alias PhoenixKit.Modules.Tickets
alias PhoenixKit.Settings
alias PhoenixKit.Users.Roles
alias PhoenixKit.Utils.Routes
@impl true
def mount(_params, _session, socket) do
if tickets_enabled?() do
current_user = socket.assigns[:phoenix_kit_current_user]
if can_access_tickets?(current_user) do
project_title = Settings.get_setting("project_title", "PhoenixKit")
socket =
socket
|> assign(:page_title, "Support Tickets")
|> assign(:project_title, project_title)
|> assign(:current_user, current_user)
|> assign(:tickets, [])
|> assign(:total_count, 0)
|> assign(:stats, Tickets.get_stats())
|> assign(:loading, true)
|> assign_filter_defaults()
|> assign_pagination_defaults()
{:ok, socket}
else
{:ok,
socket
|> put_flash(:error, "You don't have access to the ticketing system")
|> push_navigate(to: Routes.path("/admin"))}
end
else
{:ok,
socket
|> put_flash(:error, "Tickets module is not enabled")
|> push_navigate(to: Routes.path("/admin"))}
end
end
@impl true
def handle_params(params, uri, socket) do
socket =
socket
|> assign(:url_path, URI.parse(uri).path)
|> apply_params(params)
|> load_tickets()
{:noreply, assign(socket, :loading, false)}
end
@impl true
def handle_event("filter", params, socket) do
filter_params = %{}
filter_params =
case Map.get(params, "search") do
%{"query" => query} -> Map.put(filter_params, "search", String.trim(query || ""))
_ -> filter_params
end
filter_params =
case Map.get(params, "filters") do
%{"status" => status} when status != "" ->
Map.put(filter_params, "status", status)
_ ->
filter_params
end
filter_params =
case Map.get(params, "filters") do
%{"assigned_to" => assigned} when assigned != "" ->
Map.put(filter_params, "assigned_to", assigned)
_ ->
filter_params
end
{:noreply,
push_patch(socket, to: Routes.path("/admin/tickets", map_to_keyword(filter_params)))}
end
@impl true
def handle_event("clear_filters", _params, socket) do
{:noreply, push_patch(socket, to: Routes.path("/admin/tickets"))}
end
@impl true
def handle_event("change_page", %{"page" => page}, socket) do
page = String.to_integer(page)
current_params = build_current_params(socket)
params = Map.put(current_params, "page", page)
{:noreply, push_patch(socket, to: Routes.path("/admin/tickets", map_to_keyword(params)))}
end
# Private functions
defp tickets_enabled? do
Tickets.enabled?()
end
defp can_access_tickets?(nil), do: false
defp can_access_tickets?(user) do
Roles.user_has_role_owner?(user) or
Roles.user_has_role_admin?(user) or
Roles.user_has_role?(user, "SupportAgent")
end
defp assign_filter_defaults(socket) do
socket
|> assign(:status_filter, nil)
|> assign(:assigned_to_filter, nil)
|> assign(:search_query, nil)
end
defp assign_pagination_defaults(socket) do
per_page = Settings.get_setting("tickets_per_page", "20") |> String.to_integer()
socket
|> assign(:page, 1)
|> assign(:per_page, per_page)
|> assign(:total_pages, 1)
end
defp apply_params(socket, params) do
page = params |> Map.get("page", "1") |> String.to_integer() |> max(1)
status = Map.get(params, "status")
assigned_to = Map.get(params, "assigned_to")
search = Map.get(params, "search")
socket
|> assign(:page, page)
|> assign(:status_filter, status)
|> assign(:assigned_to_filter, assigned_to)
|> assign(:search_query, search)
end
defp load_tickets(socket) do
opts = build_query_opts(socket)
tickets = Tickets.list_tickets(opts)
total_count = count_filtered_tickets(socket)
total_pages = max(1, ceil(total_count / socket.assigns.per_page))
socket
|> assign(:tickets, tickets)
|> assign(:total_count, total_count)
|> assign(:total_pages, total_pages)
end
defp build_query_opts(socket) do
opts = [
page: socket.assigns.page,
per_page: socket.assigns.per_page,
preload: [:user, :assigned_to]
]
opts =
case socket.assigns.status_filter do
nil -> opts
status -> Keyword.put(opts, :status, status)
end
opts =
case socket.assigns.assigned_to_filter do
nil ->
opts
"unassigned" ->
Keyword.put(opts, :assigned_to_id, nil)
handler_id ->
Keyword.put(opts, :assigned_to_id, String.to_integer(handler_id))
end
case socket.assigns.search_query do
nil -> opts
"" -> opts
search -> Keyword.put(opts, :search, search)
end
end
defp count_filtered_tickets(socket) do
# For simplicity, count based on status filter only
case socket.assigns.status_filter do
nil -> Tickets.get_stats().total
status -> Map.get(Tickets.get_stats(), String.to_atom(status), 0)
end
end
defp build_current_params(socket) do
params = %{}
params =
if socket.assigns.status_filter,
do: Map.put(params, "status", socket.assigns.status_filter),
else: params
params =
if socket.assigns.assigned_to_filter,
do: Map.put(params, "assigned_to", socket.assigns.assigned_to_filter),
else: params
if socket.assigns.search_query,
do: Map.put(params, "search", socket.assigns.search_query),
else: params
end
defp map_to_keyword(map) when is_map(map) do
Enum.map(map, fn {k, v} -> {String.to_existing_atom(k), v} end)
end
end