[Game Systems] Stat-block editor + infobox projection (frontend) #140
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Motivation/Context
Issue 4 (#139) lands versioned stat schemas and a validated
statsJSONB envelope onlore_entries, but there's no way for a GM to actually view or edit that structureddata — the existing wiki UI only knows about the loose, string-only
sidebar_fieldsinfobox. This issue builds the schema-rendered editor and projects the player-visible
subset of
statsinto the existing infobox so there's a single display surface ratherthan two competing ones.
Approach
Typed editor. On npc/creature wiki articles in
webapp/frontend/src/pages/WikiArticle.jsx, render a form generated from the entry'sresolved
(system, schema_version)definition (fetched via the schema endpoint addedin #139): grouped fields per the schema's
groups/fieldsstructure, with the rightinput widget per type (
int→ number input withmin/max,enum→ select withchoices,list[str]/list[text]→ repeatable rows,bool→ checkbox,str/text→text input/textarea). Writes go through the validated
statswrite path from #139, soclient-side validation is a UX nicety, not the source of truth — the server is
authoritative.
Infobox projection. Player-visible fields from
stats.values(per the envelope'svisibility, matching the existingsidebar_fieldsvisibility model atrouters/campaigns.py:387-432,469) render as additional infobox rows alongsidesidebar_fields, reusing the existing sidebar UI and viewer-based filtering rather thanbuilding a second display surface. This is the "display projection" role Option A
(typed
sidebar_fields) plays in the report's design (§3.2) — no new visibility model,just a read-time merge into the existing infobox rendering.
Read-only/edge states.
stats.schema_versionis behind the campaign'scurrent schema version, show a read-only view with an "update to v{n}" nudge the GM
confirms explicitly (schemas are immutable per version; this is a deliberate
opt-in migration of the entry, not a silent one).
statsreferences a system that's no longer the campaign's current one — e.g. after
unlinking per §4.4 of the report), render the stored
statsread-only with a "not thecampaign's current system" badge rather than hiding or erroring. The GM may delete
the stale envelope per-entry from this state.
Dependencies
statsstorage + validation service) — provides theschema definitions to render, the validated write endpoint, and the envelope's
version/system self-description this UI reads to pick its display state.
Out of scope
generate_statblockLLM action — issue 6.convert_statsdraft mode, backfillingstatsfromexisting free-form data) — issue 7.
sidebar_fieldsrows themselves (existing functionality, untouched).Acceptance criteria
a typed stat-block form renders, grouped per the schema, with per-field type-correct
inputs.
statsendpoint; server-side validationerrors surface inline per field.
statsfields appear as infobox rows alongsidesidebar_fields,correctly filtered by the viewer's role (GM vs. player) using the existing visibility
model.
stats.schema_versionis stale renders read-only with an explicit"update to v{n}" action; nothing auto-migrates.
stats.systemdoesn't match the campaign's current linked system (orthe campaign has no linked system) renders read-only with a clear "not current
system" indicator, and offers a per-entry delete of the stale envelope.
a validation-error save, the infobox projection respecting visibility, and both
read-only edge states.
statsstorage + validation service #139Picking this up on
feat/140-statblock-editor(PR to target thefeat/v3.9-game-aware-systemsintegration branch), in parallel with #137.Builds on #139: consumes
GET /api/game-systems/{system_id}/schemaand thestatsenvelope now on lore responses. Delivers a schema-driven typed editor on npc/creature wiki articles (grouped fields, per-type widgets), infobox projection ofstats.valuesalongsidesidebar_fields, and the read-only edge states (version-mismatch "update to v{n}", system-unlinked/mismatch badge + per-entry delete). Writes go through the validatedstatsPATCH path (server authoritative). #145 (conversion wizard) builds on this.Done and verified — merged into the integration branch via PR #209.
Verification (Node 20 via
node:20-alpine): 331 frontend tests pass (32 files) + production build succeeds.Delivered: schema-driven
StatBlockEditor(grouped typed form perdefinition[entry_type], per-type widgets, visibility control, save via the validatedstatsPATCH), infobox projection ofstats.valuesalongsidesidebar_fields, and the read-only edge states (version-mismatch "Update to v{n}", system-mismatch/unlinked badge + per-entry delete).fetchGameSystemSchemaadded.Review fix (found during verification): the number inputs' native
max/minmade out-of-range values:invalid, so native HTML form validation was silently blocking submit before the request reached the server — contradicting #139's server-authoritative contract. AddednoValidateto the form (min/max remain as spinner hints; the server validates and its error surfaces). Committed asecf5acf.Closing; ships to
mainwith the v3.9.0 release. #145 (conversion wizard) builds on this.statsenvelope through campaign export/import #214