From 28b2a19a6d346c2a5e64a3131338ffcc525699de Mon Sep 17 00:00:00 2001 From: Johncarlo Cerna Date: Sat, 29 Aug 2026 13:44:14 -0400 Subject: [PATCH 1/3] Add user creation and login workflows --- app/views/devise/confirmations/new.html.erb | 16 +++++++ .../mailer/confirmation_instructions.html.erb | 5 +++ .../devise/mailer/email_changed.html.erb | 7 ++++ .../devise/mailer/password_change.html.erb | 3 ++ .../reset_password_instructions.html.erb | 8 ++++ .../mailer/unlock_instructions.html.erb | 7 ++++ app/views/devise/passwords/edit.html.erb | 25 +++++++++++ app/views/devise/passwords/new.html.erb | 16 +++++++ app/views/devise/registrations/edit.html.erb | 42 +++++++++++++++++++ app/views/devise/registrations/new.html.erb | 29 +++++++++++++ app/views/devise/sessions/new.html.erb | 26 ++++++++++++ .../devise/shared/_error_messages.html.erb | 15 +++++++ app/views/devise/shared/_links.html.erb | 25 +++++++++++ app/views/devise/unlocks/new.html.erb | 16 +++++++ app/views/home/index.html.erb | 9 +++- config/initializers/devise.rb | 6 +-- 16 files changed, 251 insertions(+), 4 deletions(-) create mode 100644 app/views/devise/confirmations/new.html.erb create mode 100644 app/views/devise/mailer/confirmation_instructions.html.erb create mode 100644 app/views/devise/mailer/email_changed.html.erb create mode 100644 app/views/devise/mailer/password_change.html.erb create mode 100644 app/views/devise/mailer/reset_password_instructions.html.erb create mode 100644 app/views/devise/mailer/unlock_instructions.html.erb create mode 100644 app/views/devise/passwords/edit.html.erb create mode 100644 app/views/devise/passwords/new.html.erb create mode 100644 app/views/devise/registrations/edit.html.erb create mode 100644 app/views/devise/registrations/new.html.erb create mode 100644 app/views/devise/sessions/new.html.erb create mode 100644 app/views/devise/shared/_error_messages.html.erb create mode 100644 app/views/devise/shared/_links.html.erb create mode 100644 app/views/devise/unlocks/new.html.erb diff --git a/app/views/devise/confirmations/new.html.erb b/app/views/devise/confirmations/new.html.erb new file mode 100644 index 0000000..3c21122 --- /dev/null +++ b/app/views/devise/confirmations/new.html.erb @@ -0,0 +1,16 @@ +

Resend confirmation instructions

+ +<%= form_for(resource, as: resource_name, url: confirmation_path(resource_name), html: { method: :post }) do |f| %> + <%= render "devise/shared/error_messages", resource: resource %> + +
+

<%= f.label :email %>

+

<%= f.email_field :email, autofocus: true, autocomplete: "email", value: (resource.pending_reconfirmation? ? resource.unconfirmed_email : resource.email) %>

+
+ +
+ <%= f.submit "Resend confirmation instructions" %> +
+<% end %> + +<%= render "devise/shared/links" %> diff --git a/app/views/devise/mailer/confirmation_instructions.html.erb b/app/views/devise/mailer/confirmation_instructions.html.erb new file mode 100644 index 0000000..dc55f64 --- /dev/null +++ b/app/views/devise/mailer/confirmation_instructions.html.erb @@ -0,0 +1,5 @@ +

Welcome <%= @email %>!

+ +

You can confirm your account email through the link below:

+ +

<%= link_to 'Confirm my account', confirmation_url(@resource, confirmation_token: @token) %>

diff --git a/app/views/devise/mailer/email_changed.html.erb b/app/views/devise/mailer/email_changed.html.erb new file mode 100644 index 0000000..32f4ba8 --- /dev/null +++ b/app/views/devise/mailer/email_changed.html.erb @@ -0,0 +1,7 @@ +

Hello <%= @email %>!

+ +<% if @resource.try(:unconfirmed_email?) %> +

We're contacting you to notify you that your email is being changed to <%= @resource.unconfirmed_email %>.

+<% else %> +

We're contacting you to notify you that your email has been changed to <%= @resource.email %>.

