import { useState } from "react"; import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"; import { Shield, Users, Image as ImageIcon, Key, Trash2, Crown, CrownIcon, Settings, RefreshCw, Eye, EyeOff, ChevronDown, ChevronUp, AlertCircle, CheckCircle, Lock, Zap, Bookmark, Clock, Terminal, Copy, Check, Database, Plus, Pencil, X, ToggleLeft, ToggleRight, UserPlus, Activity, } from "lucide-react"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Badge } from "@/components/ui/badge"; import { useToast } from "@/hooks/use-toast"; import { useLang } from "@/contexts/LanguageContext"; import { useAuth } from "@/contexts/AuthContext"; import { useLocation } from "wouter"; const BASE = import.meta.env.BASE_URL.replace(/\/$/, ""); const API_BASE = `${BASE}/api/admin`; interface Stats { users: number; images: number; apiKeys: number } interface AdminUser { id: number; email: string; displayName: string | null; isAdmin: boolean; createdAt: string; } interface ConfigRow { id: number; key: string; value: string; updatedAt: string } async function fetchStats(): Promise { const r = await fetch(`${API_BASE}/stats`, { credentials: "include" }); if (!r.ok) throw new Error("Forbidden"); return r.json(); } async function fetchUsers(): Promise { const r = await fetch(`${API_BASE}/users`, { credentials: "include" }); if (!r.ok) throw new Error("Forbidden"); return (await r.json()).users; } async function fetchConfig(): Promise { const r = await fetch(`${API_BASE}/config`, { credentials: "include" }); if (!r.ok) throw new Error("Forbidden"); return (await r.json()).config; } function StatCard({ icon: Icon, label, value, color }: { icon: any; label: string; value: number; color: string }) { return (
{value.toLocaleString()}
{label}
); } function UserRow({ user, currentUserId, onUpdate, onDelete }: { user: AdminUser; currentUserId: number; onUpdate: (id: number, data: Record) => Promise; onDelete: (id: number) => Promise; }) { const { toast } = useToast(); const { t } = useLang(); const [expanded, setExpanded] = useState(false); const [newPass, setNewPass] = useState(""); const [showPass, setShowPass] = useState(false); const [busy, setBusy] = useState(false); const isSelf = user.id === currentUserId; const handleToggleAdmin = async () => { if (isSelf) return; setBusy(true); try { await onUpdate(user.id, { isAdmin: !user.isAdmin }); toast({ description: t.adminUserUpdated }); } catch { toast({ variant: "destructive", description: t.adminError }); } setBusy(false); }; const handleResetPass = async () => { if (!newPass || newPass.length < 6) { toast({ variant: "destructive", description: t.adminPassTooShort }); return; } setBusy(true); try { await onUpdate(user.id, { password: newPass }); setNewPass(""); toast({ description: t.adminPassReset }); } catch { toast({ variant: "destructive", description: t.adminError }); } setBusy(false); }; const handleDelete = async () => { if (!confirm(t.adminDeleteConfirm)) return; setBusy(true); try { await onDelete(user.id); toast({ description: t.adminUserDeleted }); } catch { toast({ variant: "destructive", description: t.adminError }); } setBusy(false); }; return (
{(user.displayName || user.email)[0].toUpperCase()}
{user.displayName || user.email.split("@")[0]} {user.isAdmin && } {isSelf && {t.adminYou}}
{user.email}
{new Date(user.createdAt).toLocaleDateString()}
{expanded && (
{!isSelf && ( )} {!isSelf && ( )}
setNewPass(e.target.value)} placeholder={t.adminNewPassPlaceholder} className="bg-background/50 border-border/60 pr-9 text-sm" />
)}
); } const BASE_URL = import.meta.env.BASE_URL.replace(/\/$/, ""); function TokenGuide() { const { t } = useLang(); const [open, setOpen] = useState(false); return (

πŸ“– {t.adminGuideTitle}

{t.adminGuideSubtitle}

{open && (
    {(t.adminGuideSteps as string[]).map((step, i) => (
  1. {i + 1}
  2. ))}
Token guide screenshot

{t.adminGuideNote}

)}
); } type AccountRow = { id: number; label: string; tokenPreview: string | null; hasRefreshToken: boolean; isActive: boolean; lastUsedAt: string | null; createdAt: string; }; type AddMode = "console" | "bookmark" | "manual"; function AccountPoolCard() { const { toast } = useToast(); const { t } = useLang(); const BASE_URL = import.meta.env.BASE_URL.replace(/\/$/, ""); // Account list state const [editingId, setEditingId] = useState(null); const [editLabel, setEditLabel] = useState(""); // "Add account" panel state const [addMode, setAddMode] = useState(null); // Manual add state const [newLabel, setNewLabel] = useState(""); const [newBearer, setNewBearer] = useState(""); const [newRefresh, setNewRefresh] = useState(""); const [adding, setAdding] = useState(false); // Console / bookmarklet sync state const [syncLabel, setSyncLabel] = useState(""); const [otp, setOtp] = useState(null); const [expiresAt, setExpiresAt] = useState(null); const [generating, setGenerating] = useState(false); const [copied, setCopied] = useState(false); const [bookmarkMode, setBookmarkMode] = useState<"console" | "bookmark">("console"); const receiveUrl = `${window.location.origin}${BASE_URL}/api/public/receive-tokens`; const { data: accounts = [], refetch } = useQuery({ queryKey: ["gemini-accounts"], queryFn: async () => { const r = await fetch(`${BASE_URL}/api/admin/accounts`, { credentials: "include" }); if (!r.ok) return []; return r.json(); }, }); // ── Console / bookmarklet helpers ────────────────────────────── const buildCode = (otpVal: string, label: string) => { const safeLabel = (label || t.poolUnnamed).replace(/'/g, "\\'"); return `(function(){var t=null;for(var k in localStorage){try{var v=JSON.parse(localStorage.getItem(k)||'');if(v&&v.access_token&&v.refresh_token){t=v;break;}}catch(e){}}if(!t){var at=localStorage.getItem('access_token'),rt=localStorage.getItem('refresh_token');if(at&&rt)t={access_token:at,refresh_token:rt};}if(!t){alert('ζ‰ΎδΈεˆ° TokenοΌŒθ«‹ε…ˆη™»ε…₯ geminigen.ai');return;}fetch('${receiveUrl}',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({otp:'${otpVal}',access_token:t.access_token,refresh_token:t.refresh_token,label:'${safeLabel}'})}).then(function(r){return r.json()}).then(function(d){if(d.success)alert('βœ… Token 已成功同ζ­₯εˆ°ζ˜Ÿε…‰ε·₯坊!');else alert('❌ 同ζ­₯ε€±ζ•—οΌš'+(d.error||'ζœͺηŸ₯錯θͺ€'));}).catch(function(){alert('❌ 焑法連ζŽ₯ζ˜Ÿε…‰ε·₯坊');});})();`; }; const buildBookmarklet = (otpVal: string, label: string) => `javascript:${encodeURIComponent(buildCode(otpVal, label))}`; const handleGenerateOtp = async () => { setGenerating(true); setCopied(false); try { const r = await fetch(`${BASE_URL}/api/admin/bookmarklet-otp`, { method: "POST", credentials: "include" }); const data = await r.json(); if (!r.ok) throw new Error(data.error || "Failed"); setOtp(data.otp); setExpiresAt(Date.now() + data.expiresInSeconds * 1000); } catch (e: any) { toast({ variant: "destructive", description: e.message || t.poolGenFailed }); } setGenerating(false); }; const handleCopyCode = async () => { if (!otp) return; try { await navigator.clipboard.writeText(buildCode(otp, syncLabel)); setCopied(true); toast({ description: t.poolCopySuccess }); setTimeout(() => setCopied(false), 3000); } catch { toast({ variant: "destructive", description: t.poolCopyFailed }); } }; // ── Account CRUD ─────────────────────────────────────────────── const handleAdd = async () => { if (!newBearer.trim()) return; setAdding(true); try { const r = await fetch(`${BASE_URL}/api/admin/accounts`, { method: "POST", credentials: "include", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ label: newLabel || t.poolUnnamed, bearerToken: newBearer.trim(), refreshToken: newRefresh.trim() || undefined }), }); if (!r.ok) throw new Error((await r.json()).error || "Failed"); toast({ description: t.poolAddSuccess }); setNewLabel(""); setNewBearer(""); setNewRefresh(""); setAddMode(null); refetch(); } catch (e: any) { toast({ variant: "destructive", description: e.message }); } setAdding(false); }; const handleToggle = async (id: number) => { await fetch(`${BASE_URL}/api/admin/accounts/${id}/toggle`, { method: "PATCH", credentials: "include" }); refetch(); }; const handleDelete = async (id: number) => { await fetch(`${BASE_URL}/api/admin/accounts/${id}`, { method: "DELETE", credentials: "include" }); toast({ description: t.poolDeleteSuccess }); refetch(); }; const handleRename = async (id: number) => { if (!editLabel.trim()) return; await fetch(`${BASE_URL}/api/admin/accounts/${id}/label`, { method: "PATCH", credentials: "include", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ label: editLabel }), }); setEditingId(null); refetch(); }; const activeCount = accounts.filter((a) => a.isActive).length; const isExpired = expiresAt !== null && Date.now() > expiresAt; const minutesLeft = expiresAt ? Math.max(0, Math.floor((expiresAt - Date.now()) / 60000)) : 0; return (
{/* ── Header ── */}

{t.poolTitle} {activeCount}/{accounts.length}

{t.poolDesc}

{/* ── Account list ── */} {accounts.length === 0 ? (

{t.poolEmpty}

{t.poolEmptyDesc}

) : (
{accounts.map((acc) => (
{editingId === acc.id ? (
setEditLabel(e.target.value)} onKeyDown={(e) => { if (e.key === "Enter") handleRename(acc.id); if (e.key === "Escape") setEditingId(null); }} className="flex-1 px-2 py-0.5 rounded border border-blue-500/40 bg-black/30 text-sm text-foreground focus:outline-none" autoFocus />
) : (
{acc.label || t.poolUnnamed}
)}
{acc.tokenPreview || "β€”"} {acc.hasRefreshToken && {t.poolHasRefresh}}
{acc.lastUsedAt ? (
{new Date(acc.lastUsedAt).toLocaleString("zh-TW", { month: "numeric", day: "numeric", hour: "2-digit", minute: "2-digit" })}
) : {t.poolNeverUsed}}
))}

{t.poolLRUNote}

)} {/* ── Add account section ── */}
{/* Mode picker */}

