import { useState, useEffect, useCallback, useRef } from "react"; import { useTranslation } from "react-i18next"; import { copyToClipboard } from "@/lib/clipboard"; import { useConfirmation } from "@/hooks/use-confirmation.ts"; import { getSnippets, createSnippet as apiCreateSnippet, updateSnippet as apiUpdateSnippet, deleteSnippet as apiDeleteSnippet, getSnippetFolders, createSnippetFolder as apiCreateSnippetFolder, deleteSnippetFolder as apiDeleteSnippetFolder, renameSnippetFolder as apiRenameSnippetFolder, updateSnippetFolderMetadata as apiUpdateSnippetFolderMetadata, shareSnippet as apiShareSnippet, getSnippetAccess, revokeSnippetAccess, getUserList, getRoles, reorderSnippets, } from "@/main-axios"; import { exportSnippets, importSnippets, type SnippetExportData, executeSnippet as apiExecuteSnippet, } from "@/api/snippets-api"; import { getSSHHosts } from "@/api/ssh-host-management-api"; import type { SSHHost } from "@/types/index"; import { Button } from "@/components/button"; import { Input } from "@/components/input"; import { Separator } from "@/components/separator"; import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, } from "@/components/dialog"; import { Box, ChevronDown, Copy, Cpu, Database, Download, Folder, Globe, GripVertical, Network, Pencil, Play, Plus, Search, Server, Settings, Share2, Terminal, Trash2, Upload, UserPlus, X, MoreHorizontal, Zap, } from "lucide-react"; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, } from "@/components/dropdown-menu"; import { toast } from "sonner"; import { FOLDER_COLORS } from "@/lib/theme"; import { FOLDER_ICONS } from "@/types/ui-types"; import type { Snippet, SnippetFolder, FolderIconId, Tab, } from "@/types/ui-types"; function FolderIconEl({ icon, className, style, }: { icon: FolderIconId; className?: string; style?: React.CSSProperties; }) { const props = { className, style }; switch (icon) { case "folder": return ; case "server": return ; case "cloud": return (
); case "database": return ; case "box": return ; case "network": return ; case "copy": return ; case "settings": return ; case "cpu": return ; case "globe": return ; } } function SnippetFormDialog({ open, onOpenChange, folders, snippet, onSave, availableHosts, }: { open: boolean; onOpenChange: (v: boolean) => void; folders: SnippetFolder[]; snippet: Snippet | null; onSave: (data: Omit, id?: number) => void; availableHosts: SSHHost[]; }) { const { t } = useTranslation(); const [name, setName] = useState(""); const [description, setDescription] = useState(""); const [folder, setFolder] = useState(null); const [content, setContent] = useState(""); const [selectedHostIds, setSelectedHostIds] = useState>( new Set(), ); useEffect(() => { if (open) { setName(snippet?.name ?? ""); setDescription(snippet?.description ?? ""); setFolder(snippet?.folder ?? null); setContent(snippet?.content ?? ""); setSelectedHostIds(new Set(snippet?.hostIds ?? [])); } }, [open, snippet]); function toggleHost(id: number) { setSelectedHostIds((prev) => { const next = new Set(prev); if (next.has(id)) { next.delete(id); } else { next.add(id); } return next; }); } function handleSave() { if (!name.trim() || !content.trim()) return; onSave( { name: name.trim(), description: description.trim() || undefined, content: content.trim(), folder, hostIds: selectedHostIds.size > 0 ? Array.from(selectedHostIds) : undefined, }, snippet?.id, ); onOpenChange(false); } const isEdit = snippet !== null; return ( {isEdit ? t("newUi.sidebar.snippets.editSnippetTitle") : t("newUi.sidebar.snippets.createSnippetTitle")} {isEdit ? t("newUi.sidebar.snippets.editSnippetDescription") : t("newUi.sidebar.snippets.createSnippetDescription")}
setName(e.target.value)} />
setDescription(e.target.value)} />