Skip to content

Commit 8d275ab

Browse files
committed
feat(platform_invitations): add index action and view for platform invitations management
Improve performance of platform show view by pulling platform invitations into lazy-loaded turbo frame
1 parent 8363c53 commit 8d275ab

File tree

10 files changed

+477
-185
lines changed

10 files changed

+477
-185
lines changed

app/controllers/better_together/platform_invitations_controller.rb

Lines changed: 55 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,34 @@ class PlatformInvitationsController < ApplicationController # rubocop:todo Style
77
before_action :set_platform_invitation, only: %i[destroy resend]
88
after_action :verify_authorized
99

10+
before_action only: %i[index], if: -> { Rails.env.development? } do
11+
# Make sure that all Platform Invitation subclasses are loaded in dev to generate new block buttons
12+
::BetterTogether::PlatformInvitation.load_all_subclasses
13+
end
14+
15+
# GET /platforms/:platform_id/platform_invitations
16+
def index
17+
authorize BetterTogether::PlatformInvitation
18+
19+
# Use optimized query with all necessary includes to prevent N+1
20+
@platform_invitations = policy_scope(@platform.invitations)
21+
.includes(
22+
{ inviter: [:string_translations] },
23+
{ invitee: [:string_translations] }
24+
)
25+
26+
# Preload roles for the form to prevent N+1 queries during rendering
27+
@community_roles = BetterTogether::Role.where(resource_type: 'BetterTogether::Community')
28+
.includes(:string_translations)
29+
.order(:position)
30+
@platform_roles = BetterTogether::Role.where(resource_type: 'BetterTogether::Platform')
31+
.includes(:string_translations)
32+
.order(:position)
33+
34+
# Find the default community member role for the hidden field
35+
@default_community_role = @community_roles.find_by(identifier: 'community_member')
36+
end
37+
1038
# POST /platforms/:platform_id/platform_invitations
1139
def create # rubocop:todo Metrics/AbcSize, Metrics/MethodLength
1240
@platform_invitation = @platform.invitations.new(platform_invitation_params) do |pi|
@@ -26,22 +54,27 @@ def create # rubocop:todo Metrics/AbcSize, Metrics/MethodLength
2654
format.turbo_stream do
2755
render turbo_stream: [
2856
turbo_stream.prepend('platform_invitations_table_body',
29-
# rubocop:todo Layout/LineLength
30-
partial: 'better_together/platform_invitations/platform_invitation', locals: { platform_invitation: @platform_invitation }),
31-
# rubocop:enable Layout/LineLength
32-
turbo_stream.replace('flash_messages', partial: 'layouts/better_together/flash_messages',
33-
locals: { flash: })
57+
partial: 'better_together/platform_invitations/platform_invitation',
58+
locals: { platform_invitation: @platform_invitation }),
59+
turbo_stream.replace('flash_messages',
60+
partial: 'layouts/better_together/flash_messages',
61+
locals: { flash: })
3462
]
3563
end
3664
else
3765
flash.now[:alert] = t('flash.generic.error_create', resource: t('resources.invitation'))
38-
format.html { redirect_to @platform, alert: @platform_invitation.errors.full_messages.to_sentence }
66+
format.html do
67+
redirect_to platform_platform_invitations_path(@platform),
68+
alert: @platform_invitation.errors.full_messages.to_sentence
69+
end
3970
format.turbo_stream do
4071
render turbo_stream: [
41-
turbo_stream.update('form_errors', partial: 'layouts/better_together/errors',
42-
locals: { object: @platform_invitation }),
43-
turbo_stream.replace('flash_messages', partial: 'layouts/better_together/flash_messages',
44-
locals: { flash: })
72+
turbo_stream.update('form_errors',
73+
partial: 'layouts/better_together/errors',
74+
locals: { object: @platform_invitation }),
75+
turbo_stream.replace('flash_messages',
76+
partial: 'layouts/better_together/flash_messages',
77+
locals: { flash: })
4578
]
4679
end
4780
end
@@ -54,24 +87,24 @@ def destroy # rubocop:todo Metrics/AbcSize, Metrics/MethodLength
5487
if @platform_invitation.destroy
5588
flash.now[:notice] = t('flash.generic.removed', resource: t('resources.invitation'))
5689
respond_to do |format|
57-
format.html { redirect_to @platform }
90+
format.html { redirect_to platform_platform_invitations_path(@platform) }
5891
format.turbo_stream do
5992
render turbo_stream: [
6093
turbo_stream.remove(helpers.dom_id(@platform_invitation)),
61-
turbo_stream.replace('flash_messages', partial: 'layouts/better_together/flash_messages',
62-
locals: { flash: })
94+
turbo_stream.replace('flash_messages',
95+
partial: 'layouts/better_together/flash_messages',
96+
locals: { flash: })
6397
]
6498
end
6599
end
66100
else
67101
flash.now[:error] = t('flash.generic.error_remove', resource: t('resources.invitation'))
68102
respond_to do |format|
69-
format.html { redirect_to @platform, alert: flash.now[:error] }
103+
format.html { redirect_to platform_platform_invitations_path(@platform), alert: flash.now[:error] }
70104
format.turbo_stream do
71-
# rubocop:todo Layout/LineLength
72-
render turbo_stream: turbo_stream.replace('flash_messages', partial: 'layouts/better_together/flash_messages',
73-
# rubocop:enable Layout/LineLength
74-
locals: { flash: })
105+
render turbo_stream: turbo_stream.replace('flash_messages',
106+
partial: 'layouts/better_together/flash_messages',
107+
locals: { flash: })
75108
end
76109
end
77110
end
@@ -85,14 +118,15 @@ def resend # rubocop:todo Metrics/AbcSize, Metrics/MethodLength
85118
flash[:notice] = t('flash.generic.queued', resource: t('resources.invitation_email'))
86119

