From 635553da0b8845a534b23be6baddac797185f0fe Mon Sep 17 00:00:00 2001 From: Ying Tong Date: Sun, 5 Apr 2026 18:48:32 +0800 Subject: [PATCH 1/5] feat: Display comments for all versions --- app/controllers/projects_controller.rb | 2 +- app/controllers/topics_controller.rb | 2 +- app/models/project.rb | 1 + app/models/topic.rb | 2 ++ 4 files changed, 5 insertions(+), 2 deletions(-) diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index a433f1ce..42da31fd 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -38,7 +38,7 @@ def show @next_fields = @next_instance.project_instance_fields.includes(:project_template_field).order(project_template_field_id: :asc) end - @comments = @current_instance.comments.order(created_at: :asc) + @comments = @project.comments.order(created_at: :asc) @new_comment = Comment.new(user: current_user, location: @current_instance) return unless @course.use_progress_updates diff --git a/app/controllers/topics_controller.rb b/app/controllers/topics_controller.rb index fa30d324..066e0008 100644 --- a/app/controllers/topics_controller.rb +++ b/app/controllers/topics_controller.rb @@ -55,7 +55,7 @@ def show .order(project_template_field_id: :asc) end - @comments = @current_instance.comments.order(created_at: :asc) + @comments = @topic.comments.order(created_at: :asc) @new_comment = Comment.new @fields = @current_fields end diff --git a/app/models/project.rb b/app/models/project.rb index 053abceb..d04e3769 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -8,6 +8,7 @@ class Project < ApplicationRecord has_many :project_instances, dependent: :destroy has_many :progress_updates, dependent: :destroy + has_many :comments, through: :project_instances # DO NOT WRITE TO STATUS IN PROJECTS, IT'S ONLY MEANT TO KEEP TRACK OF THE STATUS OF THE LATEST PROJECT INSTANCE # write to the latest project instance instead diff --git a/app/models/topic.rb b/app/models/topic.rb index bb1fd79c..1ac037cb 100644 --- a/app/models/topic.rb +++ b/app/models/topic.rb @@ -9,6 +9,8 @@ class Topic < ApplicationRecord has_many :topic_instances, dependent: :destroy, foreign_key: 'project_id' + has_many :comments, through: :topic_instances + has_many :proposed_project_instances, class_name: 'ProjectInstance', foreign_key: 'source_topic_id' # DO NOT WRITE TO STATUS IN PROJECTS, IT'S ONLY MEANT TO KEEP TRACK OF THE STATUS OF THE LATEST PROJECT INSTANCE From 6e6d449021a28b1c49bd1836864f791dd5c8009b Mon Sep 17 00:00:00 2001 From: Ying Tong Date: Sun, 5 Apr 2026 21:41:43 +0800 Subject: [PATCH 2/5] feat: Add section label for different versions --- app/views/projects/_project_comments.html.erb | 117 +++++++++------- app/views/topics/_topic_comments.html.erb | 131 ++++++++++-------- 2 files changed, 137 insertions(+), 111 deletions(-) diff --git a/app/views/projects/_project_comments.html.erb b/app/views/projects/_project_comments.html.erb index fb567804..2038db90 100644 --- a/app/views/projects/_project_comments.html.erb +++ b/app/views/projects/_project_comments.html.erb @@ -31,67 +31,80 @@ <% if comments.any? %>
- <% comments.each do |comment| %> -
- -
-
- <%= comment.user.username[0].upcase %> + <% comments.group_by { |c| c.location.version }.each do |version, version_comments| %> +
+

+ Comments for v<%= version %> +

+
+ <% version_comments.each do |comment| %> + <% is_current_version = comment.location_id == current_instance_id %> + +
+ +
+
+ <%= comment.user.username[0].upcase %> +
-
- -
- <%# --- FIXED HEADER LAYOUT --- %> -
+
-
- <% if Current.user == comment.user %> -

<%= comment.user.username %>

- <% else %> -

<%= comment.user.username %>

