From a3084b06bebec974aa6b1ca86c0bab11756eb375 Mon Sep 17 00:00:00 2001 From: Mae Beale Date: Tue, 20 Oct 2026 21:42:45 -0400 Subject: [PATCH 01/11] Start: person chip with facilitator hover on comments & communications Co-Authored-By: Claude Opus 5.5 (1M context) From 31d76ed216958e0227f3b2688c9aa6d4e1e41e4e Mon Sep 17 00:00:00 2001 From: Mae Beale Date: Tue, 20 Oct 2026 21:45:34 -0400 Subject: [PATCH 02/11] Show a person chip with facilitator standing on their comms feed Staff reading someone's comments & communications had to leave the page to check whether they're a current facilitator and when that term lapses. The chip's hover card answers that in place. Co-Authored-By: Claude Opus 5.5 (1M context) --- app/decorators/affiliation_decorator.rb | 21 ++++++++++++++ app/decorators/person_decorator.rb | 7 +++++ app/helpers/person_helper.rb | 4 +-- .../index.html.erb | 7 ++--- app/views/people/_person_chip.html.erb | 29 +++++++++++++++++++ ...20-comms-person-chip-facilitator-hover.yml | 11 +++++++ spec/decorators/affiliation_decorator_spec.rb | 28 ++++++++++++++++++ spec/decorators/person_decorator_spec.rb | 19 ++++++++++++ .../comments_and_communications_spec.rb | 18 ++++++++++-- 9 files changed, 135 insertions(+), 9 deletions(-) create mode 100644 app/views/people/_person_chip.html.erb create mode 100644 db/seeds/features/2026-10-20-comms-person-chip-facilitator-hover.yml diff --git a/app/decorators/affiliation_decorator.rb b/app/decorators/affiliation_decorator.rb index 9888b34098..3e02ad9324 100644 --- a/app/decorators/affiliation_decorator.rb +++ b/app/decorators/affiliation_decorator.rb @@ -32,10 +32,31 @@ def period_label "Dates not recorded" end + # E.g. "Started March 2023, ending next month" — relative wording for months + # close to now, so an imminent lapse reads at a glance. + def period_note + parts = [] + parts << "#{start_date > Date.current ? 'starting' : 'started'} #{relative_month(start_date)}" if start_date + parts << "#{end_date < Date.current ? 'ended' : 'ending'} #{relative_month(end_date)}" if end_date + parts.join(", ").upcase_first.presence || "Dates not recorded" + end + # Where a back link should land on the person/organization editor. An inactive # row sits on the Inactive tab, so jumping to the row itself would scroll to # something the page isn't showing — land on the section instead. def return_anchor active? ? h.dom_id(object) : "affiliations" end + + private + + def relative_month(date) + today = Date.current + case (date.year * 12 + date.month) - (today.year * 12 + today.month) + when -1 then "last month" + when 0 then "this month" + when 1 then "next month" + else date.strftime("%B %Y") + end + end end diff --git a/app/decorators/person_decorator.rb b/app/decorators/person_decorator.rb index 22af74e630..e153d9a00b 100644 --- a/app/decorators/person_decorator.rb +++ b/app/decorators/person_decorator.rb @@ -205,6 +205,13 @@ def facilitator_status_label "Inactive" end + # Facilitator rows that are running now or scheduled, earliest first. Skips a + # no-show's zero-length stub, which records no facilitation. + def current_facilitator_affiliations + affiliations.select { |affiliation| affiliation.facilitator? && !affiliation.zero_length? && !affiliation.inactive_on? } + .sort_by { |affiliation| affiliation.start_date || Date.current } + end + def facilitator_since_range date_range_display(facilitator_since_date, facilitation_end_date, ended_title: "No active facilitator affiliations") end diff --git a/app/helpers/person_helper.rb b/app/helpers/person_helper.rb index c1b5fcc1d3..db0cb60765 100644 --- a/app/helpers/person_helper.rb +++ b/app/helpers/person_helper.rb @@ -7,7 +7,7 @@ def person_name(record) allowed_to?(:full_name?, person) ? person.full_name : person.name end - def person_profile_button(person, truncate_at: nil, subtitle: nil, display_name: nil, data: {}, inactive: false, tint: nil, path_params: {}, width_class: "w-full", compact: false) + def person_profile_button(person, truncate_at: nil, subtitle: nil, display_name: nil, data: {}, inactive: false, tint: nil, path_params: {}, width_class: "w-full", compact: false, show_title: true) # Compact mode shrinks the whole control (padding, avatar, type) for dense # tables like the registrants roster where horizontal space is at a premium. padding = compact ? "px-2 py-1" : "px-4 py-2" @@ -29,7 +29,7 @@ def person_profile_button(person, truncate_at: nil, subtitle: nil, display_name: link_to person_path(person, **path_params), data: { turbo_prefetch: false }.merge(data), - title: hover_title, + title: (hover_title if show_title), class: "group relative flex items-center gap-2 #{width_class} #{padding} border #{palette[:border]} #{palette[:bg]} #{palette[:hover_bg]} rounded-lg diff --git a/app/views/comments_and_communications/index.html.erb b/app/views/comments_and_communications/index.html.erb index 99f949ad9b..90e3dedcaf 100644 --- a/app/views/comments_and_communications/index.html.erb +++ b/app/views/comments_and_communications/index.html.erb @@ -28,7 +28,7 @@ <%= pluralize(@total_count, "entry", plural: "entries") %><% if @flagged_count.positive? %> · <%= @flagged_count %> flagged<% end %> <% end %> <%# Title: the person's name (or "Everyone"). On a person's feed their activity, - profile, and the composer triggers sit beside it; the composer panels open in + profile chip, and the composer triggers sit beside it; the composer panels open in the body, sharing the card-level panel-toggle scope (content_data). The person picker that scopes the feed lives in the filter row below. %> <% title = capture do %> @@ -42,10 +42,7 @@ <%= @person.first_name %>'s activity <% end %> <% end %> - <%= link_to person_path(@person), class: button_classes(:primary_outline) do %> - - <%= @person.first_name %>'s profile - <% end %> + <%= render "people/person_chip", person: @person %> <%= render "comments_and_communications/add_buttons" %> <% end %> diff --git a/app/views/people/_person_chip.html.erb b/app/views/people/_person_chip.html.erb new file mode 100644 index 0000000000..6b802dae78 --- /dev/null +++ b/app/views/people/_person_chip.html.erb @@ -0,0 +1,29 @@ +<%# The person's profile chip with an admin hover card: when they first affiliated, + and each current facilitator affiliation's org, org status, and dates. + Locals: person (decorated). %> +<% affiliated_since = person.affiliated_since_date %> +<% facilitations = person.current_facilitator_affiliations %> + + <%= person_profile_button(person, subtitle: person.preferred_email, width_class: "w-auto max-w-xs", + show_title: false) %> + + diff --git a/db/seeds/features/2026-10-20-comms-person-chip-facilitator-hover.yml b/db/seeds/features/2026-10-20-comms-person-chip-facilitator-hover.yml new file mode 100644 index 0000000000..8d2b54af2d --- /dev/null +++ b/db/seeds/features/2026-10-20-comms-person-chip-facilitator-hover.yml @@ -0,0 +1,11 @@ +name: "See a person's facilitator standing from their comments & communications" +area: communications +display_status: admin_facing +released_on: 2026-10-20 +pr_number: 2766 +summary: >- + A person's comments & communications page now shows their profile chip. + Hover it to see when they first affiliated and each current facilitator + affiliation, with the organization's status and when the term started or ends. +pro_tips: + - "Dates close to today read in plain words, like \"ending next month\", so a lapsing term stands out." diff --git a/spec/decorators/affiliation_decorator_spec.rb b/spec/decorators/affiliation_decorator_spec.rb index d19b309891..d9a9dabc3e 100644 --- a/spec/decorators/affiliation_decorator_spec.rb +++ b/spec/decorators/affiliation_decorator_spec.rb @@ -51,6 +51,34 @@ # An inactive row sits on the Inactive tab, so a back link must land on the # section rather than a row the page isn't showing. + describe "#period_note" do + around { |example| travel_to(Date.new(2026, 10, 20)) { example.run } } + + it "reads an imminent end in relative terms" do + affiliation = build(:affiliation, start_date: Date.new(2023, 3, 1), end_date: Date.new(2026, 11, 5)).decorate + expect(affiliation.period_note).to eq("Started March 2023, ending next month") + end + + it "names the month of a farther end" do + affiliation = build(:affiliation, start_date: Date.new(2023, 3, 1), end_date: Date.new(2027, 3, 1)).decorate + expect(affiliation.period_note).to eq("Started March 2023, ending March 2027") + end + + it "reads an open-ended term by its start alone" do + affiliation = build(:affiliation, start_date: Date.new(2026, 10, 1), end_date: nil).decorate + expect(affiliation.period_note).to eq("Started this month") + end + + it "reads a scheduled term as starting" do + affiliation = build(:affiliation, start_date: Date.new(2026, 11, 5), end_date: nil).decorate + expect(affiliation.period_note).to eq("Starting next month") + end + + it "notes when no dates are recorded" do + expect(build(:affiliation, start_date: nil, end_date: nil).decorate.period_note).to eq("Dates not recorded") + end + end + describe "#return_anchor" do it "points at the row itself when the affiliation is active" do affiliation = create(:affiliation, start_date: 1.year.ago.to_date, end_date: nil) diff --git a/spec/decorators/person_decorator_spec.rb b/spec/decorators/person_decorator_spec.rb index 3e12552572..91a1f65e33 100644 --- a/spec/decorators/person_decorator_spec.rb +++ b/spec/decorators/person_decorator_spec.rb @@ -294,6 +294,25 @@ end end + describe "#current_facilitator_affiliations" do + let(:person) { create(:person) } + + it "lists running and scheduled facilitator terms, earliest first" do + upcoming = create(:affiliation, person: person, start_date: 1.month.from_now, end_date: nil) + active = create(:affiliation, person: person, start_date: 2.years.ago, end_date: 1.month.from_now) + + expect(person.decorate.current_facilitator_affiliations).to eq([ active, upcoming ]) + end + + it "skips ended terms, no-show stubs, and non-facilitator titles" do + create(:affiliation, person: person, start_date: 3.years.ago, end_date: 1.year.ago) + create(:affiliation, person: person, start_date: 1.week.from_now, end_date: 1.week.from_now) + create(:affiliation, person: person, title: "Board member", start_date: 1.year.ago) + + expect(person.decorate.current_facilitator_affiliations).to be_empty + end + end + describe "#affiliated_since_note" do let(:person) { create(:person) } diff --git a/spec/requests/comments_and_communications_spec.rb b/spec/requests/comments_and_communications_spec.rb index d319fef255..3154372d74 100644 --- a/spec/requests/comments_and_communications_spec.rb +++ b/spec/requests/comments_and_communications_spec.rb @@ -44,6 +44,20 @@ expect(name.name).to eq("span") end + it "shows affiliated-since and current facilitator terms on hover over the person chip" do + organization = create(:organization, name: "Riverside Shelter") + create(:affiliation, person: person, title: "Board member", start_date: Date.new(2021, 5, 1)) + create(:affiliation, person: person, organization: organization, start_date: Date.new(2023, 3, 1), end_date: 2.years.from_now) + + get comments_and_communications_path(person_id: person.id) + + card = Nokogiri::HTML(response.body).at_css("h1 .group\\/person") + expect(card.text).to include("Affiliated since May 2021") + expect(card.text).to include("Riverside Shelter") + expect(card.text).to include("Active") + expect(card.text).to include("Started March 2023") + end + it "links to the person's activity history from the header" do person.update!(first_name: "Aisha") get comments_and_communications_path(person_id: person.id) @@ -52,8 +66,8 @@ link = doc.css("a").find { |a| a.text.strip == "Aisha's activity" } expect(link).to be_present expect(link["href"]).to eq(admin_activities_events_path(person_id: person.id, time_period: "all_time", audience: %w[visitors users staff], hide_account: "1", hide_interactions: "1")) - profile = doc.css("a").find { |a| a.text.strip == "Aisha's profile" } - expect(profile["href"]).to eq(person_path(person)) + profile = doc.at_css("h1 a[href='#{person_path(person)}']") + expect(profile.text).to include(person.full_name) everyone = doc.css("a").find { |a| a.text.strip == "View everyone" } expect(everyone["href"]).to eq(comments_and_communications_path) end From b74c57248b18dae4e46d609151a2776f69734378 Mon Sep 17 00:00:00 2001 From: Mae Beale Date: Tue, 20 Oct 2026 22:53:38 -0400 Subject: [PATCH 03/11] Put the person chip in the comms title and on their row names The title repeated the person's name next to their chip, and the rows' name chips were where staff actually look to see who wrote in. Both now open the same facilitator hover card. Co-Authored-By: Claude Opus 5.5 (1M context) --- app/helpers/person_helper.rb | 7 +++++ .../index.html.erb | 7 ++--- .../_communication_line.html.erb | 17 ++++++++-- .../people/_facilitator_hover_card.html.erb | 31 +++++++++++++++++++ app/views/people/_person_chip.html.erb | 27 ++-------------- ...20-comms-person-chip-facilitator-hover.yml | 7 +++-- .../comments_and_communications_spec.rb | 25 +++++++++++---- 7 files changed, 80 insertions(+), 41 deletions(-) create mode 100644 app/views/people/_facilitator_hover_card.html.erb diff --git a/app/helpers/person_helper.rb b/app/helpers/person_helper.rb index db0cb60765..7424226a29 100644 --- a/app/helpers/person_helper.rb +++ b/app/helpers/person_helper.rb @@ -156,6 +156,13 @@ def user_link(user, data: {}) # A piece the person hid (a `profile_show_*` flag turned off) stays visible to # them and to admins under the blue admin-or-owner wash. `flag` is a # `profile_show_*` attribute name (symbol). + # Memoized per person so a feed naming the same person on every row renders + # (and queries) their card once. + def facilitator_hover_card(person, header: false) + @facilitator_hover_cards ||= {} + @facilitator_hover_cards[[ person.id, header ]] ||= render("people/facilitator_hover_card", person: person, header: header) + end + def profile_piece_shown?(person, flag) person.public_send(:"#{flag}?") || profile_piece_hidden_for_viewer?(person, flag) end diff --git a/app/views/comments_and_communications/index.html.erb b/app/views/comments_and_communications/index.html.erb index 90e3dedcaf..ffa1dc2d81 100644 --- a/app/views/comments_and_communications/index.html.erb +++ b/app/views/comments_and_communications/index.html.erb @@ -27,13 +27,13 @@ <% subtitle = capture do %> <%= pluralize(@total_count, "entry", plural: "entries") %><% if @flagged_count.positive? %> · <%= @flagged_count %> flagged<% end %> <% end %> -<%# Title: the person's name (or "Everyone"). On a person's feed their activity, - profile chip, and the composer triggers sit beside it; the composer panels open in +<%# Title: the person's profile chip (or "Everyone"). On a person's feed their + activity and the composer triggers sit beside it; the composer panels open in the body, sharing the card-level panel-toggle scope (content_data). The person picker that scopes the feed lives in the filter row below. %> <% title = capture do %> - <%= @person ? tag.span(@person.full_name, title: @person.preferred_email) : "Everyone" %> + <%= @person ? render("people/person_chip", person: @person) : "Everyone" %> <% if @person %> <% if allowed_to?(:index?, with: Admin::AhoyActivityPolicy) %> @@ -42,7 +42,6 @@ <%= @person.first_name %>'s activity <% end %> <% end %> - <%= render "people/person_chip", person: @person %> <%= render "comments_and_communications/add_buttons" %> <% end %> diff --git a/app/views/notifications/_communication_line.html.erb b/app/views/notifications/_communication_line.html.erb index b79d53fc0d..54f71a9eba 100644 --- a/app/views/notifications/_communication_line.html.erb +++ b/app/views/notifications/_communication_line.html.erb @@ -39,9 +39,20 @@ path: local_assigns[:date_path], new_tab: local_assigns[:date_new_tab] %> <% end %> <% main = capture do %> - - <%= truncate(from_first.to_s, length: 10, omission: "..") %> - + <% from_chip_class = "#{chip[0]} #{chip[1]} max-w-full truncate rounded px-1.5 py-0.5 text-left text-xs" %> + <% from_label = truncate(from_first.to_s, length: 10, omission: "..") %> + <% if !truncate_body && decorated.from_person %> + <%# Only the untruncated row can link the person: a card-wide link can't nest one. %> + + <%= link_to from_label, person_path(decorated.from_person), target: "_blank", rel: "noopener", + class: "#{from_chip_class} hover:underline" %> + <%= facilitator_hover_card(decorated.from_person, header: true) %> + + <% else %> + + <%= from_label %> + + <% end %> <%# Subject and body share one column so the body starts flush with the subject. %> <% if truncate_body %>
diff --git a/app/views/people/_facilitator_hover_card.html.erb b/app/views/people/_facilitator_hover_card.html.erb new file mode 100644 index 0000000000..21aa93e22d --- /dev/null +++ b/app/views/people/_facilitator_hover_card.html.erb @@ -0,0 +1,31 @@ +<%# Admin hover card for a person chip: when they first affiliated, and each + current facilitator affiliation's org, org status, and dates. Shown by a + `group/person` ancestor. Locals: person. Optional: header (name + email on top, + for chips too small to show them). %> +<% person = person.decorate %> +<% affiliated_since = person.affiliated_since_date %> +<% facilitations = person.current_facilitator_affiliations %> + diff --git a/app/views/people/_person_chip.html.erb b/app/views/people/_person_chip.html.erb index 6b802dae78..79def26acb 100644 --- a/app/views/people/_person_chip.html.erb +++ b/app/views/people/_person_chip.html.erb @@ -1,29 +1,6 @@ -<%# The person's profile chip with an admin hover card: when they first affiliated, - and each current facilitator affiliation's org, org status, and dates. - Locals: person (decorated). %> -<% affiliated_since = person.affiliated_since_date %> -<% facilitations = person.current_facilitator_affiliations %> +<%# The person's profile chip with their facilitator hover card. Locals: person. %> <%= person_profile_button(person, subtitle: person.preferred_email, width_class: "w-auto max-w-xs", show_title: false) %> - + <%= facilitator_hover_card(person) %> diff --git a/db/seeds/features/2026-10-20-comms-person-chip-facilitator-hover.yml b/db/seeds/features/2026-10-20-comms-person-chip-facilitator-hover.yml index 8d2b54af2d..eaefd2638a 100644 --- a/db/seeds/features/2026-10-20-comms-person-chip-facilitator-hover.yml +++ b/db/seeds/features/2026-10-20-comms-person-chip-facilitator-hover.yml @@ -4,8 +4,9 @@ display_status: admin_facing released_on: 2026-10-20 pr_number: 2766 summary: >- - A person's comments & communications page now shows their profile chip. - Hover it to see when they first affiliated and each current facilitator - affiliation, with the organization's status and when the term started or ends. + A person's comments & communications page now leads with their profile chip, + and their name on each message from them links to their profile. Hover either + to see when they first affiliated and each current facilitator affiliation, + with the organization's status and when the term started or ends. pro_tips: - "Dates close to today read in plain words, like \"ending next month\", so a lapsing term stands out." diff --git a/spec/requests/comments_and_communications_spec.rb b/spec/requests/comments_and_communications_spec.rb index 3154372d74..28da6dea89 100644 --- a/spec/requests/comments_and_communications_spec.rb +++ b/spec/requests/comments_and_communications_spec.rb @@ -36,12 +36,12 @@ expect(form.at_css("input[type='hidden'][name='person_id']")).to be_nil end - it "shows the person's email on hover over their name in the title" do + it "titles the page with the person's profile chip, showing their name and email" do get comments_and_communications_path(person_id: person.id) - name = Nokogiri::HTML(response.body).at_css("h1 [title='primary@example.com']") - expect(name.text).to eq(person.full_name) - expect(name.name).to eq("span") + chip = Nokogiri::HTML(response.body).at_css("h1 a[href='#{person_path(person)}']") + expect(chip.text).to include(person.full_name) + expect(chip.text).to include("primary@example.com") end it "shows affiliated-since and current facilitator terms on hover over the person chip" do @@ -66,8 +66,6 @@ link = doc.css("a").find { |a| a.text.strip == "Aisha's activity" } expect(link).to be_present expect(link["href"]).to eq(admin_activities_events_path(person_id: person.id, time_period: "all_time", audience: %w[visitors users staff], hide_account: "1", hide_interactions: "1")) - profile = doc.at_css("h1 a[href='#{person_path(person)}']") - expect(profile.text).to include(person.full_name) everyone = doc.css("a").find { |a| a.text.strip == "View everyone" } expect(everyone["href"]).to eq(comments_and_communications_path) end @@ -521,6 +519,21 @@ expect(row.at_css("button[data-action='edit-toggle#toggle']")).to be_nil end + it "links an incoming row's sender chip to their profile with the facilitator hover card" do + organization = create(:organization, name: "Riverside Shelter") + create(:affiliation, person: person, organization: organization, start_date: Date.new(2023, 3, 1)) + notification = create(:notification, :incoming, noticeable: person, recipient_email: "primary@example.com", + email_subject: "Called in", kind: "manual_log", + channel: "phone", recipient_role: "person", notification_type: 0) + + get comments_and_communications_path(person_id: person.id), headers: { "Turbo-Frame" => "comments_and_communications_results" } + + row = Nokogiri::HTML(response.body).at_css("##{ActionView::RecordIdentifier.dom_id(notification)}") + expect(row.at_css("a[href='#{person_path(person)}']")).to be_present + expect(row.text).to include("Riverside Shelter") + expect(row.text).to include("Started March 2023") + end + it "shows a Responded checkbox for an incoming manual communication" do notification = create(:notification, :incoming, noticeable: person, recipient_email: "primary@example.com", email_subject: "Called in", kind: "manual_log", From 9ab33565d4c277fcd5ca80bd043adf6c688de142 Mon Sep 17 00:00:00 2001 From: Mae Beale Date: Tue, 20 Oct 2026 22:59:53 -0400 Subject: [PATCH 04/11] Name who each row is about on the everyone comms feed On the everyone feed, nothing said which person a comment or communication belonged to, so staff had to open each record to find out. Co-Authored-By: Claude Opus 5.5 (1M context) --- app/decorators/comment_decorator.rb | 13 +++++++++ app/helpers/comments_helper.rb | 15 ++++++++++ .../_about_person_chip.html.erb | 18 ++++++++++++ .../_comment_row.html.erb | 4 ++- .../_communication_row.html.erb | 4 ++- .../_communication_row_readonly.html.erb | 4 ++- ...20-comms-person-chip-facilitator-hover.yml | 11 ++++---- spec/decorators/comment_decorator_spec.rb | 26 +++++++++++++++++ .../comments_and_communications_spec.rb | 28 +++++++++++++++++++ 9 files changed, 115 insertions(+), 8 deletions(-) create mode 100644 app/views/comments_and_communications/_about_person_chip.html.erb diff --git a/app/decorators/comment_decorator.rb b/app/decorators/comment_decorator.rb index 87b36cf4dd..ea47bc77fd 100644 --- a/app/decorators/comment_decorator.rb +++ b/app/decorators/comment_decorator.rb @@ -27,6 +27,19 @@ def dedupe_label body.to_s.squish.truncate(ModelDeduper::SNIPPET_LENGTH, separator: " ") end + # The person this comment is about: whoever its source chip names, or the person + # behind any other record it's on (affiliation, author, …). nil for records that + # belong to no one person, like an event or organization. + def about_person + h.record_chip_person(commentable) || case commentable + when User then commentable.person + when Affiliation, TopicSubscription, FormSubmission then commentable.person + when StaffTagging then commentable.staff_taggable if commentable.staff_taggable.is_a?(Person) + when Grant then commentable.funder if commentable.funder.is_a?(Person) + else commentable.author_person if commentable.respond_to?(:author_person) + end + end + def author_name (updated_by || created_by)&.full_name || "System" end diff --git a/app/helpers/comments_helper.rb b/app/helpers/comments_helper.rb index d496228f41..d4cd4d933f 100644 --- a/app/helpers/comments_helper.rb +++ b/app/helpers/comments_helper.rb @@ -13,6 +13,21 @@ def feed_entry_path(entry, person: nil, return_to: nil) }.compact) end + # Names who a row on the everyone feed is about. A communication's contact + # falls back to their raw email when no person is on file. + def feed_person_chip(entry) + if entry.is_a?(Comment) + person = entry.decorate.about_person + email = nil + else + person = entry.decorate.contact_person + email = entry.recipient_email + end + return if person.nil? && email.blank? + + render "comments_and_communications/about_person_chip", person: person, email: email + end + # Stimulus wiring for a feed row the chevron and "Expand/Collapse all" toggle. # clamp: a one-line row unclamped to show the full entry; otherwise a full row # whose body hides on collapse. key lets expandable-cards remember a row toggled diff --git a/app/views/comments_and_communications/_about_person_chip.html.erb b/app/views/comments_and_communications/_about_person_chip.html.erb new file mode 100644 index 0000000000..3c7a0f4f89 --- /dev/null +++ b/app/views/comments_and_communications/_about_person_chip.html.erb @@ -0,0 +1,18 @@ +<%# Who an everyone-feed row is about: a link to that person's own feed with + their facilitator hover card, or a plain email chip when no person is on + file. Locals: person (may be nil), email. %> +<% chip_class = "inline-flex max-w-48 shrink-0 items-center gap-1 rounded-full border px-2 py-0.5 text-xs font-medium #{DomainTheme.border_class_for(:people, intensity: 200)} #{DomainTheme.bg_class_for(:people, intensity: 50)} #{DomainTheme.text_class_for(:people)}" %> +<% if person %> + + <%= link_to comments_and_communications_path(person_id: person.id), class: "#{chip_class} hover:underline", data: { turbo_frame: "_top" } do %> + + <%= person.full_name %> + <% end %> + <%= facilitator_hover_card(person, header: true) %> + +<% else %> + + + <%= email %> + +<% end %> diff --git a/app/views/comments_and_communications/_comment_row.html.erb b/app/views/comments_and_communications/_comment_row.html.erb index 5f57f453fb..68ba13e4b5 100644 --- a/app/views/comments_and_communications/_comment_row.html.erb +++ b/app/views/comments_and_communications/_comment_row.html.erb @@ -7,6 +7,8 @@ person feed's records the comment can be moved to). %> <% person = local_assigns[:person] %> <% source_chip = feed_source_chip(entry.commentable, icon: content_tag(:i, "", class: "fa-solid fa-comment text-gray-500", "aria-hidden": "true", title: "Comment")) %> +<%# Only the everyone feed names who each row is about. %> +<% leading = local_assigns[:person] ? source_chip : safe_join([ feed_person_chip(entry), source_chip || entry.decorate.type_chip ].compact, " ") %> <% edit_button = content_tag(:button, safe_join([ content_tag(:i, "", class: "fa-solid fa-pen text-2xs"), " Edit" ]), type: "button", class: "ml-auto shrink-0 text-xs #{eyebrow_link_class}", @@ -16,7 +18,7 @@ data: { edit_toggle_target: "view", **expandable_feed_row_data(clamp: false, key: dom_id(entry)) } do %>
<%= render "comments/flag_toggle", comment: entry %> - <%= render "comments/comment_line", comment: entry, truncate: false, leading: source_chip, trailing: edit_button, expandable: true, + <%= render "comments/comment_line", comment: entry, truncate: false, leading: leading, trailing: edit_button, expandable: true, date_path: feed_entry_path(entry, person: person) %>
<% end %> diff --git a/app/views/comments_and_communications/_communication_row.html.erb b/app/views/comments_and_communications/_communication_row.html.erb index 9229535fd3..53308a5efb 100644 --- a/app/views/comments_and_communications/_communication_row.html.erb +++ b/app/views/comments_and_communications/_communication_row.html.erb @@ -8,6 +8,8 @@ on the save so the re-rendered row stays scoped to them). %> <% person = local_assigns[:person] %> <% source_chip = feed_source_chip(entry.noticeable, icon: entry.decorate.channel_icon) %> +<%# Only the everyone feed names who each row is about. %> +<% leading = local_assigns[:person] ? source_chip : safe_join([ feed_person_chip(entry), source_chip || entry.decorate.channel_chip ].compact, " ") %> <% edit_button = content_tag(:button, safe_join([ content_tag(:i, "", class: "fa-solid fa-pen text-2xs"), " Edit" ]), type: "button", class: "shrink-0 text-xs #{eyebrow_link_class}", @@ -18,7 +20,7 @@ <%= tag.div data: { edit_toggle_target: "view", **expandable_feed_row_data(clamp: false, key: dom_id(entry)) } do %> <%= render "notifications/communication_line", notification: entry, admin: true, truncate: false, expandable: true, date_path: feed_entry_path(entry, person: person), - leading: source_chip, trailing: trailing, flag: render("notifications/flag_toggle", notification: entry) %> + leading: leading, trailing: trailing, flag: render("notifications/flag_toggle", notification: entry) %> <% end %>