{t.poolAddTitle}

{/* Console sync */} {/* Bookmarklet */} {/* Manual */}
{/* ── Console sync panel ── */} {(addMode === "console" || addMode === "bookmark") && (
{/* Sub-mode toggle (console vs bookmark) */}
{/* Account label */}
setSyncLabel(e.target.value)} placeholder={t.poolAccountPlaceholder} className="w-full px-3 py-1.5 rounded-lg border border-amber-500/30 bg-black/20 text-sm text-foreground placeholder:text-muted-foreground/50 focus:outline-none focus:border-amber-500/60" />
{/* Generate OTP / show code */} {(!otp || isExpired) ? ( ) : (
{/* Expiry */}
{t.poolExpiryPrefix} {minutesLeft} {t.poolExpirySuffix}
{bookmarkMode === "console" ? ( <> {/* Steps */}
  1. 1{t.poolConsoleStep1.replace("geminigen.ai", "")}geminigen.ai
  2. 2{t.poolConsoleStep2}
  3. 3{t.poolConsoleStep3}
  4. 4{t.poolConsoleStep4}
{/* Code block */}
JavaScript Console
                        {buildCode(otp!, syncLabel)}
                      
) : ( <>
  1. 1{t.poolBookmarkStep1}
  2. 2{t.poolBookmarkStep2.replace("geminigen.ai", "")}geminigen.ai
  3. 3{t.poolBookmarkStep3}
{ e.preventDefault(); toast({ description: t.poolDragToast }); }} className="flex items-center justify-center gap-2 w-full px-4 py-2.5 rounded-lg border-2 border-dashed border-amber-500/50 bg-amber-500/10 text-amber-300 text-sm font-medium hover:bg-amber-500/20 transition-colors cursor-grab active:cursor-grabbing select-none" > {t.poolBookmarkBtnText}

{t.poolBookmarkDragHint}

)}
)}
)} {/* ── Manual add panel ── */} {addMode === "manual" && (

{t.poolManualTitle}

setNewLabel(e.target.value)} placeholder={t.poolManualNamePlaceholder} className="w-full px-3 py-1.5 rounded border border-border/40 bg-black/30 text-sm text-foreground placeholder:text-muted-foreground/50 focus:outline-none" /> setNewBearer(e.target.value)} placeholder={t.poolManualBearerPlaceholder} className="w-full px-3 py-1.5 rounded border border-border/40 bg-black/30 text-sm font-mono text-foreground placeholder:text-muted-foreground/50 focus:outline-none" /> setNewRefresh(e.target.value)} placeholder={t.poolManualRefreshPlaceholder} className="w-full px-3 py-1.5 rounded border border-border/40 bg-black/30 text-sm font-mono text-foreground placeholder:text-muted-foreground/50 focus:outline-none" />
)}
); } function AutoRenewalCard() { const { toast } = useToast(); const { t } = useLang(); const qc = useQueryClient(); const BASE_URL = import.meta.env.BASE_URL.replace(/\/$/, ""); // Credential setup state const [username, setUsername] = useState(""); const [password, setPassword] = useState(""); const [showPass, setShowPass] = useState(false); const [savingCreds, setSavingCreds] = useState(false); // Fetch credential status const { data: credStatus, refetch: refetchCreds } = useQuery({ queryKey: ["credentialStatus"], queryFn: async () => { const r = await fetch(`${BASE_URL}/api/admin/credentials`, { credentials: "include" }); if (!r.ok) return { configured: false, username: null }; return r.json() as Promise<{ configured: boolean; username: string | null }>; }, }); const handleSaveCreds = async () => { if (!username.trim() || !password.trim()) { toast({ variant: "destructive", description: t.renewalRequireFields }); return; } setSavingCreds(true); try { const r = await fetch(`${BASE_URL}/api/admin/credentials`, { method: "POST", credentials: "include", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ username: username.trim(), password: password.trim() }), }); const data = await r.json(); if (!r.ok) throw new Error(data.error || "Failed"); setUsername(""); setPassword(""); await refetchCreds(); qc.invalidateQueries({ queryKey: ["adminConfig"] }); qc.invalidateQueries({ queryKey: ["setupStatus"] }); toast({ description: t.renewalSuccess }); } catch (e: any) { toast({ variant: "destructive", description: e.message || t.renewalFailed }); } setSavingCreds(false); }; const handleDeleteCreds = async () => { await fetch(`${BASE_URL}/api/admin/credentials`, { method: "DELETE", credentials: "include" }); await refetchCreds(); toast({ description: t.renewalClearedCreds }); }; return (
{/* Auto-renewal via credentials (recommended) */}
{credStatus?.configured ? : }

{credStatus?.configured ? t.renewalTitle : t.renewalSetupTitle}

{credStatus?.configured ? <>{credStatus.username && {credStatus.username}} β€” {t.renewalActiveDesc} : t.renewalInactiveDesc}

{credStatus?.configured && ( )}
{!credStatus?.configured && (
setUsername(e.target.value)} autoComplete="off" />
setPassword(e.target.value)} autoComplete="new-password" />

{t.renewalSecureNote}

)}
{/* Manual refresh token fallback */}
{t.renewalManualTitle}

