diff --git a/lib/bones73k/accounts.ex b/lib/bones73k/accounts.ex
index 6e2d9a1d..d0914c0f 100644
--- a/lib/bones73k/accounts.ex
+++ b/lib/bones73k/accounts.ex
@@ -172,10 +172,10 @@ defmodule Bones73k.Accounts do
       {:error, %Ecto.Changeset{}}
 
   """
-  def apply_user_email(user, password, attrs) do
+  def apply_user_email(user, %{"current_password" => curr_pw} = attrs) do
     user
     |> User.email_changeset(attrs)
-    |> User.validate_current_password(password)
+    |> User.validate_current_password(curr_pw)
     |> Ecto.Changeset.apply_action(:update)
   end
 
@@ -247,11 +247,11 @@ defmodule Bones73k.Accounts do
       {:error, %Ecto.Changeset{}}
 
   """
-  def update_user_password(user, password, attrs) do
+  def update_user_password(user, %{"current_password" => curr_pw} = attrs) do
     changeset =
       user
       |> User.password_changeset(attrs)
-      |> User.validate_current_password(password)
+      |> User.validate_current_password(curr_pw)
 
     Ecto.Multi.new()
     |> Ecto.Multi.update(:user, changeset)
diff --git a/lib/bones73k_web/controllers/user_auth.ex b/lib/bones73k_web/controllers/user_auth.ex
index c7c280e1..30fbd50a 100644
--- a/lib/bones73k_web/controllers/user_auth.ex
+++ b/lib/bones73k_web/controllers/user_auth.ex
@@ -35,10 +35,6 @@ defmodule Bones73kWeb.UserAuth do
     |> put_session(:user_token, token)
     |> put_session(:live_socket_id, "users_sessions:#{Base.url_encode64(token)}")
     |> maybe_write_remember_me_cookie(token, params)
-    |> 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
 
diff --git a/lib/bones73k_web/controllers/user_session_controller.ex b/lib/bones73k_web/controllers/user_session_controller.ex
index ecc84365..546943a4 100644
--- a/lib/bones73k_web/controllers/user_session_controller.ex
+++ b/lib/bones73k_web/controllers/user_session_controller.ex
@@ -1,6 +1,7 @@
 defmodule Bones73kWeb.UserSessionController do
   use Bones73kWeb, :controller
 
+  alias Phoenix.HTML
   alias Bones73k.Accounts
   alias Bones73k.Accounts.User
   alias Bones73kWeb.UserAuth
@@ -11,7 +12,12 @@ defmodule Bones73kWeb.UserSessionController do
 
   def create(conn, %{"user" => %{"email" => email, "password" => password} = user_params}) do
     if user = Accounts.get_user_by_email_and_password(email, password) do
-      UserAuth.log_in_user(conn, user, user_params)
+      conn
+      |> put_flash(
+        :info,
+        HTML.raw("Welcome back, #{user.email} — you were logged in successfuly.")
+      )
+      |> UserAuth.log_in_user(user, user_params)
     else
       render(conn, "new.html", error_message: "Invalid email or password")
     end
diff --git a/lib/bones73k_web/controllers/user_settings_controller.ex b/lib/bones73k_web/controllers/user_settings_controller.ex
index a2e9b7eb..4aefacc0 100644
--- a/lib/bones73k_web/controllers/user_settings_controller.ex
+++ b/lib/bones73k_web/controllers/user_settings_controller.ex
@@ -2,36 +2,6 @@ defmodule Bones73kWeb.UserSettingsController do
   use Bones73kWeb, :controller
 
   alias Bones73k.Accounts
-  alias Bones73kWeb.UserAuth
-
-  plug(:assign_email_and_password_changesets)
-
-  def edit(conn, _params) do
-    render(conn, "edit.html")
-  end
-
-  def update_email(conn, %{"current_password" => password, "user" => user_params}) do
-    user = conn.assigns.current_user
-
-    case Accounts.apply_user_email(user, password, user_params) do
-      {:ok, applied_user} ->
-        Accounts.deliver_update_email_instructions(
-          applied_user,
-          user.email,
-          &Routes.user_settings_url(conn, :confirm_email, &1)
-        )
-
-        conn
-        |> put_flash(
-          :info,
-          "A link to confirm your email change has been sent to the new address."
-        )
-        |> redirect(to: Routes.user_settings_path(conn, :edit))
-
-      {:error, changeset} ->
-        render(conn, "edit.html", email_changeset: changeset)
-    end
-  end
 
   def confirm_email(conn, %{"token" => token}) do
     case Accounts.update_user_email(conn.assigns.current_user, token) do
@@ -46,27 +16,4 @@ defmodule Bones73kWeb.UserSettingsController do
         |> redirect(to: Routes.user_settings_path(conn, :edit))
     end
   end
-
-  def update_password(conn, %{"current_password" => password, "user" => user_params}) do
-    user = conn.assigns.current_user
-
-    case Accounts.update_user_password(user, password, user_params) do
-      {:ok, user} ->
-        conn
-        |> put_flash(:info, "Password updated successfully.")
-        |> put_session(:user_return_to, Routes.user_settings_path(conn, :edit))
-        |> UserAuth.log_in_user(user)
-
-      {:error, changeset} ->
-        render(conn, "edit.html", password_changeset: changeset)
-    end
-  end
-
-  defp assign_email_and_password_changesets(conn, _opts) do
-    user = conn.assigns.current_user
-
-    conn
-    |> assign(:email_changeset, Accounts.change_user_email(user))
-    |> assign(:password_changeset, Accounts.change_user_password(user))
-  end
 end