- <% end %> + <%# --- FIXED HEADER LAYOUT --- %> +
- - <% if comment.created_at > 3.days.ago %> - <%= time_ago_in_words(comment.created_at) %> - ago +
+ <% if Current.user == comment.user %> +

<%= comment.user.username %>

<% else %> - <%= format_timestamp(comment.created_at) %> +

<%= comment.user.username %>

<% end %> - -
- <%# delete button %> - <% if Current.user == comment.user && !comment.deleted %> - <%= link_to soft_delete_comment_path(comment), - data: { turbo_method: :patch, confirm: "Delete this comment?" }, - class: "flex-shrink-0 text-red-400 hover:text-red-600 transition-colors p-1 -mr-1", - title: "Delete comment" do %> - + + <% if comment.created_at > 3.days.ago %> + <%= time_ago_in_words(comment.created_at) %> + ago + <% else %> + <%= format_timestamp(comment.created_at) %> + <% end %> + +
+ + <%# delete button %> + <% if Current.user == comment.user && !comment.deleted %> + <%= link_to soft_delete_comment_path(comment), + data: { turbo_method: :patch, confirm: "Delete this comment?" }, + class: "flex-shrink-0 text-red-400 hover:text-red-600 transition-colors p-1 -mr-1", + title: "Delete comment" do %> + + <% end %> <% end %> - <% end %> -
- -
- <% if !comment.deleted %> - <%= simple_format(comment.text, class: "mb-3 last:mb-0") %> - <% else %> - This comment was deleted - <% end %> + + +
+ <% if !comment.deleted %> + <%= simple_format(comment.text, class: "mb-3 last:mb-0") %> + <% else %> + This comment was deleted + <% end %> +
-
-
+ + <% end %> <% end %>
diff --git a/app/views/topics/_topic_comments.html.erb b/app/views/topics/_topic_comments.html.erb index 316e6fd6..175ffa59 100644 --- a/app/views/topics/_topic_comments.html.erb +++ b/app/views/topics/_topic_comments.html.erb @@ -32,68 +32,81 @@ <% if comments&.any? %>
- <% comments.each do |comment| %> -
- - <%# Avatar %> -
-
- <%= comment.user.username[0].upcase %> + <% comments.group_by { |c| c.location.version }.each do |version, version_comments| %> + +
+

+ Comments for v<%= version %> +

+
+ <% version_comments.each do |comment| %> + <% is_current_version = comment.location_id == current_instance_id %> + +
+ +
+
+ <%= comment.user.username[0].upcase %> +
-
- - <%# Content %> -
- <%# --- FIXED HEADER LAYOUT --- %> -
+
-
- <% if Current.user == comment.user %> -

<%= comment.user.username %>

- <% else %> -

<%= comment.user.username %>

- <% end %> + <%# --- FIXED HEADER LAYOUT --- %> +
- - <% if comment.created_at > 3.days.ago %> - <%= time_ago_in_words(comment.created_at) %> - ago +
+ <% if Current.user == comment.user %> +

<%= comment.user.username %>

<% else %> - <%= format_timestamp(comment.created_at) %> +

<%= comment.user.username %>

