From 86bb3d25cce2e721fe692064648645c24d2a4191 Mon Sep 17 00:00:00 2001 From: Dotta Date: Mon, 16 Mar 2026 09:35:26 -0500 Subject: [PATCH] fix: refine import renames panel per feedback - Remove COMPANY.md from renames panel; just uncheck it silently in the file tree when importing to existing company - Rename panel from "Conflicts to resolve" to "Renames" - Add "skip" button on the left and "confirm rename" button on the right of each rename row - Confirmed renames show a green checkmark and green-tinted row - Skipped items gray out and uncheck the file in the tree - Un-confirmed renames still proceed with the rename by default Co-Authored-By: Paperclip --- ui/src/pages/CompanyImport.tsx | 170 +++++++++++++++++++++------------ 1 file changed, 107 insertions(+), 63 deletions(-) diff --git a/ui/src/pages/CompanyImport.tsx b/ui/src/pages/CompanyImport.tsx index 74257d34..92600a8b 100644 --- a/ui/src/pages/CompanyImport.tsx +++ b/ui/src/pages/CompanyImport.tsx @@ -15,6 +15,7 @@ import { EmptyState } from "../components/EmptyState"; import { cn } from "../lib/utils"; import { ArrowRight, + Check, Download, Github, Link2, @@ -206,7 +207,7 @@ function ImportPreviewPane({ interface ConflictItem { slug: string; - kind: "agent" | "project" | "issue" | "company" | "skill"; + kind: "agent" | "project" | "issue" | "skill"; originalName: string; plannedName: string; filePath: string | null; @@ -215,23 +216,10 @@ interface ConflictItem { function buildConflictList( preview: CompanyPortabilityPreviewResult, - targetMode: "existing" | "new", ): ConflictItem[] { const conflicts: ConflictItem[] = []; const manifest = preview.manifest; - // COMPANY.md when importing to existing company - if (targetMode === "existing" && manifest.company && preview.plan.companyAction === "update") { - conflicts.push({ - slug: "__company__", - kind: "company", - originalName: manifest.company.name, - plannedName: manifest.company.name, - filePath: ensureMarkdownPath(manifest.company.path), - action: "update", - }); - } - // Agents with collisions for (const ap of preview.plan.agentPlans) { if (ap.existingAgentId) { @@ -297,77 +285,117 @@ function ConflictResolutionList({ conflicts, nameOverrides, skippedSlugs, + confirmedSlugs, onRename, onToggleSkip, + onToggleConfirm, }: { conflicts: ConflictItem[]; nameOverrides: Record; skippedSlugs: Set; + confirmedSlugs: Set; onRename: (slug: string, newName: string) => void; onToggleSkip: (slug: string, filePath: string | null) => void; + onToggleConfirm: (slug: string) => void; }) { if (conflicts.length === 0) return null; return (
-
-
-

- Conflicts to resolve +
+
+

+ Renames

- + {conflicts.length} item{conflicts.length === 1 ? "" : "s"}
-
+
{conflicts.map((item) => { const isSkipped = skippedSlugs.has(item.slug); + const isConfirmed = confirmedSlugs.has(item.slug); const currentName = nameOverrides[item.slug] ?? item.plannedName; - const kindLabel = item.kind === "company" ? "COMPANY.md" : item.kind; return (
- - {kindLabel} - - - - {item.originalName} - - - {item.kind !== "company" && !isSkipped && ( - <> - - onRename(item.slug, e.target.value)} - /> - - )} - + {/* Skip button on the left */} + + + {item.kind} + + + + {item.originalName} + + + {!isSkipped && ( + <> + + {isConfirmed ? ( + + {currentName} + + ) : ( + onRename(item.slug, e.target.value)} + /> + )} + + )} + + {/* Confirm rename button on the right */} + {!isSkipped && ( + + )}
); })} @@ -440,6 +468,7 @@ export function CompanyImport() { // Conflict resolution state const [nameOverrides, setNameOverrides] = useState>({}); const [skippedSlugs, setSkippedSlugs] = useState>(new Set()); + const [confirmedSlugs, setConfirmedSlugs] = useState>(new Set()); useEffect(() => { setBreadcrumbs([ @@ -483,29 +512,25 @@ export function CompanyImport() { setImportPreview(result); // Build conflicts and set default name overrides with prefix - const conflicts = buildConflictList(result, targetMode); + const conflicts = buildConflictList(result); const prefix = deriveSourcePrefix(sourceMode, importUrl, localPackage?.rootPath ?? null); const defaultOverrides: Record = {}; - const defaultSkipped = new Set(); for (const c of conflicts) { - if (c.kind === "company") { - // COMPANY.md defaults to skip when importing to existing company - defaultSkipped.add(c.slug); - } else if (c.action === "rename" && prefix) { + if (c.action === "rename" && prefix) { // Use prefix-based default rename defaultOverrides[c.slug] = prefixedName(prefix, c.originalName); } } setNameOverrides(defaultOverrides); - setSkippedSlugs(defaultSkipped); + setSkippedSlugs(new Set()); + setConfirmedSlugs(new Set()); - // Check all files by default, then uncheck skipped conflict files + // Check all files by default, then uncheck COMPANY.md for existing company const allFiles = new Set(Object.keys(result.files)); - for (const c of conflicts) { - if (defaultSkipped.has(c.slug) && c.filePath && allFiles.has(c.filePath)) { - allFiles.delete(c.filePath); - } + if (targetMode === "existing" && result.manifest.company && result.plan.companyAction === "update") { + const companyPath = ensureMarkdownPath(result.manifest.company.path); + allFiles.delete(companyPath); } setCheckedFiles(allFiles); @@ -585,6 +610,7 @@ export function CompanyImport() { setImportUrl(""); setNameOverrides({}); setSkippedSlugs(new Set()); + setConfirmedSlugs(new Set()); }, onError: (err) => { pushToast({ @@ -622,8 +648,8 @@ export function CompanyImport() { ); const conflicts = useMemo( - () => (importPreview ? buildConflictList(importPreview, targetMode) : []), - [importPreview, targetMode], + () => (importPreview ? buildConflictList(importPreview) : []), + [importPreview], ); const totalFiles = useMemo(() => countFiles(tree), [tree]); @@ -673,6 +699,22 @@ export function CompanyImport() { function handleConflictRename(slug: string, newName: string) { setNameOverrides((prev) => ({ ...prev, [slug]: newName })); + // Editing the name un-confirms + setConfirmedSlugs((prev) => { + if (!prev.has(slug)) return prev; + const next = new Set(prev); + next.delete(slug); + return next; + }); + } + + function handleConflictToggleConfirm(slug: string) { + setConfirmedSlugs((prev) => { + const next = new Set(prev); + if (next.has(slug)) next.delete(slug); + else next.add(slug); + return next; + }); } function handleConflictToggleSkip(slug: string, filePath: string | null) { @@ -870,7 +912,7 @@ export function CompanyImport() { {conflicts.length > 0 && ( - {conflicts.length} conflict{conflicts.length === 1 ? "" : "s"} + {conflicts.length} rename{conflicts.length === 1 ? "" : "s"} )} {importPreview.errors.length > 0 && ( @@ -897,8 +939,10 @@ export function CompanyImport() { conflicts={conflicts} nameOverrides={nameOverrides} skippedSlugs={skippedSlugs} + confirmedSlugs={confirmedSlugs} onRename={handleConflictRename} onToggleSkip={handleConflictToggleSkip} + onToggleConfirm={handleConflictToggleConfirm} /> {/* Warnings */}