diff --git a/lib/bones73k_web/live/user/registration.ex b/lib/bones73k_web/live/user/registration.ex
index 6e990fc0..0ad56c70 100644
--- a/lib/bones73k_web/live/user/registration.ex
+++ b/lib/bones73k_web/live/user/registration.ex
@@ -4,12 +4,6 @@ defmodule Bones73kWeb.UserLive.Registration do
   alias Bones73k.Accounts
   alias Bones73k.Accounts.User
 
-  @messages [
-    success: "Welcome! New accout created.",
-    info:
-      "Some features may be unavailable until you confirm your email. Check your inbox for instructions."
-  ]
-
   @impl true
   def mount(_params, session, socket) do
     socket
@@ -25,7 +19,11 @@ defmodule Bones73kWeb.UserLive.Registration do
     %{
       user_id: nil,
       user_return_to: Map.get(session, "user_return_to", "/"),
-      messages: @messages
+      messages: [
+        success: "Welcome! Your new account has been created, and you've been logged in.",
+        info:
+          "Some features may be unavailable until you confirm your email address. Check your inbox for instructions."
+      ]
     }
   end
 
diff --git a/lib/bones73k_web/live/user/reset_password.ex b/lib/bones73k_web/live/user/reset_password.ex
index c3c9f041..b7d751b4 100644
--- a/lib/bones73k_web/live/user/reset_password.ex
+++ b/lib/bones73k_web/live/user/reset_password.ex
@@ -27,7 +27,7 @@ defmodule Bones73kWeb.UserLive.ResetPassword do
       {:ok, _} ->
         {:noreply,
          socket
-         |> put_flash(:success, "Password reset successfully.")
+         |> put_flash(:info, "Password reset successfully.")
          |> redirect(to: Routes.user_session_path(socket, :new))}
 
       {:error, changeset} ->
