Packages
phoenix_kit
1.7.21
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
Current section
Files
lib/modules/billing/web/subscription_detail.ex
defmodule PhoenixKit.Modules.Billing.Web.SubscriptionDetail do
@moduledoc """
Subscription detail LiveView for the billing module.
Displays complete subscription information and provides management actions.
"""
use PhoenixKitWeb, :live_view
alias PhoenixKit.Modules.Billing
alias PhoenixKit.Modules.Billing.Subscription
alias PhoenixKit.Settings
alias PhoenixKit.Utils.Routes
@impl true
def mount(%{"id" => id}, _session, socket) do
if Billing.enabled?() do
case Billing.get_subscription(id, preload: [:user, :plan, :payment_method]) do
nil ->
{:ok,
socket
|> put_flash(:error, "Subscription not found")
|> push_navigate(to: Routes.path("/admin/billing/subscriptions"))}
subscription ->
project_title = Settings.get_setting("project_title", "PhoenixKit")
plans = Billing.list_subscription_plans(active_only: true)
socket =
socket
|> assign(:page_title, "Subscription ##{subscription.id}")
|> assign(:project_title, project_title)
|> assign(:url_path, Routes.path("/admin/billing/subscriptions/#{subscription.id}"))
|> assign(:subscription, subscription)
|> assign(:plans, plans)
|> assign(:show_change_plan_modal, false)
|> assign(:selected_new_plan_id, nil)
{:ok, socket}
end
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
{:noreply, socket}
end
@impl true
def handle_event("cancel_now", _params, socket) do
case Billing.cancel_subscription(socket.assigns.subscription, immediately: true) do
{:ok, subscription} ->
{:noreply,
socket
|> assign(:subscription, reload_subscription(subscription.id))
|> put_flash(:info, "Subscription cancelled immediately")}
{:error, reason} ->
{:noreply, put_flash(socket, :error, "Failed to cancel: #{inspect(reason)}")}
end
end
@impl true
def handle_event("cancel_at_period_end", _params, socket) do
case Billing.cancel_subscription(socket.assigns.subscription, immediately: false) do
{:ok, subscription} ->
{:noreply,
socket
|> assign(:subscription, reload_subscription(subscription.id))
|> put_flash(:info, "Subscription will cancel at period end")}
{:error, reason} ->
{:noreply, put_flash(socket, :error, "Failed to cancel: #{inspect(reason)}")}
end
end
@impl true
def handle_event("resume", _params, socket) do
case Billing.resume_subscription(socket.assigns.subscription) do
{:ok, subscription} ->
{:noreply,
socket
|> assign(:subscription, reload_subscription(subscription.id))
|> put_flash(:info, "Subscription resumed")}
{:error, reason} ->
{:noreply, put_flash(socket, :error, "Failed to resume: #{inspect(reason)}")}
end
end
@impl true
def handle_event("pause", _params, socket) do
case Billing.pause_subscription(socket.assigns.subscription) do
{:ok, subscription} ->
{:noreply,
socket
|> assign(:subscription, reload_subscription(subscription.id))
|> put_flash(:info, "Subscription paused")}
{:error, reason} ->
{:noreply, put_flash(socket, :error, "Failed to pause: #{inspect(reason)}")}
end
end
@impl true
def handle_event("open_change_plan_modal", _params, socket) do
{:noreply,
socket
|> assign(:show_change_plan_modal, true)
|> assign(:selected_new_plan_id, nil)}
end
@impl true
def handle_event("close_change_plan_modal", _params, socket) do
{:noreply, assign(socket, :show_change_plan_modal, false)}
end
@impl true
def handle_event("select_new_plan", %{"plan_id" => plan_id}, socket) do
plan_id = if plan_id == "", do: nil, else: String.to_integer(plan_id)
{:noreply, assign(socket, :selected_new_plan_id, plan_id)}
end
@impl true
def handle_event("change_plan", _params, socket) do
%{subscription: subscription, selected_new_plan_id: new_plan_id} = socket.assigns
if new_plan_id && new_plan_id != subscription.plan_id do
case Billing.change_subscription_plan(subscription, new_plan_id) do
{:ok, updated_subscription} ->
{:noreply,
socket
|> assign(:subscription, reload_subscription(updated_subscription.id))
|> assign(:show_change_plan_modal, false)
|> put_flash(:info, "Subscription plan changed successfully")}
{:error, reason} ->
{:noreply, put_flash(socket, :error, "Failed to change plan: #{inspect(reason)}")}
end
else
{:noreply, put_flash(socket, :error, "Please select a different plan")}
end
end
defp reload_subscription(id) do
Billing.get_subscription(id, preload: [:user, :plan, :payment_method])
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(nil, _), do: "-"
def format_interval(_, nil), do: "-"
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
def days_until_renewal(%Subscription{current_period_end: nil}), do: nil
def days_until_renewal(%Subscription{current_period_end: period_end}) do
Date.diff(DateTime.to_date(period_end), Date.utc_today())
end
def grace_period_remaining(%Subscription{grace_period_end: nil}), do: nil
def grace_period_remaining(%Subscription{grace_period_end: grace_end}) do
Date.diff(DateTime.to_date(grace_end), Date.utc_today())
end
end