reset_password styling & live form working; other updates & fixes

This commit is contained in:
Adam Piontek 2021-03-02 10:35:36 -05:00
parent 4a87fb9baa
commit 24502e2667
17 changed files with 215 additions and 113 deletions

View file

@ -1,6 +1,7 @@
defmodule Bones73kWeb.UserAuth do
import Plug.Conn
import Phoenix.Controller
import Phoenix.HTML
alias Bones73k.Accounts
alias Bones73kWeb.Router.Helpers, as: Routes
@ -28,14 +29,17 @@ defmodule Bones73kWeb.UserAuth do
"""
def log_in_user(conn, user, params \\ %{}) do
token = Accounts.generate_user_session_token(user)
user_return_to = get_session(conn, :user_return_to)
conn
|> renew_session()
|> put_session(:user_token, token)
|> put_session(:live_socket_id, "users_sessions:#{Base.url_encode64(token)}")
|> maybe_write_remember_me_cookie(token, params)
|> redirect(to: user_return_to || signed_in_path(conn))
|> put_flash(
:info,
raw("Welcome back, #{user.email} — you were logged in successfuly.")
)
|> redirect(to: get_session(conn, :user_return_to) || signed_in_path(conn))
end
defp maybe_write_remember_me_cookie(conn, token, %{"remember_me" => "true"}) do

View file

@ -1,9 +1,10 @@
defmodule Bones73kWeb.UserResetPasswordController do
use Bones73kWeb, :controller
import Phoenix.LiveView.Controller
alias Bones73k.Accounts
plug(:get_user_by_reset_password_token when action in [:edit, :update])
plug(:get_user_by_reset_password_token when action in [:edit])
def new(conn, _params) do
render(conn, "new.html")
@ -21,34 +22,20 @@ defmodule Bones73kWeb.UserResetPasswordController do
conn
|> put_flash(
:info,
"If your email is in our system, you will receive instructions to reset your password shortly."
"If your email is in our system, you'll receive instructions to reset your password shortly."
)
|> redirect(to: "/")
end
def edit(conn, _params) do
render(conn, "edit.html", changeset: Accounts.change_user_password(conn.assigns.user))
end
# Do not log in the user after reset password to avoid a
# leaked token giving the user access to the account.
def update(conn, %{"user" => user_params}) do
case Accounts.reset_user_password(conn.assigns.user, user_params) do
{:ok, _} ->
conn
|> put_flash(:info, "Password reset successfully.")
|> redirect(to: Routes.user_session_path(conn, :new))
{:error, changeset} ->
render(conn, "edit.html", changeset: changeset)
end
live_render(conn, Bones73kWeb.UserLive.ResetPassword)
end
defp get_user_by_reset_password_token(conn, _opts) do
%{"token" => token} = conn.params
if user = Accounts.get_user_by_reset_password_token(token) do
conn |> assign(:user, user) |> assign(:token, token)
put_session(conn, "user_id", user.id)
else
conn
|> put_flash(:error, "Reset password link is invalid or it has expired.")

View file

@ -6,7 +6,6 @@ defmodule Bones73kWeb.UserSessionController do
alias Bones73kWeb.UserAuth
def new(conn, _params) do
# IO.inspect(conn.private, label: "session_new conn.private :")
render(conn, "new.html", error_message: nil)
end

View file

@ -20,7 +20,6 @@
class: input_class(f, :email, "form-control"),
placeholder: "e.g., babka@73k.us",
maxlength: User.max_email,
required: true,
autofocus: true,
phx_debounce: "blur",
aria_describedby: error_id(f, :email)
@ -38,7 +37,6 @@
<%= password_input f, :password,
value: input_value(f, :password),
class: input_class(f, :password, "form-control"),
minlength: User.min_password,
maxlength: User.max_password,
required: true,
phx_debounce: "200",

View file

@ -0,0 +1,40 @@
defmodule Bones73kWeb.UserLive.ResetPassword do
use Bones73kWeb, :live_view
alias Bones73k.Accounts
alias Bones73k.Accounts.User
@impl true
def mount(_params, session, socket) do
user = Accounts.get_user!(session["user_id"])
socket
|> assign_defaults(session)
|> assign(page_title: "Reset password")
|> assign(changeset: Accounts.change_user_password(user))
|> assign(user: user)
|> live_okreply()
end
@impl true
def handle_event("validate", %{"user" => user_params}, socket) do
cs = Accounts.change_user_password(socket.assigns.user, user_params)
{:noreply, socket |> assign(changeset: %{cs | action: :update})}
end
def handle_event("save", %{"user" => user_params}, socket) do
case Accounts.reset_user_password(socket.assigns.user, user_params) do
{:ok, _} ->
{:noreply,
socket
|> put_flash(:success, "Password reset successfully.")
|> redirect(to: Routes.user_session_path(socket, :new))}
{:error, changeset} ->
{:noreply,
socket
|> put_flash(:error, "Please check the errors below.")
|> assign(changeset: %{changeset | action: :update})}
end
end
end

View file

@ -0,0 +1,57 @@
<div class="row justify-content-center">
<div class="col-sm-9 col-md-7 col-lg-5 col-xl-4 ">
<h3>
<%= icon_div @socket, "bi-shield-lock", [class: "icon baseline"] %>
Reset password
</h3>
<p class="lead">Hi <%= @user.email %> &mdash; What new word of passage will confirm you are who you say you are?</p>
<%= form_for @changeset, "#", [phx_change: :validate, phx_submit: :save, novalidate: true, id: "pw_reset_form"], fn f -> %>
<div class="mb-3" phx-feedback-for="<%= input_id(f, :password) %>">
<%= label f, :password, "New password", class: "form-label" %>
<div class="input-group has-validation">
<span class="input-group-text">
<%= icon_div @socket, "bi-key", [class: "icon fs-5"] %>
</span>
<%= password_input f, :password,
value: input_value(f, :password),
class: input_class(f, :password, "form-control"),
maxlength: User.max_password,
autofocus: true,
aria_describedby: error_id(f, :password)
%>
<%= error_tag f, :password %>
</div>
</div>
<div class="mb-3" phx-feedback-for="<%= input_id(f, :password_confirmation) %>">
<%= label f, :password_confirmation, "Confirm new password", class: "form-label" %>
<div class="input-group has-validation">
<span class="input-group-text">
<%= icon_div @socket, "bi-key-fill", [class: "icon fs-5"] %>
</span>
<%= password_input f, :password_confirmation,
value: input_value(f, :password_confirmation),
class: input_class(f, :password_confirmation, "form-control"),
maxlength: User.max_password,
aria_describedby: error_id(f, :password_confirmation)
%>
<%= error_tag f, :password_confirmation %>
</div>
</div>
<div class="mb-3">
<%= submit "Reset password", disabled: !@changeset.valid?, class: "btn btn-primary", phx_disable_with: "Saving..." %>
</div>
<% end %>
<p class="mt-3 is-pulled-right">
<%= link "Register", to: Routes.user_registration_path(@socket, :new) %> |
<%= link "Log in", to: Routes.user_session_path(@socket, :new) %>
</p>
</div>
</div>

View file

@ -60,19 +60,12 @@ defmodule Bones73kWeb.Router do
scope "/", Bones73kWeb do
pipe_through([:browser, :redirect_if_user_is_authenticated])
# # liveview user auth routes
# live "/users/reset_password", UserLive.ResetPassword, :new
# live "/users/reset_password/:token", UserLive.ResetPassword, :edit
# original user auth routes from phx.gen.auth
get("/users/register", UserRegistrationController, :new)
get("/users/log_in", UserSessionController, :new)
post("/users/log_in", UserSessionController, :create)
# TODO:
get("/users/reset_password", UserResetPasswordController, :new)
post("/users/reset_password", UserResetPasswordController, :create)
get("/users/reset_password/:token", UserResetPasswordController, :edit)
put("/users/reset_password/:token", UserResetPasswordController, :update)
end
scope "/", Bones73kWeb do

View file

@ -13,6 +13,7 @@
<li><hr class="dropdown-divider"></li>
<li>
<%= link nav_link_opts(@conn, to: Routes.user_settings_path(@conn, :edit), class: "dropdown-item") do %>
<%= icon_div @conn, "bi-sliders", [class: "icon baseline me-1"] %>
Settings
<% end %>
</li>

View file

@ -5,7 +5,7 @@
<%= icon_div @conn, "bi-arrow-repeat", [class: "icon baseline"] %>
Resend confirmation instructions
</h3>
<p class="lead">We'll send you another email with a link to confirm your email address.</p>
<p class="lead">We'll send you another email with instructions to confirm your email address.</p>
<%= form_for :user, Routes.user_confirmation_path(@conn, :create), [class: "needs-validation", novalidate: true], fn f -> %>

View file

@ -1,26 +0,0 @@
<h1>Reset password</h1>
<%= form_for @changeset, Routes.user_reset_password_path(@conn, :update, @token), fn f -> %>
<%= if @changeset.action do %>
<div class="alert alert-danger">
<p>Oops, something went wrong! Please check the errors below.</p>
</div>
<% end %>
<%= label f, :password, "New password" %>
<%= password_input f, :password, required: true %>
<%= error_tag f, :password %>
<%= label f, :password_confirmation, "Confirm new password" %>
<%= password_input f, :password_confirmation, required: true %>
<%= error_tag f, :password_confirmation %>
<div>
<%= submit "Reset password" %>
</div>
<% end %>
<p>
<%= link "Register", to: Routes.user_registration_path(@conn, :new) %> |
<%= link "Log in", to: Routes.user_session_path(@conn, :new) %>
</p>

View file

@ -1,15 +1,38 @@
<h1>Forgot your password?</h1>
<div class="row justify-content-center">
<div class="col-sm-9 col-md-7 col-lg-5 col-xl-4 ">
<%= form_for :user, Routes.user_reset_password_path(@conn, :create), fn f -> %>
<%= label f, :email %>
<%= email_input f, :email, required: true %>
<h3>
<%= icon_div @conn, "mdi-head-question-outline", [class: "icon baseline"] %>
Forgot your password?
</h3>
<p class="lead">We'll send you an email with instructions to reset your password.</p>
<%= form_for :user, Routes.user_reset_password_path(@conn, :create), [class: "needs-validation", novalidate: true], fn f -> %>
<%= label f, :email, class: "form-label" %>
<div class="input-group has-validation mb-3">
<span class="input-group-text">
<%= icon_div @conn, "bi-at", [class: "icon fs-5"] %>
</span>
<%= email_input f, :email,
placeholder: "e.g., babka@73k.us",
class: "form-control",
maxlength: User.max_email,
required: true,
autofocus: true
%>
<span class="invalid-feedback">must be a valid email address</span>
</div>
<div class="mb-3">
<%= submit "Send instructions to reset password", class: "btn btn-primary" %>
</div>
<% end %>
<p>
<%= link "Register", to: Routes.user_registration_path(@conn, :new) %> |
<%= link "Log in", to: Routes.user_session_path(@conn, :new) %>
</p>
<div>
<%= submit "Send instructions to reset password" %>
</div>
<% end %>
<p>
<%= link "Register", to: Routes.user_registration_path(@conn, :new) %> |
<%= link "Log in", to: Routes.user_session_path(@conn, :new) %>
</p>
</div>

View file

@ -2,7 +2,7 @@
<div class="col-sm-9 col-md-7 col-lg-5 col-xl-4 ">
<h3>
<%= icon_div @conn, "bi-box-arrow-in-left", [class: "icon baseline"] %>
<%= icon_div @conn, "bi-door-open", [class: "icon baseline"] %>
Log in
</h3>
<p class="lead">Who goes there?</p>
@ -32,7 +32,7 @@
<%= label f, :password, class: "form-label" %>
<div class="input-group has-validation mb-3">
<span class="input-group-text">
<%= icon_div @conn, "bi-shield-lock", [class: "icon fs-5"] %>
<%= icon_div @conn, "bi-lock", [class: "icon fs-5"] %>
</span>
<%= password_input f, :password,
class: "form-control",

View file

@ -1,3 +1,4 @@
defmodule Bones73kWeb.UserResetPasswordView do
use Bones73kWeb, :view
alias Bones73k.Accounts.User
end