Skip to content
Snippets Groups Projects
Commit 2c4a20c7 authored by Teal Bauer's avatar Teal Bauer
Browse files

feat: optionally show candidate sessions in list & ical feed

parent 17ca9037
Branches
No related tags found
No related merge requests found
......@@ -5,7 +5,7 @@ class AssignmentsController < ApplicationController
before_action :set_session, :set_users
def index
@assignments = Assignment.all.joins(:session, :user).order("sessions.starts_at")
@assignments = Assignment.joins(session: :conference).where(conferences: { active: true }).joins(:user).order("sessions.starts_at")
return unless params[:user_id]
@assignments = @assignments.where(user_id: params[:user_id])
......@@ -74,7 +74,15 @@ class AssignmentsController < ApplicationController
end
def by_user
@user = User.includes(:assignments).find(params[:user_id])
@user = User.find(params[:user_id])
# Filter assignments to only include those from active conferences
@active_assignments = @user.assignments.joins(session: :conference).where(conferences: { active: true }).includes(:session, session: [:conference, :stage])
# Include candidates if feature flag is enabled
if include_candidate_sessions?
@active_candidates = @user.candidates.joins(session: :conference).where(conferences: { active: true }).includes(:session, session: [:conference, :stage])
end
respond_to do |format|
format.html # This will render the existing HTML view (assignments/by_user.html.erb)
......@@ -83,7 +91,8 @@ class AssignmentsController < ApplicationController
tz = TZInfo::Timezone.get("UTC")
calendar.add_timezone tz.ical_timezone Time.now
@user.assignments.each do |assignment|
# Add confirmed assignments from active conferences only
@user.assignments.joins(session: :conference).where(conferences: { active: true }).each do |assignment|
session = assignment.session
assignees = session.assignments.map { |a| a.user.name }
desc = [
......@@ -102,9 +111,44 @@ class AssignmentsController < ApplicationController
event.created = Icalendar::Values::DateTime.new(session.created_at)
event.last_modified = Icalendar::Values::DateTime.new(session.updated_at)
event.uid = [ session.conference.slug, session.ref_id ].join("-")
event.status = "CONFIRMED"
event.append_custom_property("X-ALT-DESC;FMTTYPE=text/html", desc.join("<hr>"))
calendar.add_event(event)
end
# Add candidate sessions from active conferences if feature flag is enabled
if include_candidate_sessions?
@user.candidates.joins(session: :conference).where(conferences: { active: true }).includes(:session).each do |candidate|
session = candidate.session
# Skip if user is already assigned to this session
next if @user.assignments.any? { |a| a.session_id == session.id }
assignees = session.assignments.map { |a| a.user.name }
candidates = session.candidates.map { |c| c.user.name }
desc = [
"🤔 CANDIDATE SESSION - You've expressed interest in this session",
"Current Assignees: #{assignees.any? ? assignees.join(', ') : 'None yet'}",
"Other Candidates: #{candidates.reject { |name| name == @user.name }.join(', ')}",
"Speakers: #{session.speakers.map(&:name).join(', ')}",
session.description
]
desc.unshift("Filedrop has data for this session!<br>\n" + conference_session_url(session.conference, session)) if session.filedrop?
event = Icalendar::Event.new
event.dtstart = Icalendar::Values::DateTime.new(session.starts_at, tzid: session.starts_at.time_zone.tzinfo.name)
event.dtend = Icalendar::Values::DateTime.new(session.ends_at, tzid: session.ends_at.time_zone.tzinfo.name)
event.summary = "[CANDIDATE] #{session.title} @ #{session.stage.name}"
event.description = desc.map { |l| helpers.strip_tags(l) }.join("\n\n")
event.location = [ session.stage.name, session.conference.name ].join(" @ ")
event.created = Icalendar::Values::DateTime.new(session.created_at)
event.last_modified = Icalendar::Values::DateTime.new(session.updated_at)
event.uid = [ session.conference.slug, session.ref_id, "candidate" ].join("-")
event.status = "TENTATIVE"
event.transp = "TRANSPARENT" # Shows as "free" time in most calendar apps
event.append_custom_property("X-ALT-DESC;FMTTYPE=text/html", desc.join("<hr>"))
calendar.add_event(event)
end
end
calendar.publish
headers["Content-Type"] = "text/calendar; charset=UTF-8"
......@@ -127,4 +171,11 @@ class AssignmentsController < ApplicationController
def set_users
@users = User.all
end
def include_candidate_sessions?
# Feature flag to include candidate sessions in iCal feed
# Can be controlled via environment variable or system setting
ENV.fetch("INCLUDE_CANDIDATE_SESSIONS", "false").downcase == "true" ||
SystemSetting.include_candidate_sessions?
end
end
......@@ -17,4 +17,20 @@ class SystemSetting < ApplicationRecord
setting.save!
setting
end
# Get the include candidate sessions setting
def self.include_candidate_sessions?
setting = find_by(key: "include_candidate_sessions")
setting&.value == "true"
end
# Set the include candidate sessions setting
def self.set_include_candidate_sessions(enabled)
setting = find_or_initialize_by(key: "include_candidate_sessions")
setting.value = enabled.to_s
setting.description = "Include candidate sessions in user iCal feeds as tentative events"
setting.setting_type = "boolean"
setting.save!
setting
end
end
<div class="mb-6 <%= Time.parse(date).end_of_day < now ? "past" : "future" %>">
<h3 class="sticky top-0 z-10 text-lg font-semibold text-gray-700 dark:text-gray-300 mb-3 bg-white dark:bg-gray-800 py-2"><%= date %></h3>
<ul class="space-y-3">
<% assignments_on_date.each do |assignment| %>
<li class="<%= assignment.session.starts_at < now ? "past" : "future" %> pl-4 border-l-4 border-gray-200 dark:border-gray-700">
<% assignments_on_date.each do |item| %>
<% is_candidate = item.is_a?(Candidate) %>
<li class="<%= item.session.starts_at < now ? "past" : "future" %> pl-4 border-l-4 <%= is_candidate ? 'border-yellow-400 dark:border-yellow-600' : 'border-gray-200 dark:border-gray-700' %>">
<div class="flex flex-col md:flex-row md:items-center gap-2">
<span class="tabular-nums font-medium text-gray-900 dark:text-gray-100 min-w-28">
<%= assignment.session.starts_at.strftime('%H:%M') %> &ndash; <%= assignment.session.ends_at.strftime('%H:%M') %>
<%= item.session.starts_at.strftime('%H:%M') %> &ndash; <%= item.session.ends_at.strftime('%H:%M') %>
</span>
<div class="flex-1">
<%= render partial: 'shared/session_filedrop', locals: { session: assignment.session } %>
<%= link_to assignment.session.title, conference_session_path(assignment.session.conference, assignment.session), class: "text-blue-600 hover:text-blue-800 dark:text-blue-400 dark:hover:text-blue-300" %>
<%= render partial: 'shared/session_engelsystem', locals: { session: assignment.session } %>
<span class="text-gray-500 dark:text-gray-400 ml-1">@ <%= assignment.session.stage.name %></span>
<% if is_candidate %>
<span class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-yellow-100 text-yellow-800 dark:bg-yellow-900 dark:text-yellow-200 mr-1">
CANDIDATE
</span>
<% end %>
<%= render partial: 'shared/session_filedrop', locals: { session: item.session } %>
<%= link_to item.session.title, conference_session_path(item.session.conference, item.session), class: "text-blue-600 hover:text-blue-800 dark:text-blue-400 dark:hover:text-blue-300" %>
<%= render partial: 'shared/session_engelsystem', locals: { session: item.session } %>
<span class="text-gray-500 dark:text-gray-400 ml-1">@ <%= item.session.stage.name %></span>
</div>
<div class="flex items-center space-x-1">
<% assignment.session.assignments.map(&:user).each do |other_user| %>
<% item.session.assignments.map(&:user).each do |other_user| %>
<%= render partial: 'application/user_avatar', locals: { user: other_user } %>
<% end %>
</div>
......
......@@ -44,8 +44,22 @@
<!-- List View Tab -->
<div class="block" id="listViewTab" role="tabpanel" aria-labelledby="list-tab">
<div class="bg-white dark:bg-gray-800 shadow rounded-lg p-6 overflow-auto" style="max-height: calc(100vh - 220px);">
<% @user.assignments.includes(:session, session: :conference).order('sessions.starts_at').group_by { |a| a.session.starts_at.strftime('%Y-%m-%d') }.each do |date, assignments_on_date| %>
<%= render partial: 'listview_date', locals: { assignments_on_date:, date:, now: } %>
<%
# Combine assignments and candidates (excluding duplicates)
all_items = @active_assignments.to_a
if @active_candidates
assigned_session_ids = @active_assignments.map { |a| a.session_id }
@active_candidates.each do |candidate|
unless assigned_session_ids.include?(candidate.session_id)
all_items << candidate
end
end
end
# Group by date
all_items.sort_by { |item| item.session.starts_at }.group_by { |item| item.session.starts_at.strftime('%Y-%m-%d') }.each do |date, items_on_date|
%>
<%= render partial: 'listview_date', locals: { assignments_on_date: items_on_date, date:, now: } %>
<% end %>
</div>
</div>
......@@ -66,19 +80,38 @@
</tr>
</thead>
<tbody class="bg-white dark:bg-gray-800 divide-y divide-gray-200 dark:divide-gray-700">
<% @user.assignments.includes(:session, session: :conference).order('sessions.starts_at').each do |assignment| %>
<tr class="<%= assignment.session.ends_at < Time.now ? "past" : "future" %>">
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400"><%= assignment.session.starts_at.strftime('%Y-%m-%d') %></td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400"><%= assignment.session.starts_at.strftime('%H:%M') %></td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400"><%= assignment.session.ends_at.strftime('%H:%M') %></td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400"><%= assignment.session.stage.name %></td>
<%
# Combine assignments and candidates for table view
all_items = @active_assignments.to_a
if @active_candidates
assigned_session_ids = @active_assignments.map { |a| a.session_id }
@active_candidates.each do |candidate|
unless assigned_session_ids.include?(candidate.session_id)
all_items << candidate
end
end
end
all_items.sort_by { |item| item.session.starts_at }.each do |item|
is_candidate = item.is_a?(Candidate)
%>
<tr class="<%= item.session.ends_at < Time.now ? "past" : "future" %> <%= is_candidate ? 'bg-yellow-50 dark:bg-yellow-900/20' : '' %>">
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400"><%= item.session.starts_at.strftime('%Y-%m-%d') %></td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400"><%= item.session.starts_at.strftime('%H:%M') %></td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400"><%= item.session.ends_at.strftime('%H:%M') %></td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400"><%= item.session.stage.name %></td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900 dark:text-gray-200">
<%= render partial: 'shared/session_filedrop', locals: { session: assignment.session } %>
<%= link_to assignment.session.title, assignment.session.url, target: "_blank", class: "text-blue-600 hover:text-blue-800 dark:text-blue-400 dark:hover:text-blue-300" %>
<%= render partial: 'shared/session_engelsystem', locals: { session: assignment.session } %>
<% if is_candidate %>
<span class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-yellow-100 text-yellow-800 dark:bg-yellow-900 dark:text-yellow-200 mr-1">
CANDIDATE
</span>
<% end %>
<%= render partial: 'shared/session_filedrop', locals: { session: item.session } %>
<%= link_to item.session.title, item.session.url, target: "_blank", class: "text-blue-600 hover:text-blue-800 dark:text-blue-400 dark:hover:text-blue-300" %>
<%= render partial: 'shared/session_engelsystem', locals: { session: item.session } %>
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400">
<% assignment.session.assignments.map(&:user).each do |other_user| %>
<% item.session.assignments.map(&:user).each do |other_user| %>
<%= render partial: 'application/user_avatar', locals: { user: other_user } %>
<% end %>
</td>
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please to comment