diff --git a/.travis.yml b/.travis.yml
index 48e05b4..fc77036 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -24,3 +24,4 @@ install:
script:
- bundle exec rubocop
- bundle exec rails test
+ - bundle exec rails test:system
diff --git a/Gemfile b/Gemfile
index b8b7a33..3a119d1 100644
--- a/Gemfile
+++ b/Gemfile
@@ -69,6 +69,7 @@ group :test do
gem 'selenium-webdriver'
# Easy installation and use of chromedriver to run system tests with Chrome
gem 'chromedriver-helper'
+ gem 'launchy'
end
# Windows does not include zoneinfo files, so bundle the tzinfo-data gem
diff --git a/app/helpers/users_helper.rb b/app/helpers/users_helper.rb
new file mode 100644
index 0000000..7aa200d
--- /dev/null
+++ b/app/helpers/users_helper.rb
@@ -0,0 +1,12 @@
+module UsersHelper
+ # Inject a devise template inside a same container
+ # while translation form keys are still valid
+ # (original partial scope is preserved)
+ def devise_form_container
+ content_for(:devise_form_content) do
+ yield
+ end
+
+ render "shared/devise_form_container"
+ end
+end
diff --git a/app/javascript/packs/src/application.scss b/app/javascript/packs/src/application.scss
index 5de3350..f6121ca 100644
--- a/app/javascript/packs/src/application.scss
+++ b/app/javascript/packs/src/application.scss
@@ -1 +1,2 @@
@import '~bootstrap/scss/bootstrap';
+@import 'components/users';
diff --git a/app/javascript/packs/src/components/users.scss b/app/javascript/packs/src/components/users.scss
new file mode 100644
index 0000000..dc42ca4
--- /dev/null
+++ b/app/javascript/packs/src/components/users.scss
@@ -0,0 +1,5 @@
+.new_user {
+ .form-check-label.boolean {
+ color: inherit;
+ }
+}
diff --git a/app/models/user.rb b/app/models/user.rb
new file mode 100644
index 0000000..791ed72
--- /dev/null
+++ b/app/models/user.rb
@@ -0,0 +1,35 @@
+# == Schema Information
+#
+# Table name: users
+#
+# id :bigint(8) not null, primary key
+# confirmation_sent_at :datetime
+# confirmation_token :string(255)
+# confirmed_at :datetime
+# current_sign_in_at :datetime
+# current_sign_in_ip :string(255)
+# email :string(255) default(""), not null
+# encrypted_password :string(255) default(""), not null
+# last_sign_in_at :datetime
+# last_sign_in_ip :string(255)
+# remember_created_at :datetime
+# reset_password_sent_at :datetime
+# reset_password_token :string(255)
+# sign_in_count :integer default(0), not null
+# unconfirmed_email :string(255)
+# created_at :datetime not null
+# updated_at :datetime not null
+#
+# Indexes
+#
+# index_users_on_confirmation_token (confirmation_token) UNIQUE
+# index_users_on_email (email) UNIQUE
+# index_users_on_reset_password_token (reset_password_token) UNIQUE
+#
+
+class User < ApplicationRecord
+ # Include default devise modules. Others available are:
+ # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
+ devise :database_authenticatable, :registerable,
+ :recoverable, :rememberable, :validatable, :confirmable
+end
diff --git a/app/views/devise/confirmations/new.html.erb b/app/views/devise/confirmations/new.html.erb
new file mode 100644
index 0000000..474e5a0
--- /dev/null
+++ b/app/views/devise/confirmations/new.html.erb
@@ -0,0 +1,12 @@
+<%= devise_form_container do %>
+
<%= t('.resend_confirmation_instructions') %>
+
+ <%= simple_form_for(resource, as: resource_name, url: confirmation_path(resource_name), html: { method: :post }) do |f| %>
+ <%= f.input :email, autofocus: true, autocomplete: "email",
+ label: (resource.pending_reconfirmation? ? resource.unconfirmed_email : resource.email) %>
+
+ <%= f.button :submit, t('.resend_confirmation_instructions'), class: "btn-primary" %>
+ <% end %>
+
+ <%= render "devise/shared/links" %>
+<% end %>
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..c57770b
--- /dev/null
+++ b/app/views/devise/mailer/confirmation_instructions.html.erb
@@ -0,0 +1,4 @@
+<%= t('.greeting', recipient: @email) %>
+
+<%= t('.instruction') %>
+<%= link_to t('.action'), 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..0402428
--- /dev/null
+++ b/app/views/devise/mailer/email_changed.html.erb
@@ -0,0 +1,7 @@
+<%= t('.greeting', recipient: @email) %>
+
+<% if @resource.try(:unconfirmed_email?) %>
+ <%= t('.message', email: @resource.unconfirmed_email) %>
+<% else %>
+ <%= t('.message', email: @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..a30313b
--- /dev/null
+++ b/app/views/devise/mailer/password_change.html.erb
@@ -0,0 +1,3 @@
+<%= t('.greeting', recipient: @resource.email) %>
+
+<%= t('.message') %>
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..6c22abf
--- /dev/null
+++ b/app/views/devise/mailer/reset_password_instructions.html.erb
@@ -0,0 +1,8 @@
+<%= t('.greeting', recipient: @resource.email) %>
+
+<%= t('.instruction') %>
+
+<%= link_to t('.action'), edit_password_url(@resource, reset_password_token: @token) %>
+
+<%= t('.instruction_2') %>
+<%= t('.instruction_3') %>
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..84d0981
--- /dev/null
+++ b/app/views/devise/mailer/unlock_instructions.html.erb
@@ -0,0 +1,7 @@
+<%= t('.greeting', recipient: @resource.email) %>
+
+<%= t('.message') %>
+
+<%= t('.instruction') %>
+
+<%= link_to t('.action'), 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..964ded5
--- /dev/null
+++ b/app/views/devise/passwords/edit.html.erb
@@ -0,0 +1,16 @@
+<%= devise_form_container do %>
+ <%= t('.change_your_password') %>
+
+ <%= simple_form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :put }) do |f| %>
+ <%= f.hidden_field :reset_password_token %>
+
+ <%= f.input :password, label: t('.new_password'), autocomplete: "off",
+ hint: t('devise.shared.minimum_password_length', count: @minimum_password_length) %>
+
+ <%= f.input :password_confirmation, label: t('.confirm_new_password'), autocomplete: "off" %>
+
+ <%= f.button :submit, t('.change_my_password'), class: "btn-primary" %>
+ <% end %>
+
+ <%= render "devise/shared/links" %>
+<% end %>
diff --git a/app/views/devise/passwords/new.html.erb b/app/views/devise/passwords/new.html.erb
new file mode 100644
index 0000000..0fefc18
--- /dev/null
+++ b/app/views/devise/passwords/new.html.erb
@@ -0,0 +1,11 @@
+<%= devise_form_container do %>
+ <%= t('.forgot_your_password') %>
+
+ <%= simple_form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :post }) do |f| %>
+ <%= f.input :email, autofocus: true, autocomplete: "email" %>
+
+ <%= f.button :submit, t('.send_me_reset_password_instructions'), class: "btn-primary" %>
+ <% end %>
+
+ <%= render "devise/shared/links" %>
+<% end %>
diff --git a/app/views/devise/registrations/edit.html.erb b/app/views/devise/registrations/edit.html.erb
new file mode 100644
index 0000000..9d99eb0
--- /dev/null
+++ b/app/views/devise/registrations/edit.html.erb
@@ -0,0 +1,40 @@
+<%= devise_form_container do %>
+ <%= t('.title', resource: resource_name.to_s.humanize) %>
+
+
+ <%= simple_form_for(resource, as: resource_name, url: registration_path(resource_name), html: { method: :put }) do |f| %>
+ <%= f.input :email, autofocus: true, autocomplete: "email" %>
+
+ <% if devise_mapping.confirmable? && resource.pending_reconfirmation? %>
+
+ <%= t('.currently_waiting_confirmation_for_email', email: resource.unconfirmed_email) %>
+
+ <% end %>
+
+ <%= f.input :current_password,
+ autocomplete: "off",
+ hint: t('.we_need_your_current_password_to_confirm_your_changes') %>
+
+
+
Want to change your password ?
+ <%= f.input :password,
+ autocomplete: "off",
+ hint: (t('devise.shared.minimum_password_length', count: @minimum_password_length)
+ + " " + t('.leave_blank_if_you_don_t_want_to_change_it'))%>
+
+ <%= f.input :password_confirmation, autocomplete: "off" %>
+
+
+
+ <%= f.button :submit, t('.update'), class: "btn-primary" %>
+ <% end %>
+
+ <%= t('.cancel_my_account') %>
+
+ <%= t('.unhappy') %> <%=
+ button_to t('.cancel_my_account'), registration_path(resource_name),
+ class: "btn btn-danger",
+ data: { confirm: t('.are_you_sure') }, method: :delete %>
+
+ <%= link_to t('devise.shared.links.back'), :back %>
+<% end %>
diff --git a/app/views/devise/registrations/new.html.erb b/app/views/devise/registrations/new.html.erb
new file mode 100644
index 0000000..ed81d21
--- /dev/null
+++ b/app/views/devise/registrations/new.html.erb
@@ -0,0 +1,16 @@
+<%= devise_form_container do %>
+ <%= t('.sign_up') %>
+
+ <%= simple_form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %>
+ <%= f.input :email, autofocus: true, autocomplete: "email" %>
+
+ <%= f.input :password, autocomplete: "off",
+ hint: t('devise.shared.minimum_password_length', count: @minimum_password_length) %>
+
+ <%= f.input :password_confirmation, autocomplete: "off" %>
+
+ <%= f.button :submit, t('.sign_up'), class: "btn-primary" %>
+ <% end %>
+
+ <%= render "devise/shared/links" %>
+<% end %>
diff --git a/app/views/devise/sessions/new.html.erb b/app/views/devise/sessions/new.html.erb
new file mode 100644
index 0000000..a40f03a
--- /dev/null
+++ b/app/views/devise/sessions/new.html.erb
@@ -0,0 +1,16 @@
+<%= devise_form_container do %>
+ <%= t('.sign_in') %>
+
+ <%= simple_form_for(resource, as: resource_name, url: session_path(resource_name)) do |f| %>
+ <%= f.input :email, autofocus: true, autocomplete: "email" %>
+ <%= f.input :password, autocomplete: "off" %>
+
+ <% if devise_mapping.rememberable? -%>
+ <%= f.input :remember_me, as: :boolean %>
+ <% end -%>
+
+ <%= f.button :submit, t('.sign_in'), class: "btn-primary" %>
+ <% end %>
+
+ <%= render "devise/shared/links" %>
+<% 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..f07506a
--- /dev/null
+++ b/app/views/devise/shared/_links.html.erb
@@ -0,0 +1,25 @@
+<%- if controller_name != 'sessions' %>
+ <%= link_to t(".sign_in"), new_session_path(resource_name) %>
+<% end -%>
+
+<%- if devise_mapping.registerable? && controller_name != 'registrations' %>
+ <%= link_to t(".sign_up"), new_registration_path(resource_name) %>
+<% end -%>
+
+<%- if devise_mapping.recoverable? && controller_name != 'passwords' && controller_name != 'registrations' %>
+ <%= link_to t(".forgot_your_password"), new_password_path(resource_name) %>
+<% end -%>
+
+<%- if devise_mapping.confirmable? && controller_name != 'confirmations' %>
+ <%= link_to t('.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 t('.didn_t_receive_unlock_instructions'), new_unlock_path(resource_name) %>
+<% end -%>
+
+<%- if devise_mapping.omniauthable? %>
+ <%- resource_class.omniauth_providers.each do |provider| %>
+ <%= link_to t('.sign_in_with_provider', provider: OmniAuth::Utils.camelize(provider)), omniauth_authorize_path(resource_name, provider) %>
+ <% 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..ea83e83
--- /dev/null
+++ b/app/views/devise/unlocks/new.html.erb
@@ -0,0 +1,11 @@
+<%= devise_form_container do %>
+ <%= t('.resend_unlock_instructions') %>
+
+ <%= simple_form_for(resource, as: resource_name, url: unlock_path(resource_name), html: { method: :post }) do |f| %>
+ <%= f.input :email, autofocus: true, autocomplete: "email" %>
+
+ <%= f.button :submit, t('.resend_unlock_instructions'), class: "btn-primary" %>
+ <% end %>
+
+ <%= render "devise/shared/links" %>
+<% end %>
diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb
index c8596d2..36ba294 100644
--- a/app/views/layouts/application.html.erb
+++ b/app/views/layouts/application.html.erb
@@ -10,7 +10,9 @@
+ <%= render "shared/navbar" %>
<%= render "shared/notices" %>
+
<%= yield %>