diff --git a/app/controllers/admin/timeline_controller.rb b/app/controllers/admin/timeline_controller.rb index e213bc5..857a268 100644 --- a/app/controllers/admin/timeline_controller.rb +++ b/app/controllers/admin/timeline_controller.rb @@ -5,8 +5,32 @@ class Admin::TimelineController < Admin::BaseController # for span calculations (visual blocks in the timeline) timeout_duration = 10.minutes.to_i # This remains for span display + @review_item = Neighborhood::Post.find_by(airtable_id: params["post_id"]) if params["post_id"].present? + # all posts from user + @posts_from_user = Neighborhood::Post.where(airtable_fields: { slackId: @review_item.airtable_fields["slackId"] }) if @review_item.present? + if @review_item.present? + puts "review item found" + puts @review_item.airtable_fields + @date = @review_item.airtable_fields["createdAt"].to_date + + @slack_uid = @review_item.airtable_fields["slackId"]&.first + @review_item_user = User.find_by(slack_uid: @slack_uid) if @slack_uid.present? + + if @review_item.present? && @review_item_user.present? + @review_item_marker = { + user_id: @review_item_user.id, + timestamp: Time.parse(@review_item.airtable_fields["createdAt"]).to_f, + description: @review_item.airtable_fields["description"], + demo_video: @review_item.airtable_fields["demoVideo"], + photobooth_video: @review_item.airtable_fields["photoboothVideo"], + hackatime_time: @review_item.airtable_fields["hackatimeTime"], + hackatime_time_hours: @review_item.airtable_fields["hackatimeTimeHours"] + } + end + end + # Determine the date to display (default to today) - @date = params[:date] ? Date.parse(params[:date]) : Time.current.to_date + @date ||= params[:date] ? Date.parse(params[:date]) : Time.current.to_date # Calculate next and previous dates @next_date = @date + 1.day @@ -17,6 +41,7 @@ class Admin::TimelineController < Admin::BaseController # Always include current_user (admin) @selected_user_ids = [ current_user.id ] + raw_user_ids + @selected_user_ids << @review_item_user.id if @review_item_user.present? @selected_user_ids.uniq! user_ids_to_fetch = @selected_user_ids @@ -122,6 +147,51 @@ class Admin::TimelineController < Admin::BaseController @primary_user = @users_with_timeline_data.first&.[](:user) || current_user + # Get slack_uids for all selected users + slack_uids = User.where(id: @selected_user_ids).pluck(:slack_uid).compact + uids_sql_array = slack_uids.map { |uid| ActiveRecord::Base.connection.quote(uid) }.join(", ") + date_str = @date.to_s + posts_for_timeline = Neighborhood::Post.where( + "airtable_fields -> 'slackId' ?| array[#{uids_sql_array}]" + ).where("DATE(airtable_fields ->> 'createdAt') = ?", date_str) + + @timeline_post_markers = posts_for_timeline.map do |post| + { + user_id: User.find_by(slack_uid: post.airtable_fields["slackId"]&.first)&.id, + timestamp: Time.parse(post.airtable_fields["createdAt"]).to_f, + description: post.airtable_fields["description"], + demo_video: post.airtable_fields["demoVideo"], + photobooth_video: post.airtable_fields["photoboothVideo"], + hackatime_time: post.airtable_fields["hackatimeTime"], + hackatime_time_hours: post.airtable_fields["hackatimeTimeHours"], + highlighted: (@review_item.present? && post.id == @review_item.id), + airtable_url: post.airtable_url + } + end.compact + + # Get commit markers for timeline + commits_for_timeline = Commit.where( + user_id: @selected_user_ids, + created_at: @date.beginning_of_day..@date.end_of_day + ) + + @timeline_commit_markers = commits_for_timeline.map do |commit| + raw = commit.github_raw || {} + # Use committer date if available, else fallback to created_at + commit_time = if raw.dig("commit", "committer", "date") + Time.parse(raw["commit"]["committer"]["date"]) + else + commit.created_at + end + { + user_id: commit.user_id, + timestamp: commit_time.to_f, + additions: (raw["stats"] && raw["stats"]["additions"]) || raw.dig("files", 0, "additions"), + deletions: (raw["stats"] && raw["stats"]["deletions"]) || raw.dig("files", 0, "deletions"), + github_url: raw["html_url"] + } + end + render :show # Renders app/views/admin/timeline/show.html.erb end diff --git a/app/models/concerns/has_table_sync.rb b/app/models/concerns/has_table_sync.rb index 97aabf8..800dd7a 100644 --- a/app/models/concerns/has_table_sync.rb +++ b/app/models/concerns/has_table_sync.rb @@ -4,6 +4,10 @@ module HasTableSync included do validates :airtable_fields, presence: true validates :airtable_id, presence: true, uniqueness: true + + def airtable_url + "https://airtable.com/#{self.class.table_sync_base}/#{self.class.table_sync_table}/#{airtable_id}" + end end class_methods do @@ -20,5 +24,15 @@ module HasTableSync upsert_all(records, unique_by: :airtable_id) end end + + def table_sync_pat + @table_sync_pat + end + def table_sync_base + @table_sync_base + end + def table_sync_table + @table_sync_table + end end end diff --git a/app/views/admin/timeline/show.html.erb b/app/views/admin/timeline/show.html.erb index ea552e3..979f32b 100644 --- a/app/views/admin/timeline/show.html.erb +++ b/app/views/admin/timeline/show.html.erb @@ -211,7 +211,7 @@ duration_seconds_in_view = effective_end_time - effective_start_time height_px = (duration_seconds_in_view / 60.0) * pixels_per_minute return nil if height_px <= 0.5 - final_top_px = (minutes_from_view_start * pixels_per_minute) + final_top_px = (minutes_from_view_start * pixels_per_minute).round title_parts = [] title_parts << "Languages: #{span_data[:languages].join(', ')}" if span_data[:languages]&.any? project_title_segments = (span_data[:projects_edited_details] || []).map do |proj_detail| @@ -290,11 +290,145 @@ <% end %> <% end %> + + <% # Group markers by user and sort by timestamp %> + <% cap_width = 16 %> + <% cap_height = 4 %> + <% vertical_padding = 6 %> + <% grouped_markers = @timeline_post_markers.group_by { |m| m[:user_id] } %> + <% grouped_markers.each do |user_id, markers| %> + <% user_index = users_data_array.find_index { |data| data[:user].id == user_id } %> + <% next unless user_index %> + <% user = users_data_array[user_index][:user] %> + <% user_tz = user&.timezone || primary_user_tz %> + <% today_start_of_day_for_user = @date.in_time_zone(user_tz).beginning_of_day %> + <% view_start_datetime = today_start_of_day_for_user.advance(hours: timeline_start_hour) %> + <% base_offset_px = user_index * min_column_width_px + (user_index > 0 ? user_index * gutter_px : 0) %> + <% marker_left_px = (line_left_rem * 16) + base_offset_px + min_column_width_px - 4 %> + <% cap_left_px = marker_left_px - (cap_width / 2) + 2 %> + + <% sorted_markers = markers.sort_by { |m| m[:timestamp] } %> + <% sorted_markers.each_with_index do |marker, idx| %> + <% marker_time = Time.at(marker[:timestamp]).in_time_zone(user_tz) %> + <% marker_minutes_from_view_start = ((marker_time - view_start_datetime) / 60.0).to_f %> + <% marker_top_px = (marker_minutes_from_view_start * pixels_per_minute).round %> + + <% if idx == 0 %> + <% prev_time = view_start_datetime %> + <% else %> + <% prev_time = Time.at(sorted_markers[idx-1][:timestamp]).in_time_zone(user_tz) %> + <% end %> + <% prev_minutes_from_view_start = ((prev_time - view_start_datetime) / 60.0).to_f %> + <% prev_top_px = (prev_minutes_from_view_start * pixels_per_minute).round %> + <% line_start_px = prev_top_px + vertical_padding %> + <% line_height = marker_top_px - line_start_px %> + + +
+
+ + <% circle_diameter = 14 %> + <% circle_left_px = marker_left_px - (circle_diameter / 2) + 2 %> +
+
+ <% end %> + <% end %> + + <% @timeline_commit_markers.each do |commit| %> + <% user_index = users_data_array.find_index { |data| data[:user].id == commit[:user_id] } %> + <% next unless user_index %> + <% user = users_data_array[user_index][:user] %> + <% user_tz = user&.timezone || primary_user_tz %> + <% today_start_of_day_for_user = @date.in_time_zone(user_tz).beginning_of_day %> + <% view_start_datetime = today_start_of_day_for_user.advance(hours: timeline_start_hour) %> + <% base_offset_px = user_index * min_column_width_px + (user_index > 0 ? user_index * gutter_px : 0) %> + <% pill_left_px = (line_left_rem * 16) + base_offset_px + min_column_width_px - 60 %> + <% commit_minutes_from_view_start = ((Time.at(commit[:timestamp]).in_time_zone(user_tz) - view_start_datetime) / 60.0).to_f %> + <% commit_top_px = (commit_minutes_from_view_start * pixels_per_minute).round %> + + +<%= commit[:additions] %> + / + -<%= commit[:deletions] %> + + <% end %> + + + <% content_for :head do %>