add forms for user auth

This commit is contained in:
sloane 2025-03-24 07:04:54 -04:00
parent 7bd63caae7
commit 69db46715f
Signed by: sloanelybutsurely
SSH key fingerprint: SHA256:8SBnwhl+RY3oEyQxy1a9wByPzxWM0x+/Ejc+sIlY5qQ
3 changed files with 146 additions and 93 deletions

View file

@ -3,4 +3,99 @@ defmodule Web.CoreComponents do
Provides core UI components.
"""
use Phoenix.Component
alias Phoenix.HTML.FormField
attr :id, :any, default: nil
attr :name, :any
attr :label, :string, default: nil
attr :value, :any
attr :type, :string, default: "text", values: ~w[text password]
attr :field, FormField
attr :errors, :list, default: []
attr :rest, :global, include: ~w[disabled form pattern placeholder readonly required]
def input(%{field: %FormField{} = field} = assigns) do
errors =
if Phoenix.Component.used_input?(field) do
field.errors
else
[]
end
assigns
|> assign(field: nil, id: assigns.id || field.id, errors: Enum.map(errors, &translate_error/1))
|> assign_new(:name, fn -> field.name end)
|> assign_new(:value, fn -> field.value end)
|> input()
end
def input(assigns) do
~H"""
<div>
<.label for={@id}>{@label}</.label>
<input
id={@id}
type={@type}
name={@name}
value={Phoenix.HTML.Form.normalize_value(@type, @value)}
{@rest}
/>
<.error :for={error <- @errors}>{error}</.error>
</div>
"""
end
attr :for, :string, default: nil
slot :inner_block, required: true
def label(assigns) do
~H"""
<label for={@for}>
{render_slot(@inner_block)}
</label>
"""
end
slot :inner_block, required: true
def error(assigns) do
~H"""
<p>
<.icon name="hero-exclamation-circle-mini" class="h-5 w-5 flex-none" />
{render_slot(@inner_block)}
</p>
"""
end
attr :name, :string, required: true
attr :class, :string, default: nil
def icon(%{name: "hero-" <> _} = assigns) do
~H"""
<span class={[@name, @class]} />
"""
end
def translate_error({msg, opts}) do
Enum.reduce(opts, msg, fn {key, value}, acc ->
String.replace(acc, "%{#{key}}", fn _ -> to_string(value) end)
end)
end
def translate_errors(errors, field) when is_list(errors) do
for {^field, {msg, opts}} <- errors, do: translate_error({msg, opts})
end
attr :type, :string, default: "button", values: ~w[button submit]
attr :rest, :global
slot :inner_block, required: true
def button(assigns) do
~H"""
<button type={@type} {@rest}>
{render_slot(@inner_block)}
</button>
"""
end
end

View file

@ -2,46 +2,24 @@ defmodule Web.UserLoginLive do
@moduledoc false
use Web, :live_view
def mount(_params, _session, socket) do
form = to_form(%{}, as: "user")
{:ok, assign(socket, form: form), temporary_assigns: [form: form], layout: false}
end
def render(assigns) do
# ~H"""
# <div class="mx-auto max-w-sm">
# <.header class="text-center">
# Log in to account
# <:subtitle>
# Don't have an account?
# <.link navigate={~p/admin/users/register"} class="font-semibold text-brand hover:underline">
# Sign up
# </.link>
# for an account now.
# </:subtitle>
# </.header>
# <.simple_form for={@form} id="login_form" action={~p/admin/users/log_in"} phx-update="ignore">
# <.input field={@form[:email]} type="email" label="Email" required />
# <.input field={@form[:password]} type="password" label="Password" required />
# <:actions>
# <.input field={@form[:remember_me]} type="checkbox" label="Keep me logged in" />
# <.link href={~p/admin/users/reset_password"} class="text-sm font-semibold">
# Forgot your password?
# </.link>
# </:actions>
# <:actions>
# <.button phx-disable-with="Logging in..." class="w-full">
# Log in <span aria-hidden="true">→</span>
# </.button>
# </:actions>
# </.simple_form>
# </div>
# """
~H"""
<pre>UserLoginLive</pre>
<div class="mx-auto max-w-sm">
<header>sign in</header>
<.form for={@form} id="login_form" action={~p"/admin/users/log_in"} phx-update="ignore">
<.input field={@form[:username]} type="text" label="username" required />
<.input field={@form[:password]} type="password" label="password" required />
<.button phx-disable-with="signing in..." class="w-full" type="submit">
sign in
</.button>
</.form>
</div>
"""
end
def mount(_params, _session, socket) do
email = Phoenix.Flash.get(socket.assigns.flash, :email)
form = to_form(%{"email" => email}, as: "user")
{:ok, assign(socket, form: form), temporary_assigns: [form: form]}
end
end

