bullet_train 1.0.2 → 1.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/account/invitations_controller.rb +1 -144
  3. data/app/controllers/account/memberships_controller.rb +1 -130
  4. data/app/controllers/account/onboarding/user_details_controller.rb +1 -61
  5. data/app/controllers/account/onboarding/user_email_controller.rb +1 -63
  6. data/app/controllers/account/teams_controller.rb +1 -120
  7. data/app/controllers/account/users_controller.rb +1 -57
  8. data/app/controllers/concerns/account/invitations/controller_base.rb +150 -0
  9. data/app/controllers/concerns/account/memberships/controller_base.rb +136 -0
  10. data/app/controllers/concerns/account/onboarding/user_details/controller_base.rb +67 -0
  11. data/app/controllers/concerns/account/onboarding/user_email/controller_base.rb +69 -0
  12. data/app/controllers/concerns/account/teams/controller_base.rb +126 -0
  13. data/app/controllers/concerns/account/users/controller_base.rb +63 -0
  14. data/app/controllers/concerns/registrations/controller_base.rb +39 -0
  15. data/app/controllers/concerns/sessions/controller_base.rb +15 -0
  16. data/app/controllers/registrations_controller.rb +9 -0
  17. data/app/controllers/sessions_controller.rb +3 -0
  18. data/app/helpers/account/buttons_helper.rb +12 -0
  19. data/app/helpers/account/dates_helper.rb +38 -0
  20. data/app/helpers/account/forms_helper.rb +67 -0
  21. data/app/helpers/account/locale_helper.rb +51 -0
  22. data/app/helpers/account/markdown_helper.rb +5 -0
  23. data/app/helpers/account/role_helper.rb +5 -0
  24. data/app/helpers/attributes_helper.rb +32 -0
  25. data/app/helpers/email_helper.rb +7 -0
  26. data/app/helpers/images_helper.rb +7 -0
  27. data/app/mailers/concerns/mailers/base.rb +16 -0
  28. data/app/mailers/devise_mailer.rb +10 -0
  29. data/app/mailers/user_mailer.rb +24 -0
  30. data/app/models/concerns/{invitation.rb → invitations/base.rb} +2 -2
  31. data/app/models/concerns/{membership.rb → memberships/base.rb} +2 -2
  32. data/app/models/concerns/{team.rb → teams/base.rb} +2 -2
  33. data/app/models/concerns/{user.rb → users/base.rb} +2 -2
  34. data/app/models/invitations.rb +5 -0
  35. data/app/models/memberships.rb +5 -0
  36. data/app/models/teams.rb +5 -0
  37. data/app/models/users.rb +5 -0
  38. data/app/views/devise/confirmations/new.html.erb +16 -0
  39. data/app/views/devise/mailer/confirmation_instructions.html.erb +5 -0
  40. data/app/views/devise/mailer/password_change.html.erb +3 -0
  41. data/app/views/devise/mailer/reset_password_instructions.html.erb +30 -0
  42. data/app/views/devise/mailer/unlock_instructions.html.erb +7 -0
  43. data/app/views/devise/passwords/edit.html.erb +17 -0
  44. data/app/views/devise/passwords/new.html.erb +20 -0
  45. data/app/views/devise/registrations/_two_factor.html.erb +42 -0
  46. data/app/views/devise/registrations/edit.html.erb +43 -0
  47. data/app/views/devise/registrations/new.html.erb +30 -0
  48. data/app/views/devise/sessions/new.html.erb +59 -0
  49. data/app/views/devise/sessions/pre_otp.js.erb +11 -0
  50. data/app/views/devise/shared/_links.html.erb +21 -0
  51. data/app/views/devise/shared/_oauth.html.erb +9 -0
  52. data/app/views/devise/unlocks/new.html.erb +16 -0
  53. data/config/locales/en/devise.en.yml +110 -0
  54. data/config/routes.rb +40 -0
  55. data/lib/bullet_train/version.rb +1 -1
  56. metadata +48 -6