diff --git a/lib/bones73k_web/live/user/settings.ex b/lib/bones73k_web/live/user/settings.ex
new file mode 100644
index 00000000..2821f362
--- /dev/null
+++ b/lib/bones73k_web/live/user/settings.ex
@@ -0,0 +1,39 @@
+defmodule Bones73kWeb.UserLive.Settings do
+  use Bones73kWeb, :live_view
+
+  alias Bones73k.Accounts.User
+
+  @impl true
+  def mount(_params, session, socket) do
+    socket
+    |> assign_defaults(session)
+    |> alert_email_verified?()
+    |> live_okreply()
+  end
+
+  defp alert_email_verified?(socket) do
+    case socket.assigns.current_user do
+      %{confirmed_at: nil} ->
+        put_flash(socket, :warning, [
+          "Your email hasn't been confirmed, some areas may be restricted. Shall we ",
+          link("resend the verification email?",
+            to: Routes.user_confirmation_path(socket, :new),
+            class: "alert-link"
+          )
+        ])
+
+      _ ->
+        socket
+    end
+  end
+
+  @impl true
+  def handle_info({:put_flash_message, {flash_type, msg}}, socket) do
+    socket |> put_flash(flash_type, msg) |> live_noreply()
+  end
+
+  @impl true
+  def handle_info({:clear_flash_message, flash_type}, socket) do
+    socket |> clear_flash(flash_type) |> live_noreply()
+  end
+end
diff --git a/lib/bones73k_web/live/user/settings.html.leex b/lib/bones73k_web/live/user/settings.html.leex
new file mode 100644
index 00000000..994b1fcb
--- /dev/null
+++ b/lib/bones73k_web/live/user/settings.html.leex
@@ -0,0 +1,9 @@
+<h3 class="mb-3">
+  <%= icon_div @socket, "bi-sliders", [class: "icon baseline"] %>
+  User Settings
+</h3>
+
+<div class="row">
+  <%= live_component @socket, Bones73kWeb.UserLive.Settings.Email, id: "email-#{@current_user.id}", current_user: @current_user %>
+  <%= live_component @socket, Bones73kWeb.UserLive.Settings.Password, id: "password-#{@current_user.id}", current_user: @current_user %>
+</div>
diff --git a/lib/bones73k_web/live/user/settings/email.ex b/lib/bones73k_web/live/user/settings/email.ex
new file mode 100644
index 00000000..341ed0ff
--- /dev/null
+++ b/lib/bones73k_web/live/user/settings/email.ex
@@ -0,0 +1,62 @@
+defmodule Bones73kWeb.UserLive.Settings.Email do
+  use Bones73kWeb, :live_component
+
+  alias Bones73k.Accounts
+  alias Bones73k.Accounts.User
+
+  @impl true
+  def update(%{current_user: user} = assigns, socket) do
+    socket
+    |> assign(id: assigns.id)
+    |> assign(current_user: user)
+    |> assign(changeset: get_changeset(user))
+    |> live_okreply()
+  end
+
+  defp get_changeset(user, user_params \\ %{}) do
+    Accounts.change_user_email(user, user_params)
+  end
+
+  @impl true
+  def handle_event("validate", %{"user" => user_params}, socket) do
+    cs = get_changeset(socket.assigns.current_user, user_params)
+    {:noreply, assign(socket, changeset: %{cs | action: :update})}
+  end
+
+  # user_settings_path  GET     /users/settings/confirm_email/:token   Bones73kWeb.UserSettingsController :confirm_email
+
+  @impl true
+  def handle_event("save", %{"user" => user_params}, socket) do
+    case Accounts.apply_user_email(socket.assigns.current_user, user_params) do
+      {:ok, applied_user} ->
+        Accounts.deliver_update_email_instructions(
+          applied_user,
+          socket.assigns.current_user.email,
+          &Routes.user_settings_url(socket, :confirm_email, &1)
+        )
+
+        send(self(), {:clear_flash_message, :error})
+
+        send(
+          self(),
+          {:put_flash_message,
+           {:info, "A link to confirm your e-mail change has been sent to the new address."}}
+        )
+
+        socket
+        |> assign(changeset: get_changeset(socket.assigns.current_user))
+        |> live_noreply()
+
+      {:error, cs} ->
+        cu = socket.assigns.current_user
+        cpw = user_params["current_password"]
+        valid_password? = User.valid_password?(cu, cpw)
+        msg = (valid_password? && "Could not reset email.") || "Invalid current password."
+        send(self(), {:put_flash_message, {:error, msg}})
+
+        socket
+        |> assign(changeset: cs)
+        |> live_noreply()
+    end
+  end
+end
diff --git a/lib/bones73k_web/live/user/settings/email.html.leex b/lib/bones73k_web/live/user/settings/email.html.leex
new file mode 100644
index 00000000..b589b26e
--- /dev/null
+++ b/lib/bones73k_web/live/user/settings/email.html.leex
@@ -0,0 +1,51 @@
+<div id="<%= @id %>" class="col-sm-9 col-md-7 col-lg-5 col-xl-4  mt-1">
+
+  <h4>Change email</h4>
+
+  <%= form_for @changeset, "#", [phx_change: :validate, phx_submit: :save, phx_target: @myself], fn f -> %>
+
+    <div class="mb-3" phx-feedback-for="<%= input_id(f, :email) %>">
+      <%= label f, :email, class: "form-label" %>
+      <div class="input-group has-validation">
+        <span class="input-group-text">
+          <%= icon_div @socket, "bi-at", [class: "icon fs-5"] %>
+        </span>
+        <%= email_input f, :email,
+            value: input_value(f, :email),
+            class: input_class(f, :email, "form-control"),
+            placeholder: "e.g., babka@73k.us",
+            maxlength: User.max_email,
+            phx_debounce: "600",
+            aria_describedby: error_id(f, :email)
+          %>
+        <%= error_tag f, :email %>
+      </div>
+    </div>
+
+
+    <div class="mb-3" phx-feedback-for="<%= input_id(f, :current_password) %>">
+      <%= label f, :current_password, class: "form-label" %>
+      <div class="input-group">
+        <span class="input-group-text">
+          <%= icon_div @socket, "bi-lock", [class: "icon fs-5"] %>
+        </span>
+        <%= password_input f, :current_password,
+            class: "form-control",
+            required: true,
+            aria_describedby: error_id(f, :current_password)
+          %>
+        <%= error_tag f, :current_password %>
+      </div>
+    </div>
+
+    <div class="mb-3">
+      <%= submit "Change email",
+          disabled: !@changeset.valid? || input_value(f, :current_password) == "",
+          class: "btn btn-primary",
+          phx_disable_with: "Saving..."
+        %>
+    </div>
+
+  <% end %>
+
+</div>
diff --git a/lib/bones73k_web/live/user/settings/password.ex b/lib/bones73k_web/live/user/settings/password.ex
new file mode 100644
index 00000000..2225ff9f
--- /dev/null
+++ b/lib/bones73k_web/live/user/settings/password.ex
@@ -0,0 +1,57 @@
+defmodule Bones73kWeb.UserLive.Settings.Password do
+  use Bones73kWeb, :live_component
+
+  alias Bones73k.Accounts
+  alias Bones73k.Accounts.User
+
+  @impl true
+  def update(%{current_user: user} = assigns, socket) do
+    socket
+    |> assign(id: assigns.id)
+    |> assign(current_user: user)
+    |> assign(changeset: get_changeset(user))
+    |> assign(login_params: init_login_params(socket))
+    |> assign(trigger_submit: false)
+    |> live_okreply()
+  end
+
+  defp get_changeset(user, user_params \\ %{}) do
+    Accounts.change_user_password(user, user_params)
+  end
+
+  defp init_login_params(socket) do
+    %{
+      user_id: nil,
+      user_return_to: Routes.user_settings_path(socket, :edit),
+      messages: [info: "Password updated successfully."]
+    }
+  end
+
+  @impl true
+  def handle_event("validate", %{"user" => user_params}, socket) do
+    cs = get_changeset(socket.assigns.current_user, user_params)
+    {:noreply, assign(socket, changeset: %{cs | action: :update})}
+  end
+
+  @impl true
+  def handle_event("save", %{"user" => user_params}, socket) do
+    case Accounts.update_user_password(socket.assigns.current_user, user_params) do
+      {:ok, user} ->
+        socket
+        |> assign(login_params: %{socket.assigns.login_params | user_id: user.id})
+        |> assign(trigger_submit: true)
+        |> live_noreply()
+
+      {:error, cs} ->
+        cu = socket.assigns.current_user
+        cpw = user_params["current_password"]
+        valid_password? = User.valid_password?(cu, cpw)
+        msg = (valid_password? && "Could not change password.") || "Invalid current password."
+        send(self(), {:put_flash_message, {:error, msg}})
+
+        socket
+        |> assign(changeset: cs)
+        |> live_noreply()
+    end
+  end
+end
diff --git a/lib/bones73k_web/live/user/settings/password.html.leex b/lib/bones73k_web/live/user/settings/password.html.leex
new file mode 100644
index 00000000..9d84ad4d
--- /dev/null
+++ b/lib/bones73k_web/live/user/settings/password.html.leex
@@ -0,0 +1,76 @@
+<div id="<%= @id %>" class="col-sm-9 col-md-7 col-lg-5 col-xl-4 mt-1">
+
+  <h4>Change password</h4>
+
+  <%= form_for @changeset, "#", [phx_change: :validate, phx_submit: :save, phx_target: @myself], 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,
+            phx_debounce: "600",
+            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" phx-feedback-for="<%= input_id(f, :current_password) %>">
+      <%= label f, :current_password, class: "form-label" %>
+      <div class="input-group">
+        <span class="input-group-text">
+          <%= icon_div @socket, "bi-lock", [class: "icon fs-5"] %>
+        </span>
+        <%= password_input f, :current_password,
+            class: "form-control",
+            required: true,
+            aria_describedby: error_id(f, :current_password)
+          %>
+        <%= error_tag f, :current_password %>
+      </div>
+    </div>
+
+    <div class="mb-3">
+      <%= submit "Change password",
+          disabled: !@changeset.valid? || input_value(f, :current_password) == "",
+          class: "btn btn-primary",
+          phx_disable_with: "Saving..."
+        %>
+    </div>
+
+  <% end %>
+
+  <%# hidden form for initial login after registration %>
+  <%= form_for :user, Routes.user_session_path(@socket, :create), [phx_trigger_action: @trigger_submit, id: "settings_pw_change_trigger"], fn f -> %>
+    <%= hidden_input f, :params_token, value: Phoenix.Token.encrypt(Bones73kWeb.Endpoint, "login_params", @login_params) %>
+  <% end %>
+
+  <%# hidden form to submit user for relogin after password change %>
+  <%#= form_for :user_login, Routes.user_session_path(@socket, :create), [phx_trigger_action: @trigger_submit], fn f -> %>
+    <%#= hidden_input f, :login_params_token, value: Phoenix.Token.encrypt(Bones73kWeb.Endpoint, "login_params", @login_params) %>
+    <%#= hidden_input f, :remember_me, value: false %>
+  <%# end %>
+
+</div>
diff --git a/lib/bones73k_web/router.ex b/lib/bones73k_web/router.ex
index b7d3cfc8..3907c3ce 100644
--- a/lib/bones73k_web/router.ex
+++ b/lib/bones73k_web/router.ex
@@ -72,13 +72,9 @@ defmodule Bones73kWeb.Router do
     pipe_through([:browser, :require_authenticated_user])
 
     # # liveview user settings
