From 9bbb4925e8caa6bba2578709502dc2e129110448 Mon Sep 17 00:00:00 2001
From: Teal <git@teal.is>
Date: Sat, 25 May 2024 22:05:53 +0200
Subject: [PATCH] ton of stuff

---
 Gemfile                                       |  2 +
 Gemfile.lock                                  |  5 ++
 app/assets/stylesheets/application.css        |  6 --
 .../stylesheets/application.tailwind.css      |  2 +-
 app/controllers/assignments_controller.rb     | 60 +++++++++++++++++--
 app/controllers/conferences_controller.rb     |  3 +-
 app/controllers/sessions_controller.rb        | 34 -----------
 .../republica_2023_or_later/import_job.rb     |  2 +-
 app/models/conference.rb                      | 11 ++++
 app/models/session.rb                         |  8 +++
 app/models/user.rb                            |  3 +
 app/views/application/_user_avatar.html.erb   |  6 +-
 app/views/assignments/_user_avatar.html.erb   | 13 ++++
 app/views/assignments/by_user.html.erb        | 10 ++++
 app/views/conferences/show.html.erb           | 58 ++++++++----------
 app/views/layouts/application.html.erb        |  2 +-
 app/views/sessions/_assignment_form.html.erb  | 17 +++---
 app/views/sessions/_session.html.erb          | 12 ++++
 app/views/sessions/show.html.erb              |  7 +--
 config/routes.rb                              |  6 +-
 ...40525190720_add_time_zone_to_conference.rb |  5 ++
 db/schema.rb                                  |  3 +-
 db/seeds.rb                                   |  7 ++-
 23 files changed, 178 insertions(+), 104 deletions(-)
 create mode 100644 app/views/assignments/_user_avatar.html.erb
 create mode 100644 app/views/assignments/by_user.html.erb
 create mode 100644 app/views/sessions/_session.html.erb
 create mode 100644 db/migrate/20240525190720_add_time_zone_to_conference.rb

diff --git a/Gemfile b/Gemfile
index 4717b9b..1ea2342 100644
--- a/Gemfile
+++ b/Gemfile
@@ -65,3 +65,5 @@ gem "tailwindcss-rails", "~> 2.6"
 gem "hotwire-rails", "~> 0.1.3"
 
 gem "importmap-rails", "~> 2.0"
+
+gem "icalendar", "~> 2.10"
diff --git a/Gemfile.lock b/Gemfile.lock
index a07fabb..a26ea99 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -117,6 +117,9 @@ GEM
       multi_xml (>= 0.5.2)
     i18n (1.14.4)
       concurrent-ruby (~> 1.0)
+    icalendar (2.10.1)
+      ice_cube (~> 0.16)
+    ice_cube (0.16.4)
     importmap-rails (2.0.1)
       actionpack (>= 6.0.0)
       activesupport (>= 6.0.0)
@@ -272,6 +275,7 @@ GEM
 PLATFORMS
   aarch64-linux
   arm64-darwin-22
+  arm64-darwin-23
   x86_64-linux
 
 DEPENDENCIES
@@ -280,6 +284,7 @@ DEPENDENCIES
   debug
   hotwire-rails (~> 0.1.3)
   httparty
+  icalendar (~> 2.10)
   importmap-rails (~> 2.0)
   jbuilder
   puma (>= 5.0)
diff --git a/app/assets/stylesheets/application.css b/app/assets/stylesheets/application.css
index e5808c2..525f70c 100644
--- a/app/assets/stylesheets/application.css
+++ b/app/assets/stylesheets/application.css
@@ -14,9 +14,6 @@
  *= require_self
  */
 /* Add your styles here to layout the tabs, timeline, and session blocks */
