pummmp.fun/app/admin/page.tsx
2026-07-04 12:49:09 -07:00

196 lines
7.5 KiB
TypeScript

'use client'
import { useState } from 'react'
import { useSession } from 'next-auth/react'
import { useRouter } from 'next/navigation'
import { Header } from '@/components/header'
import { Button } from '@/components/ui/button'
import { Card } from '@/components/ui/card'
import { Input } from '@/components/ui/input'
import { Label } from '@/components/ui/label'
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'
import { toast } from 'sonner'
import { Crown, DollarSign, Search } from 'lucide-react'
export default function AdminPage() {
const { data: session, status } = useSession()
const router = useRouter()
const [targetUser, setTargetUser] = useState('')
const [amount, setAmount] = useState('')
const [action, setAction] = useState('add')
const [loading, setLoading] = useState(false)
const [noticeMessage, setNoticeMessage] = useState('')
const [noticeReason, setNoticeReason] = useState('')
const [noticeLoading, setNoticeLoading] = useState(false)
if (status === 'loading') return null
if (!session?.user?.isAdmin) {
router.push('/')
return null
}
const handleUpdateBalance = async (e: React.FormEvent) => {
e.preventDefault()
setLoading(true)
try {
const res = await fetch('/api/admin/balance', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
usernameOrId: targetUser,
amount: parseFloat(amount),
action
})
})
const data = await res.json()
if (res.ok) {
toast.success(`Success! ${data.user} balance updated to ${data.newBalance.toFixed(4)} SOL`)
setAmount('')
} else {
toast.error(data.error || 'Failed')
}
} catch (e) {
toast.error('Error executing admin command')
} finally {
setLoading(false)
}
}
const handleSetNotice = async (e: React.FormEvent) => {
e.preventDefault()
setNoticeLoading(true)
try {
const res = await fetch('/api/notice', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
message: noticeMessage,
reason: noticeReason || undefined
})
})
const data = await res.json()
if (res.ok) {
toast.success('Notice set successfully!')
setNoticeMessage('')
setNoticeReason('')
} else {
toast.error(data.error || 'Failed to set notice')
}
} catch (e) {
toast.error('Error setting notice')
} finally {
setNoticeLoading(false)
}
}
return (
<div className="min-h-screen bg-background">
<Header />
<div className="container mx-auto py-20 px-4">
<div className="flex items-center gap-2 mb-8">
<Crown className="w-8 h-8 text-red-500" />
<h1 className="text-3xl font-bold">Admin Dashboard</h1>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-8">
<Card className="p-6">
<h2 className="text-xl font-semibold mb-4 flex items-center gap-2">
<DollarSign className="w-5 h-5" />
Manage User Balance
</h2>
<form onSubmit={handleUpdateBalance} className="space-y-4">
<div className="space-y-2">
<Label>Target User</Label>
<div className="relative">
<Search className="absolute left-2 top-2.5 h-4 w-4 text-muted-foreground" />
<Input
placeholder="Username or Discord ID"
className="pl-8"
value={targetUser}
onChange={e => setTargetUser(e.target.value)}
/>
</div>
</div>
<div className="grid grid-cols-2 gap-4">
<div className="space-y-2">
<Label>Action</Label>
<Select value={action} onValueChange={setAction}>
<SelectTrigger>
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="add">Add (+)</SelectItem>
<SelectItem value="subtract">Subtract (-)</SelectItem>
<SelectItem value="set">Set (=)</SelectItem>
</SelectContent>
</Select>
</div>
<div className="space-y-2">
<Label>Amount (SOL)</Label>
<Input
type="number"
step="0.0001"
placeholder="0.00"
value={amount}
onChange={e => setAmount(e.target.value)}
/>
</div>
</div>
<Button type="submit" className="w-full" disabled={loading || !targetUser || !amount}>
{loading ? 'Executing...' : 'Update Balance'}
</Button>
</form>
</Card>
<Card className="p-6">
<h2 className="text-xl font-semibold mb-4">Set Site Notice</h2>
<form onSubmit={handleSetNotice} className="space-y-4">
<div className="space-y-2">
<Label>Notice Message</Label>
<Input
placeholder="Enter notice message..."
value={noticeMessage}
onChange={e => setNoticeMessage(e.target.value)}
/>
</div>
<div className="space-y-2">
<Label>Reason (Optional)</Label>
<Input
placeholder="Reason for notice..."
value={noticeReason}
onChange={e => setNoticeReason(e.target.value)}
/>
</div>
<Button type="submit" className="w-full" disabled={noticeLoading || !noticeMessage.trim()}>
{noticeLoading ? 'Setting...' : 'Set Notice'}
</Button>
</form>
</Card>
<Card className="p-6">
<h2 className="text-xl font-semibold mb-4">Quick Links</h2>
<div className="space-y-2">
<p className="text-sm text-muted-foreground">
Use the scripts in /scripts folder for bulk actions:
</p>
<ul className="list-disc list-inside text-sm space-y-1 font-mono bg-muted/30 p-4 rounded-lg">
<li>npx ts-node scripts/set-role.ts &lt;user&gt; admin true</li>
<li>npx ts-node scripts/give-beta-to-all.ts</li>
<li>npx ts-node scripts/database-reset.ts [reason]</li>
</ul>
</div>
</Card>
</div>
</div>
</div>
)
}