defmodule PhoenixKitWeb.Users.RegistrationLive do use PhoenixKitWeb, :live_view alias PhoenixKit.Utils.Routes alias PhoenixKit.Admin.Presence alias PhoenixKit.Settings alias PhoenixKit.Users.Auth alias PhoenixKit.Users.Auth.User alias PhoenixKit.ReferralCodes def render(assigns) do ~H"""

{@project_title} Create account

<.form for={@form} id="registration_form" phx-submit="save" phx-change="validate" phx-trigger-action={@trigger_submit} action={Routes.path("/users/log-in?_action=registered")} method="post" >
Account Information
Oops, something went wrong! Please check the errors below.
<.label for="user_email">Email <.input field={@form[:email]} type="email" placeholder="Enter your email address" required />
<%!-- Referral Code Field (shown when referral codes are enabled) --%> <%= if @referral_codes_enabled do %>
<.label for="referral_code">Referral Code<%= if @referral_codes_required do %>*<% end %> <%= if @referral_code_error do %> <.error>{@referral_code_error} <% end %>
<% end %>
<.label for="user_password">Password <.input field={@form[:password]} type="password" placeholder="Choose a secure password" required />
Already have an account? <.link navigate={Routes.path("/users/log-in")} class="font-semibold text-primary hover:underline" > Sign in
Development mode: Check <.link href="/dev/mailbox" class="font-semibold underline">mailbox for confirmation emails
""" end def mount(_params, session, socket) do # Track anonymous visitor session if connected?(socket) do session_id = session["live_socket_id"] || generate_session_id() Presence.track_anonymous(session_id, %{ connected_at: DateTime.utc_now(), ip_address: get_connect_info(socket, :peer_data) |> extract_ip_address(), user_agent: get_connect_info(socket, :user_agent), current_page: Routes.path("/users/register") }) end # Get project title from settings project_title = Settings.get_setting("project_title", "PhoenixKit") # Get referral codes configuration referral_codes_config = ReferralCodes.get_config() changeset = Auth.change_user_registration(%User{}) socket = socket |> assign(trigger_submit: false, check_errors: false) |> assign(project_title: project_title) |> assign(referral_codes_enabled: referral_codes_config.enabled) |> assign(referral_codes_required: referral_codes_config.required) |> assign(referral_code: nil) |> assign(referral_code_error: nil) |> assign_form(changeset) {:ok, socket, temporary_assigns: [form: nil]} end def handle_event("save", %{"user" => user_params} = params, socket) do referral_code = params["referral_code"] # Validate referral code if system is enabled case validate_referral_code(referral_code, socket) do {:ok, validated_code} -> case Auth.register_user(user_params) do {:ok, user} -> # Record referral code usage if provided and valid if validated_code do ReferralCodes.use_code(validated_code.code, user.id) end {:ok, _} = Auth.deliver_user_confirmation_instructions( user, &Routes.path("/users/confirm/#{&1}") ) changeset = Auth.change_user_registration(user) {:noreply, socket |> assign(trigger_submit: true) |> assign_form(changeset)} {:error, %Ecto.Changeset{} = changeset} -> {:noreply, socket |> assign(check_errors: true) |> assign_form(changeset)} end {:error, error_message} -> socket = socket |> assign(referral_code: referral_code) |> assign(referral_code_error: error_message) |> assign(check_errors: true) {:noreply, socket} end end def handle_event("validate", %{"user" => user_params} = params, socket) do referral_code = params["referral_code"] # Validate referral code and update error state case validate_referral_code(referral_code, socket) do {:ok, _} -> socket = socket |> assign(referral_code: referral_code) |> assign(referral_code_error: nil) changeset = Auth.change_user_registration(%User{}, user_params) {:noreply, assign_form(socket, Map.put(changeset, :action, :validate))} {:error, error_message} -> socket = socket |> assign(referral_code: referral_code) |> assign(referral_code_error: error_message) changeset = Auth.change_user_registration(%User{}, user_params) {:noreply, assign_form(socket, Map.put(changeset, :action, :validate))} end end defp assign_form(socket, %Ecto.Changeset{} = changeset) do form = to_form(changeset, as: "user") if changeset.valid? do assign(socket, form: form, check_errors: false) else assign(socket, form: form) end end defp validate_referral_code(referral_code, socket) do cond do # If referral codes are disabled, always allow registration not socket.assigns.referral_codes_enabled -> {:ok, nil} # If referral codes are required but none provided socket.assigns.referral_codes_required and (is_nil(referral_code) or String.trim(referral_code) == "") -> {:error, "Referral code is required"} # If referral code is provided, validate it referral_code && String.trim(referral_code) != "" -> validate_referral_code_value(String.trim(referral_code)) # If referral codes are optional and none provided true -> {:ok, nil} end end defp validate_referral_code_value(code_string) do case ReferralCodes.get_code_by_string(code_string) do nil -> {:error, "Invalid referral code"} code -> cond do not code.status -> {:error, "This referral code is no longer active"} PhoenixKit.ReferralCodes.expired?(code) -> {:error, "This referral code has expired"} PhoenixKit.ReferralCodes.usage_limit_reached?(code) -> {:error, "This referral code has reached its usage limit"} true -> {:ok, code} end end end defp show_dev_notice? do case Application.get_env(:phoenix_kit, PhoenixKit.Mailer)[:adapter] do Swoosh.Adapters.Local -> true _ -> false end end defp generate_session_id do :crypto.strong_rand_bytes(16) |> Base.encode64() end defp extract_ip_address(nil), do: "unknown" defp extract_ip_address(%{address: {a, b, c, d}}), do: "#{a}.#{b}.#{c}.#{d}" defp extract_ip_address(%{address: address}), do: to_string(address) defp extract_ip_address(_), do: "unknown" end