Skip to content

Commit 977748f

Browse files
committed
Refactor tab controller and improve localization for RSVP management
1 parent 4f615ab commit 977748f

File tree

10 files changed

+210
-190
lines changed

10 files changed

+210
-190
lines changed

app/controllers/better_together/agreements_controller.rb

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ class AgreementsController < FriendlyResourceController
99
# return only the fragment wrapped in the expected <turbo-frame id="agreement_modal_frame">...</turbo-frame>
1010
# so Turbo can swap it into the frame. For normal requests, fall back to the
1111
# default rendering (with layout).
12-
def show
12+
def show # rubocop:todo Metrics/MethodLength
1313
if @agreement.page
1414
@page = @agreement.page
1515
@content_blocks = @page.content_blocks
@@ -19,10 +19,10 @@ def show
1919

2020
# Check if this is a Turbo Frame request
2121
if request.headers['Turbo-Frame'].present?
22-
Rails.logger.debug "Rendering turbo frame response"
22+
Rails.logger.debug 'Rendering turbo frame response'
2323
render partial: 'modal_content', layout: false
2424
else
25-
Rails.logger.debug "Rendering normal response"
25+
Rails.logger.debug 'Rendering normal response'
2626
# Normal full-page rendering continues with the view
2727
end
2828
end

app/controllers/better_together/events_controller.rb

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
module BetterTogether
44
# CRUD for BetterTogether::Event
5-
class EventsController < FriendlyResourceController
5+
class EventsController < FriendlyResourceController # rubocop:todo Metrics/ClassLength
66
before_action if: -> { Rails.env.development? } do
77
# Make sure that all subclasses are loaded in dev to generate type selector
88
Rails.application.eager_load!
@@ -46,14 +46,14 @@ def rsvp_going
4646
def rsvp_cancel
4747
@event = set_resource_instance
4848
authorize @event, :show?
49-
49+
5050
# Ensure current_person exists
5151
current_person = helpers.current_person
5252
unless current_person
5353
redirect_to @event, alert: t('better_together.events.login_required', default: 'Please log in to manage RSVPs.')
5454
return
5555
end
56-
56+
5757
attendance = BetterTogether::EventAttendance.find_by(event: @event, person: current_person)
5858
attendance&.destroy
5959
redirect_to @event, notice: t('better_together.events.rsvp_cancelled', default: 'RSVP cancelled')
@@ -124,7 +124,7 @@ def rsvp_update(status) # rubocop:todo Metrics/AbcSize, Metrics/MethodLength
124124
# Override base controller method to add performance optimizations
125125
def set_resource_instance
126126
super
127-
127+
128128
# Preload associations needed for event show page to avoid N+1 queries
129129
preload_event_associations! unless json_request?
130130
end
@@ -133,34 +133,40 @@ def json_request?
133133
request.format.json?
134134
end
135135

136-
def preload_event_associations!
136+
# rubocop:todo Metrics/AbcSize
137+
# rubocop:todo Metrics/MethodLength
138+
def preload_event_associations! # rubocop:todo Metrics/CyclomaticComplexity, Metrics/AbcSize
137139
return unless @event
138140

139141
# Preload categories and their translations to avoid N+1 queries
140142
@event.categories.includes(:string_translations).load
141-
143+
142144
# Preload event hosts and their associated models
143145
@event.event_hosts.includes(:host).load
144-
146+
145147
# Preload event attendances to avoid count queries in view
146148
@event.event_attendances.includes(:person).load
147-
149+
148150
# Preload current person's attendance for RSVP buttons
149151
if current_person
150-
@current_attendance = @event.event_attendances.find { |a| a.person_id == current_person.id }
152+
@current_attendance = @event.event_attendances.find do |a|
153+
a.person_id == current_person.id
154+
end
151155
end
152-
156+
153157
# Preload translations for the event itself
154158
@event.string_translations.load
155159
@event.text_translations.load
156-
160+
157161
# Preload cover image attachment to avoid attachment queries
158162
@event.cover_image_attachment&.blob&.load if @event.cover_image.attached?
159-
163+
160164
# Preload location if present
161-
@event.location&.reload if @event.location
162-
165+
@event.location&.reload
166+
163167
self
164168
end
169+
# rubocop:enable Metrics/MethodLength
170+
# rubocop:enable Metrics/AbcSize
165171
end
166172
end

