import { useCallback, useEffect, useState } from 'react' import { Loading, ErrorState } from '../../../components/PageState.jsx' import { api } from '../../../api/client.js' // ── Admin · Shard visibility ──────────────────────────────────────────────── // // Who may see which shard surface, and which sensitive fields within it. // Admin-only, because this decides what ANONYMOUS visitors get. // // Two things the UI must communicate honestly, because they are not negotiable // server-side (see docs/link/v3.md §3.4): // • acct / webId are admin-only always and are not listed as editable fields. // • an event kind the server doesn't know about never reaches anyone below // admin, whatever is set here. // // Defaults reproduce the behavior the site had before this panel existed, so a // fresh install shows "everything as it was" rather than an empty form. const RUNG_LABEL = { anonymous: 'Everyone', logged_in: 'Signed in', player: 'Linked players', staff: 'Staff', admin: 'Admins only', } const RUNG_HINT = { anonymous: 'Visible to anyone, signed in or not.', logged_in: 'Any signed-in account, linked or not.', player: 'Accounts with a linked game account. Staff always qualify.', staff: 'Admins and moderators.', admin: 'Admins only.', } const FEATURE_LABEL = { status: 'Shard status', activity: 'Activity feed', champs: 'Champion spawns', guilds: 'Guilds', governors: 'Town governors', houses: 'Houses / IDOC', presence: 'Players online', ruleset: 'Shard rules', atlas: 'Spawn atlas', leaderboards: 'Leaderboards', market: 'Marketplace', } const FEATURE_HINT = { status: 'Connection state, online count, gold-supply series.', activity: 'Deaths, kills, skill gains, quests, logins.', champs: 'The live champion / mini-champ / sea-boss board.', guilds: 'Guild rosters, alliances and leaders.', governors: 'City Loyalty governors, elections and term history.', houses: 'Houses in danger (IDOC). Owner and price are separate fields below.', presence: 'Population aggregate and the staff-online widget.', ruleset: 'Skill/stat caps, house limits, vet rewards and the rest of the ruleset.', atlas: 'The spawn atlas and bestiary. Static shard content, not live state.', leaderboards: 'Point and loyalty standings across every points system.', market: 'The shard-wide player-vendor index.', } const FIELD_LABEL = { owner: 'House owner', price: 'House price', location: 'In-game location (map + coordinates)', connect: 'Server connect address', // Keyed on the WIRE field, which for a leaderboard entry is `name` — the // projection matches literal JSON keys, so the rule cannot be spelled after the // field's meaning. The label is what carries the meaning to the admin. name: 'Character names on leaderboards', ownerName: 'Vendor owner name', // One rule, one key — `location` is a nested object on both the wire frame and // the stored read model precisely so that hiding it takes the facet, the // coordinates, the region and the house together. ownerSerial: 'Vendor owner character id', } function RungSelect({ value, onChange, ladder, disabled }) { return ( ) } function FeatureRow({ name, settings, defaults, ladder, onPatch }) { const fields = Object.entries(settings.fields || {}) const changed = defaults && (settings.enabled !== defaults.enabled || settings.audience !== defaults.audience || settings.stream !== defaults.stream || JSON.stringify(settings.fields) !== JSON.stringify(defaults.fields)) return (
{FEATURE_HINT[name]}
Choose who can see each shard surface on the public site, and how much detail they get. Turning a feature off hides it entirely — its pages return “not found” rather than revealing that it exists. “Live updates” controls whether the feature streams changes in real time; the pages still work without it, they just refresh on load.
{lockedFields.length > 0 && (Not configurable: {lockedFields.join(', ')} — game account names and website user ids are never shown below admin, on any surface. They aren’t visible in game either, so publishing them would disclose something the shard itself doesn’t.
)}