Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
fb46057
Add Kaminari gem for pagination
piyush5050 Aug 26, 2025
8f05509
Add index route for profiles
piyush5050 Aug 26, 2025
7e99c00
Add profiles index action with pagination
piyush5050 Aug 26, 2025
bffb0b1
Add profiles index views with infinite scroll
piyush5050 Aug 26, 2025
4222698
Set speaker profiles as public in seed data
piyush5050 Aug 26, 2025
8cf44e2
Fix loading spinner animation in profiles infinite scroll
piyush5050 Aug 26, 2025
d2c4d8e
Add sticky header to profiles index page
piyush5050 Aug 27, 2025
cdbd114
Refactor profile partial to make only name clickable
piyush5050 Aug 27, 2025
65015b8
Fix profile cards width and mobile responsiveness
piyush5050 Aug 27, 2025
4506b2c
Add comprehensive tests for profiles index feature
piyush5050 Aug 27, 2025
0ce97ae
Replace Profile with Attendees in bottom navigation
piyush5050 Aug 27, 2025
800af66
Add my profile card to profiles index
piyush5050 Aug 27, 2025
e148bf0
Exclude current user from profiles list when signed in
piyush5050 Aug 27, 2025
fe773f0
Add system tests for profile navigation updates
piyush5050 Aug 27, 2025
5ec912f
Add controller tests for current user exclusion in profiles index
piyush5050 Aug 27, 2025
b260edf
Fix linter issues in profiles views and system tests
piyush5050 Aug 27, 2025
2792b1f
Add comment explaining turbo_stream format override in profiles contr…
piyush5050 Aug 27, 2025
8d3ee6b
Merge branch 'main' into profiles-index-page
LuigiR0jas Aug 29, 2025
8c2e4ae
Merge branch 'main' into profiles-index-page
LuigiR0jas Aug 29, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ gem "bootsnap", require: false
gem "draper"
gem "friendly_id", "~> 5.5.0"
gem "inline_svg"
gem "kaminari"
gem "net-pop", github: "ruby/net-pop"
gem "noticed"
gem "puma", ">= 5.0"
Expand Down
13 changes: 13 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,18 @@ GEM
sshkit (>= 1.23.0, < 2.0)
thor (~> 1.3)
zeitwerk (>= 2.6.18, < 3.0)
kaminari (1.2.2)
activesupport (>= 4.1.0)
kaminari-actionview (= 1.2.2)
kaminari-activerecord (= 1.2.2)
kaminari-core (= 1.2.2)
kaminari-actionview (1.2.2)
actionview
kaminari-core (= 1.2.2)
kaminari-activerecord (1.2.2)
activerecord
kaminari-core (= 1.2.2)
kaminari-core (1.2.2)
language_server-protocol (3.17.0.3)
launchy (3.0.1)
addressable (~> 2.8)
Expand Down Expand Up @@ -702,6 +714,7 @@ DEPENDENCIES
importmap-rails
inline_svg
kamal
kaminari
letter_opener (~> 1.10)
litestream (~> 0.10.4)
lograge
Expand Down
25 changes: 23 additions & 2 deletions app/controllers/profiles_controller.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,28 @@
class ProfilesController < ApplicationController
allow_unauthenticated_access only: :show
allow_unauthenticated_access only: [:index, :show]

before_action :set_profile, except: :show
before_action :set_profile, except: [:index, :show]

def index
profiles_scope = Profile.includes(:profileable)
.where(is_public: true)

profiles_scope = profiles_scope.where.not(id: current_profile.id) if user_signed_in?

@profiles = profiles_scope
.order(created_at: :desc)
.page(params[:page])
.per(10)

# The lazy_loading turbo frame defaults to HTML format, but we need turbo_stream
# format to handle pagination requests
request.format = :turbo_stream if turbo_frame_request? && params[:page].present?

respond_to do |format|
format.html
format.turbo_stream
end
end

def show
if user_signed_in?
Expand Down
7 changes: 4 additions & 3 deletions app/views/layouts/_bottom_navbar.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -31,17 +31,18 @@
<% end %>

<%= link_to(
user_signed_in? ? profile_path(current_profile&.uuid) : profile_root_path,
profiles_path,
class: [
nav_text_class_for(["/profiles"]),
"flex flex-col items-center justify-center"
]
],
data: { test_id: "attendees_nav" }
) do %>
<%= inline_svg_tag(
"icons/avatar_no_fill.svg",
class: nav_icon_class_for(["/profiles"])
) %>
Profile
Attendees
<% end %>