{t.renewalManualDesc}

); } function ManualTokenInput({ qc, baseUrl }: { qc: any; baseUrl: string }) { const { toast } = useToast(); const { t } = useLang(); const [newToken, setNewToken] = useState(""); const [saving, setSaving] = useState(false); const handleRenew = async () => { if (!newToken.trim() || newToken.trim().length < 20) { toast({ variant: "destructive", description: t.renewalManualInvalid }); return; } setSaving(true); try { const r = await fetch(`${baseUrl}/api/admin/setup`, { method: "POST", credentials: "include", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ refreshToken: newToken.trim() }), }); const data = await r.json(); if (!r.ok) throw new Error(data.error || "Failed"); qc.invalidateQueries({ queryKey: ["adminConfig"] }); qc.invalidateQueries({ queryKey: ["setupStatus"] }); setNewToken(""); toast({ description: t.renewalManualSuccess }); } catch (e: any) { toast({ variant: "destructive", description: e.message || t.renewalManualFailed }); } setSaving(false); }; return (
setNewToken(e.target.value)} spellCheck={false} />
); } function QuickTokenUpdate() { const { toast } = useToast(); const { t } = useLang(); const qc = useQueryClient(); const BASE_URL = import.meta.env.BASE_URL.replace(/\/$/, ""); const [accessToken, setAccessToken] = useState(""); const [capsolverKey, setCapsolverKey] = useState(""); const [yescaptchaKey, setYescaptchaKey] = useState(""); const [playwrightUrl, setPlaywrightUrl] = useState(""); const [playwrightSecret, setPlaywrightSecret] = useState(""); const [savingToken, setSavingToken] = useState(false); const [savingCapsolver, setSavingCapsolver] = useState(false); const [savingYescaptcha, setSavingYescaptcha] = useState(false); const [savingPlaywright, setSavingPlaywright] = useState(false); const [testingPlaywright, setTestingPlaywright] = useState(false); const saveConfig = async (key: string, value: string): Promise => { const r = await fetch(`${BASE_URL}/api/admin/config`, { method: "PUT", credentials: "include", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ key, value }), }); const data = await r.json(); if (!r.ok) throw new Error(data.error || "Failed"); return true; }; const handleSaveToken = async () => { const tok = accessToken.trim(); if (tok.length < 20) return; setSavingToken(true); try { await saveConfig("access_token", tok); setAccessToken(""); qc.invalidateQueries({ queryKey: ["adminConfig"] }); toast({ description: t.adminTokenSuccess }); } catch (e: any) { toast({ variant: "destructive", description: e.message || t.adminSaveFailed }); } setSavingToken(false); }; const handleSaveCapsolver = async () => { const key = capsolverKey.trim(); if (!key) return; setSavingCapsolver(true); try { await saveConfig("capsolver_api_key", key); setCapsolverKey(""); qc.invalidateQueries({ queryKey: ["adminConfig"] }); toast({ description: t.adminCapSolverSuccess }); } catch (e: any) { toast({ variant: "destructive", description: e.message || t.adminSaveFailed }); } setSavingCapsolver(false); }; const handleSaveYescaptcha = async () => { const key = yescaptchaKey.trim(); if (!key) return; setSavingYescaptcha(true); try { await saveConfig("yescaptcha_api_key", key); setYescaptchaKey(""); qc.invalidateQueries({ queryKey: ["adminConfig"] }); toast({ description: t.adminYesCaptchaSuccess }); } catch (e: any) { toast({ variant: "destructive", description: e.message || t.adminSaveFailed }); } setSavingYescaptcha(false); }; const handleSavePlaywright = async () => { const url = playwrightUrl.trim(); if (!url) return; setSavingPlaywright(true); try { await saveConfig("playwright_solver_url", url); if (playwrightSecret.trim()) { await saveConfig("playwright_solver_secret", playwrightSecret.trim()); } setPlaywrightUrl(""); setPlaywrightSecret(""); qc.invalidateQueries({ queryKey: ["adminConfig"] }); toast({ description: t.adminPlaywrightSuccess }); } catch (e: any) { toast({ variant: "destructive", description: e.message || t.adminSaveFailed }); } setSavingPlaywright(false); }; const handleTestPlaywright = async () => { const urlToTest = playwrightUrl.trim(); if (!urlToTest) return; setTestingPlaywright(true); try { const headers: Record = { "Content-Type": "application/json" }; if (playwrightSecret.trim()) headers["X-Solver-Secret"] = playwrightSecret.trim(); const r = await fetch(urlToTest, { method: "POST", headers, body: "{}", signal: AbortSignal.timeout(60000) }); const data = await r.json(); if (r.ok && data.token) { toast({ description: `βœ… ${t.adminTestSuccess}:${data.token.substring(0, 20)}... (cached=${data.cached})` }); } else { toast({ variant: "destructive", description: `${t.adminTestFailed}:${data.error || r.status}` }); } } catch (e: any) { toast({ variant: "destructive", description: `${t.videoConnectionFailed}:${e.message}` }); } setTestingPlaywright(false); }; return (
{/* Access Token */}

{t.adminTokenTitle}

{t.adminTokenDesc}

{t.adminTokenHowTo} Authorization: Bearer eyJ...

setAccessToken(e.target.value)} spellCheck={false} />
{/* Playwright Solver */}

