feat(FN-1064): replace agent sidebar with dropdown select in MailboxModal
- Replace agent sidebar layout with a compact dropdown select component - Refactor MailboxModal.tsx to use dropdown instead of sidebar navigation - Remove unused sidebar CSS styles and add dropdown-specific styles - Update tests to cover dropdown behavior and interaction patterns
This commit is contained in:
@@ -510,64 +510,68 @@ export function MailboxModal({
|
|||||||
{/* Agent Mailboxes Tab */}
|
{/* Agent Mailboxes Tab */}
|
||||||
{activeTab === "agents" && (
|
{activeTab === "agents" && (
|
||||||
<div className="mailbox-agents" data-testid="mailbox-agents">
|
<div className="mailbox-agents" data-testid="mailbox-agents">
|
||||||
<div className="mailbox-agents-sidebar">
|
{agents.length === 0 ? (
|
||||||
<div className="mailbox-agents-label">Select an agent</div>
|
<div className="mailbox-empty">
|
||||||
{agents.length === 0 && (
|
<Bot size={32} />
|
||||||
<div className="mailbox-empty">
|
<p>No agents found</p>
|
||||||
<Bot size={24} />
|
</div>
|
||||||
<p>No agents found</p>
|
) : (
|
||||||
|
<>
|
||||||
|
<div className="mailbox-agents-dropdown">
|
||||||
|
<select
|
||||||
|
className="message-composer-select mailbox-agent-select"
|
||||||
|
value={selectedAgentId ?? ""}
|
||||||
|
onChange={(e) => setSelectedAgentId(e.target.value || null)}
|
||||||
|
data-testid="mailbox-agent-select"
|
||||||
|
>
|
||||||
|
<option value="">Select an agent…</option>
|
||||||
|
{agents.map((agent) => (
|
||||||
|
<option key={agent.id} value={agent.id}>
|
||||||
|
{agent.name || agent.id}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
</div>
|
</div>
|
||||||
)}
|
<div className="mailbox-agents-content">
|
||||||
{agents.map((agent) => (
|
{!selectedAgentId && (
|
||||||
<button
|
<div className="mailbox-empty">
|
||||||
key={agent.id}
|
<Bot size={32} />
|
||||||
className={`mailbox-agent-btn ${selectedAgentId === agent.id ? "active" : ""}`}
|
<p>Select an agent to view their mailbox</p>
|
||||||
onClick={() => setSelectedAgentId(agent.id)}
|
|
||||||
data-testid={`mailbox-agent-btn-${agent.id}`}
|
|
||||||
>
|
|
||||||
<Bot size={14} />
|
|
||||||
<span>{agent.name || agent.id}</span>
|
|
||||||
</button>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
<div className="mailbox-agents-content">
|
|
||||||
{!selectedAgentId && (
|
|
||||||
<div className="mailbox-empty">
|
|
||||||
<Bot size={32} />
|
|
||||||
<p>Select an agent to view their mailbox</p>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{selectedAgentId && isLoading && !agentMailbox && <MailboxSkeleton />}
|
|
||||||
{agentMailbox && agentMailbox.messages.length === 0 && (
|
|
||||||
<div className="mailbox-empty">
|
|
||||||
<InboxIcon size={32} />
|
|
||||||
<p>No messages for this agent</p>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{agentMailbox?.messages.map((msg) => (
|
|
||||||
<div
|
|
||||||
key={msg.id}
|
|
||||||
className={`mailbox-item ${!msg.read ? "unread" : ""}`}
|
|
||||||
onClick={() => handleOpenMessage(msg)}
|
|
||||||
data-testid={`mailbox-item-${msg.id}`}
|
|
||||||
>
|
|
||||||
<div className="mailbox-item-avatar">
|
|
||||||
{msg.fromType === "agent" ? <Bot size={16} /> : <User size={16} />}
|
|
||||||
</div>
|
|
||||||
<div className="mailbox-item-content">
|
|
||||||
<div className="mailbox-item-header">
|
|
||||||
<span className="mailbox-item-from">
|
|
||||||
{msg.fromType === "agent"
|
|
||||||
? participantLabel(msg.toId, msg.toType)
|
|
||||||
: participantLabel(msg.fromId, msg.fromType)}
|
|
||||||
</span>
|
|
||||||
<span className="mailbox-item-time">{formatTimestamp(msg.createdAt)}</span>
|
|
||||||
</div>
|
</div>
|
||||||
<div className="mailbox-item-preview">{msg.content.slice(0, 80)}{msg.content.length > 80 ? "…" : ""}</div>
|
)}
|
||||||
</div>
|
{selectedAgentId && isLoading && !agentMailbox && <MailboxSkeleton />}
|
||||||
|
{agentMailbox && agentMailbox.messages.length === 0 && (
|
||||||
|
<div className="mailbox-empty">
|
||||||
|
<InboxIcon size={32} />
|
||||||
|
<p>No messages for this agent</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{agentMailbox?.messages.map((msg) => (
|
||||||
|
<div
|
||||||
|
key={msg.id}
|
||||||
|
className={`mailbox-item ${!msg.read ? "unread" : ""}`}
|
||||||
|
onClick={() => handleOpenMessage(msg)}
|
||||||
|
data-testid={`mailbox-item-${msg.id}`}
|
||||||
|
>
|
||||||
|
<div className="mailbox-item-avatar">
|
||||||
|
{msg.fromType === "agent" ? <Bot size={16} /> : <User size={16} />}
|
||||||
|
</div>
|
||||||
|
<div className="mailbox-item-content">
|
||||||
|
<div className="mailbox-item-header">
|
||||||
|
<span className="mailbox-item-from">
|
||||||
|
{msg.fromType === "agent"
|
||||||
|
? participantLabel(msg.toId, msg.toType)
|
||||||
|
: participantLabel(msg.fromId, msg.fromType)}
|
||||||
|
</span>
|
||||||
|
<span className="mailbox-item-time">{formatTimestamp(msg.createdAt)}</span>
|
||||||
|
</div>
|
||||||
|
<div className="mailbox-item-preview">{msg.content.slice(0, 80)}{msg.content.length > 80 ? "…" : ""}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
</div>
|
</div>
|
||||||
))}
|
</>
|
||||||
</div>
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -197,16 +197,31 @@ describe("MailboxModal", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it("shows agent buttons in agents tab", async () => {
|
it("shows agent dropdown in agents tab", async () => {
|
||||||
render(<MailboxModal {...defaultProps} />);
|
render(<MailboxModal {...defaultProps} />);
|
||||||
fireEvent.click(screen.getByTestId("mailbox-tab-agents"));
|
fireEvent.click(screen.getByTestId("mailbox-tab-agents"));
|
||||||
await waitFor(() => {
|
await waitFor(() => {
|
||||||
expect(screen.getByTestId("mailbox-agent-btn-agent-001")).toBeDefined();
|
expect(screen.getByTestId("mailbox-agent-select")).toBeDefined();
|
||||||
expect(screen.getByTestId("mailbox-agent-btn-agent-002")).toBeDefined();
|
});
|
||||||
|
// Should have placeholder plus two agent options
|
||||||
|
const select = screen.getByTestId("mailbox-agent-select") as HTMLSelectElement;
|
||||||
|
expect(select.options.length).toBe(3); // placeholder + 2 agents
|
||||||
|
expect(select.options[0].textContent).toBe("Select an agent…");
|
||||||
|
expect(select.options[1].textContent).toBe("Test Agent 1");
|
||||||
|
expect(select.options[2].textContent).toBe("Test Agent 2");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("shows Select an agent… placeholder in dropdown", async () => {
|
||||||
|
render(<MailboxModal {...defaultProps} />);
|
||||||
|
fireEvent.click(screen.getByTestId("mailbox-tab-agents"));
|
||||||
|
await waitFor(() => {
|
||||||
|
const select = screen.getByTestId("mailbox-agent-select") as HTMLSelectElement;
|
||||||
|
expect(select.value).toBe("");
|
||||||
|
expect(select.options[0].textContent).toBe("Select an agent…");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it("loads agent mailbox when agent is selected", async () => {
|
it("loads agent mailbox when selecting an agent from dropdown", async () => {
|
||||||
mockFetchAgentMailbox.mockResolvedValue({
|
mockFetchAgentMailbox.mockResolvedValue({
|
||||||
ownerId: "agent-001",
|
ownerId: "agent-001",
|
||||||
ownerType: "agent",
|
ownerType: "agent",
|
||||||
@@ -216,14 +231,22 @@ describe("MailboxModal", () => {
|
|||||||
render(<MailboxModal {...defaultProps} />);
|
render(<MailboxModal {...defaultProps} />);
|
||||||
fireEvent.click(screen.getByTestId("mailbox-tab-agents"));
|
fireEvent.click(screen.getByTestId("mailbox-tab-agents"));
|
||||||
await waitFor(() => {
|
await waitFor(() => {
|
||||||
expect(screen.getByTestId("mailbox-agent-btn-agent-001")).toBeDefined();
|
expect(screen.getByTestId("mailbox-agent-select")).toBeDefined();
|
||||||
});
|
});
|
||||||
fireEvent.click(screen.getByTestId("mailbox-agent-btn-agent-001"));
|
fireEvent.change(screen.getByTestId("mailbox-agent-select"), { target: { value: "agent-001" } });
|
||||||
await waitFor(() => {
|
await waitFor(() => {
|
||||||
expect(mockFetchAgentMailbox).toHaveBeenCalledWith("agent-001", undefined);
|
expect(mockFetchAgentMailbox).toHaveBeenCalledWith("agent-001", undefined);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("shows empty state when no agents exist", async () => {
|
||||||
|
render(<MailboxModal {...defaultProps} agents={[]} />);
|
||||||
|
fireEvent.click(screen.getByTestId("mailbox-tab-agents"));
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(screen.getByText("No agents found")).toBeDefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it("opens message detail when clicking a message", async () => {
|
it("opens message detail when clicking a message", async () => {
|
||||||
render(<MailboxModal {...defaultProps} />);
|
render(<MailboxModal {...defaultProps} />);
|
||||||
await waitFor(() => {
|
await waitFor(() => {
|
||||||
|
|||||||
@@ -21454,50 +21454,18 @@ html .column.drag-over * {
|
|||||||
/* Agents tab */
|
/* Agents tab */
|
||||||
.mailbox-agents {
|
.mailbox-agents {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 16px;
|
flex-direction: column;
|
||||||
|
gap: 12px;
|
||||||
min-height: 300px;
|
min-height: 300px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mailbox-agents-sidebar {
|
.mailbox-agents-dropdown {
|
||||||
width: 200px;
|
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 4px;
|
|
||||||
border-right: 1px solid var(--border);
|
|
||||||
padding-right: 12px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.mailbox-agents-label {
|
.mailbox-agent-select {
|
||||||
font-size: 0.8rem;
|
|
||||||
color: var(--text-muted);
|
|
||||||
margin-bottom: 4px;
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mailbox-agent-btn {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 6px;
|
|
||||||
padding: 8px 10px;
|
|
||||||
border: none;
|
|
||||||
border-radius: 6px;
|
|
||||||
background: none;
|
|
||||||
color: var(--text-primary);
|
|
||||||
font-size: 0.85rem;
|
|
||||||
cursor: pointer;
|
|
||||||
text-align: left;
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
transition: background-color 0.1s;
|
max-width: 300px;
|
||||||
}
|
|
||||||
|
|
||||||
.mailbox-agent-btn:hover {
|
|
||||||
background: var(--bg-hover);
|
|
||||||
}
|
|
||||||
|
|
||||||
.mailbox-agent-btn.active {
|
|
||||||
background: var(--bg-active);
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.mailbox-agents-content {
|
.mailbox-agents-content {
|
||||||
|
|||||||
Reference in New Issue
Block a user