Packages

phoenix_kit

1.7.56
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 billing web subscription_form.html.heex
Raw

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 --%>
<.admin_page_header back={PhoenixKit.Utils.Routes.path("/admin/billing/subscriptions")}>
<h1 class="text-xl sm:text-2xl lg:text-3xl font-bold text-base-content">{@page_title}</h1>
<p class="text-sm text-base-content/60 mt-0.5">
Manually create a subscription for a customer
</p>
</.admin_page_header>
<%!-- 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.uuid}</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.uuid}
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.uuid}</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 Type
</h2>
<div class="form-control mt-4">
<label class="label">
<span class="label-text">
Select a subscription type <span class="text-error">*</span>
</span>
</label>
<select
name="subscription_type_uuid"
phx-change="select_subscription_type"
class="select select-bordered w-full"
>
<option value="">Choose a subscription type...</option>
<%= for type <- @subscription_types do %>
<option
value={type.uuid}
selected={to_string(@selected_subscription_type_uuid) == to_string(type.uuid)}
>
{type.name} -
<.currency_compact amount={type.price} currency={type.currency} />
/ {format_interval(type.interval, type.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_uuid"
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.uuid}
selected={to_string(@selected_payment_method_uuid) == to_string(pm.uuid)}
>
{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_subscription_type_uuid)}
>
<.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>
<%!-- Subscription Type --%>
<div class="space-y-1">
<div class="text-sm text-base-content/60">Subscription Type</div>
<%= if @selected_subscription_type_uuid do %>
<% type =
Enum.find(
@subscription_types,
&(to_string(&1.uuid) == to_string(@selected_subscription_type_uuid))
) %>
<%= if type do %>
<div class="font-medium">{type.name}</div>
<div class="text-2xl font-bold mt-1">
<.currency_amount amount={type.price} currency={type.currency} />
</div>
<div class="text-sm text-base-content/60">
{format_interval(type.interval, type.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_uuid 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,
&(to_string(&1.uuid) == to_string(@selected_payment_method_uuid))
) %>
<%= 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>