mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-10-25 17:45:53 +01:00
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:
1 parent
a4ab7fbdf3
commit
7608255e42
2 files changed
+34
-5
No files matched your search
@@ -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");
|
||||
|
||||
@@ -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))));
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user