@@ -0,0 +1,12 @@
1
+ module Account::ButtonsHelper
2
+ def first_button_primary(context = nil)
3
+ @global ||= {}
4
+
5
+ if !@global[context]
6
+ @global[context] = true
7
+ "button"
8
+ else
9
+ "button-secondary"
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,38 @@
1
+ module Account::DatesHelper
2
+ # e.g. October 11, 2018
3
+ def display_date(timestamp)
4
+ return nil unless timestamp
5
+ if local_time(timestamp).year == local_time(Time.now).year
6
+ local_time(timestamp).strftime("%B %-d")
7
+ else
8
+ local_time(timestamp).strftime("%B %-d, %Y")
9
+ end
10
+ end
11
+
12
+ # e.g. October 11, 2018 at 4:22 PM
13
+ # e.g. Yesterday at 2:12 PM
14
+ # e.g. April 24 at 7:39 AM
15
+ def display_date_and_time(timestamp)
16
+ return nil unless timestamp
17
+
18
+ # today?
19
+ if local_time(timestamp).to_date == local_time(Time.now).to_date
20
+ "Today at #{display_time(timestamp)}"
21
+ # yesterday?
22
+ elsif (local_time(timestamp).to_date) == (local_time(Time.now).to_date - 1.day)
23
+ "Yesterday at #{display_time(timestamp)}"
24
+ else
25
+ "#{display_date(timestamp)} at #{display_time(timestamp)}"
26
+ end
27
+ end
28
+
29
+ # e.g. 4:22 PM
30
+ def display_time(timestamp)
31
+ local_time(timestamp).strftime("%l:%M %p")
32
+ end
33
+
34
+ def local_time(time)
35
+ return time if current_user.time_zone.nil?
36
+ time.in_time_zone(current_user.time_zone)
37
+ end
38
+ end
@@ -0,0 +1,67 @@
1
+ module Account::FormsHelper
2
+ PRESENCE_VALIDATORS = [ActiveRecord::Validations::PresenceValidator, ActiveModel::Validations::PresenceValidator]
3
+
4
+ def presence_validated?(object, attribute)
5
+ validators = object.class.validators
6
+ validators.select! do |validator|
7
+ PRESENCE_VALIDATORS.include?(validator.class) && validator.attributes.include?(attribute)
8
+ end
9
+ validators.any?
10
+ end
11
+
12
+ def flush_content_for(name)
13
+ content_for name, flush: true do
14
+ ""
15
+ end
16
+ end
17
+
18
+ def options_with_labels(options, namespace)
19
+ hash = {}
20
+ options.each do |option|
21
+ hash[option] = t([namespace, option].join("."))
22
+ end
23
+ hash
24
+ end
25
+
26
+ def if_present(string)
27
+ string.present? ? string : nil
28
+ end
29
+
30
+ def id_for(form, method)
31
+ [form.object.class.name, form.index, method].compact.join("_").underscore
32
+ end
33
+
34
+ def model_key(form)
35
+ form.object.class.name.pluralize.underscore
36
+ end
37
+
38
+ def labels_for(form, method)
39
+ keys = [:placeholder, :label, :help, :options_help]
40
+ path = [model_key(form), (current_fields_namespace || :fields), method].compact
41
+ Struct.new(*keys).new(*keys.map { |key| t((path + [key]).join("."), default: "").presence })
42
+ end
43
+
44
+ def options_for(form, method)
45
+ # e.g. "scaffolding/completely_concrete/tangible_things.fields.text_area_value.options"
46
+ path = [model_key(form), (current_fields_namespace || :fields), method, :options]
47
+ t(path.compact.join("."))
48
+ end
49
+
50
+ def legacy_label_for(form, method)
51
+ # e.g. 'scaffolding/things.labels.name'
52
+ key = "#{model_key(form)}.labels.#{method}"
53
+ # e.g. 'scaffolding/things.labels.name' or 'scaffolding.things.labels.name' or nil
54
+ t(key, default: "").presence || t(key.tr("/", "."), default: "").presence
55
+ end
56
+
57
+ def within_fields_namespace(namespace)
58
+ @fields_namespaces ||= []
59
+ @fields_namespaces << namespace
60
+ yield
61
+ @fields_namespaces.pop
62
+ end
63
+
64
+ def current_fields_namespace
65
+ @fields_namespaces&.last
66
+ end
67
+ end
@@ -0,0 +1,51 @@
1
+ module Account::LocaleHelper
2
+ def current_locale
3
+ current_user.locale || current_team.locale || "en"
4
+ end
5
+
6
+ # as of now, we only calculate a possessive version of nouns in english.
7
+ # if you're aware of another language where we can do this, please don't hesitate to reach out!
8
+ def possessive_string(string)
9
+ [:en].include?(I18n.locale) ? string.possessive : string
10
+ end
11
+
12
+ def model_locales(model)
13
+ name = model.label_string.presence
14
+ return {} unless name
15
+
16
+ hash = {}
17
+ prefix = model.class.name.split("::").last.underscore
18
+ hash[:"#{prefix}_name"] = name
19
+ hash[:"#{prefix.pluralize}_possessive"] = possessive_string(name)
20
+
21
+ hash
22
+ end
23
+
24
+ def models_locales(*models)
25
+ hash = {}
26
+ models.compact.each do |model|
27
+ hash.merge! model_locales(model)
28
+ end
29
+ hash
30
+ end
31
+
32
+ # this is a bit scary, no?
33
+ def account_controller?
34
+ controller.class.name.match(/^Account::/)
35
+ end
36
+
37
+ def t(key, options = {})
38
+ if account_controller?
39
+ # give preference to the options they've passed in.
40
+ options = models_locales(@child_object, @parent_object).merge(options)
41
+ end
42
+ super(key, options)
43
+ end
44
+
45
+ # like 't', but if the key isn't found, it returns nil.
46
+ def ot(key, options = {})
47
+ t(key, options)
48
+ rescue I18n::MissingTranslationData => _
49
+ nil
50
+ end
51
+ end
@@ -0,0 +1,5 @@
1
+ module Account::MarkdownHelper
2
+ def markdown(string)
3
+ CommonMarker.render_html(string, :UNSAFE, [:table]).html_safe
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ module Account::RoleHelper
2
+ def role_options_for(object)
3
+ object.class.assignable_roles.map { |role| [role.id, t("#{object.class.to_s.pluralize.underscore}.fields.role_ids.options.#{role.key}.label")] }
4
+ end
5
+ end
@@ -0,0 +1,32 @@
1
+ module AttributesHelper
2
+ def current_attributes_object
3
+ @_attributes_helper_objects ? @_attributes_helper_objects.last : nil
4
+ end
5
+
6
+ def current_attributes_strategy
7
+ @_attributes_helper_strategies ? @_attributes_helper_strategies.last : nil
8
+ end
9
+
10
+ def with_attribute_settings(options)
11
+ @_attributes_helper_objects ||= []
12
+ @_attributes_helper_strategies ||= []
13
+
14
+ if options[:object]
15
+ @_attributes_helper_objects << options[:object]
16
+ end
17
+
18
+ if options[:strategy]
19
+ @_attributes_helper_strategies << options[:strategy]
20
+ end
21
+
22
+ yield
23
+
24
+ if options[:strategy]
25
+ @_attributes_helper_strategies.pop
26
+ end
27
+
28
+ if options[:object]
29
+ @_attributes_helper_objects.pop
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,7 @@
1
+ module EmailHelper
2
+ def email_image_tag(image, **options)
3
+ image_underscore = image.tr("-", "_")
4
+ attachments.inline[image_underscore] = File.read(Rails.root.join("app/javascript/images/#{image}"))
5
+ image_tag attachments.inline[image_underscore].url, **options
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ module ImagesHelper
2
+ def image_width_for_height(filename, target_height)
3
+ source_width, source_height = FastImage.size("#{Rails.root}/app/javascript/images/#{filename}")
4
+ ratio = source_width.to_f / source_height.to_f
5
+ (target_height * ratio).to_i
6
+ end
7
+ end
@@ -0,0 +1,16 @@
1
+ module Mailers::Base
2
+ extend ActiveSupport::Concern
3
+
4
+ included do
5
+ default from: "#{I18n.t("application.name")} <#{I18n.t("application.support_email")}>"
6
+ layout "mailer"
7
+
8
+ helper :email
9
+ helper :application
10
+ helper :images
11
+ helper "account/teams"
12
+ helper "account/users"
13
+ helper "account/locale"
14
+ helper "fields/trix_editor"
15
+ end
16
+ end
@@ -0,0 +1,10 @@
1
+ class DeviseMailer < Devise::Mailer
2
+ def headers_for(action, opts)
3
+ headers = super(action, opts)
4
+ if resource.full_name.present?
5
+ headers[:to] = "\"#{resource.full_name}\" <#{resource.email}>"
6
+ @email = headers[:to]
7
+ end
8
+ headers
9
+ end
10
+ end
@@ -0,0 +1,24 @@
1
+ class UserMailer < ApplicationMailer
2
+ def welcome(user)
3
+ @user = user
4
+ @cta_url = account_dashboard_url
5
+ @values = {
6
+ # are there any substitution values you want to include?
7
+ }
8
+ mail(to: @user.email, subject: I18n.t("user_mailer.welcome.subject", **@values))
9
+ end
10
+
11
+ # technically not a 'user' email, but they'll be a user soon.
12
+ # didn't seem worth creating an entirely new mailer for.
13
+ def invited(uuid)
14
+ @invitation = Invitation.find_by_uuid uuid
15
+ return if @invitation.nil?
16
+ @cta_url = accept_account_invitation_url(@invitation.uuid)
17
+ @values = {
18
+ # Just in case the inviting user has been removed from the team...
19
+ inviter_name: @invitation.from_membership&.user&.full_name || @invitation.from_membership.name,
20
+ team_name: @invitation.team.name,
21
+ }
22
+ mail(to: @invitation.email, subject: I18n.t("user_mailer.invited.subject", **@values))
23
+ end
24
+ end
@@ -1,7 +1,7 @@
1
- module Invitations::Core
1
+ module Invitations::Base
2
2
  extend ActiveSupport::Concern
