import { useState, useEffect, FormEvent } from "react"; import { NextPage } from "next"; import NextLink from "next/link"; import { Box, VStack, HStack, Heading, Text, Input, Button, Switch, FormControl, FormLabel, FormHelperText, Divider, Alert, AlertIcon, InputGroup, InputRightElement, IconButton, Badge, Link, useColorModeValue, Spinner, Textarea } from "@chakra-ui/react"; import { FiEye, FiEyeOff, FiSave, FiLogOut, FiArrowLeft } from "react-icons/fi"; import { CustomHead } from "@components"; type Settings = { replicateApiToken: string; jigsawApiKey: string; modelVersion: string; replicateEnabled: boolean; }; const DEFAULT_MODEL = "jigsawstack/text-translate:454df4c49941c05dea05175bd37686d0872c73c1f9366d1c2505db32ade52a89"; const AdminPage: NextPage = () => { const [authed, setAuthed] = useState(null); const [password, setPassword] = useState(""); const [loginError, setLoginError] = useState(""); const [loginLoading, setLoginLoading] = useState(false); const [settings, setSettings] = useState({ replicateApiToken: "", jigsawApiKey: "", modelVersion: DEFAULT_MODEL, replicateEnabled: false }); const [newPassword, setNewPassword] = useState(""); const [saveMsg, setSaveMsg] = useState<{ type: "success" | "error"; text: string } | null>(null); const [saving, setSaving] = useState(false); const [showToken, setShowToken] = useState(false); const [showJigsawKey, setShowJigsawKey] = useState(false); const [testResult, setTestResult] = useState(null); const [testLoading, setTestLoading] = useState(false); const cardBg = useColorModeValue("white", "gray.800"); const borderCol = useColorModeValue("gray.200", "gray.600"); // Check auth on load useEffect(() => { fetch("/api/admin/auth") .then(r => r.json()) .then(d => setAuthed(d.authenticated)) .catch(() => setAuthed(false)); }, []); // Load settings when authed useEffect(() => { if (!authed) return; fetch("/api/admin/settings") .then(r => r.json()) .then(d => setSettings(s => ({ ...s, ...d }))) .catch(() => {}); }, [authed]); const login = async (e: FormEvent) => { e.preventDefault(); setLoginLoading(true); setLoginError(""); try { const res = await fetch("/api/admin/auth", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ password }) }); if (res.ok) { setAuthed(true); } else { const d = await res.json(); setLoginError(d.error ?? "Login failed"); } } catch { setLoginError("Network error"); } finally { setLoginLoading(false); } }; const logout = async () => { await fetch("/api/admin/auth", { method: "DELETE" }); setAuthed(false); setPassword(""); }; const save = async (e: FormEvent) => { e.preventDefault(); setSaving(true); setSaveMsg(null); try { const body: Record = { ...settings }; if (newPassword.length >= 6) body.newPassword = newPassword; const res = await fetch("/api/admin/settings", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(body) }); const d = await res.json(); if (res.ok) { setSettings(s => ({ ...s, ...d })); setSaveMsg({ type: "success", text: "Settings saved successfully" }); setNewPassword(""); } else { setSaveMsg({ type: "error", text: d.error ?? "Save failed" }); } } catch { setSaveMsg({ type: "error", text: "Network error" }); } finally { setSaving(false); } }; const testTranslation = async () => { setTestLoading(true); setTestResult(null); try { const res = await fetch("/api/translate/replicate", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ text: "Hello, world!", targetLanguage: "es" }) }); const d = await res.json(); if (res.ok) { setTestResult(`✓ Success: "${d.translation}"`); } else { setTestResult(`✗ Error: ${d.error}`); } } catch { setTestResult("✗ Network error"); } finally { setTestLoading(false); } }; // Loading state if (authed === null) { return ( ); } // Login form if (!authed) { return ( <> Admin Login {loginError && ( {loginError} )} Password setPassword(e.target.value)} autoFocus placeholder="Admin password" /> Default: admin (set ADMIN_PASSWORD env var) ← Back to translator ); } // Admin panel return ( <> Admin Settings {/* Replicate Section */} Replicate AI Translation {settings.replicateEnabled ? "Enabled" : "Disabled"} Enable Replicate Translation setSettings(s => ({ ...s, replicateEnabled: e.target.checked }))} /> Replicate API Token setSettings(s => ({ ...s, replicateApiToken: e.target.value }))} placeholder="r8_..." fontFamily="mono" fontSize="sm" /> : } onClick={() => setShowToken(v => !v)} /> Get your token at{" "} replicate.com/account/api-tokens JigsawStack API Key setSettings(s => ({ ...s, jigsawApiKey: e.target.value }))} placeholder="sk_..." fontFamily="mono" fontSize="sm" /> : } onClick={() => setShowJigsawKey(v => !v)} /> Required for the JigsawStack translation model. Get yours at{" "} jigsawstack.com Model Version