import { useCallback, useEffect, useRef, useState } from 'react' import { useShardFeed } from '../../../lib/useShardFeed.js' import { describe } from '../../../lib/shardEvents.js' import { ago } from '../../../lib/format.js' import { api } from '../../../api/client.js' // In-game staff operations: the uo-link write plane (broadcast / kick / ban / // unban) and the help-page support queue, plus a live audit log. Open to admins // and moderators. The acting staff member (`actor`) is attached server-side from // the session — nothing here sends it — so every action is attributable. function Flash({ ok, err }) { if (ok) return {ok} if (err) return {err} return null } // ── Broadcast ──────────────────────────────────────────────────────────────── function Broadcast() { const [text, setText] = useState('') const [hue, setHue] = useState('') const [busy, setBusy] = useState(false) const [ok, setOk] = useState('') const [err, setErr] = useState('') async function send() { if (!text.trim()) return setErr('Enter a message.') setBusy(true); setOk(''); setErr('') try { await api.admin.shardOps.broadcast({ text: text.trim(), hue: hue === '' ? undefined : Number(hue) }) setOk('Broadcast sent.') setText('') } catch (e) { setErr(e.message || 'Could not broadcast.') } finally { setBusy(false) } } return (

Broadcast

A system message shown to everyone online right now.

) } // ── Account actions (kick / ban / unban) ───────────────────────────────────── function AccountActions() { const [account, setAccount] = useState('') const [durationSec, setDurationSec] = useState('') const [reason, setReason] = useState('') const [busy, setBusy] = useState('') const [ok, setOk] = useState('') const [err, setErr] = useState('') const acct = account.trim() function guard() { if (!acct) { setErr('Enter an account name.') return false } return true } async function run(label, fn, done) { if (!guard()) return setBusy(label); setOk(''); setErr('') try { const r = await fn() setOk(done(r)) } catch (e) { setErr(e.message || 'Action failed.') } finally { setBusy('') } } const kick = () => run('kick', () => api.admin.shardOps.kick({ account: acct }), (r) => { const n = r?.sessions != null ? r.sessions : null const plural = n === 1 ? '' : 's' const sessions = n != null ? ` (${n} session${plural})` : '' return `Kicked ${acct}${sessions}.` }) const ban = () => run( 'ban', () => api.admin.shardOps.ban({ account: acct, durationSec: durationSec === '' ? undefined : Number(durationSec), reason: reason.trim() || undefined, }), () => { const when = durationSec ? ` for ${durationSec}s` : ' indefinitely' return `Banned ${acct}${when}.` }, ) const unban = () => run('unban', () => api.admin.shardOps.unban(acct), () => `Unbanned ${acct}.`) return (

Account actions

Kick, ban or unban a game account. Bans work even if the account is offline; the shard refuses to act on staff at or above co-owner.

) } // ── Support (help-page) queue ──────────────────────────────────────────────── function PageRow({ page, onDone }) { const [message, setMessage] = useState('') const [busy, setBusy] = useState('') const [err, setErr] = useState('') async function respond(close) { if (!message.trim()) return setErr('Enter a reply first.') setBusy(close ? 'respond-close' : 'respond'); setErr('') try { await api.admin.shardOps.respondPage(page.pageId, { message: message.trim(), close }) onDone() } catch (e) { setErr(e.message || 'Could not send.') setBusy('') } } async function close() { setBusy('close'); setErr('') try { await api.admin.shardOps.closePage(page.pageId) onDone() } catch (e) { setErr(e.message || 'Could not close.') setBusy('') } } return (
{page.type || 'Page'}
{page.sender?.name || page.pageId} {page.handled && · claimed{page.handler ? ` by ${page.handler}` : ''}}
{page.sentMs ? ago(page.sentMs) : ''}
{page.message &&

{page.message}

}
{page.map || '—'}{page.x != null ? ` (${page.x}, ${page.y})` : ''}