Packages

phoenix_kit

1.7.58
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 billing web subscriptions.ex
Raw

lib/modules/billing/web/subscriptions.ex

defmodule PhoenixKit.Modules.Billing.Web.Subscriptions do
@moduledoc """
Subscriptions list LiveView for the billing module.
Displays all subscriptions with filtering and search capabilities.
"""
use PhoenixKitWeb, :live_view
alias PhoenixKit.Modules.Billing
alias PhoenixKit.Modules.Billing.Events
alias PhoenixKit.Settings
alias PhoenixKit.Utils.Routes
@impl true
def mount(_params, _session, socket) do
if Billing.enabled?() do
if connected?(socket) do
Events.subscribe_subscriptions()
end
project_title = Settings.get_project_title()
socket =
socket
|> assign(:page_title, "Subscriptions")
|> assign(:project_title, project_title)
|> assign(:url_path, Routes.path("/admin/billing/subscriptions"))
|> assign(:status_filter, "all")
|> assign(:search, "")
|> load_subscriptions()
{:ok, socket}
else
{:ok,
socket
|> put_flash(:error, "Billing module is not enabled")
|> push_navigate(to: Routes.path("/admin"))}
end
end
@impl true
def handle_params(params, _url, socket) do
status = params["status"] || "all"
search = params["search"] || ""
socket =
socket
|> assign(:status_filter, status)
|> assign(:search, search)
|> load_subscriptions()
{:noreply, socket}
end
defp load_subscriptions(socket) do
opts =
[preload: [:user, :subscription_type, :payment_method]]
|> add_status_filter(socket.assigns.status_filter)
|> add_search_filter(socket.assigns.search)
subscriptions = Billing.list_subscriptions(opts)
stats = calculate_stats(subscriptions)
socket
|> assign(:subscriptions, subscriptions)
|> assign(:stats, stats)
end
defp add_status_filter(opts, "all"), do: opts
defp add_status_filter(opts, status), do: Keyword.put(opts, :status, status)
defp add_search_filter(opts, ""), do: opts
defp add_search_filter(opts, search), do: Keyword.put(opts, :search, search)
defp calculate_stats(subscriptions) do
%{
total: length(subscriptions),
active: Enum.count(subscriptions, &(&1.status == "active")),
trialing: Enum.count(subscriptions, &(&1.status == "trialing")),
past_due: Enum.count(subscriptions, &(&1.status == "past_due")),
cancelled: Enum.count(subscriptions, &(&1.status == "cancelled"))
}
end
@impl true
def handle_event("filter_status", %{"status" => status}, socket) do
{:noreply,
push_patch(socket,
to:
Routes.path("/admin/billing/subscriptions") <>
build_query_string(status, socket.assigns.search)
)}
end
@impl true
def handle_event("search", %{"search" => search}, socket) do
{:noreply,
push_patch(socket,
to:
Routes.path("/admin/billing/subscriptions") <>
build_query_string(socket.assigns.status_filter, search)
)}
end
@impl true
def handle_event("cancel_subscription", %{"uuid" => uuid}, socket) do
subscription = Enum.find(socket.assigns.subscriptions, &(&1.uuid == uuid))
if subscription do
case Billing.cancel_subscription(subscription, immediately: false) do
{:ok, _subscription} ->
{:noreply,
socket
|> load_subscriptions()
|> put_flash(:info, "Subscription will be cancelled at period end")}
{:error, reason} ->
{:noreply, put_flash(socket, :error, "Failed to cancel: #{inspect(reason)}")}
end
else
{:noreply, put_flash(socket, :error, "Subscription not found")}
end
end
# PubSub event handlers
@impl true
def handle_info({:subscription_created, _subscription}, socket) do
{:noreply, load_subscriptions(socket)}
end
@impl true
def handle_info({:subscription_cancelled, _subscription}, socket) do
{:noreply, load_subscriptions(socket)}
end
@impl true
def handle_info({:subscription_renewed, _subscription}, socket) do
{:noreply, load_subscriptions(socket)}
end
@impl true
def handle_info({:subscription_type_changed, _subscription, _old_type, _new_type}, socket) do
{:noreply, load_subscriptions(socket)}
end
@impl true
def handle_info({:subscription_status_changed, _subscription, _old_status, _new_status}, socket) do
{:noreply, load_subscriptions(socket)}
end
defp build_query_string(status, search) do
params =
[]
|> then(fn p -> if status != "all", do: [{"status", status} | p], else: p end)
|> then(fn p -> if search != "", do: [{"search", search} | p], else: p end)
case params do
[] -> ""
_ -> "?" <> URI.encode_query(params)
end
end
# Helper functions for template
def status_badge_class(status) do
case status do
"active" -> "badge-success"
"trialing" -> "badge-info"
"past_due" -> "badge-warning"
"paused" -> "badge-neutral"
"cancelled" -> "badge-error"
_ -> "badge-ghost"
end
end
def format_interval(interval, interval_count) do
case {interval, interval_count} do
{"month", 1} -> "Monthly"
{"month", n} -> "Every #{n} months"
{"year", 1} -> "Yearly"
{"year", n} -> "Every #{n} years"
{"week", 1} -> "Weekly"
{"week", n} -> "Every #{n} weeks"
{"day", 1} -> "Daily"
{"day", n} -> "Every #{n} days"
_ -> "#{interval_count} #{interval}(s)"
end
end
end