<%= link_to(
Expand Down
37 changes: 37 additions & 0 deletions app/views/profiles/_my_profile_card.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<div class="mb-4">
<div class="flex items-center p-4 bg-gradient-to-r from-red/5 to-red/10 border border-red/20 rounded-lg shadow-sm" data-test-id="my-profile-card">
<% if user_signed_in? %>
<% if current_profile.image.attached? %>
<div class="w-20 h-20 mr-4 bg-cover bg-center rounded-full shrink-0 ring-2 ring-red/20" style="background-image: url(<%= url_for(current_profile.image) %>)"></div>
<% else %>
<div class="w-20 h-20 mr-4 rounded-full bg-slate-300 p-2 flex items-center justify-center shrink-0 ring-2 ring-red/20">
<%= image_tag url_for("icons/user_avatar.svg"), class: "w-full h-full" %>
</div>
<% end %>

<div class="flex flex-col flex-1 min-w-0">
<div class="text-xs font-semibold text-red/70 uppercase tracking-wider mb-1">Your Profile</div>
<%= link_to profile_path(current_profile.uuid), class: "text-xl font-bold text-red italic truncate underline hover:text-red/80", data: { turbo_frame: "_top" } do %>
<%= current_profile.name.presence || "Set up your profile" %>
<% end %>

<% if current_profile.job_title.present? %>
<div class="text-sm text-gray-600 truncate">
<%= current_profile.job_title %>
</div>
<% end %>
</div>
<% else %>
<div class="w-20 h-20 mr-4 rounded-full bg-slate-300 p-2 flex items-center justify-center shrink-0 ring-2 ring-red/20">
<%= image_tag url_for("icons/user_avatar.svg"), class: "w-full h-full" %>
</div>

<div class="flex flex-col flex-1 min-w-0">
<div class="text-xs font-semibold text-red/70 uppercase tracking-wider mb-1">Your Profile</div>
<%= link_to new_user_session_path, class: "text-xl font-bold text-red italic truncate underline hover:text-red/80", data: { turbo_frame: "_top" } do %>
Set up your profile
<% end %>
</div>
<% end %>
</div>
</div>
21 changes: 21 additions & 0 deletions app/views/profiles/_profile.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<div class="flex items-center p-4 bg-white rounded-lg shadow-sm hover:shadow-md transition-shadow" data-test-id="profile-card">
<% if profile.image.attached? %>
<div class="w-16 h-16 mr-4 bg-cover bg-center rounded-full shrink-0" style="background-image: url(<%= url_for(profile.image) %>)"></div>
<% else %>
<div class="w-16 h-16 mr-4 rounded-full bg-slate-300 p-2 flex items-center justify-center shrink-0">
<%= image_tag url_for("icons/user_avatar.svg"), class: "w-full h-full" %>
</div>
<% end %>

<div class="flex flex-col flex-1 min-w-0">
<%= link_to profile_path(profile.uuid), class: "text-lg font-bold text-red italic truncate underline hover:text-red/80", data: { turbo_frame: "_top" } do %>
<%= profile.name.presence || "Anonymous" %>
<% end %>

<% if profile.job_title.present? %>
<div class="text-sm text-gray-500 truncate">
<%= profile.job_title %>
</div>
<% end %>
</div>
</div>
47 changes: 47 additions & 0 deletions app/views/profiles/index.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<%= title "Attendees" %>

<% content_for :top_nav do %>
<%= render partial: "layouts/primary_top_nav", locals: { title: "Attendees" } %>
<% end %>

<div class="flex flex-col flex-1 bg-grey-50 pt-safe-area-header pb-safe-area-bottom-navbar">
<div class="flex flex-col items-center w-full">
<div class="sticky z-30 flex flex-col items-center w-full px-5 pt-6 top-safe-area-header bg-grey-50">
<div class="flex w-full max-w-screen-sm mb-4">
<h1 class="text-3xl italic font-black text-gray-800 d-hotwire-native-none">Attendees</h1>
</div>
</div>

<div class="flex flex-col items-center w-full px-5 pb-6">
<div class="w-full max-w-screen-sm">
<%= render "my_profile_card" %>

<div>
<h2 class="text-lg font-semibold text-gray-700 mb-3">All Attendees</h2>
</div>
</div>

<%= turbo_frame_tag "profiles", class: "grid grid-cols-1 gap-4 w-full max-w-screen-sm" do %>
<%= render @profiles %>
<% end %>

<% unless @profiles.last_page? %>
<%= turbo_frame_tag "loader", data: { test_id: "loader" },
src: profiles_path(page: @profiles.next_page),
loading: :lazy,
class: "w-full max-w-screen-sm mt-4" do %>
<div class="flex justify-center py-4">
<svg class="animate-spin h-8 w-8 text-red" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
</svg>
</div>
<% end %>
<% end %>
</div>
</div>
</div>

<% content_for :bottom_nav do %>
<%= render partial: "layouts/bottom_navbar", locals: { unread_notifications: current_user&.notifications&.unread } %>
<% end %>
20 changes: 20 additions & 0 deletions app/views/profiles/index.turbo_stream.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<%= turbo_stream.append "profiles" do %>
<%= render @profiles %>
<% end %>

<%= turbo_stream.replace "loader" do %>
<% unless @profiles.last_page? %>
<%= turbo_frame_tag "loader",
src: profiles_path(page: @profiles.next_page),
loading: :lazy,
class: "w-full max-w-screen-sm mt-4" do
%>
<div class="flex justify-center py-4">
<svg class="animate-spin h-8 w-8 text-red" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
</svg>
</div>
<% end %>
<% end %>
<% end %>
2 changes: 1 addition & 1 deletion config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
resource :attendee, only: [:create, :destroy]
end
resources :speakers, only: [:show]
resources :profiles, only: [:show, :edit, :update, :destroy], param: :uuid
resources :profiles, except: [:new, :create], param: :uuid
resources :abuse_reports, only: [:create], param: :uuid
resources :notifications, only: [:index]
resources :web_push_subscriptions, only: [:create]
Expand Down
Loading