diff --git a/client/src/pages/Admin.tsx b/client/src/pages/Admin.tsx index 8bd5873..5772cd8 100644 --- a/client/src/pages/Admin.tsx +++ b/client/src/pages/Admin.tsx @@ -119,13 +119,244 @@ function LoginForm({ onLogin }: { onLogin: (token: string) => void }) { ); } -// ─── Main Dashboard ─────────────────────────────────────────────────────────── +// ─── Main D// ─── Settings Panel ─────────────────────────────────────────────── +function SettingsPanel({ token }: { token: string }) { + const { data: configData, refetch: refetchConfig, isLoading } = trpc.admin.getConfig.useQuery({ token }); + const [editValues, setEditValues] = useState>({}); + const [savingKey, setSavingKey] = useState(null); + const [savedKeys, setSavedKeys] = useState>(new Set()); + const [telegramBotToken, setTelegramBotToken] = useState(""); + const [telegramChatId, setTelegramChatId] = useState(""); + const [telegramStatus, setTelegramStatus] = useState<"idle" | "testing" | "success" | "error">("idle"); + const [telegramError, setTelegramError] = useState(""); + + const setConfigMutation = trpc.admin.setConfig.useMutation({ + onSuccess: (_, vars) => { + setSavedKeys(prev => { const s = new Set(Array.from(prev)); s.add(vars.key); return s; }); + setSavingKey(null); + refetchConfig(); + setTimeout(() => setSavedKeys(prev => { const n = new Set(Array.from(prev)); n.delete(vars.key); return n; }), 2000); + }, + onError: (err) => { + setSavingKey(null); + alert(`Save failed: ${err.message}`); + }, + }); + + const testTelegramMutation = trpc.admin.testTelegram.useMutation({ + onSuccess: () => { + setTelegramStatus("success"); + refetchConfig(); + }, + onError: (err) => { + setTelegramStatus("error"); + setTelegramError(err.message); + }, + }); + + // Initialize edit values from config + useEffect(() => { + if (configData) { + const vals: Record = {}; + configData.forEach(c => { vals[c.key] = c.value; }); + setEditValues(vals); + // Pre-fill Telegram fields + const botToken = configData.find(c => c.key === "telegramBotToken")?.value || ""; + const chatId = configData.find(c => c.key === "telegramChatId")?.value || ""; + if (botToken) setTelegramBotToken(botToken); + if (chatId) setTelegramChatId(chatId); + } + }, [configData]); + + const handleSave = (key: string) => { + setSavingKey(key); + setConfigMutation.mutate({ token, key, value: editValues[key] || "" }); + }; + + const handleTestTelegram = () => { + if (!telegramBotToken || !telegramChatId) { + setTelegramStatus("error"); + setTelegramError("Please enter both Bot Token and Chat ID"); + return; + } + setTelegramStatus("testing"); + setTelegramError(""); + testTelegramMutation.mutate({ token, botToken: telegramBotToken, chatId: telegramChatId }); + }; + + // Group configs by category + const presaleKeys = ["presaleEndDate", "tokenPrice", "hardCap", "listingPrice", "totalSupply", "maxPurchaseUsdt", "presaleStatus"]; + const contentKeys = ["heroTitle", "heroSubtitle", "tronReceivingAddress"]; + const telegramKeys = ["telegramBotToken", "telegramChatId"]; + + const renderConfigRow = (cfg: { key: string; value: string; label: string; description: string; type: string; updatedAt: Date | null }) => ( +
+
+
+
+ {cfg.label} + {cfg.type} +
+

{cfg.description}

+ {cfg.type === "text" && cfg.key !== "heroSubtitle" ? ( + setEditValues(prev => ({ ...prev, [cfg.key]: e.target.value }))} + className="w-full px-3 py-2 rounded-lg text-sm" + style={{ background: "rgba(255,255,255,0.05)", border: "1px solid rgba(255,255,255,0.1)", color: "white", outline: "none" }} + /> + ) : cfg.key === "heroSubtitle" ? ( +