defmodule PhoenixKit.Modules.Billing.Web.UserBillingProfileForm do @moduledoc """ User billing profile form LiveView for creating and editing own billing profiles. """ use PhoenixKitWeb, :live_view alias PhoenixKit.Modules.Billing alias PhoenixKit.Modules.Billing.BillingProfile alias PhoenixKit.Modules.Billing.CountryData 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 manage billing profiles") |> push_navigate(to: Routes.path("/phoenix_kit/users/log-in"))} true -> countries = CountryData.countries_for_select() return_to = params["return_to"] socket = socket |> assign(:user, user) |> assign(:countries, countries) |> assign(:profile_type, "individual") |> assign(:subdivision_label, "Region") |> assign(:return_to, return_to) |> load_profile(params["id"]) {:ok, socket} end end defp load_profile(socket, nil) do # New profile changeset = Billing.change_billing_profile(%BillingProfile{type: "individual"}) socket |> assign(:page_title, "New Billing Profile") |> assign(:profile, nil) |> assign(:form, to_form(changeset)) end defp load_profile(socket, id) do case Billing.get_billing_profile(id) do nil -> socket |> put_flash(:error, "Billing profile not found") |> push_navigate(to: Routes.path("/dashboard/billing-profiles")) profile -> # Verify ownership if profile.user_id != socket.assigns.user.id do socket |> put_flash(:error, "Access denied") |> push_navigate(to: Routes.path("/dashboard/billing-profiles")) else changeset = Billing.change_billing_profile(profile) socket |> assign(:page_title, "Edit Billing Profile") |> assign(:profile, profile) |> assign(:form, to_form(changeset)) |> assign(:profile_type, profile.type) |> assign(:subdivision_label, CountryData.get_subdivision_label(profile.country)) end end end @impl true def handle_event("change_type", %{"type" => type}, socket) do {:noreply, assign(socket, :profile_type, type)} end @impl true def handle_event("validate", %{"billing_profile" => params}, socket) do changeset = (socket.assigns.profile || %BillingProfile{}) |> Billing.change_billing_profile(params) |> Map.put(:action, :validate) # Update subdivision label when country changes subdivision_label = CountryData.get_subdivision_label(params["country"]) {:noreply, socket |> assign(:form, to_form(changeset)) |> assign(:subdivision_label, subdivision_label)} end @impl true def handle_event("save", %{"billing_profile" => params}, socket) do params = params |> Map.put("user_id", socket.assigns.user.id) |> Map.put("type", socket.assigns.profile_type) save_profile(socket, params) end defp save_profile(socket, params) do result = if socket.assigns.profile do Billing.update_billing_profile(socket.assigns.profile, params) else Billing.create_billing_profile(socket.assigns.user.id, params) end case result do {:ok, _profile} -> action = if socket.assigns.profile, do: "updated", else: "created" redirect_path = socket.assigns.return_to || Routes.path("/dashboard/billing-profiles") {:noreply, socket |> put_flash(:info, "Billing profile #{action} successfully") |> push_navigate(to: redirect_path)} {:error, changeset} -> {:noreply, assign(socket, :form, to_form(changeset))} end end @impl true def render(assigns) do ~H"""
<%!-- Header --%>
<.link navigate={@return_to || Routes.path("/dashboard/billing-profiles")} class="btn btn-ghost btn-sm" > <.icon name="hero-arrow-left" class="w-5 h-5" />

{@page_title}

<%= if @profile do %> Update your billing information <% else %> Create a new billing profile for orders <% end %>

<%!-- Profile Type Selection --%>

<.icon name="hero-user-circle" class="w-5 h-5" /> Profile Type

<%!-- Individual Fields --%> <%= if @profile_type == "individual" do %>

<.icon name="hero-user" class="w-5 h-5" /> Personal Information

<.error :for={msg <- @form[:first_name].errors |> Enum.map(&elem(&1, 0))}> {msg}
<.error :for={msg <- @form[:last_name].errors |> Enum.map(&elem(&1, 0))}> {msg}
<% end %> <%!-- Company Fields --%> <%= if @profile_type == "company" do %>

<.icon name="hero-building-office" class="w-5 h-5" /> Company Information

<.error :for={msg <- @form[:company_name].errors |> Enum.map(&elem(&1, 0))}> {msg}
Contact
<% end %> <%!-- Billing Address (Country FIRST) --%>

<.icon name="hero-map-pin" class="w-5 h-5" /> Billing Address

<%!-- Country first --%>
<%!-- Options --%>

<.icon name="hero-cog-6-tooth" class="w-5 h-5" /> Options

<%!-- Actions --%>
<.link navigate={@return_to || Routes.path("/dashboard/billing-profiles")} class="btn btn-ghost" > Cancel
""" 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