Fix missing error messages on user registration and other forms

This commit is contained in:
Eugene Burmakin 2025-11-07 11:08:57 +01:00
parent 888e48ccf2
commit 2f160b8d97
7 changed files with 103 additions and 18 deletions

View file

@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
## Fixed
- Taiwan flag is now shown on its own instead of in combination with China flag.
- On the registration page and other user forms, if something goes wrong, error messages are now shown to the user.
## Changed

View file

@ -51,7 +51,7 @@ class Users::RegistrationsController < Devise::RegistrationsController
end
def set_invitation
return unless invitation_token.present?
return if invitation_token.blank?
@invitation = Family::Invitation.find_by(token: invitation_token)
end

View file

@ -17,6 +17,8 @@
</div>
<div class="card flex-shrink-0 w-full max-w-sm shadow-2xl bg-base-100 px-5 py-5">
<%= form_for(resource, as: resource_name, url: registration_path(resource_name), class: 'form-body', method: :put, data: { turbo_method: :put, turbo: false }) do |f| %>
<%= render "devise/shared/error_messages", resource: resource %>
<div class="form-control">
<%= f.label :email, class: 'label' do %>
<span class="label-text">Email</span>

View file

@ -16,12 +16,23 @@
</span>
</div>
<% else %>
<h1 class="text-5xl font-bold text-base-content">Register now!</h1>
<p class="py-6 text-base-content opacity-70">and take control over your location data.</p>
<h1 class="text-5xl font-bold text-base-content">Almost there!</h1>
<% end %>
<p class="py-6 text-base-content opacity-70">
Only a few steps left until you get control over your location data!
</p>
<ol>
<li class="mb-2">1. Create your account</li>
<li class="mb-2">2. Configure your mobile app</li>
<li class="mb-2">3. Start tracking your location data securely</li>
<li class="mb-2">4. ...</li>
<li class="mb-2">5. You're beautiful!</li>
</ol>
</div>
<div class="card flex-shrink-0 w-full max-w-sm shadow-2xl bg-base-100 px-5 py-5">
<%= form_for(resource, as: resource_name, url: registration_path(resource_name), class: 'form-body', html: { data: { turbo: session[:dawarich_client] == 'ios' ? false : true } }) do |f| %>
<%= render "devise/shared/error_messages", resource: resource %>
<% if @invitation %>
<%= f.hidden_field :invitation_token, value: params[:invitation_token] %>
<% end %>
@ -32,7 +43,7 @@
<% end %>
<%= f.email_field :email, autofocus: true, autocomplete: "email",
readonly: @invitation.present?,
class: "input input-bordered #{@invitation ? 'input-disabled' : ''}" %>
class: "input input-bordered w-full #{@invitation ? 'input-disabled' : ''}" %>
</div>
<div class="form-control">
@ -42,7 +53,7 @@
<% if @minimum_password_length %>
<em class="text-base-content opacity-60 text-sm">(<%= @minimum_password_length %> characters minimum)</em>
<% end %><br />
<%= f.password_field :password, autocomplete: "new-password", class: 'input input-bordered' %>
<%= f.password_field :password, autocomplete: "new-password", class: 'input input-bordered w-full' %>
</div>
<div class="form-control">
@ -52,7 +63,7 @@
<% if @minimum_password_length %>
<em class="text-base-content opacity-60 text-sm">(<%= @minimum_password_length %> characters minimum)</em>
<% end %><br />
<%= f.password_field :password_confirmation, autocomplete: "new-password", class: 'input input-bordered' %>
<%= f.password_field :password_confirmation, autocomplete: "new-password", class: 'input input-bordered w-full' %>
</div>
<% if !DawarichSettings.self_hosted? %>

View file

@ -20,6 +20,8 @@
</div>
<div class="card flex-shrink-0 w-full max-w-sm shadow-2xl bg-base-100 px-5 py-5">
<%= form_for(resource, as: resource_name, url: session_path(resource_name), class: 'form-body', html: { data: { turbo: session[:dawarich_client] == 'ios' ? false : true } }) do |f| %>
<%= render "devise/shared/error_messages", resource: resource %>
<% if @invitation %>
<%= hidden_field_tag :invitation_token, params[:invitation_token] %>
<% end %>

View file

@ -1,15 +1,20 @@
<% if resource.errors.any? %>
<div id="error_explanation" data-turbo-cache="false">
<h2>
<%= I18n.t("errors.messages.not_saved",
count: resource.errors.count,
resource: resource.class.model_name.human.downcase)
%>
</h2>
<ul>
<% resource.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
<div id="error_explanation" class="alert alert-error mb-4" data-turbo-cache="false">
<%= icon 'circle-x' %>
<div class="font-bold mb-4 flex items-center gap-2">
<div>
<h3 class="font-bold">
<%= I18n.t("errors.messages.not_saved",
count: resource.errors.count,
resource: resource.class.model_name.human.downcase)
%>
</h3>
<ul class="text-sm mt-1">
<% resource.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
</div>
</div>
<% end %>

View file

@ -326,6 +326,70 @@ RSpec.describe 'Users::Registrations', type: :request do
end
end
describe 'Validation Error Handling' do
context 'when trying to register with an existing email' do
let!(:existing_user) { create(:user, email: 'existing@example.com') }
it 'renders the registration form with error message' do
post user_registration_path, params: {
user: {
email: existing_user.email,
password: 'password123',
password_confirmation: 'password123'
}
}
expect(response).to have_http_status(:unprocessable_content)
expect(response.body).to include('Email has already been taken')
expect(response.body).to include('error_explanation')
end
it 'does not create a new user' do
expect do
post user_registration_path, params: {
user: {
email: existing_user.email,
password: 'password123',
password_confirmation: 'password123'
}
}
end.not_to change(User, :count)
end
end
context 'when password is too short' do
it 'renders the registration form with error message' do
post user_registration_path, params: {
user: {
email: 'newuser@example.com',
password: 'short',
password_confirmation: 'short'
}
}
expect(response).to have_http_status(:unprocessable_content)
expect(response.body).to include('Password is too short')
expect(response.body).to include('error_explanation')
end
end
context 'when passwords do not match' do
it 'renders the registration form with error message' do
post user_registration_path, params: {
user: {
email: 'newuser@example.com',
password: 'password123',
password_confirmation: 'different123'
}
}
expect(response).to have_http_status(:unprocessable_content)
expect(response.body).to include("Password confirmation doesn")
expect(response.body).to include('error_explanation')
end
end
end
describe 'UTM Parameter Tracking' do
let(:utm_params) do
{