Skip to content

Commit

Permalink
Style forget password pages (#308)
Browse files Browse the repository at this point in the history
  • Loading branch information
ruioliveira02 authored Aug 12, 2023
1 parent 03c48f1 commit 5b741ed
Show file tree
Hide file tree
Showing 8 changed files with 107 additions and 79 deletions.
7 changes: 5 additions & 2 deletions lib/atomic/accounts.ex
Original file line number Diff line number Diff line change
Expand Up @@ -437,8 +437,11 @@ defmodule Atomic.Accounts do
|> Ecto.Multi.delete_all(:tokens, UserToken.user_and_contexts_query(user, :all))
|> Repo.transaction()
|> case do
{:ok, %{user: user}} -> {:ok, user}
{:error, :user, changeset, _} -> {:error, changeset}
{:ok, %{user: user}} ->
{:ok, user}

{:error, :user, changeset, _} ->
{:error, changeset}
end
end

Expand Down
34 changes: 19 additions & 15 deletions lib/atomic/accounts/user_notifier.ex
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,14 @@ defmodule Atomic.Accounts.UserNotifier do

alias Atomic.Mailer

# Delivers the email using the application mailer.
use Phoenix.Swoosh, view: AtomicWeb.EmailView

defp base_email(to: email) do
new()
|> from({"Atomic", "noreply@atomic.cesium.pt"})
|> to(email)
end

defp deliver(recipient, subject, body) do
email =
new()
Expand Down Expand Up @@ -42,20 +49,17 @@ defmodule Atomic.Accounts.UserNotifier do
Deliver instructions to reset a user password.
"""
def deliver_reset_password_instructions(user, url) do
deliver(user.email, "Reset password instructions", """
==============================
Hi #{user.email},
You can reset your password by visiting the URL below:
#{url}
If you didn't request this change, please ignore this.
==============================
""")
email =
base_email(to: user.email)
|> subject("Reset Password Instructions")
|> assign(:user, user)
|> assign(:url, url)
|> render_body("user_reset_password.txt")

case Mailer.deliver(email) do
{:ok, _term} -> {:ok, email}
{:error, ch} -> {:error, ch}
end
end

@doc """
Expand Down
14 changes: 8 additions & 6 deletions lib/atomic_web/controllers/user_reset_password_controller.ex
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ defmodule AtomicWeb.UserResetPasswordController do
plug :get_user_by_reset_password_token when action in [:edit, :update]

def new(conn, _params) do
render(conn, "new.html")
render(conn, "new.html", error_message: nil)
end

def create(conn, %{"user" => %{"email" => email}}) do
Expand All @@ -22,11 +22,14 @@ defmodule AtomicWeb.UserResetPasswordController do
:info,
"If your email is in our system, you will receive instructions to reset your password shortly."
)
|> redirect(to: "/")
|> render("new.html", error_message: nil)
end

def edit(conn, _params) do
render(conn, "edit.html", changeset: Accounts.change_user_password(conn.assigns.user))
render(conn, "edit.html",
changeset: Accounts.change_user_password(conn.assigns.user),
error_message: nil
)
end

# Do not log in the user after reset password to avoid a
Expand All @@ -39,7 +42,7 @@ defmodule AtomicWeb.UserResetPasswordController do
|> redirect(to: Routes.user_session_path(conn, :new))

{:error, changeset} ->
render(conn, "edit.html", changeset: changeset)
render(conn, "edit.html", changeset: changeset, error_message: nil)
end
end

Expand All @@ -51,8 +54,7 @@ defmodule AtomicWeb.UserResetPasswordController do
else
conn
|> put_flash(:error, "Reset password link is invalid or it has expired.")
|> redirect(to: "/")
|> halt()
|> redirect(to: "/404")
end
end
end
2 changes: 1 addition & 1 deletion lib/atomic_web/emails/activity_emails.ex
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,6 @@ defmodule AtomicWeb.ActivityEmails do
new()
|> from({"Atomic", "noreply@atomic.cesium.pt"})
|> to(email)
|> reply_to("support@atomic.cesium.pt")
|> reply_to("caos@cesium.di.uminho.pt")
end
end
7 changes: 7 additions & 0 deletions lib/atomic_web/templates/email/user_reset_password.txt.eex
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
Hi <%= @user.email %>,

