diff --git a/ui/src/pages/CompanyImport.test.tsx b/ui/src/pages/CompanyImport.test.tsx index 3a70ea3a4d..272a924705 100644 --- a/ui/src/pages/CompanyImport.test.tsx +++ b/ui/src/pages/CompanyImport.test.tsx @@ -922,7 +922,14 @@ describe("CompanyImport", () => { // Success-leaning panel, not the failure panel. expect(container.textContent).toContain("Import completed"); - expect(container.textContent).toContain("open it to view it"); + // The readable company gives the panel a name and a direct CTA into the + // new company's dashboard, plus the paused-agents pointer. + expect(container.textContent).toContain("Imported Test"); + // The default import submits with pauseAutomations checked, so the + // paused pointer must show; it is gated off when the user unchecks it. + expect(container.textContent).toContain("Imported agents arrived paused"); + const openCompany = container.querySelector('[data-testid="import-expired-open-company"]'); + expect(openCompany).not.toBeNull(); expect(container.textContent).not.toContain("Import failed"); expect(mockPushToast).toHaveBeenCalledWith(expect.objectContaining({ tone: "success" })); // The company list is refreshed so the new company appears in the switcher. @@ -934,6 +941,20 @@ describe("CompanyImport", () => { } }); + it("falls back to switcher guidance when the expired job's company is unreadable", async () => { + mockCompaniesApi.getImportJob.mockResolvedValue({ + job: { id: "job-1", status: "succeeded", result: { companyId: "company-2" } }, + }); + mockCompaniesApi.get.mockRejectedValue(new Error("forbidden")); + + await renderPageAndImport(); + + expect(container.textContent).toContain("Import completed"); + expect(container.textContent).toContain("select it from the company switcher"); + // No readable company, so no dashboard CTA — the switcher guidance stands in. + expect(container.querySelector('[data-testid="import-expired-open-company"]')).toBeNull(); + }); + it("surfaces a first-poll 404 as an error because the job never existed", async () => { // A 404 on the very first poll — before the client ever saw the job // running — means the id never existed. That stays a hard error. diff --git a/ui/src/pages/CompanyImport.tsx b/ui/src/pages/CompanyImport.tsx index 6104c9244a..4e86104af0 100644 --- a/ui/src/pages/CompanyImport.tsx +++ b/ui/src/pages/CompanyImport.tsx @@ -899,7 +899,12 @@ export function CompanyImport() { dashboardPath: string; pausedAutomations: boolean; } - | { kind: "expired" } + | { + kind: "expired"; + companyName: string | null; + dashboardPath: string | null; + pausedAutomations: boolean; + } | null >(null); const [activationChecked, setActivationChecked] = useState>(new Set()); @@ -1243,18 +1248,28 @@ export function CompanyImport() { if (outcome.status === "completed-expired") { // The import finished and wrote all its data, but the job's result // expired (or was never retained) before we could read it. This is a - // success, not a failure: surface it gently and let the refreshed - // switcher carry the user into the new company. + // success, not a failure: keep the landed company's identity so the + // outcome screen can take the user straight there instead of leaving + // them to hunt through the switcher. + let expiredCompanyName: string | null = null; + let expiredDashboardPath: string | null = null; if (outcome.companyId) { try { const importedCompany = await companiesApi.get(outcome.companyId); setSelectedCompanyId(importedCompany.id); + expiredCompanyName = importedCompany.name; + expiredDashboardPath = `/${importedCompany.issuePrefix}/dashboard`; } catch { // The company id may be unreadable (permissions, race); the // refreshed company list still surfaces the import. } } - setImportOutcome({ kind: "expired" }); + setImportOutcome({ + kind: "expired", + companyName: expiredCompanyName, + dashboardPath: expiredDashboardPath, + pausedAutomations: submittedPauseAutomations, + }); pushToast({ tone: "success", title: "Import completed", @@ -1619,16 +1634,37 @@ export function CompanyImport() { // Soft success: the import finished and wrote all its data, but the job's // in-memory result expired before we could read it. Never a failure — the // company list has been refreshed, so the imported company is available - // from the switcher. + // from the switcher, and when we could read the company we take the user + // straight to it. return (

Import completed

- The import finished and your company is ready. Its detailed summary is no - longer available, but the company has been added — open it to view it. + {importOutcome.companyName + ? <>The import finished and {importOutcome.companyName} is ready. Its detailed summary is no longer available. + : "The import finished and your company is ready. Its detailed summary is no longer available, but the company has been added — select it from the company switcher to view it."}

+ {importOutcome.pausedAutomations ? ( +

+ Imported agents arrived paused — resume them from the company's Agents page so assigned tasks can start. +

+ ) : null}
+ {importOutcome.dashboardPath ? ( +
+ +
+ ) : null}
); } @@ -1730,6 +1766,12 @@ export function CompanyImport() { )} + {importOutcome.pausedAutomations ? ( +

+ Anything left paused here stays visible on the company's Agents and Routines pages, which offer the same resume actions — nothing is lost if you leave this page. +

+ ) : null} + {/* Force a fresh dashboard load so newly imported agents are immediately visible. */}