bullet_train 1.1.9 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/concerns/account/invitations/controller_base.rb +3 -11
  3. data/app/controllers/concerns/account/users/controller_base.rb +4 -25
  4. data/app/controllers/concerns/documentation_support.rb +1 -1
  5. data/app/controllers/concerns/registrations/controller_base.rb +9 -6
  6. data/app/controllers/sessions_controller.rb +9 -0
  7. data/app/helpers/account/teams_helper.rb +1 -1
  8. data/app/helpers/concerns/helpers/base.rb +0 -9
  9. data/app/javascript/controllers/index.js +2 -0
  10. data/app/javascript/controllers/select_all_controller.js +82 -0
  11. data/app/models/concerns/memberships/base.rb +3 -7
  12. data/app/models/concerns/users/base.rb +3 -0
  13. data/app/views/account/memberships/_index.html.erb +4 -35
  14. data/app/views/account/memberships/_membership.html.erb +29 -0
  15. data/app/views/account/memberships/index.html.erb +6 -1
  16. data/app/views/account/memberships/show.html.erb +1 -1
  17. data/app/views/account/onboarding/user_details/edit.html.erb +6 -6
  18. data/app/views/account/teams/_index.html.erb +1 -25
  19. data/app/views/account/teams/_team.html.erb +23 -0
  20. data/app/views/account/teams/index.html.erb +3 -1
  21. data/app/views/account/teams/show.html.erb +1 -1
  22. data/app/views/devise/registrations/new.html.erb +2 -2
  23. data/app/views/devise/sessions/new.html.erb +1 -1
  24. data/app/views/layouts/docs.html.erb +12 -0
  25. data/config/locales/en/base.yml +1 -0
  26. data/config/locales/en/framework_packages.yml +13 -17
  27. data/docs/application-options.md +29 -0
  28. data/docs/authentication.md +9 -0
  29. data/docs/field-partials/file-field.md +25 -0
  30. data/docs/field-partials.md +3 -1
  31. data/docs/i18n.md +28 -0
  32. data/docs/index.md +2 -0
  33. data/docs/invitation_only.md +1 -1
  34. data/docs/themes.md +13 -40
  35. data/docs/trademark.md +25 -0
  36. data/docs/two-factor-authentication.md +16 -0
  37. data/lib/bullet_train/resolver.rb +47 -0
  38. data/lib/bullet_train/version.rb +1 -1
  39. data/lib/bullet_train.rb +6 -1
  40. data/lib/colorizer.rb +1 -1
  41. data/lib/tasks/bullet_train_tasks.rake +74 -12
  42. metadata +9 -4
  43. data/app/assets/javascripts/bullet-train.js +0 -2
  44. data/app/assets/javascripts/bullet-train.js.map +0 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4b2f6e8a8bd9f4303bd21b03d411b0623a8a334858e767a2495648bc826c5e9f
4
- data.tar.gz: abaf4b1241d0d8e501baf65b9fca1aee0d12030fc54c47e83e7d017f20dd0b40
3
+ metadata.gz: 4bb9dc7bc4b2ac1de4645e63ad3cf5da7bf2f4020365bf85efca8de4a4440a45
4
+ data.tar.gz: a328f4be111b4439a89ed0895bd92d16848fe81d9237e43c1f5def3759bed27a
5
5
  SHA512:
6
- metadata.gz: e65877ac21f2a4109b8a0461af3e68f9773d0c31aa85bb1a2b8d9b95a444965057d36f539bdb2921bd2bba9da77bd028614e468b9188135fd8aa7357c5908a9c
7
- data.tar.gz: a766131072e8b221f08d6c21eea4106688cb6b0ea5297730b2ecbb9c0c28f8fcfb5af3424739ffd734f31e1b0c9682b911ba9d0f26dbcf442b0b51857590acbb
6
+ metadata.gz: 4f5892c3867e7ddb2530abe9516e004939c3dc8d35d1347d40e224a6e3d10d912fbb60d8cdc5ed62dcba895a459935ca1e7f3dd33b1b53f6dc31754de867895a
7
+ data.tar.gz: d2c9875372791ca7cf424da1141db44f1f31c2cb3b60cb6001ab80bf30a87bc63b7e52dd8c2808a9c8fcc412c9ee9419b8e137eb60f75439c3b5b30d0dd53811
@@ -49,17 +49,9 @@ module Account::Invitations::ControllerBase
49
49
 