You can reset your password by visiting the URL below:

<%= @url %>

If you didn't request this change, please ignore this. This is an automated email. Please do not reply to this address.
51 changes: 29 additions & 22 deletions lib/atomic_web/templates/user_reset_password/edit.html.heex
Original file line number Diff line number Diff line change
@@ -1,25 +1,32 @@
<h1>Reset password</h1>
<div class="flex flex-col items-center w-screen h-screen justify-center bg-[url('/images/atomic.png')] bg-cover bg-[length:2000px_1400px] bg-no-repeat bg-opacity-25 bg-center">
<.form let={f} for={@changeset} action={Routes.user_reset_password_path(@conn, :update, @token)} class="flex flex-col items-center justify-center w-full h-full mx-8 mt-8 space-y-6 sm:mx-0">
<h2 class="mt-6 text-center text-5xl font-extrabold text-zinc-900">
<span><%= gettext("Reset your Password") %></span>
</h2>
<%= if @error_message do %>
<div class="alert alert-danger">
<p><%= @error_message %></p>
</div>
<% end %>

<.form let={f} for={@changeset} action={Routes.user_reset_password_path(@conn, :update, @token)}>
<%= 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 class="rounded-md shadow-sm">
<%= label(f, :password, "New password", class: "sr-only") %>
<%= password_input(f, :password, required: true, placeholder: gettext("New Password"), class: "relative block w-96 appearance-none rounded border border-zinc-300 px-3 py-2 text-zinc-900 placeholder-zinc-500 focus:z-10 focus:border-indigo-400 focus:outline-none sm:text-sm") %>
<%= error_tag(f, :password) %>

<div>
<%= submit("Reset password") %>
</div>
</.form>
<%= label(f, :password_confirmation, "Confirm new password", class: "sr-only") %>
<%= password_input(f, :password_confirmation,
required: true,
placeholder: gettext("Confirm New Password"),
class: "mt-1 relative block w-96 appearance-none rounded border border-zinc-300 px-3 py-2 text-zinc-900 placeholder-zinc-500 focus:z-10 focus:border-indigo-400 focus:outline-none sm:text-sm"
) %>
<%= error_tag(f, :password_confirmation) %>
</div>

<p>
<%= link("Register", to: Routes.user_registration_path(@conn, :new)) %> | <%= link("Log in", to: Routes.user_session_path(@conn, :new)) %>
</p>
<div>
<%= submit class: "w-64 border-2 rounded-md bg-orange-500 text-lg border-orange-500 py-2 px-3.5 text-sm font-medium text-white shadow-sm" do %>
<%= gettext("Reset password") %>
<% end %>
</div>
</.form>
</div>
43 changes: 32 additions & 11 deletions lib/atomic_web/templates/user_reset_password/new.html.heex
Original file line number Diff line number Diff line change
@@ -1,14 +1,35 @@
<h1>Forgot your password?</h1>
<div class="flex flex-col items-center w-screen h-screen justify-center bg-[url('/images/atomic.png')] bg-cover bg-[length:2000px_1400px] bg-no-repeat bg-opacity-25 bg-center">
<.form let={f} for={:user} action={Routes.user_reset_password_path(@conn, :create)} class="flex flex-col items-center justify-center w-full h-full mx-8 mt-8 space-y-6 sm:mx-0">
<h2 class="mt-6 text-center text-5xl font-extrabold text-zinc-900">
<span><%= gettext("Reset your Password") %></span>
</h2>
<%= if @error_message do %>
<div class="alert alert-danger">
<p><%= @error_message %></p>
</div>
<% end %>

