Packages

phoenix_kit

2.41.4
2.63.0 2.62.2 2.62.1 2.62.0 2.61.0 2.60.3 2.60.2 2.60.1 2.60.0 2.59.0 2.58.0 2.57.1 2.57.0 2.56.1 2.56.0 2.55.1 2.55.0 2.54.2 2.54.1 2.54.0 2.53.0 2.52.2 2.52.1 2.52.0 2.51.0 2.50.0 2.49.1 2.49.0 2.48.0 2.47.0 2.46.0 2.45.0 2.44.0 2.43.1 2.43.0 2.42.1 2.42.0 2.41.6 2.41.4 2.41.3 2.41.2 2.41.1 2.41.0 2.40.1 2.40.0 2.39.0 2.38.1 2.38.0 2.37.5 2.37.4 2.37.3 2.37.2 2.37.1 2.37.0 2.36.1 2.36.0 2.35.0 2.34.0 2.33.0 2.32.1 2.32.0 2.31.1 2.31.0 2.30.0 2.29.1 2.29.0 2.28.2 2.28.1 2.28.0 2.27.2 2.27.1 2.27.0 2.26.1 2.26.0 2.25.0 2.24.0 2.23.3 2.23.2 2.23.1 2.23.0 2.22.24 2.22.23 2.22.22 2.22.21 2.22.20 2.22.19 2.22.18 2.22.17 2.22.16 2.22.15 2.22.14 2.22.13 2.22.12 2.22.11 2.22.10 2.22.9 2.22.8 2.22.7 2.22.6 2.22.5 2.22.4 2.22.3 2.22.2 2.22.1 2.22.0 2.21.5 2.21.4 2.21.3 2.21.2 2.21.1 2.21.0 2.20.0 2.19.0 2.18.1 2.18.0 2.17.0 2.16.0 2.15.1 2.15.0 2.14.2 2.14.1 2.14.0 2.13.19 2.13.18 2.13.17 2.13.16 2.13.15 2.13.13 2.13.12 2.13.11 2.13.10 2.13.9 2.13.8 2.13.7 2.13.6 2.13.5 2.13.4 2.13.3 2.13.2 2.13.1 2.13.0 2.12.1 2.12.0 2.11.0 2.10.0 2.9.0 2.8.1 2.8.0 2.7.0 2.6.0 2.5.0 2.4.0 2.3.0 2.2.0 2.1.0 2.0.1 2.0.0 1.7.236 1.7.235 1.7.234 1.7.233 1.7.232 1.7.231 1.7.230 1.7.229 1.7.228 1.7.227 1.7.226 1.7.225 1.7.224 1.7.223 1.7.222 1.7.221 1.7.220 1.7.219 1.7.218 1.7.217 1.7.216 1.7.215 1.7.214 1.7.213 1.7.212 1.7.211 1.7.210 1.7.209 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 phoenix_kit_web users registration.html.heex
Raw

lib/phoenix_kit_web/users/registration.html.heex

<PhoenixKitWeb.Components.AuthPageWrapper.auth_page_wrapper
  flash={@flash}
  phoenix_kit_current_scope={assigns[:phoenix_kit_current_scope]}
  page_title={@page_title}
  current_path={assigns[:url_path] || "/users/register"}
  dev_mailbox_message={gettext("confirmation emails")}