50
50
  # unless the user is signed in.
51
51
  if !current_user.present?
52
- # keep track of the uuid of the invitation so we can reload it
53
- # after they sign up. at this point we don't even know if it's
54
- # valid, but that's fine.
55
- session[:invitation_uuid] = params[:id]
56
-
57
- # also, we'll queue devise up to return to the invitation url after a sign in.
58
- session["user_return_to"] = request.path
59
-
60
- # assume the user needs to create an account.
61
- # this is not the default for devise, but a sensible default here.
62
- redirect_to new_user_registration_path
52
+ # We need them to register.
53
+ # We have to send `invitation_uuid` via params, not session, because Safari doesn't set cookies on redirect.
54
+ redirect_to new_user_registration_path(invitation_uuid: @invitation&.uuid)
63
55
 
64
56
  # session[:invitation_uuid] should only be present if the user is registering for the first time.
65
57
  elsif (@invitation = Invitation.find_by(uuid: session[:invitation_uuid] || params[:id]))
@@ -10,6 +10,10 @@ module Account::Users::ControllerBase
10
10
  # for magic locales.
11
11
  @child_object = @user
12
12
  end
13
+
14
+ private
15
+
16
+ include strong_parameters_from_api
13
17
  end
14
18
 
15
19
  # GET /account/users/1/edit
@@ -53,29 +57,4 @@ module Account::Users::ControllerBase
53
57
  def process_params(strong_params)
54
58
  raise "It looks like you've removed `process_params` from your controller. This will break Super Scaffolding."
55
59
  end
56
-
57
- # Never trust parameters from the scary internet, only allow the white list through.
58
- # TODO Update this to use `include strong_parameters_from_api`.
59
- def user_params
60
- # TODO enforce permissions on updating the user's team name.
61
- strong_params = params.require(:user).permit(
62
- *([
63
- :email,
64
- :first_name,
65
- :last_name,
66
- :time_zone,
67
- :current_password,
68
- :password,
69
- :password_confirmation,
70
- :profile_photo_id,
71
- :locale,
72
- ] + permitted_fields + [
73
- {
74
- current_team_attributes: [:name]
75
- }.merge(permitted_arrays)
76
- ])
77
- )
78
-
79
- process_params(strong_params)
80
- end
81
60
  end
@@ -4,7 +4,7 @@ module DocumentationSupport
4
4
  def docs
5
5
  target = params[:page].presence || "index"
6
6
  all_paths = ([Rails.root.to_s] + `bundle show --paths`.lines.map(&:chomp))
7
- @path = all_paths.map { |path| path + "/docs/#{target}.md" }.detect { |path| File.exists?(path) }
7
+ @path = all_paths.map { |path| path + "/docs/#{target}.md" }.detect { |path| File.exist?(path) }
8
8
  render :docs, layout: "docs"
9
9
  end
10
10
  end
@@ -3,6 +3,12 @@ module Registrations::ControllerBase
3
3
 
4
4
  included do
5
5
  def new
6
+ # We have to set the session here because Safari wouldn't save it on a redirect to this URL.
7
+ if params[:invitation_uuid]
8
+ session[:invitation_uuid] = params[:invitation_uuid]
9
+ session["user_return_to"] = accept_account_invitation_path(params[:invitation_uuid])
10
+ end
11
+
6
12
  if invitation_only?
7
13
  unless session[:invitation_uuid] || session[:invitation_key]
8
14
  return redirect_to root_path
@@ -19,17 +25,14 @@ module Registrations::ControllerBase
19
25
 
20
26
  # if current_user is defined, that means they were successful registering.
21
27
  if current_user
22
- # if the user doesn't have a team at this point, create one.
23
- # If the user is accepting an invitation, then the user's current_team is populated
24
- # with the information attached to their invitation via `@invitation.accept_for` later on,
25
- # so we don't have to create a default team for them here.
26
- unless current_user.teams.any? || session[:invitation_uuid].present?
28
+ # Don't create a default team if they're being invited to another team.
29
+ # Don't create a default team if they have another one for any reason.
30
+ unless session[:invitation_uuid].present? || current_user.teams.any?
27
31
  current_user.create_default_team