app/controllers/better_together/people_controller.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ def show
1717
:string_translations,
1818
blocks: { background_image_file_attachment: :blob }
1919
)
20-
20+
2121
# Preload calendar associations to avoid N+1 queries
2222
@person.preload_calendar_associations!
2323
end

app/helpers/better_together/application_helper.rb

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -206,18 +206,22 @@ def better_together_url_helper?(method)
206206
end
207207

208208
# Returns the appropriate icon and color for an event based on the person's relationship to it
209-
def event_relationship_icon(person, event)
209+
def event_relationship_icon(person, event) # rubocop:todo Metrics/MethodLength
210210
relationship = person.event_relationship_for(event)
211-
211+
212212
case relationship
213213
when :created
214-
{ icon: 'fas fa-user-edit', color: '#28a745', tooltip: t('better_together.events.relationship.created', default: 'Created by you') }
214+
{ icon: 'fas fa-user-edit', color: '#28a745',
215+
tooltip: t('better_together.events.relationship.created', default: 'Created by you') }
215216
when :going
216-
{ icon: 'fas fa-check-circle', color: '#007bff', tooltip: t('better_together.events.relationship.going', default: 'You\'re going') }
217+
{ icon: 'fas fa-check-circle', color: '#007bff',
218+
tooltip: t('better_together.events.relationship.going', default: 'You\'re going') }
217219
when :interested
218-
{ icon: 'fas fa-heart', color: '#e91e63', tooltip: t('better_together.events.relationship.interested', default: 'You\'re interested') }
220+
{ icon: 'fas fa-heart', color: '#e91e63',
221+
tooltip: t('better_together.events.relationship.interested', default: 'You\'re interested') }
219222
else
220-
{ icon: 'fas fa-circle', color: '#6c757d', tooltip: t('better_together.events.relationship.calendar', default: 'Calendar event') }
223+
{ icon: 'fas fa-circle', color: '#6c757d',
224+
tooltip: t('better_together.events.relationship.calendar', default: 'Calendar event') }
221225
end
222226
end
223227
end

app/javascript/controllers/better_together/tabs_controller.js

