Add me and unassigned assignee options

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
Dotta
2026-03-12 16:12:38 -05:00
parent 6365e03731
commit 32ab4f8e47
6 changed files with 219 additions and 41 deletions

View File

@@ -0,0 +1,53 @@
import { describe, expect, it } from "vitest";
import {
assigneeValueFromSelection,
currentUserAssigneeOption,
formatAssigneeUserLabel,
parseAssigneeValue,
} from "./assignees";
describe("assignee selection helpers", () => {
it("encodes and parses agent assignees", () => {
const value = assigneeValueFromSelection({ assigneeAgentId: "agent-123" });
expect(value).toBe("agent:agent-123");
expect(parseAssigneeValue(value)).toEqual({
assigneeAgentId: "agent-123",
assigneeUserId: null,
});
});
it("encodes and parses current-user assignees", () => {
const [option] = currentUserAssigneeOption("local-board");
expect(option).toEqual({
id: "user:local-board",
label: "Me",
searchText: "me board human local-board",
});
expect(parseAssigneeValue(option.id)).toEqual({
assigneeAgentId: null,
assigneeUserId: "local-board",
});
});
it("treats an empty selection as no assignee", () => {
expect(parseAssigneeValue("")).toEqual({
assigneeAgentId: null,
assigneeUserId: null,
});
});
it("keeps backward compatibility for raw agent ids in saved drafts", () => {
expect(parseAssigneeValue("legacy-agent-id")).toEqual({
assigneeAgentId: "legacy-agent-id",
assigneeUserId: null,
});
});
it("formats current and board user labels consistently", () => {
expect(formatAssigneeUserLabel("user-1", "user-1")).toBe("Me");
expect(formatAssigneeUserLabel("local-board", "someone-else")).toBe("Board");
expect(formatAssigneeUserLabel("user-abcdef", "someone-else")).toBe("user-");
});
});

51
ui/src/lib/assignees.ts Normal file
View File

@@ -0,0 +1,51 @@
export interface AssigneeSelection {
assigneeAgentId: string | null;
assigneeUserId: string | null;
}
export interface AssigneeOption {
id: string;
label: string;
searchText?: string;
}
export function assigneeValueFromSelection(selection: Partial<AssigneeSelection>): string {
if (selection.assigneeAgentId) return `agent:${selection.assigneeAgentId}`;
if (selection.assigneeUserId) return `user:${selection.assigneeUserId}`;
return "";
}
export function parseAssigneeValue(value: string): AssigneeSelection {
if (!value) {
return { assigneeAgentId: null, assigneeUserId: null };
}
if (value.startsWith("agent:")) {
const assigneeAgentId = value.slice("agent:".length);
return { assigneeAgentId: assigneeAgentId || null, assigneeUserId: null };
}
if (value.startsWith("user:")) {
const assigneeUserId = value.slice("user:".length);
return { assigneeAgentId: null, assigneeUserId: assigneeUserId || null };
}
// Backward compatibility for older drafts/defaults that stored a raw agent id.
return { assigneeAgentId: value, assigneeUserId: null };
}
export function currentUserAssigneeOption(currentUserId: string | null | undefined): AssigneeOption[] {
if (!currentUserId) return [];
return [{
id: assigneeValueFromSelection({ assigneeUserId: currentUserId }),
label: "Me",
searchText: currentUserId === "local-board" ? "me board human local-board" : `me human ${currentUserId}`,
}];
}
export function formatAssigneeUserLabel(
userId: string | null | undefined,
currentUserId: string | null | undefined,
): string | null {
if (!userId) return null;
if (currentUserId && userId === currentUserId) return "Me";
if (userId === "local-board") return "Board";
return userId.slice(0, 5);
}