-    # live "/users/settings", UserLive.Settings, :edit
+    live "/users/settings", UserLive.Settings, :edit
 
     # original user routes from phx.gen.auth
-    # TODO:
-    get("/users/settings", UserSettingsController, :edit)
-    put("/users/settings/update_password", UserSettingsController, :update_password)
-    put("/users/settings/update_email", UserSettingsController, :update_email)
     get("/users/settings/confirm_email/:token", UserSettingsController, :confirm_email)
   end
 
@@ -86,7 +82,6 @@ defmodule Bones73kWeb.Router do
     pipe_through([:browser])
 
     delete("/users/log_out", UserSessionController, :delete)
-    # TODO: understanding/testing force_logout?
     get("/users/force_logout", UserSessionController, :force_logout)
     get("/users/confirm", UserConfirmationController, :new)
     post("/users/confirm", UserConfirmationController, :create)
diff --git a/lib/bones73k_web/templates/layout/app.html.eex b/lib/bones73k_web/templates/layout/app.html.eex
index dca8b5bd..c28896f7 100644
--- a/lib/bones73k_web/templates/layout/app.html.eex
+++ b/lib/bones73k_web/templates/layout/app.html.eex
@@ -2,7 +2,7 @@
 
   <%# phoenix flash alerts: %>
   <div class="row justify-content-center">
-    <div class="col-md-11 col-lg-9 col-xl-8 ">
+    <div class="col-md-12 col-lg-10 col-xl-9 ">
       <%= for {kind, color} <- alert_kinds() do %>
         <%= if flash_content = get_flash(@conn, kind) do %>
           <div class="alert alert-<%= color %> alert-dismissible fade show" role="alert">
diff --git a/lib/bones73k_web/templates/layout/live.html.leex b/lib/bones73k_web/templates/layout/live.html.leex
index 889fc6d0..a67d71df 100644
--- a/lib/bones73k_web/templates/layout/live.html.leex
+++ b/lib/bones73k_web/templates/layout/live.html.leex
@@ -2,7 +2,7 @@
 
   <%# liveview flash alerts: %>
   <div class="row justify-content-center">
-    <div class="col-md-11 col-lg-9 col-xl-8 ">
+    <div class="col-md-12 col-lg-10 col-xl-9 ">
       <%= for {kind, color} <- alert_kinds() do %>
         <%= if flash_content = live_flash(@flash, kind) do %>
           <div class="alert alert-<%= color %> alert-dismissible fade show" role="alert" id="lv-alert-<%= kind %>" phx-hook="AlertRemover" data-key="<%= kind %>">
