feat(events): the authoring UI proper (Phase 13)
Replaces the two raw JSON boxes Phase 3 shipped as explicit placeholders: a
step's params are a form rendered from the action's own declaration, and a
phase's advance condition is the engagement condition builder. Adds the live cap
meter, the searchable option source's first consumer, and a start dialog
carrying the three fields the route has taken since Phase 10.
One route: POST /admin/events/price, admin+editor. A module's cost() runs on the
server and only there, so a meter has nothing to add up until something asks --
and the dry run is the wrong thing to ask on a debounce twice over: it dispatches
every step through the module and a pass against a version is RECORDED, which is
the stamp K's unattended-start gate reads. This dispatches nothing and records
nothing, and takes the spec in the body because the plan being priced is unsaved
between keystrokes.
A form gives way to JSON on the condition builder's own rule: a value the editor
cannot round-trip is SHOWN rather than silently rewritten. Dropping a param the
action does not declare and flattening `A and (B or C)` are the same mistake.
Two defects fixed in already-merged code:
* Creating an event has been impossible since Phase 6. `events/new` was added
beside `events/:id` and binds no param, and React Router ranks a static
segment above a dynamic one whatever the order -- so the editor was handed no
id and fetched /admin/events/undefined. Worse, the failure was invisible:
`!form` is true for every failed load, so the error state sat behind a
spinner that never stopped.
* 12b's searchable sources had no consumer. The server half shipped and the
only UI that reads a source never sent a term, so the 6,707-entry spawner
list was picked from a 2,000-entry truncation with nothing saying so.
Server: 2113 tests, 2024 pass, 0 fail (89 DB-skipped). Client: 380 pass, 0 fail.
routes:manifest and swagger regenerated -- one route added, none moved.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016wDDVXWMDz82WqE1i969r4
This commit is contained in:
@@ -24,6 +24,10 @@
|
||||
// both buttons, would behave badly in exactly the case the moderator role
|
||||
// exists for.
|
||||
//
|
||||
// Phase 13 adds the meter beside `verify`, and it is the one route here that
|
||||
// neither dispatches nor records — which is what makes it safe to call on a
|
||||
// debounce while a form is edited.
|
||||
//
|
||||
// Phase 6 adds the switchboard to the `admin` column — §K puts it in the same row
|
||||
// as the world-changing actions it governs — and `verify` to the `admin, editor`
|
||||
// one, because a dry run dispatches nothing and the author who wrote the
|
||||
@@ -154,6 +158,9 @@ const SURFACE = [
|
||||
['DELETE', '/events/series/1', ['admin', 'editor']],
|
||||
// Phase 6. A dry run dispatches nothing and changes nothing.
|
||||
['POST', '/events/1/verify', ['admin', 'editor']],
|
||||
// Phase 13's meter, in the same column and for a stronger version of the
|
||||
// same reason: it dispatches nothing AND records nothing.
|
||||
['POST', '/events/price', ['admin', 'editor']],
|
||||
|
||||
// Committing the deployment: admin only (§N2).
|
||||
['POST', '/events/1/publish', ['admin']],
|
||||
@@ -214,7 +221,9 @@ test('start and stop are NOT the same gate, and that is the point', async () =>
|
||||
test('an editor may price an event but not publish or start it', async () => {
|
||||
// Phase 6's addition to the same shape: the author who wrote the definition can
|
||||
// find out what it would cost before asking an admin to commit the deployment.
|
||||
// Phase 13 gave the same author the meter, on the same argument.
|
||||
assert.equal(await forbidden('POST', '/events/1/verify', 'editor'), false)
|
||||
assert.equal(await forbidden('POST', '/events/price', 'editor'), false)
|
||||
assert.equal(await forbidden('POST', '/events/1/publish', 'editor'), true)
|
||||
assert.equal(await forbidden('POST', '/events/1/runs', 'editor'), true)
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user