3
3
 
4
- include do
4
+ included do
5
5
  belongs_to :team
6
6
  belongs_to :from_membership, class_name: "Membership"
7
7
  has_one :membership, dependent: :nullify
@@ -1,7 +1,7 @@
1
- module Memberships::Core
1
+ module Memberships::Base
2
2
  extend ActiveSupport::Concern
3
3
 
4
- include do
4
+ included do
5
5
  # See `docs/permissions.md` for details.
6
6
  include Roles::Support
7
7
 
@@ -1,7 +1,7 @@
1
- module Teams::Core
1
+ module Teams::Base
2
2
  extend ActiveSupport::Concern
3
3
 
4
- include do
4
+ included do
5
5
  # super scaffolding
6
6
  unless scaffolding_things_disabled?
7
7
  has_many :scaffolding_absolutely_abstract_creative_concepts, class_name: "Scaffolding::AbsolutelyAbstract::CreativeConcept", dependent: :destroy, enable_updates: true
@@ -1,7 +1,7 @@
1
- module Users::Core
1
+ module Users::Base
2
2
  extend ActiveSupport::Concern
3
3
 
4
- include do
4
+ included do
5
5
  if two_factor_authentication_enabled?
6
6
  devise :two_factor_authenticatable, :two_factor_backupable, :omniauthable,