28
32
  end
29
33
 
30
34
  # send the welcome email.
31
35
  current_user.send_welcome_email unless current_user.email_is_oauth_placeholder?
32
-
33
36
  end
34
37
  end
35
38
  end
@@ -1,6 +1,15 @@
1
1
  class SessionsController < Devise::SessionsController
2
2
  include Sessions::ControllerBase
3
3
 
4
+ # If user_return_to points to an oauth path we disable Turbo on the sign in form.
5
+ # This makes it work when we need to redirect to external sites and/or custom protocols.
6
+ # With Turbo enabled the browser will block those redirects with a CORS error.
7
+ # https://github.com/bullet-train-co/bullet_train/issues/384
8
+ def user_return_to_is_oauth
9
+ session["user_return_to"]&.match(/^\/oauth/)
10
+ end
11
+ helper_method :user_return_to_is_oauth
12
+
4
13
  def destroy
5
14
  if params.include?(:onboard_logout)
6
15
  signed_out = (Devise.sign_out_all_scopes ? sign_out : sign_out(resource_name))
@@ -2,7 +2,7 @@ module Account::TeamsHelper
2
2
  def current_team
3
3
  # TODO We do not want this to be based on the `current_team_id`.
4
4
  # TODO We want this to be based on the current resource being loaded.
5
- current_user&.current_team
5
+ @team || current_user&.current_team
6
6
  end
7
7
 
8
8
  def other_teams
@@ -6,13 +6,4 @@ module Helpers::Base
6
6
  # This scope has an order if the SQL changes when we remove any order clause.
7
7
  scope.to_sql != scope.reorder("").to_sql
8
8
  end
9
-
10
- # TODO This should really be in the API package and included from there.
11
- if defined?(BulletTrain::Api)
12
- def render_pagination(json)
13
- if @pagy
14
- json.has_more @pagy.has_more
15
- end
16
- end
17
- end
18
9
  end
@@ -6,6 +6,7 @@ import ClipboardController from './clipboard_controller'
6
6
  import FormController from './form_controller'
7
7
  import MobileMenuController from './mobile_menu_controller'
8
8
  import TextToggleController from './text_toggle_controller'
9
+ import SelectAllController from './select_all_controller'
9
10
 