87120
respond_to do |format|
88-
format.html { redirect_to @platform, notice: flash[:notice] }
121+
format.html { redirect_to platform_platform_invitations_path(@platform), notice: flash[:notice] }
89122
format.turbo_stream do
90123
render turbo_stream: [
91124
turbo_stream.replace(helpers.dom_id(@platform_invitation),
92125
partial: 'better_together/platform_invitations/platform_invitation',
93126
locals: { platform_invitation: @platform_invitation }),
94-
turbo_stream.replace('flash_messages', partial: 'layouts/better_together/flash_messages',
95-
locals: { flash: })
127+
turbo_stream.replace('flash_messages',
128+
partial: 'layouts/better_together/flash_messages',
129+
locals: { flash: })
96130
]
97131
end
98132
end

app/controllers/better_together/platforms_controller.rb

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,6 @@ class PlatformsController < FriendlyResourceController # rubocop:todo Style/Docu
66
before_action :authorize_platform, only: %i[show edit update destroy]
77
after_action :verify_authorized, except: :index
88

9-
before_action only: %i[show], if: -> { Rails.env.development? } do
10-
# Make sure that all Platform Invitation subclasses are loaded in dev to generate new block buttons
11-
::BetterTogether::PlatformInvitation.load_all_subclasses
12-
end
13-
149
# GET /platforms
1510
def index
1611
# @platforms = ::BetterTogether::Platform.all
@@ -129,7 +124,27 @@ def resource_class
129124
end
130125

131126
def resource_collection
132-
resource_class.includes(:invitations, { person_platform_memberships: %i[member role] })
127+
# Comprehensive eager loading to prevent N+1 queries for platform memberships
128+
# This loads all necessary associations including:
129+
# - Mobility translations (string & text)
130+
# - Active Storage attachments with blobs and variants
131+
# - Platform memberships with member/role associations
132+
# Note: Platform invitations are now loaded separately via lazy Turbo frames
133+
resource_class.with_translations.includes(
134+
# Cover and profile image attachments with blobs and variants
135+
cover_image_attachment: { blob: :variant_records },
136+
profile_image_attachment: { blob: :variant_records },
137+
# Person platform memberships with comprehensive associations
138+
person_platform_memberships: [
139+
{ member: [
140+
:string_translations,
141+
{ profile_image_attachment: { blob: :variant_records } }
142+
] },
143+
{ role: %i[
144+
string_translations
145+
] }
146+
]
147+
)
133148
end
134149
end
135150
end

app/models/better_together/platform.rb

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,13 @@ class Platform < ApplicationRecord
2020
member_type: 'person'
2121

2222
has_many :invitations,
23+
-> { order(created_at: :desc) },
24+
class_name: '::BetterTogether::PlatformInvitation',
25+
foreign_key: :invitable_id
26+
27+
# For performance - scope to limit invitations in some contexts
28+
has_many :recent_invitations,
29+
-> { where(created_at: 30.days.ago..) },
2330
class_name: '::BetterTogether::PlatformInvitation',
2431
foreign_key: :invitable_id
2532

app/policies/better_together/platform_invitation_policy.rb

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,18 +11,17 @@ def create?
1111
end
1212

1313
def destroy?
14-
user.present? && record.status_pending? && permitted_to?('manage_platform')
14+
user.present? && record.status_pending? && (record.inviter.id == agent.id || permitted_to?('manage_platform'))
1515
end
1616

1717
def resend?
18-
user.present? && record.status_pending? && permitted_to?('manage_platform')
18+
user.present? && record.status_pending? && (record.inviter.id == agent.id || permitted_to?('manage_platform'))
1919
end
2020

2121
class Scope < Scope # rubocop:todo Style/Documentation
2222
def resolve
23-
results = scope.order(:last_sent)
24-
25-
results = results.where(inviter: agent) unless permitted_to?('manage_platform')
23+
results = scope
24+
results = scope.where(inviter: agent) unless permitted_to?('manage_platform')
2625

2726
results
2827
end

app/views/better_together/person_platform_memberships/_person_platform_membership_member.html.erb

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,12 @@
55
<div>
66
<h5 class="card-title mb-1">
77
<% if policy(person_platform_membership.member).show? %>
8-
<%= link_to person_platform_membership.member, person_platform_membership.member, class: 'text-decoration-none' %>
8+
<%= link_to person_platform_membership.member.name, person_platform_membership.member, class: 'text-decoration-none' %>
99
<% else %>
10-
<%= person_platform_membership.member %>
10+
<%= person_platform_membership.member.name %>
1111
<% end %>
1212
</h5>
13-
<p class="card-text text-muted"><%= person_platform_membership.role %></p>
13+
<p class="card-text text-muted"><%= person_platform_membership.role.name %></p>
1414
</div>
1515
<div class="ml-2">
1616
<%= profile_image_tag(person_platform_membership.member, size: 150, class: 'card-image') %>

app/views/better_together/platform_invitations/_platform_invitation.html.erb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
<%= platform_invitation.invitee_email %>
3535
</td>
3636
<td>
37-
<% if platform_invitation.invitee %>
37+
<% if platform_invitation.invitee_id.present? %>
3838
<%= link_to platform_invitation.invitee.name, platform_invitation.invitee, class: "text-decoration-none" %>
3939
<% else %>
4040
<span class="text-muted"><%= t('globals.no_invitee') %></span>

0 commit comments

Comments
 (0)