+<% end %> diff --git a/app/views/devise/mailer/password_change.html.erb b/app/views/devise/mailer/password_change.html.erb new file mode 100644 index 0000000..b41daf4 --- /dev/null +++ b/app/views/devise/mailer/password_change.html.erb @@ -0,0 +1,3 @@ +

Hello <%= @resource.email %>!

+ +

We're contacting you to notify you that your password has been changed.

diff --git a/app/views/devise/mailer/reset_password_instructions.html.erb b/app/views/devise/mailer/reset_password_instructions.html.erb new file mode 100644 index 0000000..f667dc1 --- /dev/null +++ b/app/views/devise/mailer/reset_password_instructions.html.erb @@ -0,0 +1,8 @@ +

Hello <%= @resource.email %>!

+ +

Someone has requested a link to change your password. You can do this through the link below.

+ +

<%= link_to 'Change my password', edit_password_url(@resource, reset_password_token: @token) %>

+ +

If you didn't request this, please ignore this email.

+

Your password won't change until you access the link above and create a new one.

diff --git a/app/views/devise/mailer/unlock_instructions.html.erb b/app/views/devise/mailer/unlock_instructions.html.erb new file mode 100644 index 0000000..41e148b --- /dev/null +++ b/app/views/devise/mailer/unlock_instructions.html.erb @@ -0,0 +1,7 @@ +

Hello <%= @resource.email %>!

+ +

Your account has been locked due to an excessive number of unsuccessful sign in attempts.

+ +

Click the link below to unlock your account:

+ +

<%= link_to 'Unlock my account', unlock_url(@resource, unlock_token: @token) %>

diff --git a/app/views/devise/passwords/edit.html.erb b/app/views/devise/passwords/edit.html.erb new file mode 100644 index 0000000..3f1dbc7 --- /dev/null +++ b/app/views/devise/passwords/edit.html.erb @@ -0,0 +1,25 @@ +

Change your password

+ +<%= form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :put }) do |f| %> + <%= render "devise/shared/error_messages", resource: resource %> + <%= f.hidden_field :reset_password_token %> + +
+

<%= f.label :password, "New password" %>

+ <% if @minimum_password_length %> +

(<%= @minimum_password_length %> characters minimum)

+ <% end %> +

<%= f.password_field :password, autofocus: true, autocomplete: "new-password" %>

+
+ +
+

<%= f.label :password_confirmation, "Confirm new password" %>

+

<%= f.password_field :password_confirmation, autocomplete: "new-password" %>

+
+ +
+ <%= f.submit "Change my password" %> +
+<% end %> + +<%= render "devise/shared/links" %> diff --git a/app/views/devise/passwords/new.html.erb b/app/views/devise/passwords/new.html.erb new file mode 100644 index 0000000..bea7ad1 --- /dev/null +++ b/app/views/devise/passwords/new.html.erb @@ -0,0 +1,16 @@ +

Forgot your password?

+ +<%= form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :post }) do |f| %> + <%= render "devise/shared/error_messages", resource: resource %> + +
+

<%= f.label :email %>

+

<%= f.email_field :email, autofocus: true, autocomplete: "email" %>

+
+ +
+ <%= f.submit "Send me password reset instructions" %> +
+<% end %> + +<%= render "devise/shared/links" %> diff --git a/app/views/devise/registrations/edit.html.erb b/app/views/devise/registrations/edit.html.erb new file mode 100644 index 0000000..19bb019 --- /dev/null +++ b/app/views/devise/registrations/edit.html.erb @@ -0,0 +1,42 @@ +

Edit <%= resource_name.to_s.humanize %>

+ +<%= form_for(resource, as: resource_name, url: registration_path(resource_name), html: { method: :put }) do |f| %> + <%= render "devise/shared/error_messages", resource: resource %> + +
+

<%= f.label :email %>

+

<%= f.email_field :email, autofocus: true, autocomplete: "email" %>

+
+ + <% if devise_mapping.confirmable? && resource.pending_reconfirmation? %> +
Currently waiting confirmation for: <%= resource.unconfirmed_email %>
+ <% end %> + +
+