diff --git a/lib/bones73k_web/templates/user_settings/edit.html.eex b/lib/bones73k_web/templates/user_settings/edit.html.eex
deleted file mode 100644
index f6c0801e..00000000
--- a/lib/bones73k_web/templates/user_settings/edit.html.eex
+++ /dev/null
@@ -1,49 +0,0 @@
-<h1>Settings</h1>
-
-<h3>Change email</h3>
-
-<%= form_for @email_changeset, Routes.user_settings_path(@conn, :update_email), fn f -> %>
-  <%= if @email_changeset.action do %>
-    <div class="alert alert-danger">
-      <p>Oops, something went wrong! Please check the errors below.</p>
-    </div>
-  <% end %>
-
-  <%= label f, :email %>
-  <%= email_input f, :email, required: true %>
-  <%= error_tag f, :email %>
-
-  <%= label f, :current_password, for: "current_password_for_email" %>
-  <%= password_input f, :current_password, required: true, name: "current_password", id: "current_password_for_email" %>
-  <%= error_tag f, :current_password %>
-
-  <div>
-    <%= submit "Change email" %>
-  </div>
-<% end %>
-
-<h3>Change password</h3>
-
-<%= form_for @password_changeset, Routes.user_settings_path(@conn, :update_password), fn f -> %>
-  <%= if @password_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 %>
-
-  <%= label f, :current_password, for: "current_password_for_password" %>
-  <%= password_input f, :current_password, required: true, name: "current_password", id: "current_password_for_password" %>
-  <%= error_tag f, :current_password %>
-
-  <div>
-    <%= submit "Change password" %>
-  </div>
-<% end %>
diff --git a/lib/bones73k_web/views/user_settings_view.ex b/lib/bones73k_web/views/user_settings_view.ex
deleted file mode 100644
index 27735e86..00000000
--- a/lib/bones73k_web/views/user_settings_view.ex
+++ /dev/null
@@ -1,3 +0,0 @@
-defmodule Bones73kWeb.UserSettingsView do
-  use Bones73kWeb, :view
-end
diff --git a/test/bones73k/accounts_test.exs b/test/bones73k/accounts_test.exs
index f12d70ff..30b777a8 100644
--- a/test/bones73k/accounts_test.exs
+++ b/test/bones73k/accounts_test.exs
@@ -58,19 +58,19 @@ defmodule Bones73k.AccountsTest do
     end
 
     test "validates email and password when given" do
-      {:error, changeset} = Accounts.register_user(%{email: "not valid", password: "not valid"})
-
-      assert %{
-               email: ["must have the @ sign and no spaces"],
-               password: ["should be at least 12 character(s)"]
-             } = errors_on(changeset)
+      {:error, changeset} = Accounts.register_user(%{email: "not valid", password: "2shrt"})
+      pw_err = "should be at least #{User.min_password()} character(s)"
+      assert "must be a valid email address" in errors_on(changeset).email
+      assert pw_err in errors_on(changeset).password
     end
 
     test "validates maximum values for email and password for security" do
-      too_long = String.duplicate("db", 100)
+      too_long = "#{String.duplicate("db", 300)}@example.com"
       {:error, changeset} = Accounts.register_user(%{email: too_long, password: too_long})
-      assert "should be at most 160 character(s)" in errors_on(changeset).email
-      assert "should be at most 80 character(s)" in errors_on(changeset).password
+      em_err = "should be at most #{User.max_email()} character(s)"
+      pw_err = "should be at most #{User.max_password()} character(s)"
+      assert em_err in errors_on(changeset).email
+      assert pw_err in errors_on(changeset).password
     end
 
     test "validates email uniqueness" do
@@ -92,16 +92,22 @@ defmodule Bones73k.AccountsTest do
       assert is_nil(user.password)
       assert user.role == :user
     end
-  end
 
-  describe "register_admin/1" do
-    test "registers users with a hashed password and sets role to :admin" do
+    test "registers different role :manager and sets role to :manager" do
       email = unique_user_email()
-      {:ok, user} = Accounts.register_admin(%{email: email, password: valid_user_password()})
+      attrs = %{email: email, role: :manager, password: valid_user_password()}
+      {:ok, user} = Accounts.register_user(attrs)
       assert user.email == email
       assert is_binary(user.hashed_password)
       assert is_nil(user.confirmed_at)
       assert is_nil(user.password)
+      assert user.role == :manager
+    end
+
+    test "registers different role :admin and sets role to :admin" do
+      email = unique_user_email()
+      attrs = %{email: email, role: :admin, password: valid_user_password()}
+      {:ok, user} = Accounts.register_user(attrs)
       assert user.role == :admin
     end
   end
@@ -109,7 +115,7 @@ defmodule Bones73k.AccountsTest do
   describe "change_user_registration/2" do
     test "returns a changeset" do
       assert %Ecto.Changeset{} = changeset = Accounts.change_user_registration(%User{})
-      assert changeset.required == [:password, :email]
+      assert changeset.required == [:password, :email, :role]
     end
   end
 
@@ -126,45 +132,42 @@ defmodule Bones73k.AccountsTest do
     end
 
     test "requires email to change", %{user: user} do
-      {:error, changeset} = Accounts.apply_user_email(user, valid_user_password(), %{})
+      attrs = %{"current_password" => valid_user_password()}
+      {:error, changeset} = Accounts.apply_user_email(user, attrs)
       assert %{email: ["did not change"]} = errors_on(changeset)
     end
 
     test "validates email", %{user: user} do
