mirror of
https://github.com/warmbly/warmbly.git
synced 2026-10-20 16:03:08 +00:00
feat: integrate exact cross-assignment mailbox UUID navigation with native administrator device consent schemas and focused identity lookup regressions
This commit is contained in:
@@ -0,0 +1,35 @@
|
||||
import { act } from "react";
|
||||
import { createRoot } from "react-dom/client";
|
||||
import { MemoryRouter } from "react-router-dom";
|
||||
import { afterEach, expect, it, vi } from "vitest";
|
||||
import MailboxesPage from "./MailboxesPage";
|
||||
|
||||
const calls = vi.hoisted(() => ({ search: vi.fn(), queries: vi.fn() }));
|
||||
vi.mock("@/lib/api/client/admin/mailboxes", () => ({ searchMailboxes: calls.search }));
|
||||
vi.mock("@/lib/api/client/admin/fleetNodes", () => ({ listFleetNodes: vi.fn() }));
|
||||
vi.mock("@/components/data/DataTable", () => ({ DataTable: () => <div>Mailbox results</div> }));
|
||||
vi.mock("@tanstack/react-query", () => ({ keepPreviousData: vi.fn(), useQuery: (options: { queryKey: string[]; queryFn: () => unknown }) => {
|
||||
calls.queries(options);
|
||||
if (options.queryKey[1] === "mailboxes") options.queryFn();
|
||||
return { data: { data: [], pagination: {} }, isLoading: false };
|
||||
} }));
|
||||
afterEach(() => { vi.clearAllMocks(); vi.unstubAllGlobals(); });
|
||||
|
||||
it("looks up an exact mailbox across inactive/moved assignments without old filters or placeholder rows", async () => {
|
||||
vi.stubGlobal("IS_REACT_ACT_ENVIRONMENT", true);
|
||||
const mailbox = "33333333-3333-4333-8333-333333333333";
|
||||
const host = document.createElement("div");
|
||||
document.body.append(host);
|
||||
const root = createRoot(host);
|
||||
try {
|
||||
await act(async () => root.render(<MemoryRouter initialEntries={[`/mailboxes?mailbox_id=${mailbox}&worker=old-worker&q=old-search&org=old-org`]}><MailboxesPage /></MemoryRouter>));
|
||||
expect(calls.search).toHaveBeenCalledWith({ mailbox_id: mailbox, status: "all", limit: 50 });
|
||||
expect(calls.queries.mock.calls.filter(([options]) => options.queryKey[1] === "mailboxes").every(([options]) => options.placeholderData === undefined)).toBe(true);
|
||||
expect(host.textContent).toContain("Exact identity lookup");
|
||||
expect(host.textContent).toContain(mailbox);
|
||||
expect(host.querySelector("input")).toBeNull();
|
||||
} finally {
|
||||
await act(async () => root.unmount());
|
||||
host.remove();
|
||||
}
|
||||
});
|
||||
@@ -177,6 +177,7 @@ export default function MailboxesPage() {
|
||||
const orgId = params.get("org") || undefined;
|
||||
const userId = params.get("user") || undefined;
|
||||
const workerParam = params.get("worker") || "";
|
||||
const mailboxId = params.get("mailbox_id") || undefined;
|
||||
|
||||
// `?q=` seeds the search box so the command palette can land here on a
|
||||
// mailbox; a status of "all" keeps a disabled mailbox findable that way.
|
||||
@@ -225,7 +226,7 @@ export default function MailboxesPage() {
|
||||
];
|
||||
|
||||
const filterKey = JSON.stringify({
|
||||
query, status, provider, warmup, workerId, riskBand, pool, synced, orgId, userId,
|
||||
query, status, provider, warmup, workerId, riskBand, pool, synced, orgId, userId, mailboxId,
|
||||
warmupPaused, trackingVerified, hasTrackingDomain, hasOrg, signatureSync, hasOAuth, hasSmtp,
|
||||
capMin, capMax, gapMin, gapMax, connected, lastSynced, sort,
|
||||
});
|
||||
@@ -237,7 +238,7 @@ export default function MailboxesPage() {
|
||||
const { data, isLoading, error, refetch } = useQuery({
|
||||
queryKey: ["admin", "mailboxes", filterKey, pager.cursor],
|
||||
queryFn: () =>
|
||||
searchMailboxes({
|
||||
searchMailboxes(mailboxId ? { mailbox_id: mailboxId, status: "all", limit: 50 } : {
|
||||
q: query.trim() || undefined,
|
||||
status,
|
||||
provider: provider || undefined,
|
||||
@@ -270,7 +271,7 @@ export default function MailboxesPage() {
|
||||
sort_desc: sort.by ? sort.desc : undefined,
|
||||
}),
|
||||
staleTime: 30_000,
|
||||
placeholderData: keepPreviousData,
|
||||
placeholderData: mailboxId ? undefined : keepPreviousData,
|
||||
});
|
||||
|
||||
const rows = data?.data ?? [];
|
||||
@@ -330,6 +331,15 @@ export default function MailboxesPage() {
|
||||
setParams(next, { replace: true });
|
||||
}
|
||||
|
||||
if (mailboxId) return <div>
|
||||
<PageHeader title="Mailbox" description="Exact identity lookup across current assignments, including inactive mailboxes. Existing browse filters do not apply." />
|
||||
<p className="mb-4 break-all font-mono text-xs">{mailboxId}</p>
|
||||
<Button variant="outline" className="mb-4" onClick={() => clearParam("mailbox_id")}>Return to mailbox browser</Button>
|
||||
<DataTable columns={columns} rows={rows} getRowId={(m) => m.id} loading={isLoading} error={error}
|
||||
onRetry={() => refetch()} errorTitle="Mailbox lookup failed" noun="mailboxes"
|
||||
emptyTitle="Mailbox not found" emptyHint="This mailbox is no longer registered on this instance." />
|
||||
</div>;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<PageHeader
|
||||
|
||||
@@ -280,7 +280,7 @@ export default function WorkerDetailPage() {
|
||||
</Section>
|
||||
)}
|
||||
|
||||
<NodeDiagnostics node={node} mailboxes={mailboxes} />
|
||||
<NodeDiagnostics node={node} />
|
||||
|
||||
<Section
|
||||
title="Version"
|
||||
|
||||
@@ -12,7 +12,6 @@ import { useMe } from "@/hooks/useMe";
|
||||
import useBrowseState from "@/hooks/useBrowseState";
|
||||
import { AdminPerm, hasAdminPerm } from "@/lib/auth/permissions";
|
||||
import type { FleetNode } from "@/lib/api/client/admin/fleetNodes";
|
||||
import type { AdminWorkerEmail } from "@/lib/api/models/admin";
|
||||
import { getNodeLogs, getNodeBroker, type NodeBrokerSource, type NodeLogFilters } from "@/lib/api/client/admin/nodeDiagnostics";
|
||||
import { diagnosticReason, emptyLogFilters, eventLabels, isUUID, logFilterError, parseLogFilters, safeCount, safeLogEvent, validTime, validatedBroker } from "@/lib/nodeDiagnostics";
|
||||
|
||||
@@ -52,7 +51,7 @@ function BrokerScope({ title, source, group, topic }: { title: string; source: N
|
||||
</Panel>;
|
||||
}
|
||||
|
||||
export function NodeDiagnostics({ node, mailboxes }: { node: FleetNode; mailboxes: AdminWorkerEmail[] }) {
|
||||
export function NodeDiagnostics({ node }: { node: FleetNode }) {
|
||||
const { data: me } = useMe();
|
||||
const canViewBroker = !!me && hasAdminPerm(me.admin_permissions, AdminPerm.ViewAnalytics);
|
||||
const canViewMailboxes = !!me && hasAdminPerm(me.admin_permissions, AdminPerm.ViewUsers);
|
||||
@@ -121,7 +120,6 @@ export function NodeDiagnostics({ node, mailboxes }: { node: FleetNode; mailboxe
|
||||
<div className="mt-4 overflow-x-auto"><table className="w-full text-left text-xs" aria-label="Redacted node events">
|
||||
<thead><tr className="border-b text-muted-foreground"><th className="p-2">Observed</th><th className="p-2">Level</th><th className="p-2">Evidence</th><th className="p-2">Mailbox</th></tr></thead>
|
||||
<tbody>{events.map((event) => {
|
||||
const mailbox = mailboxes.find((m) => m.id === event.mailbox_id);
|
||||
const mailboxID = isUUID(event.mailbox_id ?? "") ? event.mailbox_id : null;
|
||||
return <tr key={event.id} className="border-b align-top">
|
||||
<td className="whitespace-nowrap p-2"><Timestamp value={event.observed_at} /></td>
|
||||
@@ -129,7 +127,7 @@ export function NodeDiagnostics({ node, mailboxes }: { node: FleetNode; mailboxe
|
||||
<td className="p-2"><div className="font-medium">{eventLabels[event.event].title}</div><div className="mt-1 text-muted-foreground">{event.category.replaceAll("_", " ")}
|
||||
{event.event === "sync_control_plane_held" && safeCount(event.http_status) && event.http_status >= 100 && event.http_status <= 599 && ` · HTTP ${event.http_status}`}
|
||||
{safeCount(event.count) && event.count <= 1000000 && ` · count ${event.count}`}</div></td>
|
||||
<td className="p-2 font-mono">{mailboxID ? mailbox && canViewMailboxes ? <Link className="text-[var(--admin-accent-strong)] underline underline-offset-2" to={`/mailboxes?q=${encodeURIComponent(mailbox.email)}&worker=${node.id}`} title="Open assigned mailbox in the mailbox browser">{mailboxID}</Link> : <span title="Mailbox identity is not in the currently loaded assignment page, or mailbox access is not permitted.">{mailboxID}</span> : "Not provided"}</td>
|
||||
<td className="p-2 font-mono">{mailboxID ? canViewMailboxes ? <Link className="text-[var(--admin-accent-strong)] underline underline-offset-2" to={`/mailboxes?mailbox_id=${encodeURIComponent(mailboxID)}`} title="Look up this exact mailbox, regardless of current assignment">{mailboxID}</Link> : <span title="Mailbox access is not permitted.">{mailboxID}</span> : "Not provided"}</td>
|
||||
</tr>;
|
||||
})}</tbody>
|
||||
</table></div>}
|
||||
|
||||
@@ -190,6 +190,7 @@ export interface AdminMailboxesResult {
|
||||
}
|
||||
|
||||
export interface AdminMailboxSearch {
|
||||
mailbox_id?: string;
|
||||
q?: string;
|
||||
status?: string;
|
||||
provider?: string;
|
||||
|
||||
Reference in New Issue
Block a user