7
7
  :registerable, :recoverable, :rememberable, :trackable, :validatable,
@@ -0,0 +1,5 @@
1
+ module Invitations
2
+ def self.table_name_prefix
3
+ "users_"
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ module Memberships
2
+ def self.table_name_prefix
3
+ "memberships_"
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ module Teams
2
+ def self.table_name_prefix
3
+ "teams_"
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ module Users
2
+ def self.table_name_prefix
3
+ "users_"
4
+ end
5
+ end
@@ -0,0 +1,16 @@
1
+ <h2><%= t('device.headers.resend_confirm') %></h2>
2
+
3
+ <%= form_for(resource, as: resource_name, url: confirmation_path(resource_name), html: { method: :post }) do |f| %>
4
+ <%= devise_error_messages! %>
5
+
6
+ <div class="field">
7
+ <%= f.label :email %><br />
8
+ <%= f.email_field :email, autofocus: true, value: (resource.pending_reconfirmation? ? resource.unconfirmed_email : resource.email) %>
9
+ </div>
10
+
11
+ <div class="actions">
12
+ <%= f.submit t('devise.buttons.resend_confirm') %>
13
+ </div>
14
+ <% end %>
15
+
16
+ <%= render "devise/shared/links" %>
@@ -0,0 +1,5 @@
1
+ <p><%= t('.welcome', email: @email) %></p>
2
+
3
+ <p><%= t('.description') %></p>
4
+
5
+ <p><%= link_to t('.confirm_account'), confirmation_url(@resource, confirmation_token: @token) %></p>
@@ -0,0 +1,3 @@
1
+ <p><%= t('.hello', email: @resource.email) %></p>
2
+
3
+ <p><%= t('.description') %></p>
@@ -0,0 +1,30 @@
1
+ <p><%= t('.hello', email: @resource.first_name || @resource.email) %></p>
2
+
3
+ <p><%= t('.requested_change') %></p>
4
+
5
+ <!-- Action -->
6
+ <table class="body-action" align="center" width="100%" cellpadding="0" cellspacing="0">
7
+ <tr>
8
+ <td align="center">
9
+ <!-- Border based button https://litmus.com/blog/a-guide-to-bulletproof-buttons-in-email-design -->
10
+ <table width="100%" border="0" cellspacing="0" cellpadding="0">
11
+ <tr>
12
+ <td align="center">
13
+ <table border="0" cellspacing="0" cellpadding="0">
14
+ <tr>
15
+ <td>
16
+ <%= link_to t('.change_password'), edit_password_url(@resource, reset_password_token: @token), class: 'button button--', target: '_blank' %>
17
+ </td>
18
+ </tr>
19
+ </table>
20
+ </td>
21
+ </tr>
22
+ </table>
23
+ </td>
24
+ </tr>
25
+ </table>
26
+
27
+ <p><%= t('.ignore') %></p>
28
+ <p><%= t('.access_change') %></p>
29
+
30
+ <%= t('user_mailer.signature.html', support_email: t('application.support_email')) %>
@@ -0,0 +1,7 @@
1
+ <p><% t('.hello', email: @resource.email) %></p>
2
+
3
+ <p><%= t('.account_blocked') %></p>
4
+
5
+ <p><%= t('.click_link') %></p>
6
+
7
+ <p><%= link_to t('.unlock'), unlock_url(@resource, unlock_token: @token) %></p>
@@ -0,0 +1,17 @@
1
+ <% @title = t('devise.headers.change_password') %>
2
+ <%= render 'account/shared/workflow/box' do |p| %>
3
+ <% p.content_for :title, @title %>
4
+ <% p.content_for :body do %>
5
+ <% within_fields_namespace(:update_self) do %>
6
+ <%= form_for(resource, as: resource_name, url: password_path(resource_name), class: 'form', html: { method: :put }) do |f| %>
7
+ <%= f.hidden_field :reset_password_token %>
8
+ <%= render 'account/shared/forms/errors', form: f, attributes: [:reset_password_token] %>
9
+ <%= render 'shared/fields/password_field', form: f, method: :password, options: {autofocus: true, autocomplete: "off"} %>
10
+ <%= render 'shared/fields/password_field', form: f, method: :password_confirmation, options: {autocomplete: "off"} %>
11
+ <div class="buttons">
12
+ <%= f.submit t('devise.buttons.change_password'), class: "button" %>
13
+ </div>
14
+ <% end %>
15
+ <% end %>
16
+ <% end %>
17
+ <% end %>
@@ -0,0 +1,20 @@
1
+ <%= render 'account/shared/workflow/box' do |p| %>
2
+ <% p.content_for :title, t('devise.titles.reset_password') %>
3
+ <% p.content_for :body do %>
4
+ <% within_fields_namespace(:sign_up) do %>
5
+ <%= form_for resource, as: resource_name, url: password_path(resource_name), html: {method: :post, class: 'form'} do |f| %>
6
+ <%= render 'account/shared/forms/errors', form: f %>
7
+
8
+ <%= render 'shared/fields/email_field', form: f, method: :email, options: {autofocus: true} do %>
9
+ <% if show_sign_up_options? %>
10
+ <% content_for :help do %>
11
+ <%= link_to t('devise.links.account'), new_user_registration_path %>
12
+ <% end %>
13
+ <% end %>
14
+ <% end %>
15
+
16
+ <%= f.submit t('devise.buttons.reset_password'), class: 'button full' %>
17
+ <% end %>
18
+ <% end %>
19
+ <% end %>
20
+ <% end %>
@@ -0,0 +1,42 @@
1
+ <%= render 'account/shared/box', divider: @backup_codes do |p| %>
2
+ <% p.content_for :title, t("users.edit.two_factor.header") %>
3
+ <% p.content_for :description, t("users.edit.two_factor.description_#{@user.otp_required_for_login? ? 'enabled' : 'disabled'}") %>
4
+ <% p.content_for :body do %>
5
+ <% if current_user.otp_required_for_login? %>
6
+ <% if @backup_codes %>
7
+
8
+ <%= render 'account/shared/alert' do %>
9
+ <%= t('users.edit.two_factor.warning').html_safe %>
10
+ <% end %>
11
+
12
+ <p><%= t('users.edit.two_factor.instructions').html_safe %></p>
13
+
14
+ <center class="py-4">
15
+ <%= current_user.otp_qr_code.as_svg(
16
+ offset: 0,
17
+ color: '000',
18
+ shape_rendering: 'crispEdges',
19
+ module_size: 4,
20
+ standalone: true
21
+ ).html_safe %>
22
+ </center>
23
+
24
+ <p><%= t('users.edit.two_factor.recovery_codes').html_safe %></p>
25
+
26
+ <center>
27
+ <% @backup_codes.each do |code| %>
28
+ <p><code><%= code %></code></p>
29
+ <% end %>
30
+ </center>
31
+
32
+ <% end %>
33
+ <% end %>
34
+ <% end %>
35
+ <% p.content_for :actions do %>
36
+ <% if current_user.otp_required_for_login? %>
37
+ <%= link_to t('users.edit.two_factor.buttons.disable'), account_two_factor_path, method: :delete, remote: true, class: "button" %>
38
+ <% else %>
39
+ <%= link_to t('users.edit.two_factor.buttons.enable'), account_two_factor_path, method: :post, remote: true, class: "button" %>
40
+ <% end %>
41
+ <% end %>
42
+ <% end %>
@@ -0,0 +1,43 @@
1
+ <h2><%= t('devise.headers.edit_registration', resource_name: resource_name.to_s.humanize) %></h2>
2
+
3
+ <%= form_for(resource, as: resource_name, url: registration_path(resource_name), html: { method: :put }) do |f| %>
4
+ <%= devise_error_messages! %>
5
+
6
+ <div class="field">
7
+ <%= f.label :email %><br />
8
+ <%= f.email_field :email, autofocus: true %>
9
+ </div>
10
+
11
+ <% if devise_mapping.confirmable? && resource.pending_reconfirmation? %>
12
+ <div><%= t('devise.labels.wait_confirm', unconfirmed_email: resource.unconfirmed_email) %></div>
13
+ <% end %>
14
+
15
+ <div class="field">
16
+ <%= f.label :password %> <i><%= t('devise.hints.leave_blank') %></i><br />
17
+ <%= f.password_field :password, autocomplete: "off" %>
18
+ <% if @minimum_password_length %>
19
+ <br />
20
+ <em><%= t('devise.hints.password_length', length: @minimum_password_length) %></em>
21
+ <% end %>
22
+ </div>
23
+
24
+ <div class="field">
25
+ <%= f.label :password_confirmation %><br />
26
+ <%= f.password_field :password_confirmation, autocomplete: "off" %>
27
+ </div>
28
+
29
+ <div class="field">
30
+ <%= f.label :current_password %> <i><%= t('devise.hints.need_password') %></i><br />
31
+ <%= f.password_field :current_password, autocomplete: "off" %>
32
+ </div>
33
+
34
+ <div class="actions">
35
+ <%= f.submit t('global.buttons.update') %>
36
+ </div>
37
+ <% end %>
38
+
39
+ <h3><%= t('devise.headers.cancel_account') %></h3>
40
+
41
+ <p>Unhappy? <%= button_to t('devise.buttons.cancel_account'), registration_path(resource_name), data: { confirm: t('global.confirm_message') }, method: :delete %></p>
42
+
43
+ <%= link_to t('global.buttons.back'), :back %>
@@ -0,0 +1,30 @@
1
+ <%= render 'account/shared/workflow/box' do |p| %>
2
+ <% p.content_for :title, t('devise.headers.create_account') %>
3
+ <% p.content_for :body do %>
4
+ <% within_fields_namespace(:sign_up) do %>
5
+ <%= form_for resource, as: resource_name, url: registration_path(resource_name), html: {class: 'form'} do |f| %>
6
+ <%= render 'account/shared/notices' %>
7
+ <%= render 'account/shared/forms/errors', form: f %>
8
+
9
+ <%= render 'shared/fields/email_field', form: f, method: :email, options: {autofocus: true} do %>
10
+ <% content_for :help do %>
11
+ <%= link_to t('devise.links.have_account'), new_user_session_path %>
12
+ <% end %>
13
+ <% end %>
14
+
15
+ <div class="grid grid-cols-2 gap-5">
16
+ <div>
17
+ <%= render 'shared/fields/password_field', form: f, method: :password %>
18
+ </div>
19
+ <div>
20
+ <%= render 'shared/fields/password_field', form: f, method: :password_confirmation, other_options: {error: f.object.errors.full_messages_for(:password).first, hide_custom_error: true} %>
21
+ </div>
22
+ </div>
23
+
24
+ <%= f.submit t('global.buttons.sign_up'), class: 'button full' %>
25
+
26
+ <%= render 'devise/shared/oauth', verb: 'Sign Up' %>
27
+ <% end %>
28
+ <% end %>
29
+ <% end %>
30
+ <% end %>
@@ -0,0 +1,59 @@
1
+
2
+
3
+ <%= render 'account/shared/workflow/box' do |p| %>
4
+ <% p.content_for :title, t('devise.headers.sign_in') %>
5
+ <% p.content_for :body do %>
6
+ <% within_fields_namespace(:self) do %>
7
+ <%= form_for resource, as: resource_name, url: two_factor_authentication_enabled? ? users_pre_otp_path : session_path(resource_name), remote: two_factor_authentication_enabled?, html: {class: 'form'}, authenticity_token: true do |form| %>
8
+ <% with_field_settings form: form do %>
9
+ <%= render 'account/shared/notices', form: form %>
10
+ <%= render 'account/shared/forms/errors', form: form %>
11
+
12
+ <% email_field = capture do %>
13
+ <%= render 'shared/fields/email_field', method: :email, options: {autofocus: true} do %>
14
+ <% if show_sign_up_options? %>
15
+ <% content_for :help do %>
16
+ <%= link_to t('devise.links.account'), new_user_registration_path %>
17
+ <% end %>
18
+ <% end %>
19
+ <% end %>
20
+ <% end %>
21
+
22
+ <% if two_factor_authentication_enabled? %>
23
+ <div id="step-1" class="space-y">
24
+ <%= email_field %>
25
+ <%= form.submit t('global.buttons.next'), class: 'button full' %>
26
+ </div>
27
+ <% else %>
28
+ <%= email_field %>
29
+ <% end %>
30
+
31
+ <div id="step-2" class="<%= 'hidden' if two_factor_authentication_enabled? %> space-y">
32
+ <%= render 'shared/fields/password_field', method: :password do %>
33
+ <% content_for :help do %>
34
+ <%= link_to t('devise.links.forgot_password'), new_user_password_path %>
35
+ <% end %>
36
+ <% end %>
37
+
38
+ <% if two_factor_authentication_enabled? %>
39
+ <div id="step-2-otp" class="hidden">
40
+ <%= render 'shared/fields/text_field', method: :otp_attempt %>
41
+ </div>
42
+ <% end %>
43
+
44
+ <%= form.submit t('global.buttons.sign_in'), class: 'button full' %>
45
+ </div>
46
+
47
+ <div class="flex items-center">
48
+ <input id="remember_me" name="remember_me" type="checkbox" class="h-4 w-4 text-blue focus:ring-blue-dark border-gray-300 rounded">
49
+ <label for="remember_me" class="ml-2 block">
50
+ Remember me
51
+ </label>
52
+ </div>
53
+ <% end %>
54
+ <% end %>
55
+ <% end %>
56
+
57
+ <%= render 'devise/shared/oauth' %>
58
+ <% end %>
59
+ <% end %>