<%= f.label :password %> (leave blank if you don't want to change it)

+

<%= f.password_field :password, autocomplete: "new-password" %>

+ <% if @minimum_password_length %> +

<%= @minimum_password_length %> characters minimum

+ <% end %> +
+ +
+

<%= f.label :password_confirmation %>

+

<%= f.password_field :password_confirmation, autocomplete: "new-password" %>

+
+ +
+

<%= f.label :current_password %> (we need your current password to confirm your changes)

+

<%= f.password_field :current_password, autocomplete: "current-password" %>

+
+ +
+ <%= f.submit "Update" %> +
+<% end %> + +

Cancel my account

+ +
Unhappy? <%= button_to "Cancel my account", registration_path(resource_name), data: { confirm: "Are you sure?", turbo_confirm: "Are you sure?" }, method: :delete %>
+ +<%= link_to "Back", :back %> diff --git a/app/views/devise/registrations/new.html.erb b/app/views/devise/registrations/new.html.erb new file mode 100644 index 0000000..03f48fb --- /dev/null +++ b/app/views/devise/registrations/new.html.erb @@ -0,0 +1,29 @@ +

Sign up

+ +<%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %> + <%= render "devise/shared/error_messages", resource: resource %> + +
+

<%= f.label :email %>

+

<%= f.email_field :email, autofocus: true, autocomplete: "email" %>

+
+ +
+

<%= f.label :password %>

+ <% if @minimum_password_length %> +

(<%= @minimum_password_length %> characters minimum)

+ <% end %> +

<%= f.password_field :password, autocomplete: "new-password" %>

+
+ +
+

<%= f.label :password_confirmation %>

+

<%= f.password_field :password_confirmation, autocomplete: "new-password" %>

+
+ +
+ <%= f.submit "Sign up" %> +
+<% end %> + +<%= render "devise/shared/links" %> diff --git a/app/views/devise/sessions/new.html.erb b/app/views/devise/sessions/new.html.erb new file mode 100644 index 0000000..6eeb9fc --- /dev/null +++ b/app/views/devise/sessions/new.html.erb @@ -0,0 +1,26 @@ +

Log in

+ +<%= form_for(resource, as: resource_name, url: session_path(resource_name)) do |f| %> +
+

<%= f.label :email %>

+

<%= f.email_field :email, autofocus: true, autocomplete: "email" %>

+
+ +
+

<%= f.label :password %>

+

<%= f.password_field :password, autocomplete: "current-password" %>

+
+ + <% if devise_mapping.rememberable? %> +
+

<%= f.check_box :remember_me %>

+

<%= f.label :remember_me %>

+
+ <% end %> + +
+ <%= f.submit "Log in" %> +
+<% end %> + +<%= render "devise/shared/links" %> diff --git a/app/views/devise/shared/_error_messages.html.erb b/app/views/devise/shared/_error_messages.html.erb new file mode 100644 index 0000000..8c9c171 --- /dev/null +++ b/app/views/devise/shared/_error_messages.html.erb @@ -0,0 +1,15 @@ +<% if resource.errors.any? %> +
+

+ <%= I18n.t("errors.messages.not_saved", + count: resource.errors.count, + resource: resource.class.model_name.human.downcase) + %> +

+ +
+<% end %> diff --git a/app/views/devise/shared/_links.html.erb b/app/views/devise/shared/_links.html.erb new file mode 100644 index 0000000..21cf422 --- /dev/null +++ b/app/views/devise/shared/_links.html.erb @@ -0,0 +1,25 @@ +<%- if controller_name != 'sessions' %> +

<%= link_to "Log in", new_session_path(resource_name) %>

+<% end %> + +<%- if devise_mapping.registerable? && controller_name != 'registrations' %> +

<%= link_to "Sign up", new_registration_path(resource_name) %>

+<% end %> + +<%- if devise_mapping.recoverable? && controller_name != 'passwords' && controller_name != 'registrations' %> +

<%= link_to "Forgot your password?", new_password_path(resource_name) %>

+<% end %> + +<%- if devise_mapping.confirmable? && controller_name != 'confirmations' %> +

<%= link_to "Didn't receive confirmation instructions?", new_confirmation_path(resource_name) %>

+<% end %> + +<%- if devise_mapping.lockable? && resource_class.unlock_strategy_enabled?(:email) && controller_name != 'unlocks' %> +

<%= link_to "Didn't receive unlock instructions?", new_unlock_path(resource_name) %>

+<% end %> + +<%- if devise_mapping.omniauthable? %> + <%- resource_class.omniauth_providers.each do |provider| %> +

<%= button_to "Sign in with #{OmniAuth::Utils.camelize(provider)}", omniauth_authorize_path(resource_name, provider), data: { turbo: false } %>

+ <% end %> +<% end %> diff --git a/app/views/devise/unlocks/new.html.erb b/app/views/devise/unlocks/new.html.erb new file mode 100644 index 0000000..6b68d72 --- /dev/null +++ b/app/views/devise/unlocks/new.html.erb @@ -0,0 +1,16 @@ +

Resend unlock instructions

+ +<%= form_for(resource, as: resource_name, url: unlock_path(resource_name), html: { method: :post }) do |f| %> + <%= render "devise/shared/error_messages", resource: resource %> + +
+

<%= f.label :email %>

+

<%= f.email_field :email, autofocus: true, autocomplete: "email" %>

+
+ +
+ <%= f.submit "Resend unlock instructions" %> +
+<% end %> + +<%= render "devise/shared/links" %> diff --git a/app/views/home/index.html.erb b/app/views/home/index.html.erb index f76fc29..5f429f1 100644 --- a/app/views/home/index.html.erb +++ b/app/views/home/index.html.erb @@ -1,3 +1,10 @@

heading

Hi

-<%= image_tag "horse.jpg", alt: "Horse", class: "horse-image" %> \ No newline at end of file +<%= image_tag "horse.jpg", alt: "Horse", class: "horse-image" %> + +<% if user_signed_in? %> +

<%= link_to 'Sign out', destroy_user_session_path, data: { turbo_method: :delete } %>

+<% else %> +

<%= link_to 'Sign in', new_user_session_path %>

+<% end %> + diff --git a/config/initializers/devise.rb b/config/initializers/devise.rb index b67ddd2..1c2def7 100644 --- a/config/initializers/devise.rb +++ b/config/initializers/devise.rb @@ -143,7 +143,7 @@ # without confirming their account. # Default is 0.days, meaning the user cannot access the website without # confirming their account. - # config.allow_unconfirmed_access_for = 2.days + config.allow_unconfirmed_access_for = nil # A period that the user is allowed to confirm their account before their # token becomes invalid. For example, if set to 3.days, the user can confirm @@ -222,7 +222,7 @@ # ==> Configuration for :recoverable # # Defines which key will be used when recovering the password for an account - # config.reset_password_keys = [:email] + config.reset_password_keys = [:email] # Time interval you can reset your password with a reset password key. # Don't put a too small interval or your users won't have the time to @@ -312,5 +312,5 @@ # When set to false, does not sign a user in automatically after their password is # changed. Defaults to true, so a user is signed in automatically after changing a password. - # config.sign_in_after_change_password = true + config.sign_in_after_change_password = true end From dbaac71d65a0aaa4328a61dd39b2185a3b8795de Mon Sep 17 00:00:00 2001 From: Johncarlo Cerna Date: Sat, 29 Aug 2026 13:47:50 -0400 Subject: [PATCH 2/3] Add Sign Up page to homepage when not logged in --- app/views/home/index.html.erb | 1 + 1 file changed, 1 insertion(+) diff --git a/app/views/home/index.html.erb b/app/views/home/index.html.erb index 5f429f1..3485ac1 100644 --- a/app/views/home/index.html.erb +++ b/app/views/home/index.html.erb @@ -6,5 +6,6 @@

<%= link_to 'Sign out', destroy_user_session_path, data: { turbo_method: :delete } %>

<% else %>

<%= link_to 'Sign in', new_user_session_path %>

+

<%= link_to 'Sign up', new_user_registration_path %>

<% end %> From 6cede4657a4df21eb83ca725c407774629cceee6 Mon Sep 17 00:00:00 2001 From: Johncarlo Cerna Date: Sat, 29 Aug 2026 13:51:33 -0400 Subject: [PATCH 3/3] rubocop fixes --- config/initializers/devise.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/initializers/devise.rb b/config/initializers/devise.rb index 1c2def7..75117b6 100644 --- a/config/initializers/devise.rb +++ b/config/initializers/devise.rb @@ -222,7 +222,7 @@ # ==> Configuration for :recoverable # # Defines which key will be used when recovering the password for an account - config.reset_password_keys = [:email] + config.reset_password_keys = [ :email ] # Time interval you can reset your password with a reset password key. # Don't put a too small interval or your users won't have the time to