diff --git a/CHANGELOG.md b/CHANGELOG.md
index d078ad39..85d2f888 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -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
diff --git a/app/controllers/users/registrations_controller.rb b/app/controllers/users/registrations_controller.rb
index 3412bb08..f01254a8 100644
--- a/app/controllers/users/registrations_controller.rb
+++ b/app/controllers/users/registrations_controller.rb
@@ -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
diff --git a/app/views/devise/registrations/edit.html.erb b/app/views/devise/registrations/edit.html.erb
index 257aba87..5536a889 100644
--- a/app/views/devise/registrations/edit.html.erb
+++ b/app/views/devise/registrations/edit.html.erb
@@ -17,6 +17,8 @@
<%= 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 %>
+
<%= f.label :email, class: 'label' do %>
Email
diff --git a/app/views/devise/registrations/new.html.erb b/app/views/devise/registrations/new.html.erb
index 707d9cee..4a32be9e 100644
--- a/app/views/devise/registrations/new.html.erb
+++ b/app/views/devise/registrations/new.html.erb
@@ -16,12 +16,23 @@
<% else %>
-
Register now!
-
and take control over your location data.
+
Almost there!
<% end %>
+
+ Only a few steps left until you get control over your location data!
+
+
+ - 1. Create your account
+ - 2. Configure your mobile app
+ - 3. Start tracking your location data securely
+ - 4. ...
+ - 5. You're beautiful!
+
+
<%= 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' : ''}" %>
@@ -42,7 +53,7 @@
<% if @minimum_password_length %>
(<%= @minimum_password_length %> characters minimum)
<% end %>
- <%= f.password_field :password, autocomplete: "new-password", class: 'input input-bordered' %>
+ <%= f.password_field :password, autocomplete: "new-password", class: 'input input-bordered w-full' %>
@@ -52,7 +63,7 @@
<% if @minimum_password_length %>
(<%= @minimum_password_length %> characters minimum)
<% end %>
- <%= 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' %>
<% if !DawarichSettings.self_hosted? %>
diff --git a/app/views/devise/sessions/new.html.erb b/app/views/devise/sessions/new.html.erb
index b471a5cf..633337c1 100644
--- a/app/views/devise/sessions/new.html.erb
+++ b/app/views/devise/sessions/new.html.erb
@@ -20,6 +20,8 @@
<%= 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 %>
diff --git a/app/views/devise/shared/_error_messages.html.erb b/app/views/devise/shared/_error_messages.html.erb
index cabfe307..2b2b4cfe 100644
--- a/app/views/devise/shared/_error_messages.html.erb
+++ b/app/views/devise/shared/_error_messages.html.erb
@@ -1,15 +1,20 @@
<% if resource.errors.any? %>
-
-
- <%= I18n.t("errors.messages.not_saved",
- count: resource.errors.count,
- resource: resource.class.model_name.human.downcase)
- %>
-
-
- <% resource.errors.full_messages.each do |message| %>
- - <%= message %>
- <% end %>
-
+
+ <%= icon 'circle-x' %>
+
+
+
+ <%= I18n.t("errors.messages.not_saved",
+ count: resource.errors.count,
+ resource: resource.class.model_name.human.downcase)
+ %>
+
+
+ <% resource.errors.full_messages.each do |message| %>
+ - <%= message %>
+ <% end %>
+
+
+
<% end %>
diff --git a/spec/requests/users/registrations_spec.rb b/spec/requests/users/registrations_spec.rb
index efeac67c..6a5989ea 100644
--- a/spec/requests/users/registrations_spec.rb
+++ b/spec/requests/users/registrations_spec.rb
@@ -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
{