{t.adminPlaywrightTitle}

{t.adminPlaywrightDesc}

{t.adminPlaywrightStepsLabel}

  1. {t.adminPlaywrightStep1}
  2. Vercel β€” {t.adminPlaywrightStep2}
  3. {t.adminPlaywrightStep3}
  4. {t.adminPlaywrightStep4}
setPlaywrightUrl(e.target.value)} spellCheck={false} />
setPlaywrightSecret(e.target.value)} spellCheck={false} type="password" />
{/* YesCaptcha API Key */}

{t.adminYesCaptchaTitle}

{t.adminYesCaptchaDesc}

{t.adminYesCaptchaHowTo}

setYescaptchaKey(e.target.value)} spellCheck={false} />
{/* CapSolver API Key */}

{t.adminCapSolverTitle}

{t.adminCapSolverDesc}

{t.adminCapSolverHowTo}

setCapsolverKey(e.target.value)} spellCheck={false} />
); } function ConfigPanel() { const { t } = useLang(); const { toast } = useToast(); const { data: config = [], refetch } = useQuery({ queryKey: ["adminConfig"], queryFn: fetchConfig }); const [editing, setEditing] = useState>({}); const [saving, setSaving] = useState(null); const [visible, setVisible] = useState>({}); const handleSave = async (key: string) => { const value = editing[key]; if (!value) return; setSaving(key); try { const r = await fetch(`${API_BASE}/config`, { method: "PUT", credentials: "include", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ key: KEY_MAP[key] ?? key, value }), }); if (!r.ok) throw new Error("Failed"); await refetch(); setEditing((p) => { const n = { ...p }; delete n[key]; return n; }); toast({ description: t.adminConfigSaved }); } catch { toast({ variant: "destructive", description: t.adminError }); } setSaving(null); }; const maskValue = (v: string) => v.length > 20 ? v.slice(0, 12) + "..." + v.slice(-6) : v; const LABELS: Record = { geminigen_refresh_token: "Refresh Token", geminigen_bearer_token: "Access Token", }; const KEY_MAP: Record = { geminigen_refresh_token: "refresh_token", geminigen_bearer_token: "access_token", }; return (
{config.length === 0 && (
{t.adminConfigEmpty}
)} {config.map((row) => (
{LABELS[row.key] || row.key} {t.adminConfigUpdated}: {new Date(row.updatedAt).toLocaleString()}
{visible[row.key] ? row.value : maskValue(row.value)}
setEditing(p => ({ ...p, [row.key]: e.target.value }))} />
))}
); } export function Admin() { const { t } = useLang(); const { user, isAdmin, isLoaded } = useAuth(); const [, navigate] = useLocation(); const qc = useQueryClient(); const { toast } = useToast(); const [tab, setTab] = useState<"users" | "config">("users"); const { data: stats } = useQuery({ queryKey: ["adminStats"], queryFn: fetchStats, enabled: isAdmin }); const { data: users = [], isLoading: usersLoading } = useQuery({ queryKey: ["adminUsers"], queryFn: fetchUsers, enabled: isAdmin && tab === "users", }); const updateUser = async (id: number, data: Record) => { const r = await fetch(`${API_BASE}/users/${id}`, { method: "PUT", credentials: "include", headers: { "Content-Type": "application/json" }, body: JSON.stringify(data), }); if (!r.ok) throw new Error("Failed"); qc.invalidateQueries({ queryKey: ["adminUsers"] }); }; const deleteUser = async (id: number) => { const r = await fetch(`${API_BASE}/users/${id}`, { method: "DELETE", credentials: "include" }); if (!r.ok) { const d = await r.json(); throw new Error(d.error || "Failed"); } qc.invalidateQueries({ queryKey: ["adminUsers"] }); }; if (isLoaded && !isAdmin) { return (

{t.adminForbiddenTitle}

{t.adminForbiddenDesc}

); } return (

{t.adminTitle}

{t.adminSubtitle}

{stats && (
)}
{(["users", "config"] as const).map((t2) => ( ))}
{tab === "users" && (
{usersLoading ? (
Loading...
) : users.length === 0 ? (
{t.adminNoUsers}
) : ( users.map((u) => ( )) )}
)} {tab === "config" && }
); }