mirror of
https://github.com/mfakbar127/Claude-Samurai
synced 2026-06-21 13:57:42 +00:00
ux improvement
This commit is contained in:
+1
-1
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "cc-mate",
|
||||
"name": "claude-samurai",
|
||||
"private": true,
|
||||
"version": "0.2.0",
|
||||
"type": "module",
|
||||
|
||||
+113
-81
@@ -42,68 +42,86 @@ export function Layout() {
|
||||
const location = useLocation();
|
||||
const isProjectsRoute = location.pathname.startsWith("/projects");
|
||||
|
||||
const navLinks = [
|
||||
const navSections = [
|
||||
{
|
||||
to: "/",
|
||||
icon: FileJsonIcon,
|
||||
label: t("navigation.configurations"),
|
||||
id: "core",
|
||||
label: t("navigation.section.core", { defaultValue: "Core" }),
|
||||
items: [
|
||||
{
|
||||
to: "/",
|
||||
icon: FileJsonIcon,
|
||||
label: t("navigation.configurations"),
|
||||
},
|
||||
{
|
||||
to: "/projects",
|
||||
icon: FolderIcon,
|
||||
label: t("navigation.projects"),
|
||||
},
|
||||
{
|
||||
to: "/mcp",
|
||||
icon: CpuIcon,
|
||||
label: t("navigation.mcp"),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
to: "/projects",
|
||||
icon: FolderIcon,
|
||||
label: t("navigation.projects"),
|
||||
id: "automation",
|
||||
label: t("navigation.section.automation", { defaultValue: "Automation" }),
|
||||
items: [
|
||||
{
|
||||
to: "/hooks",
|
||||
icon: CpuIcon,
|
||||
label: t("hooks.title"),
|
||||
},
|
||||
{
|
||||
to: "/commands",
|
||||
icon: TerminalIcon,
|
||||
label: t("navigation.commands"),
|
||||
},
|
||||
{
|
||||
to: "/skills",
|
||||
icon: SparklesIcon,
|
||||
label: t("navigation.skills"),
|
||||
},
|
||||
{
|
||||
to: "/agents",
|
||||
icon: BotIcon,
|
||||
label: "Agents",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
to: "/mcp",
|
||||
icon: CpuIcon,
|
||||
label: t("navigation.mcp"),
|
||||
id: "system",
|
||||
label: t("navigation.section.system", { defaultValue: "System" }),
|
||||
items: [
|
||||
{
|
||||
to: "/memory",
|
||||
icon: BrainIcon,
|
||||
label: t("navigation.memory"),
|
||||
},
|
||||
{
|
||||
to: "/plugins",
|
||||
icon: PackageIcon,
|
||||
label: t("navigation.plugins"),
|
||||
},
|
||||
{
|
||||
to: "/notification",
|
||||
icon: BellIcon,
|
||||
label: t("navigation.notifications"),
|
||||
},
|
||||
{
|
||||
to: "/usage",
|
||||
icon: ActivityIcon,
|
||||
label: t("navigation.usage"),
|
||||
},
|
||||
{
|
||||
to: "/settings",
|
||||
icon: SettingsIcon,
|
||||
label: t("navigation.settings"),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
to: "/hooks",
|
||||
icon: CpuIcon,
|
||||
label: t("hooks.title"),
|
||||
},
|
||||
{
|
||||
to: "/agents",
|
||||
icon: BotIcon,
|
||||
label: "Agents",
|
||||
},
|
||||
{
|
||||
to: "/memory",
|
||||
icon: BrainIcon,
|
||||
label: t("navigation.memory"),
|
||||
},
|
||||
{
|
||||
to: "/commands",
|
||||
icon: TerminalIcon,
|
||||
label: t("navigation.commands"),
|
||||
},
|
||||
{
|
||||
to: "/skills",
|
||||
icon: SparklesIcon,
|
||||
label: t("navigation.skills"),
|
||||
},
|
||||
{
|
||||
to: "/plugins",
|
||||
icon: PackageIcon,
|
||||
label: t("navigation.plugins"),
|
||||
},
|
||||
{
|
||||
to: "/notification",
|
||||
icon: BellIcon,
|
||||
label: t("navigation.notifications"),
|
||||
},
|
||||
{
|
||||
to: "/usage",
|
||||
icon: ActivityIcon,
|
||||
label: t("navigation.usage"),
|
||||
},
|
||||
{
|
||||
to: "/settings",
|
||||
icon: SettingsIcon,
|
||||
label: t("navigation.settings"),
|
||||
},
|
||||
];
|
||||
] as const;
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-background flex flex-col">
|
||||
@@ -113,37 +131,51 @@ export function Layout() {
|
||||
<div className="flex flex-1 overflow-hidden ">
|
||||
<nav
|
||||
className="w-[200px] bg-background border-r flex flex-col"
|
||||
aria-label={t("navigation.primary", {
|
||||
defaultValue: "Primary navigation",
|
||||
})}
|
||||
data-tauri-drag-region
|
||||
>
|
||||
<MacosDragRegionSpacer className="h-10" />
|
||||
<div
|
||||
className="flex flex-col flex-1 justify-between"
|
||||
data-tauri-drag-region
|
||||
>
|
||||
<ul className="px-3 pt-3 space-y-2">
|
||||
{navLinks.map((link) => (
|
||||
<li key={link.to}>
|
||||
<NavLink
|
||||
to={link.to}
|
||||
className={({ isActive }) =>
|
||||
cn(
|
||||
"flex items-center gap-2 px-3 py-2 rounded-xl cursor-default select-none ",
|
||||
{
|
||||
"bg-primary text-primary-foreground": isActive,
|
||||
"hover:bg-accent hover:text-accent-foreground":
|
||||
!isActive,
|
||||
},
|
||||
)
|
||||
}
|
||||
<div className="flex flex-col flex-1 justify-between">
|
||||
<div className="px-3 pt-3 space-y-4">
|
||||
{navSections.map((section) => (
|
||||
<div key={section.id} className="space-y-2">
|
||||
<p
|
||||
className="px-1 text-[11px] font-medium uppercase tracking-wide text-muted-foreground/80"
|
||||
role="heading"
|
||||
aria-level={2}
|
||||
>
|
||||
<link.icon size={14} />
|
||||
{link.label}
|
||||
</NavLink>
|
||||
</li>
|
||||
{section.label}
|
||||
</p>
|
||||
<ul className="space-y-1">
|
||||
{section.items.map((link) => (
|
||||
<li key={link.to}>
|
||||
<NavLink
|
||||
to={link.to}
|
||||
className={({ isActive }) =>
|
||||
cn(
|
||||
"flex items-center gap-2 px-3 py-2 rounded-xl cursor-default select-none text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-2 focus-visible:ring-offset-background",
|
||||
{
|
||||
"bg-primary text-primary-foreground":
|
||||
isActive,
|
||||
"hover:bg-accent hover:text-accent-foreground":
|
||||
!isActive,
|
||||
},
|
||||
)
|
||||
}
|
||||
>
|
||||
<link.icon size={14} />
|
||||
{link.label}
|
||||
</NavLink>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<div className="space-y-2 px-3 pb-3 pt-4 border-t">
|
||||
<UpdateButton />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
+320
-119
@@ -5,17 +5,29 @@ import { useNavigate } from "react-router-dom";
|
||||
import { GLMDialog } from "@/components/GLMBanner";
|
||||
import { KimiDialog } from "@/components/KimiDialog";
|
||||
import { MiniMaxDialog } from "@/components/MiniMaxDialog";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { ButtonGroup } from "@/components/ui/button-group";
|
||||
import {
|
||||
AlertDialog,
|
||||
AlertDialogAction,
|
||||
AlertDialogCancel,
|
||||
AlertDialogContent,
|
||||
AlertDialogDescription,
|
||||
AlertDialogFooter,
|
||||
AlertDialogHeader,
|
||||
AlertDialogTitle,
|
||||
AlertDialogTrigger,
|
||||
} from "@/components/ui/alert-dialog";
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuTrigger,
|
||||
} from "@/components/ui/dropdown-menu";
|
||||
import { cn } from "@/lib/utils";
|
||||
import {
|
||||
useCreateConfig,
|
||||
useDeleteConfig,
|
||||
useResetToOriginalConfig,
|
||||
useSetCurrentConfig,
|
||||
useStores,
|
||||
@@ -23,11 +35,9 @@ import {
|
||||
|
||||
export function ConfigSwitcherPage() {
|
||||
return (
|
||||
<div className="">
|
||||
<section>
|
||||
<ConfigStores />
|
||||
</section>
|
||||
</div>
|
||||
<section>
|
||||
<ConfigStores />
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -39,6 +49,10 @@ function ConfigStores() {
|
||||
const navigate = useNavigate();
|
||||
|
||||
const isOriginalConfigActive = !stores.some((store) => store.using);
|
||||
const activeStore = stores.find((store) => store.using) ?? null;
|
||||
const otherStores = activeStore
|
||||
? stores.filter((store) => store.id !== activeStore.id)
|
||||
: stores;
|
||||
|
||||
const handleStoreClick = (storeId: string, isCurrentStore: boolean) => {
|
||||
if (!isCurrentStore) {
|
||||
@@ -53,94 +67,106 @@ function ConfigStores() {
|
||||
};
|
||||
|
||||
const createStoreMutation = useCreateConfig();
|
||||
const deleteStoreMutation = useDeleteConfig();
|
||||
|
||||
const onCreateStore = async () => {
|
||||
const store = await createStoreMutation.mutateAsync({
|
||||
title: t("configSwitcher.newConfig"),
|
||||
settings: {},
|
||||
});
|
||||
navigate(`/edit/${store.id}`);
|
||||
if (store) {
|
||||
navigate(`/edit/${store.id}`);
|
||||
}
|
||||
};
|
||||
|
||||
if (stores.length === 0) {
|
||||
return (
|
||||
<div
|
||||
className="flex justify-center items-center h-screen"
|
||||
data-tauri-drag-region
|
||||
>
|
||||
<div className="flex flex-col items-center gap-2">
|
||||
<Button variant="ghost" onClick={onCreateStore} className="">
|
||||
<PlusIcon size={14} />
|
||||
{t("configSwitcher.createConfig")}
|
||||
</Button>
|
||||
|
||||
<p className="text-sm text-muted-foreground" data-tauri-drag-region>
|
||||
<div className="flex h-[calc(100vh-64px)] items-center justify-center px-6">
|
||||
<div className="flex max-w-md flex-col items-center gap-3 text-center">
|
||||
<h1 className="text-xl font-semibold">
|
||||
{t("configSwitcher.emptyTitle", {
|
||||
defaultValue: "No configurations yet",
|
||||
})}
|
||||
</h1>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
{t("configSwitcher.description")}
|
||||
</p>
|
||||
|
||||
<div className="mt-4 space-y-2">
|
||||
<GLMDialog
|
||||
trigger={
|
||||
<ButtonGroup className="mt-2">
|
||||
<Button
|
||||
onClick={onCreateStore}
|
||||
size="sm"
|
||||
disabled={createStoreMutation.isPending}
|
||||
>
|
||||
<PlusIcon size={14} />
|
||||
{t("configSwitcher.createConfig")}
|
||||
</Button>
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button
|
||||
variant="ghost"
|
||||
className="text-muted-foreground text-sm"
|
||||
variant="outline"
|
||||
size="sm"
|
||||
disabled={createStoreMutation.isPending}
|
||||
>
|
||||
<ZAI />
|
||||
{t("glm.useZhipuGlm")}
|
||||
<EllipsisVerticalIcon size={14} />
|
||||
</Button>
|
||||
}
|
||||
/>
|
||||
<MiniMaxDialog
|
||||
trigger={
|
||||
<Button
|
||||
variant="ghost"
|
||||
className="text-muted-foreground text-sm"
|
||||
size="sm"
|
||||
>
|
||||
<Minimax />
|
||||
{t("minimax.useMiniMax")}
|
||||
</Button>
|
||||
}
|
||||
/>
|
||||
<KimiDialog
|
||||
trigger={
|
||||
<Button
|
||||
variant="ghost"
|
||||
className="text-muted-foreground text-sm"
|
||||
size="sm"
|
||||
>
|
||||
<Kimi />
|
||||
{t("kimi.useKimi")}
|
||||
</Button>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end">
|
||||
<GLMDialog
|
||||
trigger={
|
||||
<DropdownMenuItem onSelect={(e) => e.preventDefault()}>
|
||||
<ZAI />
|
||||
{t("glm.useZhipuGlm")}
|
||||
</DropdownMenuItem>
|
||||
}
|
||||
/>
|
||||
<MiniMaxDialog
|
||||
trigger={
|
||||
<DropdownMenuItem onSelect={(e) => e.preventDefault()}>
|
||||
<Minimax />
|
||||
{t("minimax.useMiniMax")}
|
||||
</DropdownMenuItem>
|
||||
}
|
||||
/>
|
||||
<KimiDialog
|
||||
trigger={
|
||||
<DropdownMenuItem onSelect={(e) => e.preventDefault()}>
|
||||
<Kimi />
|
||||
{t("kimi.useKimi")}
|
||||
</DropdownMenuItem>
|
||||
}
|
||||
/>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</ButtonGroup>
|
||||
|
||||
<p className="mt-4 text-xs text-muted-foreground">
|
||||
{t("configSwitcher.emptyHelper", {
|
||||
defaultValue:
|
||||
"Start from a template like Zhipu GLM, MiniMax, or Kimi, or create a config from scratch.",
|
||||
})}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="">
|
||||
<div
|
||||
className="flex items-center p-3 border-b px-3 justify-between sticky top-0 bg-background z-10"
|
||||
data-tauri-drag-region
|
||||
>
|
||||
<div data-tauri-drag-region>
|
||||
<h3 className="font-bold" data-tauri-drag-region>
|
||||
<div className="space-y-6 p-4">
|
||||
<header className="flex items-start justify-between gap-4 border-b pb-3">
|
||||
<div>
|
||||
<h1 className="text-lg font-semibold">
|
||||
{t("configSwitcher.title")}
|
||||
</h3>
|
||||
<p className="text-sm text-muted-foreground" data-tauri-drag-region>
|
||||
</h1>
|
||||
<p className="mt-1 text-sm text-muted-foreground">
|
||||
{t("configSwitcher.description")}
|
||||
</p>
|
||||
</div>
|
||||
<ButtonGroup>
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={onCreateStore}
|
||||
className="text-muted-foreground"
|
||||
size="sm"
|
||||
disabled={createStoreMutation.isPending}
|
||||
>
|
||||
<PlusIcon size={14} />
|
||||
{t("configSwitcher.createConfig")}
|
||||
@@ -149,8 +175,8 @@ function ConfigStores() {
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button
|
||||
variant="outline"
|
||||
className="text-muted-foreground"
|
||||
size="sm"
|
||||
disabled={createStoreMutation.isPending}
|
||||
>
|
||||
<EllipsisVerticalIcon size={14} />
|
||||
</Button>
|
||||
@@ -183,71 +209,246 @@ function ConfigStores() {
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</ButtonGroup>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
{/* <GLMBanner className="mx-4 mt-4" /> */}
|
||||
|
||||
<div className="grid grid-cols-3 lg:grid-cols-4 gap-3 p-4">
|
||||
{/* Fixed Claude Original Config Item */}
|
||||
<div
|
||||
role="button"
|
||||
onClick={handleOriginalConfigClick}
|
||||
className={cn(
|
||||
"border rounded-xl p-3 h-[100px] flex flex-col justify-between transition-colors",
|
||||
{
|
||||
"bg-primary/10 border-primary border-2": isOriginalConfigActive,
|
||||
},
|
||||
)}
|
||||
>
|
||||
<div>
|
||||
<div>{t("configSwitcher.originalConfig")}</div>
|
||||
<div className="text-xs text-muted-foreground mt-1">
|
||||
{t("configSwitcher.originalConfigDescription")}
|
||||
</div>
|
||||
<section className="space-y-3">
|
||||
<div className="flex items-center justify-between">
|
||||
<h2 className="text-sm font-medium">
|
||||
{t("configSwitcher.activeConfigHeading", {
|
||||
defaultValue: "Active configuration",
|
||||
})}
|
||||
</h2>
|
||||
<div className="flex items-center gap-2">
|
||||
<Badge variant="success">
|
||||
{t("configSwitcher.activeBadge", { defaultValue: "Active" })}
|
||||
</Badge>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
className="h-7 px-2 text-xs text-muted-foreground hover:text-foreground"
|
||||
onClick={() => navigate("/settings")}
|
||||
>
|
||||
{t("configSwitcher.viewHistory", {
|
||||
defaultValue: "Backups & history",
|
||||
})}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{stores.map((store) => {
|
||||
const isCurrentStore = store.using;
|
||||
return (
|
||||
<div
|
||||
role="button"
|
||||
key={store.id}
|
||||
onClick={() => handleStoreClick(store.id, isCurrentStore)}
|
||||
className={cn(
|
||||
"border rounded-xl p-3 h-[100px] flex flex-col justify-between transition-colors disabled:opacity-50",
|
||||
{
|
||||
"bg-primary/10 border-primary border-2": isCurrentStore,
|
||||
},
|
||||
)}
|
||||
>
|
||||
<div>
|
||||
<div>{store.title}</div>
|
||||
{store.settings.env?.ANTHROPIC_BASE_URL && (
|
||||
<div
|
||||
className="text-xs text-muted-foreground mt-1 truncate "
|
||||
title={store.settings.env.ANTHROPIC_BASE_URL}
|
||||
<div className="rounded-xl border bg-card p-4">
|
||||
{activeStore ? (
|
||||
<div className="flex flex-col gap-3 md:flex-row md:items-center md:justify-between">
|
||||
<div className="space-y-1">
|
||||
<div className="flex items-center gap-2">
|
||||
<h3 className="font-medium">{activeStore.title}</h3>
|
||||
<Badge
|
||||
variant="secondary"
|
||||
className="text-[11px] font-medium uppercase"
|
||||
>
|
||||
{store.settings.env.ANTHROPIC_BASE_URL}
|
||||
</div>
|
||||
{t("configSwitcher.tagDailyDriver", {
|
||||
defaultValue: "Daily driver",
|
||||
})}
|
||||
</Badge>
|
||||
</div>
|
||||
{activeStore.settings.env?.ANTHROPIC_BASE_URL && (
|
||||
<p className="text-xs text-muted-foreground">
|
||||
{activeStore.settings.env.ANTHROPIC_BASE_URL}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="flex justify-end">
|
||||
<button
|
||||
className="hover:bg-primary/10 rounded-lg p-2 hover:text-primary"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
navigate(`/edit/${store.id}`);
|
||||
}}
|
||||
<div className="flex gap-2">
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => navigate(`/edit/${activeStore.id}`)}
|
||||
>
|
||||
<PencilLineIcon className="text-muted-foreground" size={14} />
|
||||
</button>
|
||||
<PencilLineIcon size={14} className="mr-1" />
|
||||
{t("configSwitcher.edit", { defaultValue: "Edit" })}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex flex-col gap-2 md:flex-row md:items-center md:justify-between">
|
||||
<div className="space-y-1">
|
||||
<div className="flex items-center gap-2">
|
||||
<h3 className="font-medium">
|
||||
{t("configSwitcher.originalConfig", {
|
||||
defaultValue: "Original Claude config",
|
||||
})}
|
||||
</h3>
|
||||
<Badge
|
||||
variant="secondary"
|
||||
className="text-[11px] font-medium uppercase"
|
||||
>
|
||||
{t("configSwitcher.tagDefault", {
|
||||
defaultValue: "Default",
|
||||
})}
|
||||
</Badge>
|
||||
</div>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
{t("configSwitcher.originalConfigDescription")}
|
||||
</p>
|
||||
</div>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={handleOriginalConfigClick}
|
||||
disabled={
|
||||
isOriginalConfigActive || resetToOriginalMutation.isPending
|
||||
}
|
||||
>
|
||||
{t("configSwitcher.restoreOriginal", {
|
||||
defaultValue: "Restore original",
|
||||
})}
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="space-y-3">
|
||||
<div className="flex items-center justify-between">
|
||||
<h2 className="text-sm font-medium">
|
||||
{t("configSwitcher.otherConfigsHeading", {
|
||||
defaultValue: "Other configurations",
|
||||
})}
|
||||
</h2>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
{t("configSwitcher.otherConfigsHelper", {
|
||||
defaultValue:
|
||||
"Use these for experiments, different providers, or project-specific setups.",
|
||||
})}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{otherStores.length === 0 ? (
|
||||
<p className="text-sm text-muted-foreground">
|
||||
{t("configSwitcher.noOtherConfigs", {
|
||||
defaultValue: "You only have the active configuration right now.",
|
||||
})}
|
||||
</p>
|
||||
) : (
|
||||
<div className="grid gap-3 md:grid-cols-2 lg:grid-cols-3">
|
||||
{otherStores.map((store) => (
|
||||
<div
|
||||
key={store.id}
|
||||
className="flex flex-col justify-between rounded-xl border bg-card p-3 transition-colors hover:border-primary/60"
|
||||
>
|
||||
<div className="space-y-1">
|
||||
<div className="flex items-center justify-between gap-2">
|
||||
<h3 className="truncate text-sm font-medium">
|
||||
{store.title}
|
||||
</h3>
|
||||
</div>
|
||||
{store.settings.env?.ANTHROPIC_BASE_URL && (
|
||||
<p
|
||||
className="truncate text-xs text-muted-foreground"
|
||||
title={store.settings.env.ANTHROPIC_BASE_URL}
|
||||
>
|
||||
{store.settings.env.ANTHROPIC_BASE_URL}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="mt-3 flex items-center justify-between gap-2">
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
className="h-8 px-2 text-xs"
|
||||
onClick={() => navigate(`/edit/${store.id}`)}
|
||||
>
|
||||
<PencilLineIcon size={12} className="mr-1" />
|
||||
{t("configSwitcher.edit", { defaultValue: "Edit" })}
|
||||
</Button>
|
||||
<div className="flex gap-1">
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
className="h-8 px-2 text-xs"
|
||||
onClick={() =>
|
||||
handleStoreClick(store.id, store.using)
|
||||
}
|
||||
disabled={setCurrentStoreMutation.isPending}
|
||||
>
|
||||
{t("configSwitcher.activate", {
|
||||
defaultValue: "Activate",
|
||||
})}
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
className="h-8 px-2 text-xs"
|
||||
onClick={() =>
|
||||
createStoreMutation.mutate({
|
||||
title: `${store.title} (copy)`,
|
||||
settings: store.settings,
|
||||
})
|
||||
}
|
||||
disabled={createStoreMutation.isPending}
|
||||
>
|
||||
{t("configSwitcher.duplicate", {
|
||||
defaultValue: "Duplicate",
|
||||
})}
|
||||
</Button>
|
||||
<AlertDialog>
|
||||
<AlertDialogTrigger asChild>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
className="h-8 px-2 text-xs"
|
||||
disabled={deleteStoreMutation.isPending}
|
||||
>
|
||||
{t("configSwitcher.delete", {
|
||||
defaultValue: "Delete",
|
||||
})}
|
||||
</Button>
|
||||
</AlertDialogTrigger>
|
||||
<AlertDialogContent>
|
||||
<AlertDialogHeader>
|
||||
<AlertDialogTitle>
|
||||
{t("configSwitcher.deleteConfirmTitle", {
|
||||
defaultValue: "Delete configuration?",
|
||||
})}
|
||||
</AlertDialogTitle>
|
||||
<AlertDialogDescription>
|
||||
{t(
|
||||
"configSwitcher.deleteConfirmDescription",
|
||||
{
|
||||
defaultValue:
|
||||
"This will permanently remove this configuration from Claude Samurai. This action cannot be undone.",
|
||||
},
|
||||
)}
|
||||
</AlertDialogDescription>
|
||||
</AlertDialogHeader>
|
||||
<AlertDialogFooter>
|
||||
<AlertDialogCancel disabled={deleteStoreMutation.isPending}>
|
||||
{t("common.cancel", {
|
||||
defaultValue: "Cancel",
|
||||
})}
|
||||
</AlertDialogCancel>
|
||||
<AlertDialogAction
|
||||
onClick={() =>
|
||||
deleteStoreMutation.mutate({
|
||||
storeId: store.id,
|
||||
})
|
||||
}
|
||||
className="bg-destructive text-destructive-foreground hover:bg-destructive/90"
|
||||
disabled={deleteStoreMutation.isPending}
|
||||
>
|
||||
{t("configSwitcher.deleteConfirmAction", {
|
||||
defaultValue: "Delete",
|
||||
})}
|
||||
</AlertDialogAction>
|
||||
</AlertDialogFooter>
|
||||
</AlertDialogContent>
|
||||
</AlertDialog>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user