Skip to content

Commit c5c78bb

Browse files
authored
fix(example-todo,docs): a modal action is client-only — defer/reminder are script actions (#3959) (#3974)
app-todo declared defer_task and set_reminder as type:'modal' with targets naming modal pages that do not exist, while task.handlers.ts registered deferTask and setReminder under keys no declaration could address. A modal action has no server dispatch — headlessActionTypeError rejects it over REST — so neither handler had ever executed, and the example was teaching that shape to every app scaffolded from it. ADR-0110 D5's boot inventory flagged both on its first pass. Both actions already declared the params their handlers read, so they were always 'collect input, then run server-side': that is type:'script' with params. The runner collects the same dialog and the handler now runs. Also corrects the action-type table in actions.mdx, which described modal as 'collect input, then submit to a handler' while the same page's REST table said modal returns 400 with nothing for the server to run — the inconsistency that made the wrong shape look sanctioned. Resolves #3959 by option 2 (modal stays client-only).
1 parent 03d26f7 commit c5c78bb

4 files changed

Lines changed: 41 additions & 9 deletions

File tree

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
---
2+
'@objectstack/example-todo': patch
3+
---
4+
5+
**[#3959] `app-todo`'s `defer_task` / `set_reminder` are `type: 'script'`, not `type: 'modal'`.**
6+
7+
Both declared `type: 'modal'` with a `target` naming a modal page that does not
8+
exist (`defer_task_modal`, `set_reminder_modal`), while their handlers sat
9+
registered under `deferTask` / `setReminder` — keys no declaration could
10+
address. A `modal` action has no server dispatch (`headlessActionTypeError`
11+
rejects it over REST), so neither handler had ever executed: the example
12+
shipped business logic that could not run, and ADR-0110 D5's boot inventory
13+
flagged both on its first pass.
14+
15+
Both already declared the `params` their handlers read, so they were always
16+
"collect input, then run server-side" actions — which is `type: 'script'` with
17+
`params`. The runner collects the same dialog and the handler now actually runs.
18+
19+
The action-type table in `content/docs/ui/actions.mdx` said `modal` meant
20+
"collect input, then submit to a handler", contradicting the same page's own
21+
REST table (`modal` → 400, nothing for the server to run). Corrected.

content/docs/ui/actions.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ The types you'll actually use:
1919
| `script` *(default)* | Run server-side logic | Inline `body` **or** a handler registered via `target` |
2020
| `flow` | Launch a flow (e.g. a screen-flow wizard) | `target` names the flow |
2121
| `url` | Navigate / open a link | `target` is the URL (`${ctx.record.id}` interpolation supported) |
22-
| `modal` | Open a modal page to collect input, then submit to a handler | `target` names the modal page |
22+
| `modal` | Open a modal page — client-side only, no server dispatch | `target` names the modal page (to collect input *and* run logic, use `script` + `params`) |
2323
| `api` | Call an HTTP endpoint directly | `target` is the endpoint; `method` / `bodyShape` / `bodyExtra` shape the request |
2424
| `form` | Open a form view, prefilled with the current record | `target` names the FormView; routed to `/forms/:target?recordId=…` |
2525

examples/app-todo/src/actions/task.actions.ts

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -36,14 +36,25 @@ export const StartTaskAction = defineAction({
3636
},
3737
});
3838

39-
/** Defer Task */
39+
/**
40+
* Defer Task — collect input, then run server-side.
41+
*
42+
* [ADR-0110] This is `type: 'script'` with `params`, NOT `type: 'modal'`. A
43+
* `modal` action's `target` names a page to OPEN and has no server dispatch
44+
* (`headlessActionTypeError` rejects it), so the old
45+
* `type: 'modal', target: 'defer_task_modal'` pointed at a page that does not
46+
* exist while `deferTask` sat registered under a key no declaration could
47+
* address — business logic that had never once executed (#3959). `script` +
48+
* `params` is the supported shape: the runner collects the same dialog, then
49+
* the handler runs with those values.
50+
*/
4051
export const DeferTaskAction = defineAction({
4152
name: 'defer_task',
4253
label: 'Defer Task',
4354
objectName: 'todo_task',
4455
icon: 'clock',
45-
type: 'modal',
46-
target: 'defer_task_modal',
56+
type: 'script',
57+
target: 'deferTask',
4758
locations: ['record_header'],
4859
params: [
4960
{
@@ -63,14 +74,14 @@ export const DeferTaskAction = defineAction({
6374
refreshAfter: true,
6475
});
6576

66-
/** Set Reminder */
77+
/** Set Reminder — collect input, then run server-side (see DeferTaskAction). */
6778
export const SetReminderAction = defineAction({
6879
name: 'set_reminder',
6980
label: 'Set Reminder',
7081
objectName: 'todo_task',
7182
icon: 'bell',
72-
type: 'modal',
73-
target: 'set_reminder_modal',
83+
type: 'script',
84+
target: 'setReminder',
7485
locations: ['record_header', 'list_item'],
7586
params: [
7687
{

examples/app-todo/src/actions/task.handlers.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ export async function deleteCompletedTasks(ctx: ActionContext): Promise<void> {
8282
}
8383
}
8484

85-
/** Defer a task by updating its due date (modal form submission handler) */
85+
/** Defer a task by updating its due date (params collected by the action dialog) */
8686
export async function deferTask(ctx: ActionContext): Promise<void> {
8787
const { record, engine, params } = ctx;
8888
await engine.update('todo_task', record.id as string, {
@@ -92,7 +92,7 @@ export async function deferTask(ctx: ActionContext): Promise<void> {
9292
});
9393
}
9494

95-
/** Set a reminder on a task (modal form submission handler) */
95+
/** Set a reminder on a task (params collected by the action dialog) */
9696
export async function setReminder(ctx: ActionContext): Promise<void> {
9797
const { record, engine, params } = ctx;
9898
await engine.update('todo_task', record.id as string, {

0 commit comments

Comments
 (0)