-      {:error, changeset} =
-        Accounts.apply_user_email(user, valid_user_password(), %{email: "not valid"})
-
-      assert %{email: ["must have the @ sign and no spaces"]} = errors_on(changeset)
+      attrs = %{"current_password" => valid_user_password(), "email" => "not valid"}
+      {:error, changeset} = Accounts.apply_user_email(user, attrs)
+      assert %{email: ["must be a valid email address"]} = errors_on(changeset)
     end
 
     test "validates maximum value for email for security", %{user: user} do
-      too_long = String.duplicate("db", 100)
-
-      {:error, changeset} =
-        Accounts.apply_user_email(user, valid_user_password(), %{email: too_long})
-
-      assert "should be at most 160 character(s)" in errors_on(changeset).email
+      too_long = "#{String.duplicate("db", 300)}@example.com"
+      attrs = %{"current_password" => valid_user_password(), "email" => too_long}
+      {:error, changeset} = Accounts.apply_user_email(user, attrs)
+      em_err = "should be at most #{User.max_email()} character(s)"
+      assert em_err in errors_on(changeset).email
     end
 
     test "validates email uniqueness", %{user: user} do
       %{email: email} = user_fixture()
-
-      {:error, changeset} =
-        Accounts.apply_user_email(user, valid_user_password(), %{email: email})
-
+      attrs = %{"current_password" => valid_user_password(), "email" => email}
+      {:error, changeset} = Accounts.apply_user_email(user, attrs)
       assert "has already been taken" in errors_on(changeset).email
     end
 
     test "validates current password", %{user: user} do
-      {:error, changeset} =
-        Accounts.apply_user_email(user, "invalid", %{email: unique_user_email()})
-
+      attrs = %{"current_password" => "invalid", "email" => unique_user_email()}
+      {:error, changeset} = Accounts.apply_user_email(user, attrs)
       assert %{current_password: ["is not valid"]} = errors_on(changeset)
     end
 
     test "applies the email without persisting it", %{user: user} do
       email = unique_user_email()
-      {:ok, user} = Accounts.apply_user_email(user, valid_user_password(), %{email: email})
+      attrs = %{"current_password" => valid_user_password(), "email" => email}
+      {:ok, user} = Accounts.apply_user_email(user, attrs)
       assert user.email == email
       assert Accounts.get_user!(user.id).email != email
     end
@@ -245,52 +248,47 @@ defmodule Bones73k.AccountsTest do
     end
 
     test "validates password", %{user: user} do
-      {:error, changeset} =
-        Accounts.update_user_password(user, valid_user_password(), %{
-          password: "not valid",
-          password_confirmation: "another"
-        })
+      attrs = %{
+        "current_password" => valid_user_password(),
+        "password" => "2shrt",
+        "password_confirmation" => "another"
+      }
 
-      assert %{
-               password: ["should be at least 12 character(s)"],
-               password_confirmation: ["does not match password"]
-             } = errors_on(changeset)
+      {:error, changeset} = Accounts.update_user_password(user, attrs)
+      pw_err = "should be at least #{User.min_password()} character(s)"
+      conf_err = "does not match password"
+      assert pw_err in errors_on(changeset).password
+      assert conf_err in errors_on(changeset).password_confirmation
     end
 
     test "validates maximum values for password for security", %{user: user} do
-      too_long = String.duplicate("db", 100)
+      attrs = %{
+        "current_password" => valid_user_password(),
+        "password" => String.duplicate("db", 100)
+      }
 
-      {:error, changeset} =
-        Accounts.update_user_password(user, valid_user_password(), %{password: too_long})
-
-      assert "should be at most 80 character(s)" in errors_on(changeset).password
+      {:error, changeset} = Accounts.update_user_password(user, attrs)
+      pw_err = "should be at most #{User.max_password()} character(s)"
+      assert pw_err in errors_on(changeset).password
     end
 
     test "validates current password", %{user: user} do
-      {:error, changeset} =
-        Accounts.update_user_password(user, "invalid", %{password: valid_user_password()})
-
+      attrs = %{"current_password" => "invalid", "password" => valid_user_password()}
+      {:error, changeset} = Accounts.update_user_password(user, attrs)
       assert %{current_password: ["is not valid"]} = errors_on(changeset)
     end
 
     test "updates the password", %{user: user} do
-      {:ok, user} =
-        Accounts.update_user_password(user, valid_user_password(), %{
-          password: "new valid password"
-        })
-
+      attrs = %{"current_password" => valid_user_password(), "password" => "new valid password"}
+      {:ok, user} = Accounts.update_user_password(user, attrs)
       assert is_nil(user.password)
       assert Accounts.get_user_by_email_and_password(user.email, "new valid password")
     end
 
     test "deletes all tokens for the given user", %{user: user} do
       _ = Accounts.generate_user_session_token(user)
-
-      {:ok, _} =
-        Accounts.update_user_password(user, valid_user_password(), %{
-          password: "new valid password"
-        })
-
+      attrs = %{"current_password" => valid_user_password(), "password" => "new valid password"}
+      {:ok, _} = Accounts.update_user_password(user, attrs)
       refute Repo.get_by(UserToken, user_id: user.id)
     end
   end
@@ -456,14 +454,13 @@ defmodule Bones73k.AccountsTest do
     test "validates password", %{user: user} do
       {:error, changeset} =
         Accounts.reset_user_password(user, %{
-          password: "not valid",
+          password: "2shrt",
           password_confirmation: "another"
         })
 
