Improve user edit UX, incl add comments#1040
Conversation
- Add inline comments to user edit form matching person form pattern - Track created_by/updated_by on comments via controller - Show updated_by initials instead of created_by on comment display - Restrict comment policy to admin-only access - Move comment edit toggle from inline onclick to Stimulus controller - Improve dirty form controller with turbo:before-visit and beforeunload guards Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…r's email if that user is already connected to that person
…cess param search of users index
| def set_form_variables | ||
| @organizations = Organization.pluck(:name, :id).sort_by(&:first) | ||
| @authors = User.active.or(User.where(id: @community_news.author_id)) | ||
| @authors = User.has_access.or(User.where(id: @community_news.author_id)) |
There was a problem hiding this comment.
this scope isn't jsut inactive flag anymore bc it takes into account other devise fields
| @account_events = user_auth_events | ||
| .includes(:user) | ||
| .order(time: :desc) | ||
| .paginate(page: params[:page], per_page: 10) |
There was a problem hiding this comment.
add more event data to user show
| if email.present? && !email.downcase.end_with?("@example.com") | ||
| duplicates = find_duplicate_users(email) | ||
| person_id = params[:person_id].presence || params.dig(:user, :person_id).presence || @user.person_id | ||
| duplicates = find_duplicate_users(email, exclude_person_id: person_id) |
There was a problem hiding this comment.
don't identify duplicates w people emails for an associated person
| person_id = params[:person_id].presence || params.dig(:user, :person_id).presence | ||
| @user.person = Person.find(person_id) if person_id | ||
| @user.created_by = current_user | ||
| @user.updated_by = current_user |
There was a problem hiding this comment.
add audit tracking to user. we maybe want to do this more sitewide w a gem instead of direct. or maybe use ahoy records, but they really weren't designed for that.
| bypass_sign_in(@user) if @user == current_user | ||
| notice = "User was successfully updated." | ||
| notice += " A confirmation email has been sent to #{@user.unconfirmed_email}." if @user.unconfirmed_email.present? && @user.saved_change_to_unconfirmed_email? | ||
| redirect_to users_path, notice: notice |
There was a problem hiding this comment.
initiate normal confirmation flow when a user email is changed, to set it back up to confirm the new email. existing email login will still work.
| def user_params | ||
| params.require(:user).permit( | ||
| :email, :comment, :person_id, :inactive, :primary_address, :time_zone, :super_user, | ||
| :email, :comment, :person_id, :inactive, :locked, :primary_address, :time_zone, :super_user, |
There was a problem hiding this comment.
accept :locked as a param bc form now has it as a boolean checkbox even tho it's not a field on user
| // Uses three layers of protection: | ||
| // 1. turbo:before-visit – catches Turbo Drive link clicks (custom message) | ||
| // 2. beforeunload – catches non-Turbo navigations / tab close (browser message) | ||
| // 3. confirmCancel action – explicit cancel button binding (custom message) |
There was a problem hiding this comment.
add prompt if there are unsaved changes and you try to navigate away.
| content_tag(:span, "confirmed", class: "text-green-600 font-medium", title: "Email confirmed") | ||
| else | ||
| content_tag(:span, "unconfirmed", class: "text-red-600 ml-2 font-medium", title: "Email not confirmed") | ||
| content_tag(:span, "unconfirmed", class: "text-red-600 font-medium", title: "Email not confirmed") |
There was a problem hiding this comment.
remove left padding for email confirmation status
| { record_id: @record.id, record_type: "User" }, | ||
| user: Current.user | ||
| ) | ||
| end |
There was a problem hiding this comment.
we weren't making ahoy logs for devise emails sent. want to show these on user show.
|
|
||
| included do | ||
| after_create -> { track_lifecycle_event("create") } | ||
| after_create -> { track_create_event } |
There was a problem hiding this comment.
beef up after_create to save more data into ahoy record properties
| after_create -> { track_create_event } | ||
| after_update -> { track_update_event } | ||
| after_destroy -> { track_lifecycle_event("destroy") } | ||
| after_destroy -> { track_lifecycle_event("destroy", @_destroy_snapshot || {}) } |
There was a problem hiding this comment.
beef up after_destroy to save all data into ahoy record properties
| extra[:changes] = format_tracked_changes(changes) if changes.present? && !devise_only_changes?(changes) | ||
| extra[:association_changes] = assoc_changes if assoc_changes.present? | ||
|
|
||
| track_lifecycle_event("update", extra) |
There was a problem hiding this comment.
save more details in ahoy record after_update
|
|
||
| # Validations | ||
| validates_presence_of :organization_id | ||
| validates_presence_of :person_id |
There was a problem hiding this comment.
this was causing error
| devise :database_authenticatable, :recoverable, :confirmable, | ||
| :rememberable, :trackable, :validatable | ||
|
|
||
| attr_accessor :locked_will_change |
There was a problem hiding this comment.
need this so the Lock boolean can be toggled and add/hide lock icon
| # Sort by the most recent timestamp (updated_at preferred, fallback to created_at) | ||
| recent.sort_by { |item| item.try(:updated_at) || item.created_at }.reverse.first(activity_limit * 8) | ||
| end | ||
|
|
There was a problem hiding this comment.
remove unused method. we're now doing ahoy event records for this
| public_send("bookmarked_#{bookmarkable_type.downcase.pluralize}") | ||
| .pluck(:id) | ||
| end | ||
|
|
There was a problem hiding this comment.
remove unused methods
| end | ||
|
|
||
| def gallery_assets # method needed for idea_submission_fyi mailer | ||
| def gallery_assets # method needed for idea_submitted_fyi mailer |
There was a problem hiding this comment.
fix comment to correct mailer name
| payload = { name: name, properties: properties.merge( | ||
| record_id: id, record_type: "User", updated_by_id: updated_by_id, | ||
| resource_type: "User", resource_id: id, resource_title: "#{self.name} (#{email})" | ||
| ) } |
There was a problem hiding this comment.
save resource_title on auth events
|
|
||
| def create? | ||
| authenticated? | ||
| admin? |
There was a problem hiding this comment.
comments should be admin-only
| if resource.is_a?(Asset) && resource.file.attached? | ||
| return resource.file.filename.to_s | ||
| end | ||
|
|
There was a problem hiding this comment.
add resource_title for asset changes so we see filename in ahoy records
| <td class="px-4 py-3 text-gray-500"> | ||
| <%= event.properties["resource_title"].presence || "—" %> | ||
| </td> | ||
|
|
There was a problem hiding this comment.
show resource_title for more context in ahoy events table
| # Number of authentication tries before locking an account if lock_strategy | ||
| # is failed attempts. | ||
| config.maximum_attempts = 10 | ||
| config.unlock_strategy = :none |
There was a problem hiding this comment.
don't allow people to unlock themselves via email. only admins can unlock an account.
deprecating usage of Inactive in favor of using devise's lockable, which makes users unable to log in
| puts "Done. Sent: #{sent}, Failed: #{errors.size}" | ||
| errors.each { |e| puts " FAILED: #{e[:email]} — #{e[:error]}" } if errors.any? | ||
| end | ||
| end |
There was a problem hiding this comment.
added rake task for when we need to bulk invite users. we'll want to test this w awbw admins first.
* Add comments to user edit form, improve comment UX - Add inline comments to user edit form matching person form pattern - Track created_by/updated_by on comments via controller - Show updated_by initials instead of created_by on comment display - Restrict comment policy to admin-only access - Move comment edit toggle from inline onclick to Stimulus controller - Improve dirty form controller with turbo:before-visit and beforeunload guards Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * rubocop * Update claude * Add created_by_id and updated_by_id directly to user for better tracking * Change lockable settings for devise so only admins can unlock * update claude * Show comments and ahoy events on user show * Reinstate confirmable if user email is changed * WIP: rework lock flow (via boolean) and comment display on user * Update email_confirmation_icon helper to not have left padding * Update ahoy globally so it captures more in the properties payload * Update user callbacks and remove unused methods * Update user show layout (label+value vs br, and, show ahoy events and comments) * Remove toggle_lock_button from turbo * Add includes to avoid n+1 * Change user icon display and behavior * WIP: user ux * Add ahoy event for all devise mailers * Rake task for bulk inviting users * Clean up flow from person to user * Don't find/flag 'duplicates' if person has email that matches the user's email if that user is already connected to that person * Update person email if associated user email changes * Add person email and email_2 to searching * Set as no access, not active if confirmed_at is nil * Use has_access instead of active scope bc it's more accurate name now * Change dropdown search params to be true/false vs another word for access param search of users index * Update check dupes styling for user dupes display * Adjust icons and hover text * Retain hidden user_id on person form * Fix tests based on ui changes * Add error handling to ahoy * Update specs to latest view changes * Add wait time to help w flaky test --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Summary
User/Person UX
Polymorphic comments — Add inline comments to
user formmatching person form pattern. Add updated_by to comments and show thatRedesigned
user showpage with structured label/value layout, account event history (Ahoy), and comments sectionUpdated
user formwith improved lock/unlock flow using a boolean field and Stimulus controllersUser-Person email sync — Updating a user's email now automatically updates the associated person's email field via after_update callback
Updated
user index, esp around which icons are showing and adding informative hover textAdded created_by/updated_by to users — Migration adding these tracking columns directly to the User model
Expanded person and user search — User search now also matches against person.email and person.email_2
Scope rename — Renamed
User.activescope toUser.has_accessfor clarity, updated all call sites across controllers and policiesPerson form flow — Improved navigation flow from person to user creation, retaining hidden user_id on person form
Duplicate check improvements — Skip flagging duplicates when a person's email matches the user's email and they're already linked; updated check duplicates view styling
Devise configuration — Reinstated
confirmableon email change; changedlockablesettings so only admins can unlock accountsOther updates
bulk_inviterake task for inviting multiple users at once