fix(ui): remove dead delete API method and add confirmation for delete-by-source

- Remove duplicate `delete` method (identical to `remove`)
- Route delete-by-source through confirmation dialog with source
  locator displayed and "Remove all from source" button

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-09 16:02:05 -04:00
parent 89909db27c
commit e3c172a06f
2 changed files with 36 additions and 25 deletions
-4
View File
@@ -64,8 +64,4 @@ export const companySkillsApi = {
`/companies/${encodeURIComponent(companyId)}/skills/${encodeURIComponent(skillId)}/install-update`, `/companies/${encodeURIComponent(companyId)}/skills/${encodeURIComponent(skillId)}/install-update`,
{}, {},
), ),
delete: (companyId: string, skillId: string) =>
api.delete<CompanySkill>(
`/companies/${encodeURIComponent(companyId)}/skills/${encodeURIComponent(skillId)}`,
),
}; };
+36 -21
View File
@@ -890,6 +890,7 @@ export function CompanySkills() {
const [deleteOpen, setDeleteOpen] = useState(false); const [deleteOpen, setDeleteOpen] = useState(false);
const [deleteTargetSkillId, setDeleteTargetSkillId] = useState<string | null>(null); const [deleteTargetSkillId, setDeleteTargetSkillId] = useState<string | null>(null);
const [deleteTargetDetail, setDeleteTargetDetail] = useState<CompanySkillDetail | null>(null); const [deleteTargetDetail, setDeleteTargetDetail] = useState<CompanySkillDetail | null>(null);
const [deleteTargetSourceLocator, setDeleteTargetSourceLocator] = useState<string | null>(null);
const parsedRoute = useMemo(() => parseSkillRoute(routePath), [routePath]); const parsedRoute = useMemo(() => parseSkillRoute(routePath), [routePath]);
const routeSkillId = parsedRoute.skillId; const routeSkillId = parsedRoute.skillId;
const selectedPath = parsedRoute.filePath; const selectedPath = parsedRoute.filePath;
@@ -998,12 +999,16 @@ export function CompanySkills() {
if (!open) { if (!open) {
setDeleteTargetSkillId(null); setDeleteTargetSkillId(null);
setDeleteTargetDetail(null); setDeleteTargetDetail(null);
setDeleteTargetSourceLocator(null);
} }
} }
function handleDeleteSkill(sourceLocator?: string | null) { function handleDeleteSkill(sourceLocator?: string | null) {
if (sourceLocator) { if (sourceLocator) {
deleteSkill.mutate(sourceLocator); setDeleteTargetSourceLocator(sourceLocator);
setDeleteTargetSkillId(null);
setDeleteTargetDetail(null);
setDeleteOpen(true);
} else { } else {
openDeleteDialog(); openDeleteDialog();
} }
@@ -1212,30 +1217,40 @@ export function CompanySkills() {
<Dialog open={deleteOpen} onOpenChange={closeDeleteDialog}> <Dialog open={deleteOpen} onOpenChange={closeDeleteDialog}>
<DialogContent className="sm:max-w-md"> <DialogContent className="sm:max-w-md">
<DialogHeader> <DialogHeader>
<DialogTitle>Remove skill</DialogTitle> <DialogTitle>{deleteTargetSourceLocator ? "Remove skills from source" : "Remove skill"}</DialogTitle>
<DialogDescription> <DialogDescription>
Remove this skill from the company library. If any agents still use it, removal will be blocked until it is detached. {deleteTargetSourceLocator
? `All skills imported from this source will be permanently removed from the company library.`
: "Remove this skill from the company library. If any agents still use it, removal will be blocked until it is detached."}
</DialogDescription> </DialogDescription>
</DialogHeader> </DialogHeader>
<div className="space-y-3 text-sm"> <div className="space-y-3 text-sm">
<p> {deleteTargetSourceLocator ? (
{deleteTargetDetail <p className="rounded-md border border-destructive/50 bg-destructive/5 px-3 py-2 font-mono text-xs break-all">
? `You are about to remove ${deleteTargetDetail.name}.` {deleteTargetSourceLocator}
: "You are about to remove this skill."}
</p>
{deleteTargetDetail?.usedByAgents?.length ? (
<div className="rounded-md border border-border px-3 py-3 text-muted-foreground">
Currently used by {deleteTargetDetail.usedByAgents.map((agent) => agent.name).join(", ")}.
</div>
) : null}
{(deleteTargetDetail?.usedByAgents.length ?? 0) > 0 ? (
<p className="text-muted-foreground">
Detach this skill from all agents to enable removal.
</p> </p>
) : null} ) : (
<>
<p>
{deleteTargetDetail
? `You are about to remove ${deleteTargetDetail.name}.`
: "You are about to remove this skill."}
</p>
{deleteTargetDetail?.usedByAgents?.length ? (
<div className="rounded-md border border-border px-3 py-3 text-muted-foreground">
Currently used by {deleteTargetDetail.usedByAgents.map((agent) => agent.name).join(", ")}.
</div>
) : null}
{(deleteTargetDetail?.usedByAgents.length ?? 0) > 0 ? (
<p className="text-muted-foreground">
Detach this skill from all agents to enable removal.
</p>
) : null}
</>
)}
</div> </div>
<DialogFooter> <DialogFooter>
{(deleteTargetDetail?.usedByAgents.length ?? 0) > 0 ? ( {(deleteTargetDetail?.usedByAgents.length ?? 0) > 0 && !deleteTargetSourceLocator ? (
<Button variant="ghost" onClick={() => closeDeleteDialog(false)}> <Button variant="ghost" onClick={() => closeDeleteDialog(false)}>
Close Close
</Button> </Button>
@@ -1246,10 +1261,10 @@ export function CompanySkills() {
</Button> </Button>
<Button <Button
variant="destructive" variant="destructive"
onClick={() => deleteSkill.mutate(undefined)} onClick={() => deleteSkill.mutate(deleteTargetSourceLocator ?? undefined)}
disabled={deleteSkill.isPending || !deleteTargetSkillId} disabled={deleteSkill.isPending || (!deleteTargetSkillId && !deleteTargetSourceLocator)}
> >
{deleteSkill.isPending ? "Removing..." : "Remove skill"} {deleteSkill.isPending ? "Removing..." : deleteTargetSourceLocator ? "Remove all from source" : "Remove skill"}
</Button> </Button>
</> </>
)} )}