feat: add confirm dialogs for delete/reset actions

This commit is contained in:
Bohdan Triapitsyn
2026-02-10 00:27:17 +02:00
parent 8c42edcf24
commit 29eb71a7d5
7 changed files with 383 additions and 68 deletions
@@ -102,6 +102,9 @@ const rulesetToPermissionConfig = (ruleset: unknown): AgentDraft['permission'] =
export const AgentsSidebar: React.FC<AgentsSidebarProps> = ({ onItemSelect }) => { export const AgentsSidebar: React.FC<AgentsSidebarProps> = ({ onItemSelect }) => {
const [renameDialogAgent, setRenameDialogAgent] = React.useState<Agent | null>(null); const [renameDialogAgent, setRenameDialogAgent] = React.useState<Agent | null>(null);
const [renameNewName, setRenameNewName] = React.useState(''); const [renameNewName, setRenameNewName] = React.useState('');
const [confirmActionAgent, setConfirmActionAgent] = React.useState<Agent | null>(null);
const [confirmActionType, setConfirmActionType] = React.useState<'delete' | 'reset' | null>(null);
const [isConfirmActionPending, setIsConfirmActionPending] = React.useState(false);
const { const {
selectedAgentName, selectedAgentName,
@@ -150,14 +153,8 @@ export const AgentsSidebar: React.FC<AgentsSidebarProps> = ({ onItemSelect }) =>
return; return;
} }
if (window.confirm(`Are you sure you want to delete agent "${agent.name}"?`)) { setConfirmActionAgent(agent);
const success = await deleteAgent(agent.name); setConfirmActionType('delete');
if (success) {
toast.success(`Agent "${agent.name}" deleted successfully`);
} else {
toast.error('Failed to delete agent');
}
}
}; };
const handleResetAgent = async (agent: Agent) => { const handleResetAgent = async (agent: Agent) => {
@@ -165,14 +162,37 @@ export const AgentsSidebar: React.FC<AgentsSidebarProps> = ({ onItemSelect }) =>
return; return;
} }
if (window.confirm(`Are you sure you want to reset agent "${agent.name}" to its default configuration?`)) { setConfirmActionAgent(agent);
const success = await deleteAgent(agent.name); setConfirmActionType('reset');
if (success) { };
toast.success(`Agent "${agent.name}" reset to default`);
} else { const closeConfirmActionDialog = () => {
toast.error('Failed to reset agent'); setConfirmActionAgent(null);
} setConfirmActionType(null);
};
const handleConfirmAction = async () => {
if (!confirmActionAgent || !confirmActionType) {
return;
} }
setIsConfirmActionPending(true);
const success = await deleteAgent(confirmActionAgent.name);
if (success) {
if (confirmActionType === 'delete') {
toast.success(`Agent "${confirmActionAgent.name}" deleted successfully`);
} else {
toast.success(`Agent "${confirmActionAgent.name}" reset to default`);
}
closeConfirmActionDialog();
} else if (confirmActionType === 'delete') {
toast.error('Failed to delete agent');
} else {
toast.error('Failed to reset agent');
}
setIsConfirmActionPending(false);
}; };
const handleDuplicateAgent = (agent: Agent) => { const handleDuplicateAgent = (agent: Agent) => {
@@ -367,6 +387,39 @@ export const AgentsSidebar: React.FC<AgentsSidebarProps> = ({ onItemSelect }) =>
)} )}
</ScrollableOverlay> </ScrollableOverlay>
<Dialog
open={confirmActionAgent !== null && confirmActionType !== null}
onOpenChange={(open) => {
if (!open && !isConfirmActionPending) {
closeConfirmActionDialog();
}
}}
>
<DialogContent className="max-w-md">
<DialogHeader>
<DialogTitle>{confirmActionType === 'delete' ? 'Delete Agent' : 'Reset Agent'}</DialogTitle>
<DialogDescription>
{confirmActionType === 'delete'
? `Are you sure you want to delete agent "${confirmActionAgent?.name}"?`
: `Are you sure you want to reset agent "${confirmActionAgent?.name}" to its default configuration?`}
</DialogDescription>
</DialogHeader>
<DialogFooter>
<Button
variant="ghost"
onClick={closeConfirmActionDialog}
disabled={isConfirmActionPending}
className="text-foreground hover:bg-interactive-hover hover:text-foreground"
>
Cancel
</Button>
<ButtonLarge onClick={handleConfirmAction} disabled={isConfirmActionPending}>
{confirmActionType === 'delete' ? 'Delete' : 'Reset'}
</ButtonLarge>
</DialogFooter>
</DialogContent>
</Dialog>
{/* Rename Dialog */} {/* Rename Dialog */}
<Dialog open={renameDialogAgent !== null} onOpenChange={(open) => !open && setRenameDialogAgent(null)}> <Dialog open={renameDialogAgent !== null} onOpenChange={(open) => !open && setRenameDialogAgent(null)}>
<DialogContent> <DialogContent>
@@ -32,6 +32,9 @@ interface CommandsSidebarProps {
export const CommandsSidebar: React.FC<CommandsSidebarProps> = ({ onItemSelect }) => { export const CommandsSidebar: React.FC<CommandsSidebarProps> = ({ onItemSelect }) => {
const [renameDialogCommand, setRenameDialogCommand] = React.useState<Command | null>(null); const [renameDialogCommand, setRenameDialogCommand] = React.useState<Command | null>(null);
const [renameNewName, setRenameNewName] = React.useState(''); const [renameNewName, setRenameNewName] = React.useState('');
const [confirmActionCommand, setConfirmActionCommand] = React.useState<Command | null>(null);
const [confirmActionType, setConfirmActionType] = React.useState<'delete' | 'reset' | null>(null);
const [isConfirmActionPending, setIsConfirmActionPending] = React.useState(false);
const { const {
selectedCommandName, selectedCommandName,
@@ -80,14 +83,8 @@ export const CommandsSidebar: React.FC<CommandsSidebarProps> = ({ onItemSelect }
return; return;
} }
if (window.confirm(`Are you sure you want to delete command "${command.name}"?`)) { setConfirmActionCommand(command);
const success = await deleteCommand(command.name); setConfirmActionType('delete');
if (success) {
toast.success(`Command "${command.name}" deleted successfully`);
} else {
toast.error('Failed to delete command');
}
}
}; };
const handleResetCommand = async (command: Command) => { const handleResetCommand = async (command: Command) => {
@@ -95,14 +92,37 @@ export const CommandsSidebar: React.FC<CommandsSidebarProps> = ({ onItemSelect }
return; return;
} }
if (window.confirm(`Are you sure you want to reset command "${command.name}" to its default configuration?`)) { setConfirmActionCommand(command);
const success = await deleteCommand(command.name); setConfirmActionType('reset');
if (success) { };
toast.success(`Command "${command.name}" reset to default`);
} else { const closeConfirmActionDialog = () => {
toast.error('Failed to reset command'); setConfirmActionCommand(null);
} setConfirmActionType(null);
};
const handleConfirmAction = async () => {
if (!confirmActionCommand || !confirmActionType) {
return;
} }
setIsConfirmActionPending(true);
const success = await deleteCommand(confirmActionCommand.name);
if (success) {
if (confirmActionType === 'delete') {
toast.success(`Command "${confirmActionCommand.name}" deleted successfully`);
} else {
toast.success(`Command "${confirmActionCommand.name}" reset to default`);
}
closeConfirmActionDialog();
} else if (confirmActionType === 'delete') {
toast.error('Failed to delete command');
} else {
toast.error('Failed to reset command');
}
setIsConfirmActionPending(false);
}; };
const handleDuplicateCommand = (command: Command) => { const handleDuplicateCommand = (command: Command) => {
@@ -264,6 +284,39 @@ export const CommandsSidebar: React.FC<CommandsSidebarProps> = ({ onItemSelect }
)} )}
</ScrollableOverlay> </ScrollableOverlay>
<Dialog
open={confirmActionCommand !== null && confirmActionType !== null}
onOpenChange={(open) => {
if (!open && !isConfirmActionPending) {
closeConfirmActionDialog();
}
}}
>
<DialogContent className="max-w-md">
<DialogHeader>
<DialogTitle>{confirmActionType === 'delete' ? 'Delete Command' : 'Reset Command'}</DialogTitle>
<DialogDescription>
{confirmActionType === 'delete'
? `Are you sure you want to delete command "${confirmActionCommand?.name}"?`
: `Are you sure you want to reset command "${confirmActionCommand?.name}" to its default configuration?`}
</DialogDescription>
</DialogHeader>
<DialogFooter>
<Button
variant="ghost"
onClick={closeConfirmActionDialog}
disabled={isConfirmActionPending}
className="text-foreground hover:bg-interactive-hover hover:text-foreground"
>
Cancel
</Button>
<ButtonLarge onClick={handleConfirmAction} disabled={isConfirmActionPending}>
{confirmActionType === 'delete' ? 'Delete' : 'Reset'}
</ButtonLarge>
</DialogFooter>
</DialogContent>
</Dialog>
{/* Rename Dialog */} {/* Rename Dialog */}
<Dialog open={renameDialogCommand !== null} onOpenChange={(open) => !open && setRenameDialogCommand(null)}> <Dialog open={renameDialogCommand !== null} onOpenChange={(open) => !open && setRenameDialogCommand(null)}>
<DialogContent> <DialogContent>
@@ -19,6 +19,14 @@ import {
import { cn } from '@/lib/utils'; import { cn } from '@/lib/utils';
import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip'; import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip';
import { ScrollableOverlay } from '@/components/ui/ScrollableOverlay'; import { ScrollableOverlay } from '@/components/ui/ScrollableOverlay';
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
} from '@/components/ui/dialog';
const PROFILE_COLORS = [ const PROFILE_COLORS = [
{ key: 'keyword', label: 'Green', cssVar: 'var(--syntax-keyword)' }, { key: 'keyword', label: 'Green', cssVar: 'var(--syntax-keyword)' },
@@ -70,6 +78,8 @@ export const GitIdentitiesPage: React.FC = () => {
const [color, setColor] = React.useState('keyword'); const [color, setColor] = React.useState('keyword');
const [icon, setIcon] = React.useState('branch'); const [icon, setIcon] = React.useState('branch');
const [isSaving, setIsSaving] = React.useState(false); const [isSaving, setIsSaving] = React.useState(false);
const [isDeleteDialogOpen, setIsDeleteDialogOpen] = React.useState(false);
const [isDeleting, setIsDeleting] = React.useState(false);
React.useEffect(() => { React.useEffect(() => {
if (importData) { if (importData) {
@@ -152,23 +162,31 @@ export const GitIdentitiesPage: React.FC = () => {
} }
}; };
const handleDelete = async () => { const handleDelete = () => {
if (!selectedProfileId || isNewProfile) return; if (!selectedProfileId || isNewProfile) return;
if (!confirm('Are you sure you want to delete this profile?')) { setIsDeleteDialogOpen(true);
};
const handleConfirmDelete = async () => {
if (!selectedProfileId || isNewProfile) {
return; return;
} }
setIsDeleting(true);
try { try {
const success = await deleteProfile(selectedProfileId); const success = await deleteProfile(selectedProfileId);
if (success) { if (success) {
toast.success('Profile deleted successfully'); toast.success('Profile deleted successfully');
setIsDeleteDialogOpen(false);
} else { } else {
toast.error('Failed to delete profile'); toast.error('Failed to delete profile');
} }
} catch (error) { } catch (error) {
console.error('Error deleting profile:', error); console.error('Error deleting profile:', error);
toast.error('An error occurred while deleting'); toast.error('An error occurred while deleting');
} finally {
setIsDeleting(false);
} }
}; };
@@ -483,6 +501,32 @@ export const GitIdentitiesPage: React.FC = () => {
</div> </div>
)} )}
</div> </div>
<Dialog
open={isDeleteDialogOpen}
onOpenChange={(open) => {
if (!isDeleting) {
setIsDeleteDialogOpen(open);
}
}}
>
<DialogContent className="max-w-md">
<DialogHeader>
<DialogTitle>Delete Profile</DialogTitle>
<DialogDescription>
Are you sure you want to delete profile "{selectedProfile?.name || name || 'this profile'}"?
</DialogDescription>
</DialogHeader>
<DialogFooter>
<Button variant="ghost" onClick={() => setIsDeleteDialogOpen(false)} disabled={isDeleting}>
Cancel
</Button>
<Button variant="destructive" onClick={() => void handleConfirmDelete()} disabled={isDeleting}>
Delete
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
</div> </div>
</ScrollableOverlay> </ScrollableOverlay>
); );
@@ -1,6 +1,14 @@
import React from 'react'; import React from 'react';
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import { toast } from '@/components/ui'; import { toast } from '@/components/ui';
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
} from '@/components/ui/dialog';
import { import {
DropdownMenu, DropdownMenu,
DropdownMenuContent, DropdownMenuContent,
@@ -49,6 +57,9 @@ interface GitIdentitiesSidebarProps {
} }
export const GitIdentitiesSidebar: React.FC<GitIdentitiesSidebarProps> = ({ onItemSelect }) => { export const GitIdentitiesSidebar: React.FC<GitIdentitiesSidebarProps> = ({ onItemSelect }) => {
const [deleteDialogProfile, setDeleteDialogProfile] = React.useState<GitIdentityProfile | null>(null);
const [isDeletePending, setIsDeletePending] = React.useState(false);
const { const {
selectedProfileId, selectedProfileId,
defaultGitIdentityId, defaultGitIdentityId,
@@ -99,14 +110,23 @@ export const GitIdentitiesSidebar: React.FC<GitIdentitiesSidebarProps> = ({ onIt
}; };
const handleDeleteProfile = async (profile: GitIdentityProfile) => { const handleDeleteProfile = async (profile: GitIdentityProfile) => {
if (window.confirm(`Are you sure you want to delete profile "${profile.name}"?`)) { setDeleteDialogProfile(profile);
const success = await deleteProfile(profile.id); };
if (success) {
toast.success(`Profile "${profile.name}" deleted successfully`); const handleConfirmDeleteProfile = async () => {
} else { if (!deleteDialogProfile) {
toast.error('Failed to delete profile'); return;
}
} }
setIsDeletePending(true);
const success = await deleteProfile(deleteDialogProfile.id);
if (success) {
toast.success(`Profile "${deleteDialogProfile.name}" deleted successfully`);
setDeleteDialogProfile(null);
} else {
toast.error('Failed to delete profile');
}
setIsDeletePending(false);
}; };
const handleToggleDefault = async (profileId: string) => { const handleToggleDefault = async (profileId: string) => {
@@ -216,6 +236,32 @@ export const GitIdentitiesSidebar: React.FC<GitIdentitiesSidebarProps> = ({ onIt
</> </>
)} )}
</ScrollableOverlay> </ScrollableOverlay>
<Dialog
open={deleteDialogProfile !== null}
onOpenChange={(open) => {
if (!open && !isDeletePending) {
setDeleteDialogProfile(null);
}
}}
>
<DialogContent className="max-w-md">
<DialogHeader>
<DialogTitle>Delete Profile</DialogTitle>
<DialogDescription>
Are you sure you want to delete profile "{deleteDialogProfile?.name}"?
</DialogDescription>
</DialogHeader>
<DialogFooter>
<Button variant="ghost" onClick={() => setDeleteDialogProfile(null)} disabled={isDeletePending}>
Cancel
</Button>
<Button variant="destructive" onClick={() => void handleConfirmDeleteProfile()} disabled={isDeletePending}>
Delete
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
</div> </div>
); );
}; };
@@ -91,6 +91,8 @@ export const SkillsPage: React.FC = () => {
const [editingFilePath, setEditingFilePath] = React.useState<string | null>(null); // null = adding, string = editing const [editingFilePath, setEditingFilePath] = React.useState<string | null>(null); // null = adding, string = editing
const [isLoadingFile, setIsLoadingFile] = React.useState(false); const [isLoadingFile, setIsLoadingFile] = React.useState(false);
const [originalFileContent, setOriginalFileContent] = React.useState(''); // Track original for change detection const [originalFileContent, setOriginalFileContent] = React.useState(''); // Track original for change detection
const [deleteFilePath, setDeleteFilePath] = React.useState<string | null>(null);
const [isDeletingFile, setIsDeletingFile] = React.useState(false);
// Detect if skill-level fields have changed // Detect if skill-level fields have changed
const hasSkillChanges = isNewSkill const hasSkillChanges = isNewSkill
@@ -306,7 +308,7 @@ export const SkillsPage: React.FC = () => {
} }
}; };
const handleDeleteFile = async (filePath: string) => { const handleDeleteFile = (filePath: string) => {
// For new skills, remove from pending files // For new skills, remove from pending files
if (isNewSkill) { if (isNewSkill) {
setPendingFiles(prev => prev.filter(f => f.path !== filePath)); setPendingFiles(prev => prev.filter(f => f.path !== filePath));
@@ -315,23 +317,34 @@ export const SkillsPage: React.FC = () => {
} }
// For existing skills, delete from disk // For existing skills, delete from disk
if (!selectedSkillName) return; if (!selectedSkillName) {
return;
if (window.confirm(`Are you sure you want to delete "${filePath}"?`)) {
const { deleteSupportingFile } = useSkillsStore.getState();
const success = await deleteSupportingFile(selectedSkillName, filePath);
if (success) {
toast.success(`File "${filePath}" deleted`);
// Refresh skill details
const detail = await getSkillDetail(selectedSkillName);
if (detail) {
setSupportingFiles(detail.sources.md.supportingFiles || []);
}
} else {
toast.error('Failed to delete file');
}
} }
setDeleteFilePath(filePath);
};
const handleConfirmDeleteFile = async () => {
if (!deleteFilePath || !selectedSkillName) {
return;
}
setIsDeletingFile(true);
const { deleteSupportingFile } = useSkillsStore.getState();
const success = await deleteSupportingFile(selectedSkillName, deleteFilePath);
if (success) {
toast.success(`File "${deleteFilePath}" deleted`);
const detail = await getSkillDetail(selectedSkillName);
if (detail) {
setSupportingFiles(detail.sources.md.supportingFiles || []);
}
setDeleteFilePath(null);
} else {
toast.error('Failed to delete file');
}
setIsDeletingFile(false);
}; };
if (isNewSkill && mode === 'external') { if (isNewSkill && mode === 'external') {
@@ -554,6 +567,37 @@ export const SkillsPage: React.FC = () => {
</div> </div>
{/* Add/Edit File Dialog */} {/* Add/Edit File Dialog */}
<Dialog
open={deleteFilePath !== null}
onOpenChange={(open) => {
if (!open && !isDeletingFile) {
setDeleteFilePath(null);
}
}}
>
<DialogContent className="max-w-md">
<DialogHeader>
<DialogTitle>Delete Supporting File</DialogTitle>
<DialogDescription>
Are you sure you want to delete "{deleteFilePath}"?
</DialogDescription>
</DialogHeader>
<DialogFooter>
<Button
variant="ghost"
onClick={() => setDeleteFilePath(null)}
disabled={isDeletingFile}
className="text-foreground hover:bg-interactive-hover hover:text-foreground"
>
Cancel
</Button>
<ButtonLarge onClick={handleConfirmDeleteFile} disabled={isDeletingFile}>
Delete
</ButtonLarge>
</DialogFooter>
</DialogContent>
</Dialog>
<Dialog open={isFileDialogOpen} onOpenChange={(open) => { <Dialog open={isFileDialogOpen} onOpenChange={(open) => {
setIsFileDialogOpen(open); setIsFileDialogOpen(open);
if (!open) setEditingFilePath(null); if (!open) setEditingFilePath(null);
@@ -32,6 +32,8 @@ interface SkillsSidebarProps {
export const SkillsSidebar: React.FC<SkillsSidebarProps> = ({ onItemSelect }) => { export const SkillsSidebar: React.FC<SkillsSidebarProps> = ({ onItemSelect }) => {
const [renameDialogSkill, setRenameDialogSkill] = React.useState<DiscoveredSkill | null>(null); const [renameDialogSkill, setRenameDialogSkill] = React.useState<DiscoveredSkill | null>(null);
const [renameNewName, setRenameNewName] = React.useState(''); const [renameNewName, setRenameNewName] = React.useState('');
const [deleteDialogSkill, setDeleteDialogSkill] = React.useState<DiscoveredSkill | null>(null);
const [isDeletePending, setIsDeletePending] = React.useState(false);
const { const {
selectedSkillName, selectedSkillName,
@@ -76,14 +78,23 @@ export const SkillsSidebar: React.FC<SkillsSidebarProps> = ({ onItemSelect }) =>
}; };
const handleDeleteSkill = async (skill: DiscoveredSkill) => { const handleDeleteSkill = async (skill: DiscoveredSkill) => {
if (window.confirm(`Are you sure you want to delete skill "${skill.name}"?`)) { setDeleteDialogSkill(skill);
const success = await deleteSkill(skill.name); };
if (success) {
toast.success(`Skill "${skill.name}" deleted successfully`); const handleConfirmDeleteSkill = async () => {
} else { if (!deleteDialogSkill) {
toast.error('Failed to delete skill'); return;
}
} }
setIsDeletePending(true);
const success = await deleteSkill(deleteDialogSkill.name);
if (success) {
toast.success(`Skill "${deleteDialogSkill.name}" deleted successfully`);
setDeleteDialogSkill(null);
} else {
toast.error('Failed to delete skill');
}
setIsDeletePending(false);
}; };
const handleDuplicateSkill = async (skill: DiscoveredSkill) => { const handleDuplicateSkill = async (skill: DiscoveredSkill) => {
@@ -256,6 +267,37 @@ export const SkillsSidebar: React.FC<SkillsSidebarProps> = ({ onItemSelect }) =>
)} )}
</ScrollableOverlay> </ScrollableOverlay>
<Dialog
open={deleteDialogSkill !== null}
onOpenChange={(open) => {
if (!open && !isDeletePending) {
setDeleteDialogSkill(null);
}
}}
>
<DialogContent className="max-w-md">
<DialogHeader>
<DialogTitle>Delete Skill</DialogTitle>
<DialogDescription>
Are you sure you want to delete skill "{deleteDialogSkill?.name}"?
</DialogDescription>
</DialogHeader>
<DialogFooter>
<Button
variant="ghost"
onClick={() => setDeleteDialogSkill(null)}
disabled={isDeletePending}
className="text-foreground hover:bg-interactive-hover hover:text-foreground"
>
Cancel
</Button>
<ButtonLarge onClick={handleConfirmDeleteSkill} disabled={isDeletePending}>
Delete
</ButtonLarge>
</DialogFooter>
</DialogContent>
</Dialog>
{/* Rename Dialog */} {/* Rename Dialog */}
<Dialog open={renameDialogSkill !== null} onOpenChange={(open) => !open && setRenameDialogSkill(null)}> <Dialog open={renameDialogSkill !== null} onOpenChange={(open) => !open && setRenameDialogSkill(null)}>
<DialogContent> <DialogContent>
@@ -4,6 +4,14 @@ import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input'; import { Input } from '@/components/ui/input';
import { ScrollableOverlay } from '@/components/ui/ScrollableOverlay'; import { ScrollableOverlay } from '@/components/ui/ScrollableOverlay';
import { AnimatedTabs } from '@/components/ui/animated-tabs'; import { AnimatedTabs } from '@/components/ui/animated-tabs';
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
} from '@/components/ui/dialog';
import { import {
Select, Select,
SelectContent, SelectContent,
@@ -75,6 +83,7 @@ export const SkillsCatalogPage: React.FC<SkillsCatalogPageProps> = ({ mode, onMo
const [installDialogOpen, setInstallDialogOpen] = React.useState(false); const [installDialogOpen, setInstallDialogOpen] = React.useState(false);
const [installItem, setInstallItem] = React.useState<SkillsCatalogItem | null>(null); const [installItem, setInstallItem] = React.useState<SkillsCatalogItem | null>(null);
const [isRemovingCatalog, setIsRemovingCatalog] = React.useState(false); const [isRemovingCatalog, setIsRemovingCatalog] = React.useState(false);
const [isRemoveCatalogDialogOpen, setIsRemoveCatalogDialogOpen] = React.useState(false);
React.useEffect(() => { React.useEffect(() => {
void loadCatalog(); void loadCatalog();
@@ -118,10 +127,6 @@ export const SkillsCatalogPage: React.FC<SkillsCatalogPageProps> = ({ mode, onMo
return; return;
} }
if (!window.confirm('Remove this catalog?')) {
return;
}
setIsRemovingCatalog(true); setIsRemovingCatalog(true);
try { try {
const settings = await loadSettings(); const settings = await loadSettings();
@@ -129,6 +134,7 @@ export const SkillsCatalogPage: React.FC<SkillsCatalogPageProps> = ({ mode, onMo
const updated = catalogs.filter((c) => c.id !== selectedSourceId); const updated = catalogs.filter((c) => c.id !== selectedSourceId);
await updateDesktopSettings({ skillCatalogs: updated }); await updateDesktopSettings({ skillCatalogs: updated });
await loadCatalog({ refresh: true }); await loadCatalog({ refresh: true });
setIsRemoveCatalogDialogOpen(false);
} finally { } finally {
setIsRemovingCatalog(false); setIsRemovingCatalog(false);
} }
@@ -198,7 +204,7 @@ export const SkillsCatalogPage: React.FC<SkillsCatalogPageProps> = ({ mode, onMo
<Button <Button
type="button" type="button"
variant="outline" variant="outline"
onClick={() => void removeSelectedCatalog()} onClick={() => setIsRemoveCatalogDialogOpen(true)}
disabled={isRemovingCatalog} disabled={isRemovingCatalog}
className="gap-2" className="gap-2"
> >
@@ -337,6 +343,33 @@ export const SkillsCatalogPage: React.FC<SkillsCatalogPageProps> = ({ mode, onMo
<AddCatalogDialog open={addCatalogOpen} onOpenChange={setAddCatalogOpen} /> <AddCatalogDialog open={addCatalogOpen} onOpenChange={setAddCatalogOpen} />
<InstallSkillDialog open={installDialogOpen} onOpenChange={setInstallDialogOpen} item={installItem} /> <InstallSkillDialog open={installDialogOpen} onOpenChange={setInstallDialogOpen} item={installItem} />
<Dialog
open={isRemoveCatalogDialogOpen}
onOpenChange={(open) => {
if (!isRemovingCatalog) {
setIsRemoveCatalogDialogOpen(open);
}
}}
>
<DialogContent className="max-w-md">
<DialogHeader>
<DialogTitle>Remove Catalog</DialogTitle>
<DialogDescription>Are you sure you want to remove this catalog?</DialogDescription>
</DialogHeader>
<DialogFooter>
<Button
variant="ghost"
onClick={() => setIsRemoveCatalogDialogOpen(false)}
disabled={isRemovingCatalog}
>
Cancel
</Button>
<Button variant="destructive" onClick={() => void removeSelectedCatalog()} disabled={isRemovingCatalog}>
Remove
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
</div> </div>
</ScrollableOverlay> </ScrollableOverlay>
); );