-      assert %{
-               password: ["should be at least 12 character(s)"],
-               password_confirmation: ["does not match password"]
-             } = errors_on(changeset)
+      pw_err = "should be at least #{User.min_password()} character(s)"
+      assert pw_err in errors_on(changeset).password
+      assert "does not match password" in errors_on(changeset).password_confirmation
     end
 
     test "validates maximum values for password for security", %{user: user} do
diff --git a/test/bones73k_web/controllers/user_registration_controller_test.exs b/test/bones73k_web/controllers/user_registration_controller_test.exs
index f1d708e4..5466e1d3 100644
--- a/test/bones73k_web/controllers/user_registration_controller_test.exs
+++ b/test/bones73k_web/controllers/user_registration_controller_test.exs
@@ -13,7 +13,8 @@ defmodule Bones73kWeb.UserRegistrationControllerTest do
     end
 
     test "redirects if already logged in", %{conn: conn} do
-      conn = conn |> log_in_user(user_fixture()) |> get(Routes.user_registration_path(conn, :new))
+      to = Routes.user_registration_path(conn, :new)
+      conn = conn |> log_in_user(user_fixture()) |> get(to)
       assert redirected_to(conn) == "/"
     end
   end
diff --git a/test/bones73k_web/controllers/user_reset_password_controller_test.exs b/test/bones73k_web/controllers/user_reset_password_controller_test.exs
index 9a7de0ec..6e173ae6 100644
--- a/test/bones73k_web/controllers/user_reset_password_controller_test.exs
+++ b/test/bones73k_web/controllers/user_reset_password_controller_test.exs
@@ -15,6 +15,12 @@ defmodule Bones73kWeb.UserResetPasswordControllerTest do
       response = html_response(conn, 200)
       assert response =~ "Forgot your password?\n  </h3>"
     end
+
+    test "redirects if already logged in", %{conn: conn} do
+      to = Routes.user_reset_password_path(conn, :new)
+      conn = conn |> log_in_user(user_fixture()) |> get(to)
+      assert redirected_to(conn) == "/"
+    end
   end
 
   describe "POST /users/reset_password" do
diff --git a/test/bones73k_web/controllers/user_settings_controller_test.exs b/test/bones73k_web/controllers/user_settings_controller_test.exs
index 94dc734d..dde5fb66 100644
--- a/test/bones73k_web/controllers/user_settings_controller_test.exs
+++ b/test/bones73k_web/controllers/user_settings_controller_test.exs
@@ -10,7 +10,7 @@ defmodule Bones73kWeb.UserSettingsControllerTest do
     test "renders settings page", %{conn: conn} do
       conn = get(conn, Routes.user_settings_path(conn, :edit))
       response = html_response(conn, 200)
-      assert response =~ "<h1>Settings</h1>"
+      assert response =~ "User Settings\n</h3>"
     end
 
     test "redirects if user is not logged in" do
@@ -20,71 +20,6 @@ defmodule Bones73kWeb.UserSettingsControllerTest do
     end
   end
 
-  describe "PUT /users/settings/update_password" do
-    test "updates the user password and resets tokens", %{conn: conn, user: user} do
-      new_password_conn =
-        put(conn, Routes.user_settings_path(conn, :update_password), %{
-          "current_password" => valid_user_password(),
-          "user" => %{
-            "password" => "new valid password",
-            "password_confirmation" => "new valid password"
-          }
-        })
-
-      assert redirected_to(new_password_conn) == Routes.user_settings_path(conn, :edit)
-      assert get_session(new_password_conn, :user_token) != get_session(conn, :user_token)
-      assert get_flash(new_password_conn, :info) =~ "Password updated successfully"
-      assert Accounts.get_user_by_email_and_password(user.email, "new valid password")
-    end
-
-    test "does not update password on invalid data", %{conn: conn} do
-      old_password_conn =
-        put(conn, Routes.user_settings_path(conn, :update_password), %{
-          "current_password" => "invalid",
-          "user" => %{
-            "password" => "too short",
-            "password_confirmation" => "does not match"
-          }
-        })
-
-      response = html_response(old_password_conn, 200)
-      assert response =~ "<h1>Settings</h1>"
-      assert response =~ "should be at least 12 character(s)"
-      assert response =~ "does not match password"
-      assert response =~ "is not valid"
-
-      assert get_session(old_password_conn, :user_token) == get_session(conn, :user_token)
-    end
-  end
-
-  describe "PUT /users/settings/update_email" do
-    @tag :capture_log
-    test "updates the user email", %{conn: conn, user: user} do
-      conn =
-        put(conn, Routes.user_settings_path(conn, :update_email), %{
-          "current_password" => valid_user_password(),
-          "user" => %{"email" => unique_user_email()}
-        })
-
-      assert redirected_to(conn) == Routes.user_settings_path(conn, :edit)
-      assert get_flash(conn, :info) =~ "A link to confirm your email"
-      assert Accounts.get_user_by_email(user.email)
-    end
-
-    test "does not update email on invalid data", %{conn: conn} do
-      conn =
-        put(conn, Routes.user_settings_path(conn, :update_email), %{
-          "current_password" => "invalid",
-          "user" => %{"email" => "with spaces"}
-        })
-
-      response = html_response(conn, 200)
-      assert response =~ "<h1>Settings</h1>"
-      assert response =~ "must have the @ sign and no spaces"
-      assert response =~ "is not valid"
-    end
-  end
-
   describe "GET /users/settings/confirm_email/:token" do
     setup %{user: user} do
       email = unique_user_email()
