Packages
phoenix_kit
1.7.23
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_form.html.heex
<PhoenixKitWeb.Components.LayoutWrapper.app_layout
flash={@flash}
phoenix_kit_current_scope={assigns[:phoenix_kit_current_scope]}
page_title="{@project_title} {@page_title}"
current_path={@url_path}
project_title={@project_title}
>
<div class="container mx-auto px-4 py-6">
<%!-- Header --%>
<div class="flex items-center gap-4 mb-6">
<.link
navigate={PhoenixKit.Utils.Routes.path("/admin/billing/subscriptions")}
class="btn btn-ghost btn-sm"
>
<.icon name="hero-arrow-left" class="w-4 h-4" />
</.link>
<div>
<h1 class="text-3xl font-bold">{@page_title}</h1>
<p class="text-base-content/60 mt-1">
Manually create a subscription for a customer
</p>
</div>
</div>
<%!-- Error Alert --%>
<%= if @error do %>
<div class="alert alert-error mb-6">
<.icon name="hero-exclamation-circle" class="w-5 h-5" />
<span>{@error}</span>
<button type="button" phx-click="clear_error" class="btn btn-ghost btn-sm btn-circle">
<.icon name="hero-x-mark" class="w-4 h-4" />
</button>
</div>
<% end %>
<div class="grid grid-cols-1 lg:grid-cols-3 gap-6">
<%!-- Form --%>
<div class="lg:col-span-2 space-y-6">
<%!-- Customer Selection --%>
<div class="card bg-base-100 shadow-xl">
<div class="card-body">
<h2 class="card-title">
<.icon name="hero-user" class="w-5 h-5" /> Customer
</h2>
<%= if @selected_user do %>
<%!-- Selected user display --%>
<div class="flex items-center justify-between p-4 bg-base-200 rounded-lg mt-4">
<div class="flex items-center gap-3">
<.user_avatar user={@selected_user} size="md" />
<div>
<div class="font-medium">{@selected_user.email}</div>
<div class="text-sm text-base-content/60">ID: {@selected_user.id}</div>
</div>
</div>
<button type="button" phx-click="clear_user" class="btn btn-ghost btn-sm">
<.icon name="hero-x-mark" class="w-4 h-4" /> Change
</button>
</div>
<% else %>
<%!-- User search --%>
<div class="form-control mt-4">
<label class="label">
<span class="label-text">
Search by email <span class="text-error">*</span>
</span>
</label>
<div class="relative">
<input
type="text"
value={@user_search}
phx-keyup="search_user"
phx-debounce="300"
name="query"
class="input input-bordered w-full"
placeholder="Enter email address..."
autocomplete="off"
/>
<%= if @user_search != "" && length(@user_results) > 0 do %>
<div class="absolute z-10 w-full mt-1 bg-base-100 border border-base-300 rounded-lg shadow-lg max-h-60 overflow-y-auto">
<%= for user <- @user_results do %>
<button
type="button"
phx-click="select_user"
phx-value-id={user.id}
class="w-full px-4 py-3 text-left hover:bg-base-200 flex items-center gap-3 border-b border-base-200 last:border-b-0"
>
<.user_avatar user={user} size="sm" />
<div>
<div class="font-medium">{user.email}</div>
<div class="text-xs text-base-content/60">ID: {user.id}</div>
</div>
</button>
<% end %>
</div>
<% end %>
<%= if @user_search != "" && length(@user_results) == 0 do %>
<div class="absolute z-10 w-full mt-1 bg-base-100 border border-base-300 rounded-lg shadow-lg p-4 text-center text-base-content/60">
No users found matching "{@user_search}"
</div>
<% end %>
</div>
</div>
<% end %>
</div>
</div>
<%!-- Plan Selection --%>
<div class="card bg-base-100 shadow-xl">
<div class="card-body">
<h2 class="card-title">
<.icon name="hero-squares-2x2" class="w-5 h-5" /> Subscription Plan
</h2>
<div class="form-control mt-4">
<label class="label">
<span class="label-text">Select a plan <span class="text-error">*</span></span>
</label>
<select
name="plan_id"
phx-change="select_plan"
class="select select-bordered w-full"
>
<option value="">Choose a plan...</option>
<%= for plan <- @plans do %>
<option value={plan.id} selected={@selected_plan_id == plan.id}>
{plan.name} -
<.currency_compact amount={plan.price} currency={plan.currency} />
/ {format_interval(plan.interval, plan.interval_count)}
</option>
<% end %>
</select>
</div>
<%!-- Trial Period --%>
<div class="form-control mt-4">
<label class="label cursor-pointer justify-start gap-3">
<input
type="checkbox"
name="enable"
value="true"
checked={@enable_trial}
phx-click="toggle_trial"
phx-value-enable={if @enable_trial, do: "false", else: "true"}
class="checkbox checkbox-primary"
/>
<span class="label-text">Start with trial period</span>
</label>
</div>
<%= if @enable_trial do %>
<div class="form-control mt-2">
<label class="label">
<span class="label-text">Trial days</span>
</label>
<input
type="number"
name="days"
value={@trial_days}
phx-keyup="update_trial_days"
phx-debounce="300"
class="input input-bordered w-32"
min="1"
max="365"
placeholder="14"
/>
</div>
<% end %>
</div>
</div>
<%!-- Payment Method (if user selected) --%>
<%= if @selected_user && length(@payment_methods) > 0 do %>
<div class="card bg-base-100 shadow-xl">
<div class="card-body">
<h2 class="card-title">
<.icon name="hero-credit-card" class="w-5 h-5" /> Payment Method
<span class="badge badge-ghost">Optional</span>
</h2>
<div class="form-control mt-4">
<label class="label">
<span class="label-text">Select a saved payment method</span>
</label>
<select
name="payment_method_id"
phx-change="select_payment_method"
class="select select-bordered w-full"
>
<option value="">No payment method</option>
<%= for pm <- @payment_methods do %>
<option value={pm.id} selected={@selected_payment_method_id == pm.id}>
{format_payment_method(pm)}
<%= if pm.is_default do %>
(Default)
<% end %>
</option>
<% end %>
</select>
<label class="label">
<span class="label-text-alt text-base-content/60">
Payment method is optional for manually created subscriptions
</span>
</label>
</div>
</div>
</div>
<% end %>
<%!-- Actions --%>
<div class="flex justify-end gap-4">
<.link
navigate={PhoenixKit.Utils.Routes.path("/admin/billing/subscriptions")}
class="btn btn-ghost"
>
Cancel
</.link>
<button
type="button"
phx-click="save"
class="btn btn-primary"
disabled={is_nil(@selected_user) || is_nil(@selected_plan_id)}
>
<.icon name="hero-plus" class="w-4 h-4" /> Create Subscription
</button>
</div>
</div>
<%!-- Summary Sidebar --%>
<div class="lg:col-span-1">
<div class="card bg-base-100 shadow-xl sticky top-6">
<div class="card-body">
<h2 class="card-title">Summary</h2>
<div class="divider my-2"></div>
<%!-- Customer --%>
<div class="space-y-1">
<div class="text-sm text-base-content/60">Customer</div>
<%= if @selected_user do %>
<div class="font-medium">{@selected_user.email}</div>
<% else %>
<div class="text-base-content/40">Not selected</div>
<% end %>
</div>
<div class="divider my-2"></div>
<%!-- Plan --%>
<div class="space-y-1">
<div class="text-sm text-base-content/60">Plan</div>
<%= if @selected_plan_id do %>
<% plan = Enum.find(@plans, &(&1.id == @selected_plan_id)) %>
<%= if plan do %>
<div class="font-medium">{plan.name}</div>
<div class="text-2xl font-bold mt-1">
<.currency_amount amount={plan.price} currency={plan.currency} />
</div>
<div class="text-sm text-base-content/60">
{format_interval(plan.interval, plan.interval_count)}
</div>
<% end %>
<% else %>
<div class="text-base-content/40">Not selected</div>
<% end %>
</div>
<%= if @enable_trial && @trial_days != "" do %>
<div class="divider my-2"></div>
<div class="space-y-1">
<div class="text-sm text-base-content/60">Trial Period</div>
<div class="badge badge-info">{@trial_days} days</div>
</div>
<% end %>
<%= if @selected_payment_method_id do %>
<div class="divider my-2"></div>
<div class="space-y-1">
<div class="text-sm text-base-content/60">Payment Method</div>
<% pm = Enum.find(@payment_methods, &(&1.id == @selected_payment_method_id)) %>
<%= if pm do %>
<div class="font-medium">{format_payment_method(pm)}</div>
<% end %>
</div>
<% end %>
<div class="divider my-2"></div>
<%!-- Status Preview --%>
<div class="space-y-1">
<div class="text-sm text-base-content/60">Initial Status</div>
<%= if @enable_trial && @trial_days != "" do %>
<div class="badge badge-info">Trialing</div>
<% else %>
<div class="badge badge-success">Active</div>
<% end %>
</div>
</div>
</div>
</div>
</div>
</div>
</PhoenixKitWeb.Components.LayoutWrapper.app_layout>