feat: add user profile editing functionality

- Implemented edit and update actions in UsersController
- Created edit user view and form partial
- Updated user model validation to allow nil password
- Modified header to link to user settings
- Added integration tests for successful and unsuccessful edits

This commit introduces the ability for users to edit their profile
information, including name and email. It also includes validation
updates to allow users to update their profiles without changing
their password. Integration tests ensure that both successful and
unsuccessful edit attempts are handled correctly.
This commit is contained in:
songtianlun 2025-01-03 10:55:42 +08:00
parent 63cebef027
commit 978c44b682
7 changed files with 84 additions and 19 deletions

View File

@ -22,6 +22,21 @@ class UsersController < ApplicationController
end end
end end
def edit
@user = User.find(params[:id])
end
def update
@user = User.find(params[:id])
if @user.update(user_params)
flash[:success] = "Profile updated"
redirect_to @user
# redirect_to user_url(@user)
else
render 'edit'
end
end
private private
def user_params def user_params

View File

@ -8,7 +8,7 @@ class User < ApplicationRecord
format: { with: VALID_EMAIL_REGEX }, format: { with: VALID_EMAIL_REGEX },
uniqueness: true uniqueness: true
has_secure_password has_secure_password
validates :password, presence: true, length: { minimum: 6 } validates :password, presence: true, length: { minimum: 6 }, allow_nil: true
def User.digest(string) def User.digest(string)
cost = ActiveModel::SecurePassword.min_cost ? BCrypt::Engine::MIN_COST : cost = ActiveModel::SecurePassword.min_cost ? BCrypt::Engine::MIN_COST :

View File

@ -25,7 +25,7 @@
</a> </a>
<ul class="dropdown-menu"> <ul class="dropdown-menu">
<li><%= link_to "Profile", current_user %></li> <li><%= link_to "Profile", current_user %></li>
<li><%= link_to "Settings", '#' %></li> <li><%= link_to "Settings", edit_user_path(current_user) %></li>
<li class="divider"></li> <li class="divider"></li>
<li> <li>
<%= link_to "Log out", logout_path, data: { turbo_method: :delete } %> <%= link_to "Log out", logout_path, data: { turbo_method: :delete } %>

View File

@ -0,0 +1,18 @@
<%= form_with(model: @user, local: true) do |f| %>
<%= render 'shared/error_messages' %>
<%= f.label :name %>
<%= f.text_field :name, class: 'form-control' %>
<%= f.label :email %>
<%= f.email_field :email, class: 'form-control' %>
<%= f.label :password %>
<%= f.password_field :password, class: 'form-control' %>
<%= f.label :password_confirmation, "Confirmation" %>
<%= f.password_field :password_confirmation, class: 'form-control' %>
<%= f.submit yield(:button_text), class: "btn btn-primary" %>
<% end %>

View File

@ -0,0 +1,13 @@
<% provide(:title, "Edit user") %>
<% provide(:button_text, 'Save changes') %>
<h1>Update your profile</h1>
<div class="row">
<div class="col-md-6 col-md-offset-3">
<%= render 'form' %>
<div class="gravatar_edit">
<%= gravatar_for @user %>
<a href="https://gravatar.com/emails" target="_blank">change</a>
</div>
</div>
</div>

View File

@ -1,26 +1,11 @@
<% provide(:title, 'Sign up') %> <% provide(:title, 'Sign up') %>
<% provide(:button_text, 'Create my account') %>
<h1>Sign up</h1> <h1>Sign up</h1>
<div class="row"> <div class="row">
<div class="col-md-6 col-md-offset-3"> <div class="col-md-6 col-md-offset-3">
<%= turbo_frame_tag "signup_form" do %> <%= turbo_frame_tag "signup_form" do %>
<%= form_with(model: @user, local: true) do |f| %> <%= render 'form' %>
<%= render 'shared/error_messages' %>
<%= f.label :name %>
<%= f.text_field :name, class: 'form-control' %>
<%= f.label :email %>
<%= f.email_field :email, class: 'form-control' %>
<%= f.label :password %>
<%= f.password_field :password, class: 'form-control' %>
<%= f.label :password_confirmation, "Confirmation" %>
<%= f.password_field :password_confirmation, class: 'form-control' %>
<%= f.submit "Create my account", class: "btn, btn-primary" %>
<% end %>
<% end %> <% end %>
</div> </div>
</div> </div>

View File

@ -0,0 +1,34 @@
require "test_helper"
class UsersEditTest < ActionDispatch::IntegrationTest
def setup
@user = users(:michael)
end
test "successful edit" do
get edit_user_path(@user)
assert_template 'users/edit'
name = "Foo Bae"
email = "foo@bar.com"
patch user_path(@user), params: { user: { name: name,
email: email,
password: "",
password_confirmation: "" } }
assert_not flash.empty?
assert_redirected_to @user
@user.reload
assert_equal name, @user.name
assert_equal email, @user.email
end
test "unsuccessful edit" do
get edit_user_path(@user)
assert_template 'users/edit'
patch user_path(@user), params: { user: { name: "",
email: "foo@invalid",
password: "foo",
password_confirmation: "bar"
} }
assert_template 'users/edit'
end
end