View file

@ -4,67 +4,29 @@ defmodule Web.UserRegistrationLive do
alias Core.Accounts
def render(assigns) do
# ~H"""
# <div class="mx-auto max-w-sm">
# <.header class="text-center">
# Register for an account
# <:subtitle>
# Already registered?
# <.link navigate={~p/admin/users/log_in"} class="font-semibold text-brand hover:underline">
# Log in
# </.link>
# to your account now.
# </:subtitle>
# </.header>
# <.simple_form
# for={@form}
# id="registration_form"
# phx-submit="save"
# phx-change="validate"
# phx-trigger-action={@trigger_submit}
# action={~p/admin/users/log_in?_action=registered"}
# method="post"
# >
# <.error :if={@check_errors}>
# Oops, something went wrong! Please check the errors below.
# </.error>
# <.input field={@form[:email]} type="email" label="Email" required />
# <.input field={@form[:password]} type="password" label="Password" required />
# <:actions>
# <.button phx-disable-with="Creating account..." class="w-full">Create an account</.button>
# </:actions>
# </.simple_form>
# </div>
# """
~H"""
<pre>UserRegistrationLive</pre>
"""
end
def mount(_params, _session, socket) do
changeset = Accounts.change_user_registration(%Schema.User{})
socket =
socket
|> assign(trigger_submit: false, check_errors: false)
|> assign(trigger_submit: false)
|> assign_form(changeset)
{:ok, socket, temporary_assigns: [form: nil]}
{:ok, socket, temporary_assigns: [form: nil], layout: false}
end
def handle_event("save", %{"user" => user_params}, socket) do
case Accounts.register_user(user_params) do
{:ok, user} ->
changeset = Accounts.change_user_registration(user)
{:noreply, socket |> assign(trigger_submit: true) |> assign_form(changeset)}
socket =
case Accounts.register_user(user_params) do
{:ok, user} ->
changeset = Accounts.change_user_registration(user)
socket |> assign(trigger_submit: true) |> assign_form(changeset)
{:error, %Ecto.Changeset{} = changeset} ->
{:noreply, socket |> assign(check_errors: true) |> assign_form(changeset)}
end
{:error, %Ecto.Changeset{} = changeset} ->
assign_form(socket, changeset)
end
{:noreply, socket}
end
def handle_event("validate", %{"user" => user_params}, socket) do
@ -72,13 +34,31 @@ defmodule Web.UserRegistrationLive do
{:noreply, assign_form(socket, Map.put(changeset, :action, :validate))}
end
def render(assigns) do
~H"""
<div class="mx-auto max-w-sm">
<header class="text-center">finish installation</header>
<.form
for={@form}
id="registration_form"
phx-submit="save"
phx-change="validate"
phx-trigger-action={@trigger_submit}
action={~p"/admin/users/log_in?_action=registered"}
method="post"
>
<.input field={@form[:username]} type="text" label="username" />
<.input field={@form[:password]} type="password" label="password" />
<.button type="submit">create administrator</.button>
</.form>
</div>
"""
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
assign(socket, form: form)
end
end