-* {
-  box-sizing: border-box;
-}
 
 .timeline {
   position: relative;
@@ -47,9 +44,6 @@
 }
 .stage-sessions {
   position: relative;
-}
-.times {
-
 }
 .time {
   border-top: 1px dotted gray;
diff --git a/app/assets/stylesheets/application.tailwind.css b/app/assets/stylesheets/application.tailwind.css
index 6f20685..9c0e8aa 100644
--- a/app/assets/stylesheets/application.tailwind.css
+++ b/app/assets/stylesheets/application.tailwind.css
@@ -15,7 +15,7 @@
 h1, h2, h3, h4, h5, h6 {
   @apply font-bold;
 }
-input[type=submit], button {
+input[type=submit] {
   @apply border rounded-md px-2 py-1;
   &.primary {
     @apply bg-teal-800 text-teal-50;
diff --git a/app/controllers/assignments_controller.rb b/app/controllers/assignments_controller.rb
index aa30625..e0ff578 100644
--- a/app/controllers/assignments_controller.rb
+++ b/app/controllers/assignments_controller.rb
@@ -9,22 +9,70 @@ class AssignmentsController < ApplicationController
   end
 
   def create
+    @session = Session.find_by(ref_id: params[:session_ref_id])
+    @conference = Conference.find_by(slug: params[:conference_slug])
     @user = User.find(params[:user_id])
     @assignment = @session.assignments.new(user: @user)
 
     if @assignment.save
-      redirect_to @session, notice: "User was successfully assigned to session."
+      flash.now[:success] = 'User assigned successfully.'
+      respond_to do |format|
+        format.turbo_stream { render turbo_stream: turbo_stream.replace(helpers.dom_id(@session), partial: "sessions/session", locals: { session: @session }) }
+        format.html { redirect_to conference_session_path(@session.conference, @session), success: 'User assigned successfully.' }
+      end
     else
-      flash[:alert] = @assignment.errors.full_messages.join(", ")
-      render 'sessions/show'
+      flash.now[:alert] = 'Failed to assign user.'
+      respond_to do |format|
+        format.turbo_stream { render turbo_stream: turbo_stream.replace(helpers.dom_id(@session), partial: "sessions/session", locals: { session: @session }), status: :unprocessable_entity }
+        format.html { render :show, status: :unprocessable_entity }
+      end
     end
   end
 
   def destroy
-    @assignment = @session.assignments.find_by(user_id: params[:user_id])
-    @assignment.destroy
+    @assignment = Assignment.find(params[:id])
+    @session = @assignment.session
+
+    if @assignment&.destroy
+      respond_to do |format|
+        format.turbo_stream { render turbo_stream: turbo_stream.replace(helpers.dom_id(@session), partial: "sessions/session", locals: { session: @session }) }
+        format.html { redirect_to conference_session_path(@session.conference, @session), notice: 'User removed successfully.' }
+      end
+    else
+      respond_to do |format|
+        format.turbo_stream { render turbo_stream: turbo_stream.replace(helpers.dom_id(@session), partial: "sessions/session", locals: { session: @session }), status: :unprocessable_entity }
+        format.html { redirect_to conference_session_path(@session.conference, @session), alert: 'Failed to remove user.' }
+      end
+    end
+  end
+
+  def by_user
+    @user = User.includes(:assignments).find(params[:user_id])
     respond_to do |format|
-      format.html { redirect_to @session, notice: "User assignment removed successfully." }
+      format.html # This will render the existing HTML view (assignments/by_user.html.erb)
+
+      format.ics do
+        calendar = Icalendar::Calendar.new
+
+        @user.assignments.each do |assignment|
+          session = assignment.session
+
+          event = Icalendar::Event.new
+          event.dtstart = Icalendar::Values::DateTime.new(session.starts_at, tzid: session.starts_at.zone)
+          event.dtend = Icalendar::Values::DateTime.new(session.ends_at, tzid: session.ends_at.zone)
+          event.summary = session.title
+          event.description = session.description
+          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].join('-')
+          calendar.add_event(event)
+        end
+
+        calendar.publish
+        headers['Content-Type'] = 'text/calendar; charset=UTF-8'
+        render plain: calendar.to_ical
+      end
     end
   end
 
diff --git a/app/controllers/conferences_controller.rb b/app/controllers/conferences_controller.rb
index 038e590..4077705 100644
--- a/app/controllers/conferences_controller.rb
+++ b/app/controllers/conferences_controller.rb
@@ -5,8 +5,7 @@ class ConferencesController < ApplicationController
 
   def show
     @conference = Conference.find_by(slug: params[:slug])
-    @sessions = @conference.sessions.where.not(starts_at: nil).includes(:stage).order(:starts_at)
-    @sessions_by_date = @sessions.group_by{ |x| x.starts_at.to_date }
+    @sessions = @conference.sessions.where.not(starts_at: nil).includes(:stage, :assignments).where(stage: { name: ["Stage 1", "Stage 2"] }).order(:starts_at)
     @users = User.all
   end
 end
diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb
index 5f107b0..2feb7ef 100644
--- a/app/controllers/sessions_controller.rb
+++ b/app/controllers/sessions_controller.rb
@@ -22,38 +22,4 @@ class SessionsController < ApplicationController
     @session = Session.includes(:stage).find_by(ref_id: params[:ref_id])
     @users = User.all
   end
-
-  def assign_user
-    @session = Session.find_by(ref_id: params[:ref_id])
-    @conference = Conference.find_by(slug: params[:slug])
-    @user = User.find(params[:user_id])
-    @assignment = @session.assignments.new(session: @session, user: @user)
-    @users = User.all
-
-    if @assignment.save
-      flash.now[:success] = 'User assigned successfully.'
-      respond_to do |format|
-        # format.turbo_stream
-        format.html { redirect_to conference_session_path(@session.conference, @session), success: 'User assigned successfully.' }
-      end
-    else
-      flash.now[:alert] = 'Failed to assign user.'
-      respond_to do |format|
-        # format.turbo_stream { render :show, status: :unprocessable_entity }
-        format.html { render :show, status: :unprocessable_entity, alert: 'Failed to assign user.' }
-      end
-    end
-  end
-
-  def unassign_user
-    @session = Session.find_by(ref_id: params[:ref_id])
-    @user = User.find(params[:user_id])
-    @assignment = Assignment.find_by(session: @session, user: @user)
-
-    if @assignment&.destroy
-      redirect_to conference_session_path(@session.conference, @session), notice: 'User removed successfully.'
-    else
-      redirect_to conference_session_path(@session.conference, @session), alert: 'Failed to remove user.'
-    end
-  end
 end
diff --git a/app/jobs/republica_2023_or_later/import_job.rb b/app/jobs/republica_2023_or_later/import_job.rb
index 2c3b542..4d20870 100644
--- a/app/jobs/republica_2023_or_later/import_job.rb
+++ b/app/jobs/republica_2023_or_later/import_job.rb
@@ -25,7 +25,7 @@ module Republica2023OrLater
               end
             session.status = session_data['status']
             session.description = session_data['description']
-            session.format = session_data['format']
+            session.session_format = session_data['format']
             session.track = session_data['track']
             session.is_interpreted = (session_data['live_translation'] == "1")
             session.starts_at = session_data['datetime_start']
diff --git a/app/models/conference.rb b/app/models/conference.rb
index 40b40a5..a4efb3d 100644
--- a/app/models/conference.rb
+++ b/app/models/conference.rb
@@ -1,8 +1,11 @@
 class Conference < ApplicationRecord
   has_many :sessions
   has_many :stages
+
   serialize :data, coder: JSON
 
+  validates :time_zone, presence: true, inclusion: { in: ActiveSupport::TimeZone.all.map(&:name) }
+
   def days
     (starts_at.to_date..ends_at.to_date)
   end
@@ -10,4 +13,12 @@ class Conference < ApplicationRecord
   def to_param
     slug
   end
+
+  def starts_at_in_local_time
+    starts_at.in_time_zone(time_zone || 'UTC')
+  end
+
+  def ends_at_in_local_time
+    ends_at.in_time_zone(time_zone || 'UTC')
+  end
 end
diff --git a/app/models/session.rb b/app/models/session.rb
index 6a18ee7..1a757bd 100644
--- a/app/models/session.rb
+++ b/app/models/session.rb
@@ -9,4 +9,12 @@ class Session < ApplicationRecord
   def to_param
     ref_id
   end
+
+  def starts_at
+    super.in_time_zone(conference.time_zone)
+  end
+
+  def ends_at
+    super.in_time_zone(conference.time_zone)
+  end
 end
diff --git a/app/models/user.rb b/app/models/user.rb
index 429f19a..8e3dc5a 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -1,4 +1,7 @@
 class User < ApplicationRecord
+  has_many :assignments
+  validates :email, uniqueness: { case_sensitive: false, message: "already in use" }
+
   after_initialize :set_avatar_color
 
   def text_color
diff --git a/app/views/application/_user_avatar.html.erb b/app/views/application/_user_avatar.html.erb
index ff2f9b4..6e26c21 100644
--- a/app/views/application/_user_avatar.html.erb
+++ b/app/views/application/_user_avatar.html.erb
@@ -1,3 +1,3 @@
-<div class="relative inline-flex items-center justify-center h-8 p-2 overflow-hidden rounded-full" style="background-color: <%= user.avatar_color %>" title="<%= user.name %>">
-    <span class="font-medium" style="color: <%= user.text_color %>"><%= user.name %></span>
-</div>
+<span class="inline-flex items-center gap-x-0.5 rounded-full bg-gray-50 px-2 py-1 text-xs font-medium text-gray-600 ring-1 ring-inset ring-gray-500/10" style="background-color: <%= user.avatar_color %>" title="<%= user.name %>">
+  <span style="color: <%= user.text_color %>"><%= user.name.split(/\s+/).map(&:first).join('') %></span>
+</span>
diff --git a/app/views/assignments/_user_avatar.html.erb b/app/views/assignments/_user_avatar.html.erb
new file mode 100644
index 0000000..8f1cc9b
--- /dev/null
+++ b/app/views/assignments/_user_avatar.html.erb
@@ -0,0 +1,13 @@
+<% user = assignment.user %>
+<span class="inline-flex items-center gap-x-0.5 rounded-md bg-gray-50 px-2 py-1 text-xs font-medium text-gray-600 ring-1 ring-inset ring-gray-500/10" style="background-color: <%= user.avatar_color %>" title="<%= user.name %>">
+  <span style="color: <%= user.text_color %>"><%= user.name %></span>
+  <button type="button" class="group relative -mr-1 size-3.5 rounded-sm hover:bg-gray-500/20">
+    <%= link_to conference_session_assignment_path(assignment.session.conference, assignment.session, assignment), data: { turbo_method: :delete, confirm: 'Are you sure?' }, method: :delete do %>
+      <span class="sr-only">Remove</span>
+      <svg viewBox="0 0 14 14" class="size-3.5 stroke-gray-600/50 group-hover:stroke-gray-600/75" style="stroke: <%= user.text_color %>">
+        <path d="M4 4l6 6m0-6l-6 6" />
+      </svg>
+      <span class="absolute -inset-1"></span>
+    <% end %>
+  </button>
+</span>
diff --git a/app/views/assignments/by_user.html.erb b/app/views/assignments/by_user.html.erb
new file mode 100644
index 0000000..9014b7f
--- /dev/null
+++ b/app/views/assignments/by_user.html.erb
@@ -0,0 +1,10 @@
+<ul>
+  <% @user.assignments.includes(:session, session: :conference).order('sessions.starts_at').each do |assignment| %>
+    <li>
+      <%= assignment.session.conference.name %>
+      <%= assignment.session.starts_at %>
+      <%= assignment.session.stage.name %>
+      <%= assignment.session.title %>
+    </li>
+  <% end %>
+</ul>
\ No newline at end of file
diff --git a/app/views/conferences/show.html.erb b/app/views/conferences/show.html.erb
index 8cba38a..00b8cd4 100644
--- a/app/views/conferences/show.html.erb
+++ b/app/views/conferences/show.html.erb
@@ -2,12 +2,13 @@
 pixels_per_hour = 300.0
 timeline_granularity = 15
 # current_time = Time.zone.now
-current_time = @sessions_by_date[@conference.days.first].first.starts_at.advance(minutes: 5)
+current_time = Time.parse(@conference.days.first.strftime("%Y-%m-%d 10:37"))
+#current_time = @sessions_by_date[@conference.days.first].first.starts_at.advance(minutes: 5)
 %>
 <div>
-  <h1><%= @conference.name %></h1>
+  <h1 class="text-2xl font-bold my-6"><%= @conference.name %></h1>
 
-  <nav id="conference-days">
+  <nav id="conference-days" class="list-disc list-inside">
     <% @conference.days.each do |date| %>
       <li><%= link_to date.strftime("%Y-%m-%d"), "\##{date.strftime('day-%Y-%m-%d')}" %></li>
     <% end %>
@@ -15,21 +16,22 @@ current_time = @sessions_by_date[@conference.days.first].first.starts_at.advance
 
   <%
   @conference.days.each do |date|
+    @sessions_by_date = @sessions.group_by { |x| x.starts_at.to_date }
+    @sessions_by_date_and_stage = @sessions_by_date.transform_values{ |sessions| sessions.group_by { |s| s.stage } }
     day_starts_at = @sessions_by_date[date].first.starts_at
     day_ends_at = @sessions_by_date[date].last.ends_at
     # round to previous interval
-    timeline_starts_at = day_starts_at.beginning_of_hour.advance(minutes: (day_starts_at.min / timeline_granularity.to_f).floor * timeline_granularity)
+    timeline_starts_at = day_starts_at.advance(minutes: (day_starts_at.min / timeline_granularity.to_f).floor * timeline_granularity * -0.5)
     # ... , except rounding up to later interval
-    timeline_ends_at = day_ends_at.beginning_of_hour.advance(minutes: (day_ends_at.min / timeline_granularity.to_f).ceil * timeline_granularity)
+    timeline_ends_at = day_ends_at.advance(minutes: (day_ends_at.min / timeline_granularity.to_f).ceil * timeline_granularity)
   %>
-  <div class="conference-day" id="<%= date.strftime('day-%Y-%m-%d') %>" style="position: relative">
-    <h3><%= date.strftime('%B %d, %Y') %></h3>
-    <div class="day-wrapper flex" style="display: flex;">
+    <div class="conference-day my-8" id="<%= date.strftime('day-%Y-%m-%d') %>">
+      <h3 class="text-xl my-4 border-b"><%= date.strftime('%B %d, %Y') %></h3>
+      <div class="day-wrapper flex relative">
 
       <div class="times" style="">
         <h4>Time</h4>
-        <%
-        if current_time.strftime('%Y%m%d') == date.strftime('%Y%m%d')
+        <% if current_time.strftime('%Y%m%d') == date.strftime('%Y%m%d')
         %>
         <div class="current-time" style="top: <%= (current_time - timeline_starts_at) / 3600.0 * pixels_per_hour %>px"></div>
         <%
@@ -45,33 +47,23 @@ current_time = @sessions_by_date[@conference.days.first].first.starts_at.advance
         end
         %>
       </div>
+
       <div class="stages">
-      <% @sessions_by_date[date].group_by(&:stage).each do |stage, sessions| %>
-        <% next unless ["Stage 1", "Stage 2"].include? stage.name %>
-        <div class="stage">
-          <h4><%= stage.name %></h4>
-          <div class="stage-sessions">
-          <% sessions.each do |session| %>
-            <div class="session" style="position: absolute; top: <%= (session.starts_at - timeline_starts_at) / 3600.0 * pixels_per_hour %>px; height: <%= (session.ends_at - session.starts_at) / 3600.0 * pixels_per_hour%>px; overflow: scroll;">
-              <h4><%= link_to session.title, [@conference, session] %></h4>
-              <p class="session-time"><%= session.starts_at.strftime('%H:%M') %> - <%= session.ends_at.strftime('%H:%M') %></p>
-              <%#<div class="session-desc"><%= session.description.html_safe %><%#/div>%>
-              <ul class="list-disc">
-                <% session.assignments.each do |assignment| %>
-                  <li>
-                    <span class="assigned-user"><%= render partial: 'application/user_avatar', locals: { user: assignment.user } %></span>
-                    <%= link_to '[Remove]', conference_session_assignments_path(session.conference, session, user_id: assignment.user_id), data: { turbo_method: :delete, confirm: 'Are you sure?' } %>
-                  </li>
-                <% end %>
-              </ul>
-              <%= render partial: "sessions/assignment_form", locals: { session: session } %>
+
+        <% @sessions_by_date_and_stage[date].each do |stage, sessions| %>
+          <div class="stage">
+            <h4><%= stage.name %></h4>
+            <div class="stage-sessions">
+              <% sessions.each do |session| %>
+                <div class="session text-sm" style="position: absolute; top: <%= (session.starts_at - timeline_starts_at) / 3600.0 * pixels_per_hour %>px; height: <%= (session.ends_at - session.starts_at) / 3600.0 * pixels_per_hour %>px; overflow: scroll;">
+                  <%= render partial: "sessions/session", locals: { session: session } %>
+                </div>
+              <% end %>
             </div>
-          <% end %>
           </div>
-        </div>
-      <% end %>
+        <% end %>
       </div>
     </div>
   </div>
   <% end %>
-</div>
+</div>
\ No newline at end of file
diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb
index 9f79c2f..d8c6680 100644
--- a/app/views/layouts/application.html.erb
+++ b/app/views/layouts/application.html.erb
@@ -12,7 +12,7 @@
   </head>
 
   <body>
-    <main class="container mx-auto mt-28 px-5 flex">
+    <main class="container mx-auto mt-8 px-5 flex">
       <%= yield %>
     </main>
   </body>
diff --git a/app/views/sessions/_assignment_form.html.erb b/app/views/sessions/_assignment_form.html.erb
index d9a4d98..45ce934 100644
--- a/app/views/sessions/_assignment_form.html.erb
+++ b/app/views/sessions/_assignment_form.html.erb
@@ -1,9 +1,12 @@
-<%= form_with url: conference_session_assignments_path(session.conference, session), method: :post, local: true do |f| %>
-  <%= f.select :user_id, options_from_collection_for_select(@users - session.assignments.collect(&:user), :id, :name) %>
-  <%= f.submit "Assign", class: 'primary' %>
-  <% if @assignment&.errors&.any? %>
-    <div class="alert alert-danger">
-      <%= @assignment.errors.full_messages.join(", ") %>
-    </div>
+<% unassigned_users = @users - session.assignments.collect(&:user) %>
+<% if unassigned_users.length > 0 %>
+  <%= form_with url: conference_session_assignments_path(session.conference, session), method: :post, data: { turbo_frame: dom_id(session) } do |f| %>
+    <%= f.select :user_id, options_from_collection_for_select(unassigned_users, :id, :name) %>
+    <%= f.submit "Assign", class: 'primary' %>
+    <% if @assignment&.errors&.any? %>
+      <div class="alert alert-danger">
+        <%= @assignment.errors.full_messages.join(", ") %>
+      </div>
+    <% end %>
   <% end %>
 <% end %>
diff --git a/app/views/sessions/_session.html.erb b/app/views/sessions/_session.html.erb
new file mode 100644
index 0000000..1f03f31
--- /dev/null
+++ b/app/views/sessions/_session.html.erb
@@ -0,0 +1,12 @@
+<%= turbo_frame_tag dom_id(session) do %>
+  <h4><%= link_to session.title, [session.conference, session], target: "_top" %></h4>
+  <p class="session-time"><%= session.starts_at.strftime('%H:%M') %> - <%= session.ends_at.strftime('%H:%M') %></p>
+  <ul class="inline-flex flex-wrap gap-1 my-1">
+    <% session.assignments.each do |assignment| %>
+      <li>
+        <span class="assigned-user"><%= render partial: 'assignments/user_avatar', locals: { assignment: assignment } %></span>
+      </li>
+    <% end %>
+  </ul>
+  <%= render partial: "sessions/assignment_form", locals: { session: session } %>
+<% end %>
diff --git a/app/views/sessions/show.html.erb b/app/views/sessions/show.html.erb
index e98f422..58d3a86 100644
--- a/app/views/sessions/show.html.erb
+++ b/app/views/sessions/show.html.erb
@@ -1,5 +1,5 @@
 <div>
-  <h6><%= @session.conference.name %></h6>
+  <h6><%= link_to @session.conference.name, @session.conference %></h6>
   <h1><%= @session.title %></h1>
   <h2><%= @session.language %> <%= @session.is_interpreted ? "(live interpretation)" : "" %></h2>
   <h2><%= @session.starts_at.strftime("%Y-%m-%d") %> <%= @session.starts_at.strftime("%H:%M") %> &ndash; <%= @session.ends_at.strftime("%H:%M") %></h2>
@@ -7,11 +7,10 @@
   <p><%= @session.description.html_safe %></p>
   
   <h3>Assigned Users</h3>
-  <ul>
+  <ul class="inline-flex flex-wrap gap-1 my-1">
     <% @session.assignments.each do |assignment| %>
       <li>
-        <%= render partial: 'application/user_avatar', locals: { user: assignment.user } %>
-        <%= link_to '[Remove]', conference_session_assignments_path(@session.conference, @session, user_id: assignment.user_id), data: { turbo_method: :delete, confirm: 'Are you sure?' } %>
+        <span class="assigned-user"><%= render partial: 'assignments/user_avatar', locals: { assignment: assignment } %></span>
       </li>
     <% end %>
   </ul>
diff --git a/config/routes.rb b/config/routes.rb
index ffe5506..ead8585 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -11,11 +11,13 @@ Rails.application.routes.draw do
 
   resources :conferences, param: :slug do
     resources :sessions, param: :ref_id do
-      resource :assignments, only: [:create, :destroy]
+      resources :assignments, only: [:create, :destroy]
     end
   end
 
-  resources :assignments, only: [:index]
+  resources :assignments, only: [:index] do
+    get 'for/:user_id', action: 'by_user', on: :collection
+  end
   resources :sessions, param: :ref_id
 
   # get 'conferences/:slug', to: 'conferences#show', as: :conference
diff --git a/db/migrate/20240525190720_add_time_zone_to_conference.rb b/db/migrate/20240525190720_add_time_zone_to_conference.rb
new file mode 100644
index 0000000..9436332
--- /dev/null
+++ b/db/migrate/20240525190720_add_time_zone_to_conference.rb
@@ -0,0 +1,5 @@
+class AddTimeZoneToConference < ActiveRecord::Migration[7.1]
+  def change
+    add_column :conferences, :time_zone, :string
+  end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 322e2d2..b6e8e04 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -10,7 +10,7 @@
 #
 # It's strongly recommended that you check this file into your version control system.
 
-ActiveRecord::Schema[7.1].define(version: 2024_05_24_124124) do
+ActiveRecord::Schema[7.1].define(version: 2024_05_25_190720) do
   create_table "assignments", force: :cascade do |t|
     t.integer "user_id", null: false
     t.integer "session_id", null: false
@@ -32,6 +32,7 @@ ActiveRecord::Schema[7.1].define(version: 2024_05_24_124124) do
     t.text "data"
     t.string "slug"
     t.string "import_job_class"
+    t.string "time_zone"
   end
 
   create_table "sessions", force: :cascade do |t|
diff --git a/db/seeds.rb b/db/seeds.rb
index 20d6c02..cf19d0f 100644
--- a/db/seeds.rb
+++ b/db/seeds.rb
@@ -17,6 +17,7 @@ Conference.find_or_create_by!(slug: "rp2023") do |c|
     "sessions_url" => "https://re-publica.com/sites/default/files/extappdata/2023/session.json"
   }
   c.import_job_class = "republica_2023_or_later"
+  c.time_zone = "Berlin"
 end
 
 Conference.find_or_create_by!(slug: "rp2024") do |c|
@@ -28,9 +29,9 @@ Conference.find_or_create_by!(slug: "rp2024") do |c|
     "sessions_url" => "https://re-publica.com/sites/default/files/extappdata/2024/session.json"
   }
   c.import_job_class = "republica_2023_or_later"
+  c.time_zone = "Berlin"
 end
 
-User.find_or_create_by!(email: "teal@teal.is") do |user|
-  user.name = "Teal"
-  user.avatar_color = "#14bfb5"
+%w[Teal hdsjulian Sophie bergpiratin sblsg Max aerowaffle ningwie Senana ToniHDS].each do |username|
+  User.find_or_create_by!(name: username, email: "c3lingo+#{username}@x.moeffju.net")
 end
-- 
GitLab