diff --git a/plugins/email_service/app/javascript/widgets/app/AppContent.tsx b/plugins/email_service/app/javascript/widgets/app/AppContent.tsx index a89f3d1fbb..946ae8b4bc 100644 --- a/plugins/email_service/app/javascript/widgets/app/AppContent.tsx +++ b/plugins/email_service/app/javascript/widgets/app/AppContent.tsx @@ -18,7 +18,7 @@ interface AppContentProps { const baseName = widgetBasePath("email-service") -const TAB_SLUGS = ["setup", "error-report", "maillog", "email-identity-domains"] +const TAB_SLUGS = ["setup", "error-report", "maillog", "email-identity-domains", "suppression-list"] const getInitialTab = (): number => { const hash = window.location.hash.replace("#", "").split("?")[0] @@ -59,6 +59,7 @@ const AppContent: React.FC = ({ props }) => { Error Report Maillog Email Identity Domains + Suppression List @@ -75,6 +76,9 @@ const AppContent: React.FC = ({ props }) => { + + + ) } diff --git a/plugins/email_service/app/javascript/widgets/app/components/EmailIdentityDomains.tsx b/plugins/email_service/app/javascript/widgets/app/components/EmailIdentityDomains.tsx index 314e747989..8ca5419766 100644 --- a/plugins/email_service/app/javascript/widgets/app/components/EmailIdentityDomains.tsx +++ b/plugins/email_service/app/javascript/widgets/app/components/EmailIdentityDomains.tsx @@ -434,7 +434,19 @@ const DomainRow: React.FC = ({ domain, bearerToken, cronusEndpoi Not configured )} - + +
+ +
+ ) @@ -449,7 +461,7 @@ const ConfirmDeleteModal: React.FC<{ domain: string; onConfirm: () => void; onCl >
-

Remove Domain

+

Delete {domain}

Are you sure you want to remove:

{domain} @@ -541,6 +553,12 @@ const EmailIdentityDomains: React.FC = () => { )}

Email Identity Domains

+
{isError && ( diff --git a/plugins/email_service/app/javascript/widgets/app/components/SuppressionList.test.tsx b/plugins/email_service/app/javascript/widgets/app/components/SuppressionList.test.tsx index cf5a3f9a3c..66d4638f38 100644 --- a/plugins/email_service/app/javascript/widgets/app/components/SuppressionList.test.tsx +++ b/plugins/email_service/app/javascript/widgets/app/components/SuppressionList.test.tsx @@ -27,7 +27,7 @@ describe("SuppressionList", () => { expect(screen.getByText("Email")).toBeInTheDocument() expect(screen.getByText("Reason")).toBeInTheDocument() expect(screen.getByText("Original Message ID")).toBeInTheDocument() - expect(screen.getByText("Added")).toBeInTheDocument() + expect(screen.getByText("Date Added")).toBeInTheDocument() expect(screen.getByText("Actions")).toBeInTheDocument() }) @@ -47,12 +47,12 @@ describe("SuppressionList", () => { it("renders the Add button", () => { render() - expect(screen.getByRole("button", { name: "Add" })).toBeInTheDocument() + expect(screen.getByTitle("Add")).toBeInTheDocument() }) it("renders Remove buttons for existing rows", () => { render() - const removeButtons = screen.getAllByRole("button", { name: "Remove" }) + const removeButtons = screen.getAllByTitle("Remove") expect(removeButtons).toHaveLength(3) }) @@ -62,7 +62,7 @@ describe("SuppressionList", () => { const emailInput = screen.getByPlaceholderText("blocked-user@example.com") await user.type(emailInput, "new-user@example.com") - await user.click(screen.getByRole("button", { name: "Add" })) + await user.click(screen.getByTitle("Add")) expect(screen.getByText("new-user@example.com")).toBeInTheDocument() }) @@ -73,7 +73,7 @@ describe("SuppressionList", () => { const emailInput = screen.getByPlaceholderText("blocked-user@example.com") await user.type(emailInput, "new-user@example.com") - await user.click(screen.getByRole("button", { name: "Add" })) + await user.click(screen.getByTitle("Add")) expect(emailInput).toHaveValue("") }) @@ -82,10 +82,10 @@ describe("SuppressionList", () => { const user = userEvent.setup() render() - await user.click(screen.getByRole("button", { name: "Add" })) + await user.click(screen.getByTitle("Add")) // Should still only have 3 initial rows - const removeButtons = screen.getAllByRole("button", { name: "Remove" }) + const removeButtons = screen.getAllByTitle("Remove") expect(removeButtons).toHaveLength(3) }) @@ -94,8 +94,11 @@ describe("SuppressionList", () => { render() expect(screen.getByText("bounce-user@example.com")).toBeInTheDocument() - const removeButtons = screen.getAllByRole("button", { name: "Remove" }) + const removeButtons = screen.getAllByTitle("Remove") await user.click(removeButtons[0]) + // confirm modal appears — modal renders before the table, so modal's Remove button is first + const confirmButtons = screen.getAllByRole("button", { name: "Remove" }) + await user.click(confirmButtons[0]) expect(screen.queryByText("bounce-user@example.com")).not.toBeInTheDocument() }) @@ -104,10 +107,12 @@ describe("SuppressionList", () => { const user = userEvent.setup() render() - const removeButtons = screen.getAllByRole("button", { name: "Remove" }) + const removeButtons = screen.getAllByTitle("Remove") await user.click(removeButtons[0]) + const confirmButtons = screen.getAllByRole("button", { name: "Remove" }) + await user.click(confirmButtons[0]) - expect(screen.getAllByRole("button", { name: "Remove" })).toHaveLength(2) + expect(screen.getAllByTitle("Remove")).toHaveLength(2) }) it("renders the footer hint text", () => { @@ -126,7 +131,7 @@ describe("SuppressionList", () => { await user.type(screen.getByPlaceholderText("blocked-user@example.com"), "x@y.com") await user.type(screen.getByPlaceholderText(/Manual suppression/), "test reason") - await user.click(screen.getByRole("button", { name: "Add" })) + await user.click(screen.getByTitle("Add")) expect(screen.getByText("x@y.com")).toBeInTheDocument() expect(screen.getByText("test reason")).toBeInTheDocument() diff --git a/plugins/email_service/app/javascript/widgets/app/components/SuppressionList.tsx b/plugins/email_service/app/javascript/widgets/app/components/SuppressionList.tsx index 9a08cb75c7..ef2522e987 100644 --- a/plugins/email_service/app/javascript/widgets/app/components/SuppressionList.tsx +++ b/plugins/email_service/app/javascript/widgets/app/components/SuppressionList.tsx @@ -69,6 +69,7 @@ const SuppressionList: React.FC = () => { reason: "", originalMessageId: "", }) + const [confirmId, setConfirmId] = useState(null) const handleAdd = () => { if (!newRow.email.trim()) return @@ -85,6 +86,7 @@ const SuppressionList: React.FC = () => { const handleRemove = (id: string) => { setRows((prev) => prev.filter((r) => r.id !== id)) + setConfirmId(null) } const formatDate = (d: Date | null) => { @@ -92,8 +94,31 @@ const SuppressionList: React.FC = () => { return moment.utc(d).format("YYYY-MM-DD HH:mm [UTC]") } + const confirmRow = rows.find((r) => r.id === confirmId) + return ( -
+ {confirmId && confirmRow && ( +
{ if (e.target === e.currentTarget) setConfirmId(null) }} + > +
+ +

Delete {confirmRow.email}

+

Are you sure you want to remove:

+
+ {confirmRow.email} +
+

This cannot be undone.

+
+ + +
+
+
+ )} +
{ Email Reason Original Message ID - Added - Actions + Date Added + Actions @@ -128,21 +153,15 @@ const SuppressionList: React.FC = () => { setNewRow((r) => ({ ...r, originalMessageId: v }))} placeholder="" /> Now - + @@ -154,21 +173,15 @@ const SuppressionList: React.FC = () => { {row.reason} {row.originalMessageId || "—"} {formatDate(row.addedAt)} - + @@ -180,6 +193,7 @@ const SuppressionList: React.FC = () => { Add a new suppressed email directly in the table.
+ ) }