Lines changed: 25 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -6,56 +6,45 @@ export default class extends Controller {
66
static targets = ["tab"];
77

88
connect() {
9+
// Get all available tabs (both via Stimulus targets and manual selection)
10+
this.allTabs = this.tabTargets.length > 0 ? this.tabTargets :
11+
Array.from(this.element.querySelectorAll('[data-bs-toggle="tab"]'));
12+
913
this.activateTabFromHash();
1014
this.setupTabChangeListener();
1115
}
1216

1317
activateTabFromHash() {
1418
const hash = window.location.hash;
1519
if (hash) {
16-
let selectedTab = this.element.querySelector(`[data-bs-target="${hash}"]`);
17-
while (selectedTab) {
18-
// Skip tabs inside the localized-fields class
19-
if (selectedTab.closest('.localized-fields')) break;
20-
21-
const tabTarget = this.element.querySelector(`${selectedTab.dataset.bsTarget}`);
22-
const tabPanes = this.element.querySelectorAll('.nav-tab-pane');
23-
24-
this.tabTargets.forEach((tab) => {
25-
tab.classList.remove('active');
26-
});
27-
selectedTab.classList.add('active');
28-
29-
tabPanes.forEach((pane) => {
30-
pane.classList.remove('active');
31-
pane.classList.remove('show');
32-
});
33-
34-
if (tabTarget) {
35-
tabTarget.classList.add('active');
36-
tabTarget.classList.add('show');
37-
}
38-
39-
// Check if the selected tab is nested and activate its parent tab
40-
const parentTabPane = selectedTab.closest('.nav-tab-pane');
41-
if (parentTabPane) {
42-
const parentTab = this.element.querySelector(`[data-bs-target="#${parentTabPane.id}"]`);
43-
selectedTab = parentTab; // Move up to the parent tab for the next iteration
44-
} else {
45-
selectedTab = null; // Exit the loop if no parent tab exists
46-
}
20+
// Look for tabs that target this hash with either href or data-bs-target
21+
let selectedTab = this.element.querySelector(`[href="${hash}"]`) ||
22+
this.element.querySelector(`[data-bs-target="${hash}"]`);
23+
24+
if (selectedTab && !selectedTab.closest('.localized-fields')) {
25+
console.log('Activating tab from hash:', hash, selectedTab);
26+
27+
// Let Bootstrap handle the tab activation
28+
const tabInstance = new bootstrap.Tab(selectedTab);
29+
tabInstance.show();
4730
}
4831
}
4932
}
5033

5134
setupTabChangeListener() {
52-
this.tabTargets.forEach((link) => {
35+
// Use the unified collection of all available tabs
36+
const tabsToSetup = this.allTabs || [];
37+
38+
tabsToSetup.forEach((link) => {
5339
if (link.closest('.localized-fields')) return;
5440

55-
link.addEventListener("shown.bs.tab", (event) => {
56-
const targetHash = event.target.getAttribute("data-bs-target");
57-
if (targetHash) {
58-
history.pushState({}, "", targetHash); // Add the hash to the address bar
41+
// Primary: Listen for click events
42+
link.addEventListener("click", (event) => {
43+
const targetHash = event.target.getAttribute("href") || event.target.getAttribute("data-bs-target");
44+
45+
if (targetHash && targetHash.startsWith('#')) {
46+
// Update immediately - no delay needed
47+
history.pushState({}, "", targetHash);
5948
}
6049
});
6150
});

app/models/better_together/person.rb

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,6 @@ def self.primary_community_delegation_attrs
7070
joinable_type: 'platform'
7171

7272
slugged :identifier, use: %i[slugged mobility], dependent: :delete_all
73-
7473
store_attributes :preferences do
7574
locale String, default: I18n.default_locale.to_s
7675
time_zone String, default: ENV.fetch('APP_TIME_ZONE', 'Newfoundland')
@@ -164,23 +163,23 @@ def after_record_created
164163

165164
# Returns all events relevant to this person's calendar view
166165
# Combines events they're going to, created, and interested in
167-
def all_calendar_events
166+
def all_calendar_events # rubocop:todo Metrics/AbcSize, Metrics/MethodLength
168167
@all_calendar_events ||= begin
169168
# Build a single query to get all relevant events with proper includes
170169
event_ids = Set.new
171-
170+
172171
# Get event IDs from calendar entries (going events)
173172
calendar_event_ids = primary_calendar.calendar_entries.pluck(:event_id)
174173
event_ids.merge(calendar_event_ids)
175-
174+
176175
# Get event IDs from attendances (interested events)
177176
attendance_event_ids = event_attendances.pluck(:event_id)
178177
event_ids.merge(attendance_event_ids)
179-
178+
180179
# Get event IDs from created events
181180
created_event_ids = Event.where(creator_id: id).pluck(:id)
182181
event_ids.merge(created_event_ids)
183-
182+
184183
# Single query to fetch all events with necessary includes
185184
if event_ids.any?
186185
Event.includes(:string_translations, :text_translations)
@@ -197,33 +196,33 @@ def all_calendar_events
197196
def event_relationship_for(event)
198197
# Check if they created it first (highest priority)
199198
return :created if event.creator_id == id
200-
199+
201200
# Use memoized attendances to avoid N+1 queries
202201
@_event_attendances_by_event_id ||= event_attendances.index_by(&:event_id)
203202
attendance = @_event_attendances_by_event_id[event.id]
204-
203+
205204
return attendance.status.to_sym if attendance
206-
205+
207206
# Check if it's in their calendar (for events added directly to calendar)
208207
@_calendar_event_ids ||= Set.new(primary_calendar.calendar_entries.pluck(:event_id))
209208
return :going if @_calendar_event_ids.include?(event.id)
210-
209+
211210
:calendar # Default for calendar events
212211
end
213212

214213
# Preloads associations needed for calendar display to avoid N+1 queries
215214
def preload_calendar_associations!
216215
# Preload event attendances
217216
event_attendances.includes(:event).load
218-
217+
219218
# Preload calendar entries
220219
primary_calendar.calendar_entries.includes(:event).load
221-
220+
222221
# Reset memoized variables
223222
@all_calendar_events = nil
224223
@_event_attendances_by_event_id = nil
225224
@_calendar_event_ids = nil
226-
225+
227226
self
228227
end
229228

0 commit comments

Comments
 (0)