>
  <h1 class="text-2xl font-bold text-center mb-2">{gettext("Create account")}</h1>
  <p class="text-center text-base-content/60 text-sm mb-6">
    {gettext("Join %{project_title} today", project_title: @project_title)}
  </p>

  <%!-- Invitation banner: shown when user arrives via org invitation link --%>
  <%= if @pending_invitation do %>
    <div class="alert alert-info mb-4">
      <.icon name="hero-building-office-2" class="w-5 h-5 shrink-0" />
      <span>
        {gettext(
          "You've been invited to join %{organization}. Create an account to accept the invitation.",
          organization: @pending_invitation.organization.organization_name
        )}
      </span>
    </div>
  <% end %>

  <.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"
  >
    <input :if={@return_to} type="hidden" name="user[return_to]" value={@return_to} />

    <%!-- Silently captures the browser's UTC offset so new accounts start
         with a timezone instead of "Not set" — no UI, just a value the
         TimezoneOffset hook fills in before submit. Never overwrites an
         already-present value (e.g. a re-rendered form after a validation
         error), and users can always change it later in their settings. --%>
    <input
      type="hidden"
      name="user[user_timezone]"
      id="user_timezone_offset"
      phx-hook="TimezoneOffset"
      phx-update="ignore"
      value={Phoenix.HTML.Form.input_value(@form, :user_timezone)}
    />

    <fieldset class="fieldset min-w-0">
      <legend class="fieldset-legend sr-only">{gettext("Account Information")}</legend>

      <div phx-feedback-for="user[email]">
        <label class="label" for="user_email">
          <span class="fieldset-legend">{gettext("Email")}</span>
        </label>
        <.input field={@form[:email]} type="email" autocomplete="email" required>
          <:icon>
            <PhoenixKitWeb.Components.Core.Icons.icon_email class="h-[1em] w-[1em] opacity-50" />
          </:icon>
        </.input>
      </div>

      <%!-- Username Field (optional, controlled by admin setting) --%>
      <%= if @show_username do %>
        <div phx-feedback-for="user[username]">
          <label class="label" for="user_username">
            <span class="fieldset-legend">{gettext("Username")}</span>
          </label>
          <.input field={@form[:username]} type="text" autocomplete="username">
            <:icon>
              <PhoenixKitWeb.Components.Core.Icons.icon_user_profile class="h-[1em] w-[1em] opacity-50" />
            </:icon>
          </.input>
          <div class="text-xs text-base-content/60 mt-1">
            {gettext("Auto-filled from your email — edit it to choose your own")}
          </div>
        </div>
      <% end %>

      <%!-- Referral Code Field (shown when referral codes are enabled) --%>
      <%= if @referral_codes_enabled do %>
        <div phx-feedback-for="referral_code">
          <.label for="referral_code">
            {gettext("Referral Code")}
            <%= if @referral_codes_required do %>
              *
            <% end %>
          </.label>
          <input
            id="referral_code"
            name="referral_code"
            type="text"
            class={[
              "input w-full",
              (@referral_code_error ||
                 (@check_errors && @referral_codes_required &&
                    (is_nil(@referral_code) || @referral_code == ""))) && "input-error"
            ]}
            value={@referral_code || ""}
            required={@referral_codes_required}
          />
          <%= if @referral_code_error do %>
            <.error>{@referral_code_error}</.error>
          <% end %>
        </div>
      <% end %>

      <%!-- Account Type. Rendered only where there is a choice to make: the
           site can fix the type instead (organization accounts off, one of the
           single-type registration modes, or an organization invitation), and
           then the picker is one dead question. Hiding it is cosmetic — the
           type is fixed server-side in `enforce_registration_account_type/2`
           regardless of what the payload carries. --%>
      <%= if @account_type_selectable do %>
        <div phx-feedback-for="user[account_type]">
          <label class="label" for="user_account_type">
            <span class="fieldset-legend">{gettext("Account Type")}</span>
          </label>
          <select
            id="user_account_type"
            name="user[account_type]"
            class="select w-full"
          >
            <option
              value="person"
              selected={Phoenix.HTML.Form.input_value(@form, :account_type) != "organization"}
            >
              {gettext("Personal")}
            </option>
            <option
              value="organization"
              selected={Phoenix.HTML.Form.input_value(@form, :account_type) == "organization"}
            >
              {gettext("Organization")}
            </option>
          </select>
        </div>
      <% end %>

      <%!-- Organization Name — asked whenever the account being created is an
           organization, whether the visitor picked that above or the site fixed
           it for them. --%>
      <%= if Phoenix.HTML.Form.input_value(@form, :account_type) == "organization" do %>
        <div phx-feedback-for="user[organization_name]">
          <label class="label" for="user_organization_name">
            <span class="fieldset-legend">{gettext("Organization Name")}</span>
          </label>
          <.input field={@form[:organization_name]} type="text" required>
            <:icon>
              <.icon name="hero-building-office" class="h-[1em] w-[1em] opacity-50" />
            </:icon>
          </.input>
        </div>
      <% end %>

      <div phx-feedback-for="user[password]">
        <label class="label" for="user_password">
          <span class="fieldset-legend">{gettext("Password")}</span>
        </label>
        <.input field={@form[:password]} type="password" autocomplete="new-password" required>
          <:icon>
            <PhoenixKitWeb.Components.Core.Icons.icon_lock class="h-[1em] w-[1em] opacity-50" />
          </:icon>
        </.input>
      </div>

      <%!-- Checked by default so a new account stays signed in on this device;
           without the persistent cookie the session rides a browser-session
           cookie that phones drop overnight. Users can untick it, and the
           site can change the default (or forbid persistence) in settings. --%>
      <div :if={@remember_me_available} class="fieldset mt-4">
        <.checkbox
          id="user_remember_me"
          name="user[remember_me]"
          checked={@remember_me}
          label={gettext("Keep me logged in")}
        />
      </div>

      <button
        type="submit"
        class="btn btn-primary w-full mt-4 transition-all hover:scale-[1.02] active:scale-[0.98]"
      >
        <PhoenixKitWeb.Components.Core.Icons.icon_user_add class="w-5 h-5 mr-2" /> {gettext(
          "Create account"
        )}
        <span aria-hidden="true">→</span>
      </button>
    </fieldset>
  </.form>

  <%!-- Alternative registration methods --%>
  <%!-- Only show this section if at least one alternative method is available --%>
  <%= if @magic_link_registration_enabled or PhoenixKit.Users.OAuthAvailability.oauth_available?() do %>
    <div class="mt-6">
      <div class="relative">
        <div class="absolute inset-0 flex items-center">
          <div class="w-full border-t border-base-300" />
        </div>
        <div class="relative flex justify-center text-sm">
          <span class="px-2 bg-base-100 text-base-content/60">{gettext("Or continue with")}</span>
        </div>
      </div>

      <div class="mt-4 space-y-2">
        <%!-- Magic Link Registration --%>
        <%= if @magic_link_registration_enabled do %>
          <.link
            navigate={Routes.path("/users/register/magic-link") <> Routes.return_to_query(@return_to)}
            class="btn btn-outline w-full transition-all hover:scale-[1.02] active:scale-[0.98] hover:shadow-lg"
          >
            <PhoenixKitWeb.Components.Core.Icons.icon_email class="w-5 h-5 mr-2" />
            {gettext("Register with Magic Link")}
          </.link>
        <% end %>
        <%!-- OAuth authentication --%>
        <PhoenixKitWeb.Components.OAuthButtons.oauth_buttons
          show_divider={false}
          return_to={@return_to}
        />
      </div>
    </div>
  <% end %>

  <%!-- Login link --%>
  <div class="text-center mt-4 text-sm">
    <span>{gettext("Already have an account?")}</span>
    <.link
      navigate={Routes.path("/users/log-in") <> Routes.return_to_query(@return_to)}
      class="font-semibold text-primary hover:underline"
    >
      {gettext("Sign in")}
    </.link>
  </div>
</PhoenixKitWeb.Components.AuthPageWrapper.auth_page_wrapper>