diff --git a/test/bones73k_web/live/admin_dashboard_live_test.exs b/test/bones73k_web/live/admin_dashboard_live_test.exs
index 356529d3..1f3c50a6 100644
--- a/test/bones73k_web/live/admin_dashboard_live_test.exs
+++ b/test/bones73k_web/live/admin_dashboard_live_test.exs
@@ -52,9 +52,6 @@ defmodule Bones73kWeb.AdminDashboardLiveTest do
     assert "/" = redir_path = redirected_to(conn, 302)
     conn = get(recycle(conn), redir_path)
 
-    assert "/users/log_in" = redir_path = redirected_to(conn, 302)
-    conn = get(recycle(conn), redir_path)
-
     assert html_response(conn, 200) =~
              "You were logged out. Please login again to continue using our application."
   end
diff --git a/test/bones73k_web/live/page_live_test.exs b/test/bones73k_web/live/page_live_test.exs
index b8719e7a..a271747e 100644
--- a/test/bones73k_web/live/page_live_test.exs
+++ b/test/bones73k_web/live/page_live_test.exs
@@ -4,12 +4,6 @@ defmodule Bones73kWeb.PageLiveTest do
   import Phoenix.LiveViewTest
   import Bones73k.AccountsFixtures
 
-  test "disconnected and connected render without authentication should redirect to login page",
-       %{conn: conn} do
-    # If we don't previously log in we will be redirected to the login page
-    assert {:error, {:redirect, %{to: "/users/log_in"}}} = live(conn, "/")
-  end
-
   test "disconnected and connected render with authentication should redirect to index page", %{
     conn: conn
   } do
@@ -44,9 +38,6 @@ defmodule Bones73kWeb.PageLiveTest do
     assert "/" = redir_path = redirected_to(conn, 302)
     conn = get(recycle(conn), redir_path)
 
-    assert "/users/log_in" = redir_path = redirected_to(conn, 302)
-    conn = get(recycle(conn), redir_path)
-
     assert html_response(conn, 200) =~
              "You were logged out. Please login again to continue using our application."
   end
diff --git a/test/bones73k_web/live/property_live_test.exs b/test/bones73k_web/live/property_live_test.exs
index c3429664..64a4fc7d 100644
--- a/test/bones73k_web/live/property_live_test.exs
+++ b/test/bones73k_web/live/property_live_test.exs
@@ -188,16 +188,13 @@ defmodule Bones73kWeb.PropertyLiveTest do
       assert_receive {:DOWN, ^ref, _, _, _}
       refute Process.alive?(index_live.pid)
 
-      # Assert our liveview was redirected, following first to /users/force_logout, then to "/", and then to "/users/log_in"
+      # Assert our liveview was redirected, following first to /users/force_logout, then to "/"
       assert_redirect(index_live, "/users/force_logout")
 
       conn = get(conn, "/users/force_logout")
       assert "/" = redir_path = redirected_to(conn, 302)
       conn = get(recycle(conn), redir_path)
 
-      assert "/users/log_in" = redir_path = redirected_to(conn, 302)
-      conn = get(recycle(conn), redir_path)
-
       assert html_response(conn, 200) =~
                "You were logged out. Please login again to continue using our application."
     end
@@ -338,9 +335,6 @@ defmodule Bones73kWeb.PropertyLiveTest do
       assert "/" = redir_path = redirected_to(conn, 302)
       conn = get(recycle(conn), redir_path)
 
-      assert "/users/log_in" = redir_path = redirected_to(conn, 302)
-      conn = get(recycle(conn), redir_path)
-
       assert html_response(conn, 200) =~
                "You were logged out. Please login again to continue using our application."
     end
diff --git a/test/bones73k_web/live/user/reset_password_test.exs b/test/bones73k_web/live/user/reset_password_test.exs
index afab58d1..3526766c 100644
--- a/test/bones73k_web/live/user/reset_password_test.exs
+++ b/test/bones73k_web/live/user/reset_password_test.exs
@@ -6,8 +6,7 @@ defmodule Bones73kWeb.UserLive.ResetPasswordTest do
 
   alias Bones73k.Repo
   alias Bones73k.Accounts
-  alias Bones73k.Accounts.User
-  alias Bones73k.Accounts.UserToken
+  alias Bones73k.Accounts.{User, UserToken}
 
   setup %{conn: conn} do
     user = user_fixture()
@@ -46,7 +45,7 @@ defmodule Bones73kWeb.UserLive.ResetPasswordTest do
 
     # Confirm redirected
     flash = assert_redirected(view, Routes.user_session_path(conn, :new))
-    assert flash["success"] == "Password reset successfully."
+    assert flash["info"] == "Password reset successfully."
 
     # Confirm password was updated
     assert Accounts.get_user_by_email_and_password(user.email, new_pw)
diff --git a/test/bones73k_web/live/user_dashboard_live_test.exs b/test/bones73k_web/live/user_dashboard_live_test.exs
index a0f4e8b2..d2fea218 100644
--- a/test/bones73k_web/live/user_dashboard_live_test.exs
+++ b/test/bones73k_web/live/user_dashboard_live_test.exs
@@ -54,9 +54,6 @@ defmodule Bones73kWeb.UserDashboardLiveTest do
     assert "/" = redir_path = redirected_to(conn, 302)
     conn = get(recycle(conn), redir_path)
 
-    assert "/users/log_in" = redir_path = redirected_to(conn, 302)
-    conn = get(recycle(conn), redir_path)
-
     assert html_response(conn, 200) =~
              "You were logged out. Please login again to continue using our application."
   end