fix(ui): supersede settled import errors on every payload edit

File selection, conflict rename/confirm/skip, adapter overrides and
config, and continue-without-attachments all shape the next import
request but did not clear a settled failure, so the persistent panel
could keep describing a superseded request. Route them all through the
settled-state reset (pending requests remain untouched), and cover the
file-selection case in the component tests.
This commit is contained in:
Devin Foley committed 2026-07-30 13:37:52 -07:00
1 parent a4ab7fbdf3
commit 7608255e42
2 files changed
+34 -5

No files matched your search

+20
View File
@@ -479,6 +479,26 @@ describe("CompanyImport", () => {
expect(container.textContent).not.toContain("Import failed:");
// File-selection edits feed the payload too and supersede a failure.
await clickButton((text) => text.startsWith("Import 3 file"));
await act(async () => {
rejectImport(new Error("third failure"));
});
await flushReact();
expect(container.textContent).toContain("Import failed: third failure");
const fileCheckbox = Array.from(
container.querySelectorAll<HTMLInputElement>('input[type="checkbox"]'),
).find((input) => !input.closest("label")?.textContent?.includes("Start imported agents"));
expect(fileCheckbox).toBeTruthy();
await act(async () => {
fileCheckbox!.click();
});
await flushReact();
expect(container.textContent).not.toContain("Import failed:");
// Changing the package supersedes the failed import: previewing a fresh
// package must not resurface the stale import error panel.
await enterGithubUrl("https://github.com/acme/other-starter/tree/main/company");
+14 -5
View File
@@ -941,11 +941,13 @@ export function CompanyImport() {
// Any change to the import configuration supersedes the request a settled
// progress/error panel describes, so it clears settled mutation state. A
// pending request is never detached: its panel keeps reporting it (and the
// action buttons stay disabled) until it settles. The new-company name and
// pause toggle are set against a rendered preview, so they reset only the
// panels; structural changes also discard the preview itself. Structural
// controls are locked while an import runs, so this can never unmount the
// preview section that hosts a running import's status panels.
// action buttons stay disabled) until it settles. Payload edits made
// against a rendered preview (new-company name, pause toggle, file
// selection, conflict choices, adapter overrides, dropping attachments)
// reset only the panels; structural changes also discard the preview
// itself. Structural controls are locked while an import runs, so this can
// never unmount the preview section that hosts a running import's status
// panels.
function resetMutationState() {
if (!previewMutation.isPending) previewMutation.reset();
if (!importMutation.isPending) importMutation.reset();
@@ -1022,6 +1024,7 @@ export function CompanyImport() {
function handleToggleCheck(path: string, kind: "file" | "dir") {
if (!importPreview) return;
resetMutationState();
setCheckedFiles((prev) => {
const next = new Set(prev);
if (kind === "file") {
@@ -1054,6 +1057,7 @@ export function CompanyImport() {
}
function handleConflictRename(slug: string, newName: string) {
resetMutationState();
setNameOverrides((prev) => ({ ...prev, [slug]: newName }));
// Editing the name un-confirms
setConfirmedSlugs((prev) => {
@@ -1065,6 +1069,7 @@ export function CompanyImport() {
}
function handleConflictToggleConfirm(slug: string) {
resetMutationState();
setConfirmedSlugs((prev) => {
const next = new Set(prev);
if (next.has(slug)) next.delete(slug);
@@ -1074,6 +1079,7 @@ export function CompanyImport() {
}
function handleConflictToggleSkip(slug: string, filePath: string | null) {
resetMutationState();
setSkippedSlugs((prev) => {
const next = new Set(prev);
const wasSkipped = next.has(slug);
@@ -1101,6 +1107,7 @@ export function CompanyImport() {
}
function handleAdapterChange(slug: string, adapterType: string) {
resetMutationState();
setAdapterOverrides((prev) => ({ ...prev, [slug]: adapterType }));
// Reset config values when adapter type changes
setAdapterConfigValues((prev) => {
@@ -1120,6 +1127,7 @@ export function CompanyImport() {
}
function handleAdapterConfigChange(slug: string, patch: Partial<CreateConfigValues>) {
resetMutationState();
setAdapterConfigValues((prev) => ({
...prev,
[slug]: { ...(prev[slug] ?? { ...defaultCreateValues, adapterType: adapterOverrides[slug] ?? "claude_local" }), ...patch },
@@ -1207,6 +1215,7 @@ export function CompanyImport() {
function handleContinueWithoutAttachments() {
if (!localPackage) return;
resetMutationState();
setLocalPackage({ ...localPackage, files: stripBlobFiles(localPackage.files) });
setCheckedFiles((prev) => new Set([...prev].filter((filePath) => !isBlobStoreFilePath(filePath))));
}