Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 17 additions & 13 deletions packages/junior-dashboard/e2e/conversations-attention.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,38 +81,40 @@ async function mockWaitingConversation(
});
}

test.describe("conversation time sections", () => {
test("puts waiting conversations first in their time section", async ({
test.describe("conversation sections", () => {
test("puts waiting conversations in Needs attention first", async ({
page,
dashboard,
}) => {
await mockWaitingConversation(page);
await page.goto(dashboard.baseURL);

const list = page.getByRole("list", { name: "Your conversations" });
const today = list.getByRole("button", { name: "Today" });
const attention = list.getByRole("button", { name: "Needs attention" });
const waitingCard = list.getByRole("listitem").filter({
has: page.getByRole("heading", { name: "Checkout latency triage" }),
});
await expect(list.getByRole("button").first()).toHaveText(/^Today/);
await expect(list.getByRole("button").first()).toHaveText(
/^Needs attention/,
);
await expect(list.getByRole("listitem").first()).toContainText(
"Checkout latency triage",
);
await expect(
waitingCard.getByRole("img", { name: "Waiting on you" }),
).toBeVisible();
await screenshot(page, "conversations-waiting-first");
await screenshot(page, "conversations-needs-attention");

await today.click();
await expect(today).toHaveAttribute("aria-expanded", "false");
await attention.click();
await expect(attention).toHaveAttribute("aria-expanded", "false");
await expect(waitingCard).toHaveCount(0);
await screenshot(page, "conversations-sections-collapsed");

await page.reload();
await expect(today).toHaveAttribute("aria-expanded", "false");
await expect(attention).toHaveAttribute("aria-expanded", "false");
await expect(waitingCard).toHaveCount(0);

await today.click();
await attention.click();
await expect(waitingCard).toBeVisible();
});

Expand Down Expand Up @@ -193,7 +195,9 @@ test.describe("conversation time sections", () => {
const chip = sidebar.getByRole("link", {
name: /Approve the checkout fix/,
});
await expect(sidebar.getByRole("button").first()).toHaveText(/^Today/);
await expect(sidebar.getByRole("button").first()).toHaveText(
/^Needs attention/,
);
await expect(
waitingRow.getByRole("img", { name: "Waiting on you" }),
).toBeVisible();
Expand Down Expand Up @@ -239,9 +243,9 @@ test.describe("conversation time sections", () => {
.getByRole("button", { name: "Close conversation details" })
.click();

const today = sidebar.getByRole("button", { name: "Today" });
await today.click();
await expect(today).toHaveAttribute("aria-expanded", "false");
const attention = sidebar.getByRole("button", { name: "Needs attention" });
await attention.click();
await expect(attention).toHaveAttribute("aria-expanded", "false");
await expect(waitingRow).toHaveCount(0);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,18 @@ export type ConversationSection = {
};

const DAY_MS = 24 * 60 * 60 * 1000;
// Sorts before every time section.
const ATTENTION_SECTION = {
key: "attention",
label: "Needs attention",
order: Number.NEGATIVE_INFINITY,
};

/**
* Group conversations by last activity: Today, Yesterday, Previous 7 days,
* Previous 30 days, then one section per month.
* Group conversations into Needs attention, then by last activity: Today,
* Yesterday, Previous 7 days, Previous 30 days, then one section per month.
*
* Conversations in `attentionIds` lead their section, so unread and waiting
* rows sit above read rows without leaving their time group.
* Conversations in `attentionIds` go to Needs attention, newest first.
*/
export function buildConversationSections(
conversations: Conversation[],
Expand All @@ -25,20 +30,19 @@ export function buildConversationSections(
): ConversationSection[] {
const nowDay = calendarDay(options.nowMs, options.timeZone);
const sections = new Map<string, ConversationSection & { order: number }>();
const byActivity = [...conversations].sort(
const sorted = [...conversations].sort(
(left, right) => activityTime(right) - activityTime(left),
);
const sorted = options.attentionIds
? attentionFirst(byActivity, options.attentionIds)
: byActivity;

for (const conversation of sorted) {
const section = conversationSection(
activityTime(conversation),
nowDay,
options.nowMs,
options.timeZone,
);
const section = options.attentionIds?.has(conversation.id)
? ATTENTION_SECTION
: conversationSection(
activityTime(conversation),
nowDay,
options.nowMs,
options.timeZone,
);
const existing = sections.get(section.key);
if (existing) {
existing.conversations.push(conversation);
Expand Down
11 changes: 6 additions & 5 deletions packages/junior-dashboard/tests/conversationSections.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ describe("conversation activity sections", () => {
]);
});

it("puts attention conversations first inside their own time section", () => {
it("puts attention conversations in Needs attention before Today", () => {
const sections = buildConversationSections(
[
conversation("today", "2026-08-03T11:00:00-07:00"),
Expand All @@ -73,12 +73,13 @@ describe("conversation activity sections", () => {
);

expect(sectionIds(sections)).toEqual([
{ conversations: ["today-unread", "today"], label: "Today" },
{ conversations: ["yesterday"], label: "Yesterday" },
{
conversations: ["waiting-old", "saturday"],
label: "Previous 7 days",
conversations: ["today-unread", "waiting-old"],
label: "Needs attention",
},
{ conversations: ["today"], label: "Today" },
{ conversations: ["yesterday"], label: "Yesterday" },
{ conversations: ["saturday"], label: "Previous 7 days" },
]);
});

Expand Down
Loading