diff --git a/src/app/(dashboard)/dashboard/settings/components/ProxyRegistryManager.tsx b/src/app/(dashboard)/dashboard/settings/components/ProxyRegistryManager.tsx index b87aa200d8..091875676e 100644 --- a/src/app/(dashboard)/dashboard/settings/components/ProxyRegistryManager.tsx +++ b/src/app/(dashboard)/dashboard/settings/components/ProxyRegistryManager.tsx @@ -1,6 +1,6 @@ "use client"; -import { useCallback, useEffect, useMemo, useState } from "react"; +import { useCallback, useEffect, useMemo, useRef, useState } from "react"; import { useTranslations } from "next-intl"; import { z } from "zod"; import { Button, Card, Modal } from "@/shared/components"; @@ -52,7 +52,7 @@ const EMPTY_FORM = { family: "auto", }; -const BULK_IMPORT_TEMPLATE = `# Proxy Bulk Import +const BULK_IMPORT_PLACEHOLDER = `# Proxy Bulk Import # ───────────────────────────────────────────────────────────────────────────── # FORMAT 1 — Pipe-delimited (full control): # NAME|HOST|PORT|USERNAME|PASSWORD|TYPE|REGION|STATUS|NOTES @@ -96,10 +96,23 @@ const BULK_IMPORT_TEMPLATE = `# Proxy Bulk Import export default function ProxyRegistryManager({ onRedeployRelay, + showVercelRelay = false, + showDenoRelay = false, + showCloudflareRelay = false, + onOpenVercelRelay, + onOpenDenoRelay, + onOpenCloudflareRelay, }: { onRedeployRelay?: (proxy: ProxyItem) => void; + showVercelRelay?: boolean; + showDenoRelay?: boolean; + showCloudflareRelay?: boolean; + onOpenVercelRelay?: () => void; + onOpenDenoRelay?: () => void; + onOpenCloudflareRelay?: () => void; } = {}) { const t = useTranslations("proxyRegistry"); + const settingsT = useTranslations("settings"); const [items, setItems] = useState([]); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); @@ -135,7 +148,7 @@ export default function ProxyRegistryManager({ const [poolLoaded, setPoolLoaded] = useState(false); const [poolSaving, setPoolSaving] = useState(false); const [bulkImportOpen, setBulkImportOpen] = useState(false); - const [bulkImportText, setBulkImportText] = useState(BULK_IMPORT_TEMPLATE); + const [bulkImportText, setBulkImportText] = useState(""); const [bulkImportParsed, setBulkImportParsed] = useState([]); const [bulkImportErrors, setBulkImportErrors] = useState([]); const [bulkImportSkipped, setBulkImportSkipped] = useState(0); @@ -146,6 +159,32 @@ export default function ProxyRegistryManager({ updated: number; failed: number; } | null>(null); + const [actionsOpen, setActionsOpen] = useState(false); + const [relayMenuOpen, setRelayMenuOpen] = useState(false); + const actionsRef = useRef(null); + const relayRef = useRef(null); + + const showAnyRelay = showVercelRelay || showDenoRelay || showCloudflareRelay; + + useEffect(() => { + if (!actionsOpen && !relayMenuOpen) return; + const onMouseDown = (event: MouseEvent) => { + const target = event.target as Node; + if (actionsOpen && actionsRef.current && !actionsRef.current.contains(target)) { + setActionsOpen(false); + } + if (relayMenuOpen && relayRef.current && !relayRef.current.contains(target)) { + setRelayMenuOpen(false); + } + }; + document.addEventListener("mousedown", onMouseDown); + return () => document.removeEventListener("mousedown", onMouseDown); + }, [actionsOpen, relayMenuOpen]); + + const closeActions = () => { + setActionsOpen(false); + setRelayMenuOpen(false); + }; const editingId = useMemo(() => form.id || "", [form.id]); @@ -724,7 +763,7 @@ export default function ProxyRegistryManager({ }; const openBulkImport = () => { - setBulkImportText(BULK_IMPORT_TEMPLATE); + setBulkImportText(""); setBulkImportParsed([]); setBulkImportErrors([]); setBulkImportSkipped(0); @@ -736,49 +775,13 @@ export default function ProxyRegistryManager({ return ( <> -
-
+
+

{t("title")}

{t("description")}

-
- - - - +