DEV: Group PMs by date (#1287)
# Preview https://github.com/user-attachments/assets/3fe3ac8f-c938-4df4-9afe-11980046944d # Details - Group pms by `last_posted_at`. In this first iteration we are group by `7 days`, `30 days`, then by month beyond that. - I inject a sidebar section link with the relative (last_posted_at) date and then update a tracked value to ensure we don't do it again. Then for each month beyond the first 30days, I add a value to the `loadedMonthLabels` set and we reference that (plus the year) to see if we need to load a new month label. - I took the creative liberty to remove the `Conversations` section label - this had no purpose - I hid the _collapse all sidebar sections_ carrot. This had no purpose. - Swap `BasicTopicSerializer` to `ListableTopicSerializer` to get access to `last_posted_at`
This commit is contained in:
parent
60ea590ba5
commit
cd0cfc0bfc
|
@ -10,7 +10,6 @@ module DiscourseAi
|
||||||
page = params[:page].to_i
|
page = params[:page].to_i
|
||||||
per_page = params[:per_page]&.to_i || 40
|
per_page = params[:per_page]&.to_i || 40
|
||||||
|
|
||||||
bot_user_ids = EntryPoint.all_bot_ids
|
|
||||||
base_query =
|
base_query =
|
||||||
Topic
|
Topic
|
||||||
.private_messages_for_user(current_user)
|
.private_messages_for_user(current_user)
|
||||||
|
@ -26,7 +25,7 @@ module DiscourseAi
|
||||||
pms = base_query.order(last_posted_at: :desc).offset(page * per_page).limit(per_page)
|
pms = base_query.order(last_posted_at: :desc).offset(page * per_page).limit(per_page)
|
||||||
|
|
||||||
render json: {
|
render json: {
|
||||||
conversations: serialize_data(pms, BasicTopicSerializer),
|
conversations: serialize_data(pms, ListableTopicSerializer),
|
||||||
meta: {
|
meta: {
|
||||||
total: total,
|
total: total,
|
||||||
page: page,
|
page: page,
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
import { tracked } from "@glimmer/tracking";
|
import { tracked } from "@glimmer/tracking";
|
||||||
|
import { htmlSafe } from "@ember/template";
|
||||||
import { TrackedArray } from "@ember-compat/tracked-built-ins";
|
import { TrackedArray } from "@ember-compat/tracked-built-ins";
|
||||||
import { ajax } from "discourse/lib/ajax";
|
import { ajax } from "discourse/lib/ajax";
|
||||||
import { bind } from "discourse/lib/decorators";
|
import { bind } from "discourse/lib/decorators";
|
||||||
|
import { autoUpdatingRelativeAge } from "discourse/lib/formatter";
|
||||||
import { withPluginApi } from "discourse/lib/plugin-api";
|
import { withPluginApi } from "discourse/lib/plugin-api";
|
||||||
import { i18n } from "discourse-i18n";
|
import { i18n } from "discourse-i18n";
|
||||||
import AiBotSidebarNewConversation from "../discourse/components/ai-bot-sidebar-new-conversation";
|
import AiBotSidebarNewConversation from "../discourse/components/ai-bot-sidebar-new-conversation";
|
||||||
|
@ -85,6 +87,9 @@ export default {
|
||||||
@tracked links = new TrackedArray();
|
@tracked links = new TrackedArray();
|
||||||
@tracked topics = [];
|
@tracked topics = [];
|
||||||
@tracked hasMore = [];
|
@tracked hasMore = [];
|
||||||
|
@tracked loadedSevenDayLabel = false;
|
||||||
|
@tracked loadedThirtyDayLabel = false;
|
||||||
|
@tracked loadedMonthLabels = new Set();
|
||||||
page = 0;
|
page = 0;
|
||||||
isFetching = false;
|
isFetching = false;
|
||||||
totalTopicsCount = 0;
|
totalTopicsCount = 0;
|
||||||
|
@ -127,7 +132,14 @@ export default {
|
||||||
}
|
}
|
||||||
|
|
||||||
addNewPMToSidebar(topic) {
|
addNewPMToSidebar(topic) {
|
||||||
this.links = [new AiConversationLink(topic), ...this.links];
|
// Reset category labels since we're adding a new topic
|
||||||
|
this.loadedSevenDayLabel = false;
|
||||||
|
this.loadedThirtyDayLabel = false;
|
||||||
|
this.loadedMonthLabels.clear();
|
||||||
|
|
||||||
|
this.topics = [topic, ...this.topics];
|
||||||
|
this.buildSidebarLinks();
|
||||||
|
|
||||||
this.watchForTitleUpdate(topic);
|
this.watchForTitleUpdate(topic);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -206,10 +218,71 @@ export default {
|
||||||
this.fetchMessages(true);
|
this.fetchMessages(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
buildSidebarLinks() {
|
groupByDate(topic) {
|
||||||
this.links = this.topics.map(
|
const now = new Date();
|
||||||
(topic) => new AiConversationLink(topic)
|
const lastPostedAt = new Date(topic.last_posted_at);
|
||||||
|
const daysDiff = Math.round(
|
||||||
|
(now - lastPostedAt) / (1000 * 60 * 60 * 24)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Last 7 days group
|
||||||
|
if (daysDiff <= 7) {
|
||||||
|
if (!this.loadedSevenDayLabel) {
|
||||||
|
this.loadedSevenDayLabel = true;
|
||||||
|
return {
|
||||||
|
text: i18n("discourse_ai.ai_bot.conversations.last_7_days"),
|
||||||
|
classNames: "date-heading",
|
||||||
|
name: "date-heading-last-7-days",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Last 30 days group
|
||||||
|
else if (daysDiff <= 30) {
|
||||||
|
if (!this.loadedThirtyDayLabel) {
|
||||||
|
this.loadedThirtyDayLabel = true;
|
||||||
|
return {
|
||||||
|
text: i18n(
|
||||||
|
"discourse_ai.ai_bot.conversations.last_30_days"
|
||||||
|
),
|
||||||
|
classNames: "date-heading",
|
||||||
|
name: "date-heading-last-30-days",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Group by month for older conversations
|
||||||
|
else {
|
||||||
|
const month = lastPostedAt.getMonth();
|
||||||
|
const year = lastPostedAt.getFullYear();
|
||||||
|
const monthKey = `${year}-${month}`;
|
||||||
|
|
||||||
|
if (!this.loadedMonthLabels.has(monthKey)) {
|
||||||
|
this.loadedMonthLabels.add(monthKey);
|
||||||
|
|
||||||
|
const formattedDate = autoUpdatingRelativeAge(
|
||||||
|
new Date(topic.last_posted_at)
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
text: htmlSafe(formattedDate),
|
||||||
|
classNames: "date-heading",
|
||||||
|
name: `date-heading-${monthKey}`,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
buildSidebarLinks() {
|
||||||
|
// Reset date header tracking
|
||||||
|
this.loadedSevenDayLabel = false;
|
||||||
|
this.loadedThirtyDayLabel = false;
|
||||||
|
this.loadedMonthLabels.clear();
|
||||||
|
|
||||||
|
this.links = [...this.topics].flatMap((topic) => {
|
||||||
|
const dateLabel = this.groupByDate(topic);
|
||||||
|
return dateLabel
|
||||||
|
? [dateLabel, new AiConversationLink(topic)]
|
||||||
|
: [new AiConversationLink(topic)];
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
watchForTitleUpdate(topic) {
|
watchForTitleUpdate(topic) {
|
||||||
|
|
|
@ -10,6 +10,10 @@ body.has-ai-conversations-sidebar {
|
||||||
margin: 1.8em 1rem 0;
|
margin: 1.8em 1rem 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.sidebar-toggle-all-sections {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
.sidebar-wrapper {
|
.sidebar-wrapper {
|
||||||
.ai-conversations-panel {
|
.ai-conversations-panel {
|
||||||
padding-top: 1em;
|
padding-top: 1em;
|
||||||
|
@ -18,19 +22,23 @@ body.has-ai-conversations-sidebar {
|
||||||
// ai related sidebar content
|
// ai related sidebar content
|
||||||
[data-section-name="ai-conversations-history"] {
|
[data-section-name="ai-conversations-history"] {
|
||||||
.sidebar-section-header-wrapper {
|
.sidebar-section-header-wrapper {
|
||||||
pointer-events: none;
|
display: none;
|
||||||
font-size: var(--font-down-1);
|
|
||||||
|
|
||||||
.sidebar-section-header-caret {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar-section-header-text {
|
|
||||||
letter-spacing: 0.5px;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar-section-link-wrapper {
|
.sidebar-section-link-wrapper {
|
||||||
|
.sidebar-section-link.date-heading {
|
||||||
|
pointer-events: none;
|
||||||
|
cursor: default;
|
||||||
|
color: var(--primary-medium);
|
||||||
|
opacity: 0.8;
|
||||||
|
font-weight: 700;
|
||||||
|
margin-top: 1em;
|
||||||
|
|
||||||
|
&[data-link-name="date-heading-last-7-days"] {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.sidebar-section-link {
|
.sidebar-section-link {
|
||||||
height: unset;
|
height: unset;
|
||||||
padding-block: 0.65em;
|
padding-block: 0.65em;
|
||||||
|
|
|
@ -727,6 +727,8 @@ en:
|
||||||
new: "New Question"
|
new: "New Question"
|
||||||
min_input_length_message: "Message must be longer than 10 characters"
|
min_input_length_message: "Message must be longer than 10 characters"
|
||||||
messages_sidebar_title: "Conversations"
|
messages_sidebar_title: "Conversations"
|
||||||
|
last_7_days: "Last 7 days"
|
||||||
|
last_30_days: "Last 30 days"
|
||||||
sentiments:
|
sentiments:
|
||||||
dashboard:
|
dashboard:
|
||||||
title: "Sentiment"
|
title: "Sentiment"
|
||||||
|
|
|
@ -49,6 +49,7 @@ RSpec.describe "AI Bot - Homepage", type: :system do
|
||||||
:private_message_topic,
|
:private_message_topic,
|
||||||
title: "This is my special PM",
|
title: "This is my special PM",
|
||||||
user: user,
|
user: user,
|
||||||
|
last_posted_at: Time.zone.now,
|
||||||
topic_allowed_users: [
|
topic_allowed_users: [
|
||||||
Fabricate.build(:topic_allowed_user, user: user),
|
Fabricate.build(:topic_allowed_user, user: user),
|
||||||
Fabricate.build(:topic_allowed_user, user: bot_user),
|
Fabricate.build(:topic_allowed_user, user: bot_user),
|
||||||
|
@ -150,18 +151,35 @@ RSpec.describe "AI Bot - Homepage", type: :system do
|
||||||
|
|
||||||
expect(ai_pm_homepage).to have_homepage
|
expect(ai_pm_homepage).to have_homepage
|
||||||
expect(sidebar).to have_section("ai-conversations-history")
|
expect(sidebar).to have_section("ai-conversations-history")
|
||||||
|
expect(sidebar).to have_section_link("Last 7 days")
|
||||||
expect(sidebar).to have_section_link(pm.title)
|
expect(sidebar).to have_section_link(pm.title)
|
||||||
expect(sidebar).to have_no_css("button.ai-new-question-button")
|
expect(sidebar).to have_no_css("button.ai-new-question-button")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "displays last_30_days label in the sidebar" do
|
||||||
|
pm.update!(last_posted_at: Time.zone.now - 28.days)
|
||||||
|
visit "/"
|
||||||
|
header.click_bot_button
|
||||||
|
|
||||||
|
expect(ai_pm_homepage).to have_homepage
|
||||||
|
expect(sidebar).to have_section_link("Last 30 days")
|
||||||
|
end
|
||||||
|
|
||||||
|
it "displays month and year label in the sidebar for older conversations" do
|
||||||
|
pm.update!(last_posted_at: "2024-04-10 15:39:11.406192000 +00:00")
|
||||||
|
visit "/"
|
||||||
|
header.click_bot_button
|
||||||
|
|
||||||
|
expect(ai_pm_homepage).to have_homepage
|
||||||
|
expect(sidebar).to have_section_link("Apr 2024")
|
||||||
|
end
|
||||||
|
|
||||||
it "navigates to the bot conversation when clicked" do
|
it "navigates to the bot conversation when clicked" do
|
||||||
visit "/"
|
visit "/"
|
||||||
header.click_bot_button
|
header.click_bot_button
|
||||||
|
|
||||||
expect(ai_pm_homepage).to have_homepage
|
expect(ai_pm_homepage).to have_homepage
|
||||||
sidebar.find(
|
ai_pm_homepage.click_fist_sidebar_conversation
|
||||||
".sidebar-section[data-section-name='ai-conversations-history'] a.sidebar-section-link",
|
|
||||||
).click
|
|
||||||
expect(topic_page).to have_topic_title(pm.title)
|
expect(topic_page).to have_topic_title(pm.title)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -173,9 +191,7 @@ RSpec.describe "AI Bot - Homepage", type: :system do
|
||||||
expect(header).to have_icon_in_bot_button(icon: "shuffle")
|
expect(header).to have_icon_in_bot_button(icon: "shuffle")
|
||||||
|
|
||||||
# Go to a PM and assert that the icon is still shuffle
|
# Go to a PM and assert that the icon is still shuffle
|
||||||
sidebar.find(
|
ai_pm_homepage.click_fist_sidebar_conversation
|
||||||
".sidebar-section[data-section-name='ai-conversations-history'] a.sidebar-section-link",
|
|
||||||
).click
|
|
||||||
expect(header).to have_icon_in_bot_button(icon: "shuffle")
|
expect(header).to have_icon_in_bot_button(icon: "shuffle")
|
||||||
|
|
||||||
# Go back home and assert that the icon is now robot again
|
# Go back home and assert that the icon is now robot again
|
||||||
|
|
|
@ -40,6 +40,12 @@ module PageObjects
|
||||||
page.find(".ai-new-question-button").click
|
page.find(".ai-new-question-button").click
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def click_fist_sidebar_conversation
|
||||||
|
page.find(
|
||||||
|
".sidebar-section[data-section-name='ai-conversations-history'] a.sidebar-section-link:not(.date-heading)",
|
||||||
|
).click
|
||||||
|
end
|
||||||
|
|
||||||
def persona_selector
|
def persona_selector
|
||||||
PageObjects::Components::SelectKit.new(".persona-llm-selector__persona-dropdown")
|
PageObjects::Components::SelectKit.new(".persona-llm-selector__persona-dropdown")
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in New Issue