Packages
phoenix_kit
1.7.44
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/user_billing_profiles.ex
defmodule PhoenixKit.Modules.Billing.Web.UserBillingProfiles do
@moduledoc """
User billing profiles list LiveView.
Allows users to manage their own billing profiles.
"""
use PhoenixKitWeb, :live_view
alias PhoenixKit.Modules.Billing
alias PhoenixKit.Modules.Billing.Events
alias PhoenixKit.Utils.Routes
@impl true
def mount(_params, _session, socket) do
user = get_current_user(socket)
cond do
not Billing.enabled?() ->
{:ok,
socket
|> put_flash(:error, "Billing module is not enabled")
|> push_navigate(to: Routes.path("/dashboard"))}
is_nil(user) ->
{:ok,
socket
|> put_flash(:error, "Please log in to view your billing profiles")
|> push_navigate(to: Routes.path("/phoenix_kit/users/log-in"))}
true ->
# Subscribe to billing profile events for real-time updates
if connected?(socket), do: Events.subscribe_profiles()
profiles = Billing.list_user_billing_profiles(user.id)
socket =
socket
|> assign(:page_title, "My Billing Profiles")
|> assign(:profiles, profiles)
|> assign(:user, user)
{:ok, socket}
end
end
@impl true
def handle_event("set_default", %{"uuid" => uuid}, socket) do
profile = Enum.find(socket.assigns.profiles, &(&1.uuid == uuid))
if profile && profile.user_id == socket.assigns.user.id do
case Billing.set_default_billing_profile(profile) do
{:ok, _profile} ->
profiles = Billing.list_user_billing_profiles(socket.assigns.user.id)
{:noreply,
socket
|> assign(:profiles, profiles)
|> put_flash(:info, "Default profile updated")}
{:error, _} ->
{:noreply, put_flash(socket, :error, "Failed to set default profile")}
end
else
{:noreply, put_flash(socket, :error, "Profile not found")}
end
end
@impl true
def handle_event("delete", %{"uuid" => uuid}, socket) do
profile = Enum.find(socket.assigns.profiles, &(&1.uuid == uuid))
if profile && profile.user_id == socket.assigns.user.id do
case Billing.delete_billing_profile(profile) do
{:ok, _} ->
profiles = Billing.list_user_billing_profiles(socket.assigns.user.id)
{:noreply,
socket
|> assign(:profiles, profiles)
|> put_flash(:info, "Profile deleted successfully")}
{:error, _} ->
{:noreply, put_flash(socket, :error, "Failed to delete profile")}
end
else
{:noreply, put_flash(socket, :error, "Profile not found")}
end
end
# PubSub event handlers for real-time updates
@impl true
def handle_info({event, _profile}, socket)
when event in [:profile_created, :profile_updated, :profile_deleted] do
# Only refresh if we have a user assigned
if socket.assigns[:user] do
profiles = Billing.list_user_billing_profiles(socket.assigns.user.id)
{:noreply, assign(socket, :profiles, profiles)}
else
{:noreply, socket}
end
end
@impl true
def handle_info(_msg, socket) do
{:noreply, socket}
end
@impl true
def render(assigns) do
~H"""
<PhoenixKitWeb.Layouts.dashboard {dashboard_assigns(assigns)}>
<div class="p-6 max-w-4xl mx-auto">
<%!-- Header --%>
<div class="flex flex-col sm:flex-row justify-between items-start sm:items-center mb-8 gap-4">
<div>
<h1 class="text-3xl font-bold">My Billing Profiles</h1>
<p class="text-base-content/60 mt-1">
Manage your billing information for orders and invoices
</p>
</div>
<.link
navigate={Routes.path("/dashboard/billing-profiles/new")}
class="btn btn-primary"
>
<.icon name="hero-plus" class="w-5 h-5 mr-2" /> New Profile
</.link>
</div>
<%!-- Profiles List --%>
<%= if Enum.empty?(@profiles) do %>
<div class="card bg-base-100 shadow-lg">
<div class="card-body text-center py-16">
<.icon name="hero-identification" class="w-16 h-16 mx-auto mb-4 opacity-30" />
<h2 class="text-xl font-medium text-base-content/60">No billing profiles yet</h2>
<p class="text-base-content/50 mb-6">
Create a billing profile to use for your orders
</p>
<.link navigate={Routes.path("/dashboard/billing-profiles/new")} class="btn btn-primary">
Create Your First Profile
</.link>
</div>
</div>
<% else %>
<div class="space-y-4">
<%= for profile <- @profiles do %>
<div class="card bg-base-100 shadow-lg">
<div class="card-body">
<div class="flex flex-col sm:flex-row justify-between gap-4">
<%!-- Profile Info --%>
<div class="flex-1">
<div class="flex items-center gap-3 mb-2">
<span class={"badge #{if profile.type == "company", do: "badge-primary", else: "badge-secondary"}"}>
{String.capitalize(profile.type)}
</span>
<%= if profile.is_default do %>
<span class="badge badge-success">Default</span>
<% end %>
</div>
<%= if profile.type == "company" do %>
<h3 class="text-lg font-semibold">{profile.company_name}</h3>
<%= if profile.company_vat_number do %>
<p class="text-sm text-base-content/60">
VAT: {profile.company_vat_number}
</p>
<% end %>
<% else %>
<h3 class="text-lg font-semibold">
{profile.first_name} {profile.last_name}
</h3>
<%= if profile.email do %>
<p class="text-sm text-base-content/60">{profile.email}</p>
<% end %>
<% end %>
<%= if profile.address_line1 do %>
<p class="text-sm text-base-content/60 mt-2">
{profile.address_line1}
<%= if profile.city do %>
, {profile.city}
<% end %>
<%= if profile.country do %>
, {profile.country}
<% end %>
</p>
<% end %>
</div>
<%!-- Actions --%>
<div class="flex flex-wrap gap-2 sm:flex-col">
<.link
navigate={Routes.path("/dashboard/billing-profiles/#{profile.uuid}/edit")}
class="btn btn-outline btn-sm"
>
<.icon name="hero-pencil" class="w-4 h-4" /> Edit
</.link>
<%= if not profile.is_default do %>
<button
phx-click="set_default"
phx-value-uuid={profile.uuid}
class="btn btn-outline btn-sm"
>
<.icon name="hero-star" class="w-4 h-4" /> Set Default
</button>
<% end %>
<button
phx-click="delete"
phx-value-uuid={profile.uuid}
data-confirm="Are you sure you want to delete this billing profile?"
class="btn btn-outline btn-error btn-sm"
>
<.icon name="hero-trash" class="w-4 h-4" /> Delete
</button>
</div>
</div>
</div>
</div>
<% end %>
</div>
<% end %>
</div>
</PhoenixKitWeb.Layouts.dashboard>
"""
end
# Private helpers
defp get_current_user(socket) do
case socket.assigns[:phoenix_kit_current_scope] do
%{user: %{id: _} = user} -> user
_ -> nil
end
end
end