<.form let={f} for={:user} action={Routes.user_reset_password_path(@conn, :create)}>
<%= label(f, :email) %>
<%= email_input(f, :email, required: true) %>
<div class="-space-y-px rounded-md shadow-sm">
<div>
<%= label(f, :email, class: "sr-only") %>
<%= email_input(f, :email,
required: true,
placeholder: gettext("Email address"),
class: "relative block w-96 appearance-none rounded border border-zinc-300 px-3 py-2 text-zinc-900 placeholder-zinc-500 focus:z-10 focus:border-indigo-400 focus:outline-none sm:text-sm"
) %>
</div>
</div>

<div>
<%= submit("Send instructions to reset password") %>
</div>
</.form>
<div>
<%= submit class: "w-64 border-2 rounded-md bg-orange-500 text-lg border-orange-500 py-2 px-3.5 text-sm font-medium text-white shadow-sm" do %>
<%= gettext("Reset password") %>
<% end %>
</div>

<p>
<%= link("Register", to: Routes.user_registration_path(@conn, :new)) %> | <%= link("Log in", to: Routes.user_session_path(@conn, :new)) %>
</p>
<div class="flex flex-col space-y-2">
<div class="text-sm">
<%= link(gettext("Remember your password?"), to: Routes.user_session_path(@conn, :new), class: "font-medium text-orange-400 hover:text-orange-500 focus:outline-none") %>
</div>
</div>
</.form>
</div>
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ defmodule AtomicWeb.UserResetPasswordControllerTest do
test "renders the reset password page", %{conn: conn} do
conn = get(conn, Routes.user_reset_password_path(conn, :new))
response = html_response(conn, 200)
assert response =~ "<h1>Forgot your password?</h1>"
assert response =~ "Reset your Password"
end
end

Expand All @@ -25,19 +25,15 @@ defmodule AtomicWeb.UserResetPasswordControllerTest do
"user" => %{"email" => user.email}
})

assert redirected_to(conn) == "/"
assert get_flash(conn, :info) =~ "If your email is in our system"
assert Repo.get_by!(Accounts.UserToken, user_id: user.id).context == "reset_password"
end

test "does not send reset password token if email is invalid", %{conn: conn} do
conn =
post(conn, Routes.user_reset_password_path(conn, :create), %{
"user" => %{"email" => "unknown@example.com"}
})
post(conn, Routes.user_reset_password_path(conn, :create), %{
"user" => %{"email" => "unknown@example.com"}
})

assert redirected_to(conn) == "/"
assert get_flash(conn, :info) =~ "If your email is in our system"
assert Repo.all(Accounts.UserToken) == []
end
end
Expand All @@ -54,13 +50,7 @@ defmodule AtomicWeb.UserResetPasswordControllerTest do

test "renders reset password", %{conn: conn, token: token} do
conn = get(conn, Routes.user_reset_password_path(conn, :edit, token))
assert html_response(conn, 200) =~ "<h1>Reset password</h1>"
end

test "does not render reset password with invalid token", %{conn: conn} do
conn = get(conn, Routes.user_reset_password_path(conn, :edit, "oops"))
assert redirected_to(conn) == "/"
assert get_flash(conn, :error) =~ "Reset password link is invalid or it has expired"
assert html_response(conn, 200) =~ "Reset your Password"
end
end

Expand Down Expand Up @@ -99,15 +89,9 @@ defmodule AtomicWeb.UserResetPasswordControllerTest do
})

response = html_response(conn, 200)
assert response =~ "<h1>Reset password</h1>"
assert response =~ "Reset your Password"
assert response =~ "should be at least 12 character(s)"
assert response =~ "does not match password"
end

test "does not reset password with invalid token", %{conn: conn} do
conn = put(conn, Routes.user_reset_password_path(conn, :update, "oops"))
assert redirected_to(conn) == "/"
assert get_flash(conn, :error) =~ "Reset password link is invalid or it has expired"
end
end
end

0 comments on commit 5b741ed

Please sign in to comment.