<% end %> - -
- <% if Current.user == comment.user && !comment.deleted %> - <%= link_to soft_delete_comment_path(comment), - data: { turbo_method: :patch, turbo_confirm: "Delete this comment?" }, + + <% if comment.created_at > 3.days.ago %> + <%= time_ago_in_words(comment.created_at) %> + ago + <% else %> + <%= format_timestamp(comment.created_at) %> + <% end %> + +
+ + <%# delete button %> + <% if Current.user == comment.user && !comment.deleted %> + <%= link_to soft_delete_comment_path(comment), + data: { turbo_method: :patch, confirm: "Delete this comment?" }, class: "flex-shrink-0 text-red-400 hover:text-red-600 transition-colors p-1 -mr-1", title: "Delete comment" do %> - + + <% end %> <% end %> - <% end %> -
- -
- <% if !comment.deleted %> - <%= simple_format(comment.text, class: "mb-3 last:mb-0") %> - <% else %> - This comment was deleted - <% end %> + + +
+ <% if !comment.deleted %> + <%= simple_format(comment.text, class: "mb-3 last:mb-0") %> + <% else %> + This comment was deleted + <% end %> +
-
-
+ + <% end %> <% end %>
@@ -136,14 +149,14 @@ > <%= form.text_area :user_comment, - class: - "w-full bg-transparent border-0 text-sm text-gray-800 placeholder-gray-400 py-2 px-2 focus:outline-none resize-none overflow-y-auto max-h-[120px]", - rows: 1, - data: { - controller: "textarea-resize", - action: "input->textarea-resize#resize", - }, - placeholder: "Write a comment..." %> + class: + "w-full bg-transparent border-0 text-sm text-gray-800 placeholder-gray-400 py-2 px-2 focus:outline-none resize-none overflow-y-auto max-h-[120px]", + rows: 1, + data: { + controller: "textarea-resize", + action: "input->textarea-resize#resize", + }, + placeholder: "Write a comment..." %> <%= form.button type: "submit", class: "p-2 bg-white text-blue-600 rounded-lg shadow-sm border border-gray-100 hover:bg-blue-50 hover:text-blue-700 cursor-pointer transition-colors flex-shrink-0", title: "Send" do %> Date: Sun, 5 Apr 2026 22:09:53 +0800 Subject: [PATCH 3/5] styling: Center align status icon and status text in project status bar --- .../courses/_project_status_bar.html.erb | 23 ++++++++++++++----- 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/app/views/courses/_project_status_bar.html.erb b/app/views/courses/_project_status_bar.html.erb index 441cffed..6b6feef6 100644 --- a/app/views/courses/_project_status_bar.html.erb +++ b/app/views/courses/_project_status_bar.html.erb @@ -12,12 +12,22 @@
    <% statuses.each do |status| %> <% is_active = @current_status.to_s == status %> -
  1. - - <%= image_tag "#{status}.svg", class: "w-4 h-4 #{is_active ? 'opacity-100' : 'opacity-30'}" %> +
  2. + + <%= image_tag "#{status}.svg", + class: "w-4 h-4 #{is_active ? "opacity-100" : "opacity-30"}" %> -

    +

    <%= status.humanize %>

  3. @@ -36,7 +46,8 @@

    No proposal submitted yet.

    <%= link_to "Submit Proposal", new_course_project_path(@course), - class: "inline-flex items-center justify-center px-6 py-3 text-base font-semibold text-white transition-all duration-200 bg-blue-600 rounded-lg hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 hover:shadow-md" %> + class: + "inline-flex items-center justify-center px-6 py-3 text-base font-semibold text-white transition-all duration-200 bg-blue-600 rounded-lg hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 hover:shadow-md" %> <% end %> From ba9a676d2b3d0a362bd04d4fab187362c29796b7 Mon Sep 17 00:00:00 2001 From: Ying Tong Date: Sun, 5 Apr 2026 23:18:17 +0800 Subject: [PATCH 4/5] feat: Make comments scroll to bottom --- .../controllers/scroll_to_bottom_controller.js | 15 +++++++++++++++ app/views/projects/_project_comments.html.erb | 2 ++ app/views/topics/_topic_comments.html.erb | 8 +++++++- 3 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 app/javascript/controllers/scroll_to_bottom_controller.js diff --git a/app/javascript/controllers/scroll_to_bottom_controller.js b/app/javascript/controllers/scroll_to_bottom_controller.js new file mode 100644 index 00000000..478479af --- /dev/null +++ b/app/javascript/controllers/scroll_to_bottom_controller.js @@ -0,0 +1,15 @@ +import { Controller } from "@hotwired/stimulus"; + +export default class extends Controller { + static values = { enabled: Boolean }; + + connect() { + if (this.enabledValue) { + this.scrollToBottom(); + } + } + + scrollToBottom() { + this.element.scrollTop = this.element.scrollHeight; + } +} diff --git a/app/views/projects/_project_comments.html.erb b/app/views/projects/_project_comments.html.erb index 70188bdd..3ef4cd4b 100644 --- a/app/views/projects/_project_comments.html.erb +++ b/app/views/projects/_project_comments.html.erb @@ -26,6 +26,8 @@ class=" flex-1 min-h-0 overflow-y-auto bg-gray-50/30 p-5 custom-scrollbar relative " + data-controller="scroll-to-bottom" + data-scroll-to-bottom-enabled-value="<%= current_version == latest_version %>" > <% if comments.any? %> diff --git a/app/views/topics/_topic_comments.html.erb b/app/views/topics/_topic_comments.html.erb index 88d6f974..5a0164ed 100644 --- a/app/views/topics/_topic_comments.html.erb +++ b/app/views/topics/_topic_comments.html.erb @@ -27,7 +27,13 @@ <%# --- Comments Body --- %> -
    +
    <% if comments&.any? %>
    From cef5ee673b102f8c3459908a65bef76171f2807d Mon Sep 17 00:00:00 2001 From: Ying Tong Date: Sun, 5 Apr 2026 23:34:29 +0800 Subject: [PATCH 5/5] feat: Make comments scroll to corresponding version --- .../scroll_to_bottom_controller.js | 37 +++++++++++++++++-- app/views/projects/_project_comments.html.erb | 3 +- app/views/topics/_topic_comments.html.erb | 3 +- 3 files changed, 37 insertions(+), 6 deletions(-) diff --git a/app/javascript/controllers/scroll_to_bottom_controller.js b/app/javascript/controllers/scroll_to_bottom_controller.js index 478479af..a29bf5b9 100644 --- a/app/javascript/controllers/scroll_to_bottom_controller.js +++ b/app/javascript/controllers/scroll_to_bottom_controller.js @@ -1,15 +1,44 @@ import { Controller } from "@hotwired/stimulus"; export default class extends Controller { - static values = { enabled: Boolean }; + static values = { + enabled: Boolean, + targetVersion: String, + }; connect() { - if (this.enabledValue) { - this.scrollToBottom(); - } + // Wait for the browser to finish painting the DOM before calculating heights + requestAnimationFrame(() => { + setTimeout(() => { + if (this.enabledValue) { + this.scrollToBottom(); + } else if (this.hasTargetVersionValue) { + this.scrollToVersion(); + } + }, 50); + }); } scrollToBottom() { this.element.scrollTop = this.element.scrollHeight; } + + scrollToVersion() { + const targetId = `version-${this.targetVersionValue}-comments`; + const targetElement = document.getElementById(targetId); + + if (targetElement) { + const containerRect = this.element.getBoundingClientRect(); + + const targetRect = targetElement.getBoundingClientRect(); + + const scrollPosition = + this.element.scrollTop + (targetRect.top - containerRect.top) - 20; + + this.element.scrollTo({ + top: scrollPosition, + behavior: "smooth", + }); + } + } } diff --git a/app/views/projects/_project_comments.html.erb b/app/views/projects/_project_comments.html.erb index 3ef4cd4b..0d547eae 100644 --- a/app/views/projects/_project_comments.html.erb +++ b/app/views/projects/_project_comments.html.erb @@ -28,13 +28,14 @@ " data-controller="scroll-to-bottom" data-scroll-to-bottom-enabled-value="<%= current_version == latest_version %>" + data-scroll-to-bottom-target-version-value="<%= current_version %>" > <% if comments.any? %>
    <% comments.group_by { |c| c.location.version }.each do |version, version_comments| %> -
    +

    Comments for v<%= version %>

    diff --git a/app/views/topics/_topic_comments.html.erb b/app/views/topics/_topic_comments.html.erb index 5a0164ed..454b4f98 100644 --- a/app/views/topics/_topic_comments.html.erb +++ b/app/views/topics/_topic_comments.html.erb @@ -33,6 +33,7 @@ " data-controller="scroll-to-bottom" data-scroll-to-bottom-enabled-value="<%= current_version == latest_version %>" + data-scroll-to-bottom-target-version-value="<%= current_version %>" > <% if comments&.any? %> @@ -40,7 +41,7 @@ <% comments.group_by { |c| c.location.version }.each do |version, version_comments| %> -
    +

    Comments for v<%= version %>