Wire up the three uo-link sidecar surfaces that weren't integrated yet. Champion spawns - Ingest champ.update/champ.remove into a new shard_champs table (served from our own store, like online/houses); public /site/champs board with a nav link, live via the existing SSE feed (champ.* added to the public allowlist). Staff write plane (admin + moderator) - kick / ban / unban / broadcast via /admin/shard/*; actor is stamped server-side from the session, never the browser. Sidecar status codes mapped (403 disabled/ protected, 404 unknown, 503/504 transient). admin.audit events are logged and surfaced at /admin/shard/audit. - New admin "In-Game Ops" view (/admin/shard-ops), plus per-account Kick/Ban/Unban on the user-detail and character views (ShardAccountActions, self-gated to staff). Help-page (support) queue - Ingest page.new/updated/closed into a new shard_pages table; respond/close via /admin/shard/pages/*. Champ board and page queue are snapshotted from the sidecar's /champs and /pages on every WS (re)connect (guarded so a failed call never wipes local state). Verified live end-to-end against MariaDB + the Rust sidecar + ServUO; unit tests cover ingest routing (shardIngest.champsPages.test.js). Swagger regenerated. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0114TpmrNW4wNXsHq5CR72jQ
30 lines
1.4 KiB
JavaScript
30 lines
1.4 KiB
JavaScript
import { useParams, Link } from 'react-router-dom'
|
|
import { Loading, ErrorState } from '../../../components/PageState.jsx'
|
|
import CharacterSheet from '../../../components/CharacterSheet.jsx'
|
|
import { useAsync } from '../../../lib/useAsync.js'
|
|
import { api } from '../../../api/client.js'
|
|
|
|
// A staff member's own character sheet inside the admin shell. Owner-checked —
|
|
// the endpoint only returns a sheet for a character on the caller's linked account.
|
|
export default function AdminCharacter() {
|
|
const { serial } = useParams()
|
|
const { loading, error, data } = useAsync(() => api.admin.shard.char(serial), [serial])
|
|
const restarting = error && error.status === 503
|
|
const forbidden = error && error.status === 403
|
|
|
|
return (
|
|
<div style={{ maxWidth: 760 }}>
|
|
<p style={{ margin: '0 0 18px' }}>
|
|
<Link to="/admin/characters" className="sans" style={{ color: 'var(--accent)', textDecoration: 'none', fontSize: '0.86rem' }}>
|
|
← Back to my characters
|
|
</Link>
|
|
</p>
|
|
{loading && <Loading />}
|
|
{restarting && <ErrorState message="The game server is restarting — try again shortly." />}
|
|
{forbidden && <ErrorState message="That character is not on an account linked to you." />}
|
|
{error && !restarting && !forbidden && <ErrorState message="Could not load that character right now." />}
|
|
{!loading && !error && data && <CharacterSheet char={data} moderation />}
|
|
</div>
|
|
)
|
|
}
|