10
11
  export const controllerDefinitions = [
11
12
  [BulkActionFormController, 'bulk_action_form_controller.js'],
@@ -14,6 +15,7 @@ export const controllerDefinitions = [
14
15
  [FormController, 'form_controller.js'],
15
16
  [MobileMenuController, 'mobile_menu_controller.js'],
16
17
  [TextToggleController, 'text_toggle_controller.js'],
18
+ [SelectAllController, 'select_all_controller.js'],
17
19
  ].map(function(d) {
18
20
  const key = d[1]
19
21
  const controller = d[0]
@@ -0,0 +1,82 @@
1
+ import { Controller } from "@hotwired/stimulus";
2
+
3
+ export default class extends Controller {
4
+ static targets = [ "checkbox", "toggleCheckbox", "toggleLabel", "wrapper" ]
5
+ static classes = [ "unavailable" ]
6
+
7
+ connect() {
8
+ this.enableSelectAll()
9
+ }
10
+
11
+ enableSelectAll() {
12
+ if (!this.hasWrapperTarget) { return }
13
+ if (!this.hasUnavailableClass) { return }
14
+
15
+ this.wrapperTarget.classList.remove(this.unavailableClass)
16
+ this.updateToggle()
17
+ }
18
+
19
+ selectAllOrNone(event) {
20
+ event.preventDefault()
21
+ event.stopPropagation()
22
+ if (this.allSelected) {
23
+ this.selectNone()
24
+ } else {
25
+ this.selectAll()
26
+ }
27
+ this.updateToggle()
28
+ this.dispatch('toggled')
29
+ }
30
+
31
+ selectAll() {
32
+ this.checkboxTargets.forEach(checkbox => {
33
+ checkbox.checked = true
34
+ })
35
+ }
36
+
37
+ selectNone() {
38
+ this.checkboxTargets.forEach(checkbox => {
39
+ checkbox.checked = false
40
+ })
41
+ }
42
+
43
+ updateToggle() {
44
+ let checkbox = this.toggleCheckboxTarget
45
+ let useAlternateLabel = false
46
+
47
+ if (this.allSelected) {
48
+ if (checkbox) {
49
+ checkbox.checked = true
50
+ checkbox.indeterminate = false
51
+ }
52
+ useAlternateLabel = true
53
+ } else if (this.selectedValues.length > 0) {
54
+ if (checkbox) {
55
+ checkbox.indeterminate = true
56
+ }
57
+ } else {
58
+ if (checkbox) {
59
+ checkbox.checked = false
60
+ checkbox.indeterminate = false
61
+ }
62
+ }
63
+
64
+ if (this.hasToggleLabelTarget) {
65
+ this.toggleLabelTarget.dispatchEvent(new CustomEvent(`${this.identifier}:toggle-select-all-label`, { detail: { useAlternate: useAlternateLabel }} ))
66
+ }
67
+ }
68
+
69
+ get selectedValues() {
70
+ let values = []
71
+ this.checkboxTargets.forEach(checkbox => {
72
+ if (checkbox.checked) {
73
+ values.push(checkbox.value)
74
+ }
75
+ })
76
+ return values
77
+ }
78
+
79
+ get allSelected() {
80
+ return this.selectedValues.length === this.checkboxTargets.length
81
+ }
82
+ }
@@ -27,7 +27,7 @@ module Memberships::Base
27
27
 
28
28
  scope :current_and_invited, -> { includes(:invitation).where("user_id IS NOT NULL OR invitations.id IS NOT NULL").references(:invitation) }
29
29
  scope :current, -> { where("user_id IS NOT NULL") }
30
- scope :tombstones, -> { includes(:invitation).where("user_id IS NULL AND invitations.id IS NULL").references(:invitation) }
30
+ scope :tombstones, -> { includes(:invitation).where("user_id IS NULL AND invitations.id IS NULL AND platform_agent IS FALSE").references(:invitation) }
31
31
 
32
32
  # TODO Probably we can provide a way for gem packages to define these kinds of extensions.
33
33
  if billing_enabled?
@@ -62,7 +62,7 @@ module Memberships::Base
62
62
  end
63
63
 
64
64
  def tombstone?
65
- user.nil? && invitation.nil? && !platform_agent?
65
+ user.nil? && invitation.nil? && !platform_agent
66
66
  end
67
67
 
68
68
  def last_admin?
@@ -130,7 +130,7 @@ module Memberships::Base
130
130
  end
131
131
 
132
132
  def first_name_last_initial
133
- [first_name, last_initial].map(&:present?).join(" ")
133
+ [first_name, last_initial].select(&:present?).join(" ")
134
134
  end
135
135
 
136
136
  # TODO utilize this.
@@ -138,8 +138,4 @@ module Memberships::Base
138
138
  def should_receive_notifications?
139
139
  invitation.present? || user.present?
140
140
  end
141
-
142
- def platform_agent?
143
- platform_agent_of_id.present?
144
- end
145
141
  end
@@ -109,10 +109,12 @@ module Users::Base
109
109
  end
110
110
  end
111
111
 
112
+ # TODO https://github.com/bullet-train-co/bullet_train-base/pull/121 should have removed this, but it caused errors.
112
113
  def administrating_team_ids
113
114
  parent_ids_for(Role.admin, :memberships, :team)
114
115
  end
115
116
 
117
+ # TODO https://github.com/bullet-train-co/bullet_train-base/pull/121 should have removed this, but it caused errors.
116
118
  def parent_ids_for(role, through, parent)
117
119
  parent_id_column = "#{parent}_id"
118
120
  key = "#{role.key}_#{through}_#{parent_id_column}s"
@@ -125,6 +127,7 @@ module Users::Base
125
127
  value
126
128
  end
127
129
 
130
+ # TODO https://github.com/bullet-train-co/bullet_train-base/pull/121 should have removed this, but it caused errors.
128
131
  def invalidate_ability_cache
129
132
  update_column(:ability_cache, {})
130
133
  end
@@ -6,12 +6,12 @@
6
6
  <%= render 'account/shared/box' do |p| %>
7
7
  <% p.content_for :title, t(".contexts.#{context.class.name.underscore}.header") %>
8
8
  <% p.content_for :description do %>
9
- <%= raw t(".contexts.#{context.class.name.underscore}.#{memberships.any? ? 'description' : 'description_empty'}") %>
10
- <%= render "shared/limits/index", model: memberships.model %>
9
+ <%= raw t(".contexts.#{context.class.name.underscore}.#{@memberships.any? ? 'description' : 'description_empty'}") %>
10
+ <%= render "shared/limits/index", model: @memberships.model %>
11
11
  <% end %>
12
12
 
13
13
  <% p.content_for :table do %>
14
- <% if memberships.any? %>
14
+ <% if @memberships.any? %>
15
15
  <table class="table">
16
16
  <thead>
17
17
  <tr>
@@ -22,38 +22,7 @@
22
22
  </tr>
23
23
  </thead>
24
24
  <tbody data-model="Membership" data-scope="current">
25
- <% memberships.each do |membership| %>
26
- <tr data-id="<%= membership.id %>">
27
-
28
- <td class="px-6 py-4 whitespace-nowrap">
29
- <%= link_to [:account, membership], class: 'block flex items-center group hover:no-underline no-underline' do %>
30
- <div class="flex-shrink-0 h-10 w-10">
31
- <%= image_tag membership_profile_photo_url(membership), title: membership.label_string, class: 'h-10 w-10 rounded-full' %>
32
- </div>
33
-
34
- <div class="ml-3">
35
- <span class="group-hover:underline"><%= membership.label_string %></span>
36
- <% if membership.unclaimed? %>
37
- <span class="ml-1.5 px-2 inline-flex text-xs text-green-dark bg-green-light border border-green-dark rounded-md">
38
- Invited
39
- </span>
40
- <% end %>
41
- </div>
42
- <% end %>
43
- </td>
44
-
45
- <td>
46
- <% if membership.roles_without_defaults.any? %>
47
- <%= membership.roles_without_defaults.map { |role| t("memberships.fields.role_ids.options.#{role.key}.label") }.to_sentence %>
48
- <% else %>
49
- <%= t("memberships.fields.role_ids.options.default.label") %>
50
- <% end %>
51
- </td>
52
- <td class="text-right">
53
- <%= link_to t('.buttons.show'), [:account, membership], class: 'button-secondary button-smaller' %>
54
- </td>
55
- </tr>
56
- <% end %>
25
+ <%= yield %>
57
26
  </tbody>
58
27
  </table>
59
28
  <% end %>
@@ -0,0 +1,29 @@
1
+ <tr data-id="<%= membership.id %>">
2
+ <td class="px-6 py-4 whitespace-nowrap">
3
+ <%= link_to [:account, membership], class: 'block flex items-center group hover:no-underline no-underline' do %>
4
+ <div class="flex-shrink-0 h-10 w-10">
5
+ <%= image_tag membership_profile_photo_url(membership), title: membership.label_string, class: 'h-10 w-10 rounded-full' %>
6
+ </div>
7
+
8
+ <div class="ml-3">
9
+ <span class="group-hover:underline"><%= membership.label_string %></span>
10
+ <% if membership.unclaimed? %>
11
+ <span class="ml-1.5 px-2 inline-flex text-xs text-green-dark bg-green-light border border-green-dark rounded-md">
12
+ Invited
13
+ </span>
14
+ <% end %>
15
+ </div>
16
+ <% end %>
17
+ </td>
18
+
19
+ <td>
20
+ <% if membership.roles_without_defaults.any? %>
21
+ <%= membership.roles_without_defaults.map { |role| t("memberships.fields.role_ids.options.#{role.key}.label") }.to_sentence %>
22
+ <% else %>
23
+ <%= t("memberships.fields.role_ids.options.default.label") %>
24
+ <% end %>
25
+ </td>
26
+ <td class="text-right">
27
+ <%= link_to t('.buttons.show'), [:account, membership], class: 'button-secondary button-smaller' %>
28
+ </td>
29
+ </tr>
@@ -1,7 +1,12 @@
1
1
  <%= render 'account/shared/page' do |p| %>
2
2
  <% p.content_for :title, t('.section') %>
3
3
  <% p.content_for :body do %>
4
- <%= render 'index', memberships: @memberships.current_and_invited.includes(:user) if @memberships.current_and_invited.any? %>
4
+ <% if @memberships.current_and_invited.any? %>
5
+ <%= render 'index' do %>
6
+ <%= render @memberships.current_and_invited.includes(:user) %>
7
+ <% end %>
8
+ <% end %>
9
+
5
10
  <%= render 'tombstones', memberships: @memberships.tombstones.includes(:user) if @memberships.tombstones.any? %>
6
11
  <% end %>
7
12
  <% end %>
@@ -46,7 +46,7 @@
46
46
  <% else %>
47
47
  <%= link_to t('.buttons.promote'), [:promote, :account, @membership], method: :post, data: { confirm: t('global.confirm_message') }, class: first_button_primary if can? :promote, @membership %>
48
48
  <% end %>
49
- <% if (can? :destroy, @membership) && (!@membership.platform_agent?) %>
49
+ <% if (can? :destroy, @membership) && (!@membership.platform_agent) %>
50
50
  <%= button_to t(".buttons.#{membership_destroy_locale_key(@membership)}"), [:account, @membership], method: :delete, data: { confirm: t(".buttons.confirmations.#{membership_destroy_locale_key(@membership)}", model_locales(@membership)) }, class: first_button_primary %>
51
51
  <% end %>
52
52
  <% end %>
@@ -16,15 +16,15 @@
16
16
  <%= render 'shared/fields/text_field', form: f, method: :last_name %>
17
17
  </div>
18
18
 
19
- <div class="sm:col-span-2">
20
- <% # only edit the team name if this user is an admin and they are the first user. %>
21
- <% # yes, that's redundant. %>
22
- <% if can?(:edit, f.object.current_team) && f.object.current_team.users.count == 1 %>
19
+ <% # only edit the team name if this user is an admin and they are the first user. %>
20
+ <% # yes, that's redundant. %>
21
+ <% if can?(:edit, f.object.current_team) && f.object.current_team.users.count == 1 %>
22
+ <div class="sm:col-span-2">
23
23
  <%= f.fields_for :current_team do |tf| %>
24
24
  <%= render 'shared/fields/text_field', form: tf, method: :name %>
25
25
  <% end %>
26
- <% end %>
27
- </div>
26
+ </div>
27
+ <% end %>
28
28
 
29
29
  <div class="sm:col-span-2">
30
30
  <%= render 'shared/fields/super_select', form: f, method: :time_zone,
@@ -1,29 +1,5 @@
1
1
  <ul class="space-y">
2
- <% @teams.each do |team| %>
3
- <li class="bg-white shadow overflow-hidden sm:rounded-md dark:bg-sealBlue-400">
4
- <%= link_to [:account, team], class: "group block hover:bg-gray-50 dark:hover:bg-sealBlue-400 dark:text-sealBlue-800" do %>
5
- <div class="px-4 py-4 flex items-center sm:pl-8 sm:pr-6">
6
- <div class="min-w-0 flex-1 sm:flex sm:items-center sm:justify-between">
7
- <div>
8
- <div class="flex text-xl font-semibold text-blue uppercase group-hover:text-blue-dark tracking-widest dark:text-white">
9
- <%= team.name %>
10
- </div>
11
- </div>
12
- <div class="mt-4 flex-shrink-0 sm:mt-0">
13
- <div class="flex overflow-hidden">
14
- <%= render 'account/shared/memberships/photos', memberships: team.memberships.current_and_invited.first(10) %>
15
- </div>
16
- </div>
17
- </div>
18
- <div class="ml-5 flex-shrink-0">
19
- <svg class="h-5 w-5 text-gray-400" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
20
- <path fill-rule="evenodd" d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" clip-rule="evenodd" />
21
- </svg>
22
- </div>
23
- </div>
24
- <% end %>
25
- </li>
26
- <% end %>
2
+ <%= yield %>
27
3
  </ul>
28
4
 
29
5
  <% if show_sign_up_options? && can?(:create, Team.new) %>
@@ -0,0 +1,23 @@
1
+ <li class="bg-white shadow overflow-hidden sm:rounded-md dark:bg-sealBlue-400">
2
+ <%= link_to [:account, team], class: "group block hover:bg-gray-50 dark:hover:bg-sealBlue-400 dark:text-sealBlue-800" do %>
3
+ <div class="px-4 py-4 flex items-center sm:pl-8 sm:pr-6">
4
+ <div class="min-w-0 flex-1 sm:flex sm:items-center sm:justify-between">
5
+ <div>
6
+ <div class="flex text-xl font-semibold text-blue uppercase group-hover:text-blue-dark tracking-widest dark:text-white">
7
+ <%= team.name %>
8
+ </div>
9
+ </div>
10
+ <div class="mt-4 flex-shrink-0 sm:mt-0">
11
+ <div class="flex overflow-hidden">
12
+ <%= render 'account/shared/memberships/photos', memberships: team.memberships.current_and_invited.first(10) %>
13
+ </div>
14
+ </div>
15
+ </div>
16
+ <div class="ml-5 flex-shrink-0">
17
+ <svg class="h-5 w-5 text-gray-400" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
18
+ <path fill-rule="evenodd" d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" clip-rule="evenodd" />
19
+ </svg>
20
+ </div>
21
+ </div>
22
+ <% end %>
23
+ </li>
@@ -1,6 +1,8 @@
1
1
  <%= render 'account/shared/page' do |p| %>
2
2
  <% p.content_for :title, t('.section') %>
3
3
  <% p.content_for :body do %>
4
- <%= render 'index', creative_concepts: @creative_concepts %>
4
+ <%= render 'index', creative_concepts: @creative_concepts do %>
5
+ <%= render @teams %>
6
+ <% end %>
5
7
  <% end %>
6
8
  <% end %>
@@ -13,7 +13,7 @@
13
13
  <% unless scaffolding_things_disabled? %>
14
14
  <%= render 'account/shared/commentary/box' do |p| %>
15
15
  <% p.content_for :content do %>
16
- <%= render 'account/scaffolding/absolutely_abstract/creative_concepts/index', creative_concepts: @team.scaffolding_absolutely_abstract_creative_concepts.accessible_by(current_ability), hide_back: true unless scaffolding_things_disabled? %>
16
+ <%= render 'account/scaffolding/absolutely_abstract/creative_concepts/index', creative_concepts: @team.scaffolding_absolutely_abstract_creative_concepts, hide_back: true unless scaffolding_things_disabled? %>
17
17
  <% end %>
18
18
 
19
19
  <% p.content_for :commentary do %>
@@ -22,9 +22,9 @@
22
22
  </div>
23
23
 
24
24
  <%= f.submit t('global.buttons.sign_up'), class: 'button full' %>
25
-
26
- <%= render 'devise/shared/oauth', verb: 'Sign Up' %>
27
25
  <% end %>
26
+
27
+ <%= render 'devise/shared/oauth', verb: 'Sign Up' %>
28
28
  <% end %>
29
29
  <% end %>
30
30
  <% end %>
@@ -2,7 +2,7 @@
2
2
  <% p.content_for :title, t('devise.headers.sign_in') %>
3
3
  <% p.content_for :body do %>
4
4
  <% within_fields_namespace(:self) do %>
5
- <%= 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| %>
5
+ <%= 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, data: {turbo: !user_return_to_is_oauth} do |form| %>
6
6
  <% with_field_settings form: form do %>
7
7
  <%= render 'account/shared/notices', form: form %>
8
8
  <%= render 'account/shared/forms/errors', form: form %>
@@ -108,6 +108,12 @@
108
108
  <i class="fa-brands fa-js ti ti-pulse"></i>
109
109
  <% end %>
110
110
  <% end %>
111
+
112
+ <%= render 'account/shared/menu/item', url: '/docs/i18n', label: 'Internationalzation' do |p| %>
113
+ <% p.content_for :icon do %>
114
+ <i class="fa-brands fa-js ti ti-world"></i>
115
+ <% end %>
116
+ <% end %>
111
117
  <% end %>
112
118
 
113
119
  <%= render 'account/shared/menu/section', title: 'Developer Tools' do %>
@@ -140,6 +146,12 @@
140
146
  <i class="fal fa-check ti ti-video-camera"></i>
141
147
  <% end %>
142
148
  <% end %>
149
+
150
+ <%= render 'account/shared/menu/item', url: 'docs/application-options.md', label: 'Application Options' do |p| %>
151
+ <% p.content_for :icon do %>
152
+ <i class="fal fa-gear ti ti-settings"></i>
153
+ <% end %>
154
+ <% end %>
143
155
  <% end %>
144
156
 
145
157
  <%= render 'account/shared/menu/section', title: 'Accounts & Teams' do %>
@@ -77,6 +77,7 @@ en:
77
77
  one: Last Day
78
78
  other: Last %{count} Days
79
79
  formats:
80
+ timestamp_unavailable: Never
80
81
  date: '%m/%d/%Y'
81
82
  date_and_time: '%m/%d/%Y %l:%M %p'
82
83
 
@@ -6,38 +6,34 @@ en:
6
6
  bullet_train-api:
7
7
  git: "bullet-train-co/bullet_train-api"
8
8
  bullet_train-fields:
9
- git: "bullet-train-co/bullet_train-fields"
9
+ git: "bullet-train-co/bullet_train-core"
10
10
  npm: "@bullet-train/fields"
11
11
  bullet_train-has_uuid:
12
- git: "bullet-train-co/bullet_train-has_uuid"
12
+ git: "bullet-train-co/bullet_train-core"
13
13
  bullet_train-incoming_webhooks:
14
- git: "bullet-train-co/bullet_train-incoming_webhooks"
14
+ git: "bullet-train-co/bullet_train-core"
15
15
  bullet_train-integrations:
16
- git: "bullet-train-co/bullet_train-integrations"
16
+ git: "bullet-train-co/bullet_train-core"
17
17
  bullet_train-integrations-stripe:
18
- git: "bullet-train-co/bullet_train-base-integrations-stripe"
18
+ git: "bullet-train-co/bullet_train-core"
19
19
  bullet_train-obfuscates_id:
20
- git: "bullet-train-co/bullet_train-obfuscates_id"
20
+ git: "bullet-train-co/bullet_train-core"
21
21
  bullet_train-outgoing_webhooks:
22
22
  git: "bullet-train-co/bullet_train-outgoing_webhooks"
23
- bullet_train-outgoing_webhooks-core:
24
- git: "bullet-train-co/bullet_train-outgoing_webhooks-core"
25
23
  bullet_train-scope_questions:
26
- git: "bullet-train-co/bullet_train-scope_questions"
24
+ git: "bullet-train-co/bullet_train-core"
27
25
  bullet_train-scope_validator:
28
- git: "bullet-train-co/bullet_train-scope_validator"
26
+ git: "bullet-train-co/bullet_train-core"
29
27
  bullet_train-sortable:
30
- git: "bullet-train-co/bullet_train-sortable"
28
+ git: "bullet-train-co/bullet_train-core"
31
29
  npm: "@bullet-train/bullet-train-sortable"
32
30
  bullet_train-super_scaffolding:
33
31
  git: "bullet-train-co/bullet_train-super_scaffolding"
34
32
  bullet_train-super_load_and_authorize_resource:
35
- git: "bullet-train-co/bullet_train-super_load_and_authorize_resource"
33
+ git: "bullet-train-co/bullet_train-core"
36
34
  bullet_train-themes:
37
- git: "bullet-train-co/bullet_train-themes"
38
- bullet_train-themes-base:
39
- git: "bullet-train-co/bullet_train-themes-base"
35
+ git: "bullet-train-co/bullet_train-core"
40
36
  bullet_train-themes-light:
41
- git: "bullet-train-co/bullet_train-themes-light"
37
+ git: "bullet-train-co/bullet_train-core"
42
38
  bullet_train-themes-tailwind_css:
43
- git: "bullet-train-co/bullet_train-themes-tailwind_css"
39
+ git: "bullet-train-co/bullet_train-core"