domma-cms 1.6.60 → 1.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (168) hide show
  1. package/CHANGELOG.md +4349 -0
  2. package/README.md +1 -1
  3. package/admin/dist/domma/domma-tools.css +3 -3
  4. package/admin/dist/domma/domma-tools.min.js +3 -3
  5. package/admin/js/lib/effect-defs.js +1 -1
  6. package/admin/js/lib/effects-builder.js +2 -2
  7. package/admin/js/lib/mail-compose.js +1 -0
  8. package/admin/js/lib/page-editor-reference.js +5 -5
  9. package/admin/js/lib/shortcode-tags.js +1 -1
  10. package/admin/js/templates/call-new.html +1 -1
  11. package/admin/js/templates/docs/usage-shortcodes.html +39 -0
  12. package/admin/js/views/call-new.js +1 -1
  13. package/admin/js/views/contacts.js +15 -15
  14. package/admin/js/views/form-submissions.js +2 -2
  15. package/docs/DommaFeatures.md +3005 -0
  16. package/docs/MultiTenant.md +176 -0
  17. package/docs/api-reference.md +1667 -0
  18. package/docs/cache.md +96 -0
  19. package/docs/configuration.md +1811 -0
  20. package/docs/editions-and-licences.md +190 -0
  21. package/docs/getting-started.md +201 -0
  22. package/docs/guides/form-triggers.md +260 -0
  23. package/docs/guides/mongodb-storage-adapter.md +282 -0
  24. package/docs/guides/multi-page-forms.md +121 -0
  25. package/docs/images/scribe-composer.png +0 -0
  26. package/docs/markdown-shortcodes.md +1738 -0
  27. package/docs/pentest-report-2026-04-03.md +181 -0
  28. package/docs/plans/2026-02-28-admin-topbar-login-forms.md +349 -0
  29. package/docs/plans/2026-02-28-domma-ui-polish.md +286 -0
  30. package/docs/plans/2026-02-28-reset-seed-fresh-commands.md +520 -0
  31. package/docs/plans/2026-03-02-cookie-consent-design.md +86 -0
  32. package/docs/plans/2026-03-02-cookie-consent-plugin.md +453 -0
  33. package/docs/plans/2026-03-02-tooltip-sweep-and-back-to-top-plugin-design.md +126 -0
  34. package/docs/plans/2026-03-02-tooltip-sweep-back-to-top.md +427 -0
  35. package/docs/plans/2026-03-05-cookie-consent-btt-core.md +115 -0
  36. package/docs/plans/2026-03-05-custom-css-migrate-to-core-design.md +35 -0
  37. package/docs/plans/2026-03-05-custom-css-migrate-to-core.md +352 -0
  38. package/docs/plans/2026-03-05-scribe-script-shortcode.md +373 -0
  39. package/docs/plans/2026-03-05-user-types-design.md +139 -0
  40. package/docs/plans/2026-03-05-user-types-implementation.md +1035 -0
  41. package/docs/plans/2026-03-09-page-editor-view-cta-inserts-design.md +78 -0
  42. package/docs/plans/2026-03-09-page-editor-view-cta-inserts.md +388 -0
  43. package/docs/plans/2026-03-09-pro-documentation-design.md +80 -0
  44. package/docs/plans/2026-03-09-pro-documentation.md +1017 -0
  45. package/docs/plans/2026-06-10-core-project-design.md +240 -0
  46. package/docs/plugin-development.md +827 -0
  47. package/docs/recruitment-recipe.md +501 -0
  48. package/docs/scaffolding.md +312 -0
  49. package/docs/screenshots/calculator-floating.png +0 -0
  50. package/docs/screenshots/calculator-standard.png +0 -0
  51. package/docs/screenshots/email-settings.png +0 -0
  52. package/docs/screenshots/messages-count.png +0 -0
  53. package/docs/structure/user-types.md +107 -0
  54. package/docs/superpowers/plans/2026-05-25-admin-sidebar-via-menus.md +1112 -0
  55. package/docs/superpowers/plans/2026-06-08-navbar-mobile-drawer-and-menu-enrichment.md +1161 -0
  56. package/docs/superpowers/plans/2026-06-25-domma-docs-project.md +695 -0
  57. package/docs/superpowers/plans/2026-06-26-surveys-HANDOFF.md +68 -0
  58. package/docs/superpowers/plans/2026-06-26-surveys-pro-plugin.md +1902 -0
  59. package/docs/superpowers/plans/2026-08-19-collection-context-sort-export.md +1577 -0
  60. package/docs/superpowers/plans/2026-10-09-content-api.md +1192 -0
  61. package/docs/superpowers/specs/2026-03-12-form-followup-actions-design.md +152 -0
  62. package/docs/superpowers/specs/2026-03-12-page-editor-form-insert-and-per-page-theme-design.md +115 -0
  63. package/docs/superpowers/specs/2026-03-17-nav-hidden-items-design.md +85 -0
  64. package/docs/superpowers/specs/2026-03-19-accordion-insert-design.md +175 -0
  65. package/docs/superpowers/specs/2026-03-25-static-block-shortcode-design.md +153 -0
  66. package/docs/superpowers/specs/2026-03-25-text-shortcode-design.md +168 -0
  67. package/docs/superpowers/specs/2026-04-05-card-builder-design.md +315 -0
  68. package/docs/superpowers/specs/2026-04-06-insert-builders-design.md +278 -0
  69. package/docs/superpowers/specs/2026-04-06-layouts-design.md +226 -0
  70. package/docs/superpowers/specs/2026-04-07-blog-plugin-design.md +300 -0
  71. package/docs/superpowers/specs/2026-04-07-calendar-plugin-design.md +538 -0
  72. package/docs/superpowers/specs/2026-04-17-components-area-design.md +368 -0
  73. package/docs/superpowers/specs/2026-05-02-effects-slideover-overhaul-design.md +193 -0
  74. package/docs/superpowers/specs/2026-05-06-dashboard-rebuild-design.md +257 -0
  75. package/docs/superpowers/specs/2026-05-24-menus-design.md +466 -0
  76. package/docs/superpowers/specs/2026-05-25-admin-sidebar-via-menus-design.md +358 -0
  77. package/docs/superpowers/specs/2026-05-25-projects-design.md +380 -0
  78. package/docs/superpowers/specs/2026-06-08-navbar-mobile-drawer-and-menu-enrichment-design.md +292 -0
  79. package/docs/superpowers/specs/2026-06-08-sidebar-tools-plugins-folders-design.md +59 -0
  80. package/docs/superpowers/specs/2026-06-25-domma-docs-project-design.md +192 -0
  81. package/docs/superpowers/specs/2026-06-26-domma-charts-upstream-proposal.md +44 -0
  82. package/docs/superpowers/specs/2026-06-26-manager-scheduler-upstream-proposal.md +46 -0
  83. package/docs/superpowers/specs/2026-06-26-surveys-pro-plugin-design.md +206 -0
  84. package/docs/superpowers/specs/2026-07-02-shortcode-context-menu-design.md +96 -0
  85. package/docs/superpowers/specs/2026-08-19-collection-context-sort-export-design.md +330 -0
  86. package/docs/superpowers/specs/2026-09-22-dcms-marketplace-design.md +1150 -0
  87. package/docs/superpowers/specs/2026-10-09-content-api-design.md +325 -0
  88. package/docs/superpowers/specs/plans/2026-03-12-form-followup-actions.md +624 -0
  89. package/docs/superpowers/specs/plans/2026-03-12-page-editor-form-insert-and-per-page-theme.md +125 -0
  90. package/docs/superpowers/specs/plans/2026-03-17-nav-hidden-items.md +578 -0
  91. package/docs/superpowers/specs/plans/2026-03-19-accordion-insert.md +635 -0
  92. package/docs/superpowers/specs/plans/2026-03-25-text-shortcode.md +545 -0
  93. package/docs/superpowers/specs/plans/2026-04-05-card-builder.md +1970 -0
  94. package/docs/superpowers/specs/plans/2026-04-06-insert-builders.md +1049 -0
  95. package/docs/superpowers/specs/plans/2026-04-06-layouts.md +1015 -0
  96. package/docs/superpowers/specs/plans/2026-04-07-calendar-plugin.md +1526 -0
  97. package/docs/superpowers/specs/plans/2026-04-08-plugin-monetisation-split.md +242 -0
  98. package/docs/superpowers/specs/plans/2026-04-17-components-area.md +2038 -0
  99. package/docs/superpowers/specs/plans/2026-05-02-effects-slideover-overhaul.md +2335 -0
  100. package/docs/superpowers/specs/plans/2026-05-06-dashboard-rebuild.md +1999 -0
  101. package/docs/superpowers/specs/plans/2026-05-17-cache-system.md +1436 -0
  102. package/docs/superpowers/specs/plans/2026-05-24-menus.md +3096 -0
  103. package/docs/superpowers/specs/plans/2026-05-25-projects.md +2869 -0
  104. package/docs/superpowers/specs/plans/2026-09-22-marketplace-step2-consolidation.md +505 -0
  105. package/docs/superpowers/specs/plans/2026-09-22-marketplace-step3-catalogue-and-key.md +154 -0
  106. package/docs/superpowers/specs/plans/2026-09-22-tools-promotion.md +260 -0
  107. package/docs/theming.md +320 -0
  108. package/package.json +2 -2
  109. package/plugins/_lib/admin/mail/compose-window.js +3 -1
  110. package/plugins/_lib/admin/mail/identity.js +18 -3
  111. package/plugins/_lib/admin/mail/reader-view.js +32 -3
  112. package/plugins/blog/CLAUDE.md +4 -0
  113. package/plugins/blog/admin/lib/email.js +191 -0
  114. package/plugins/blog/admin/views/comments.js +3 -1
  115. package/plugins/blog/plugin.json +2 -2
  116. package/plugins/blog/plugin.public.js +1 -1
  117. package/plugins/blog/tests/email.test.js +69 -0
  118. package/plugins/booking/CLAUDE.md +4 -0
  119. package/plugins/booking/admin/lib/email.js +191 -0
  120. package/plugins/booking/admin/views/panels.js +7 -3
  121. package/plugins/booking/plugin.json +1 -1
  122. package/plugins/booking/tests/email.test.js +69 -0
  123. package/plugins/free-tier.lock.json +27 -19
  124. package/plugins/shopping-cart/CLAUDE.md +4 -0
  125. package/plugins/shopping-cart/admin/lib/email.js +191 -0
  126. package/plugins/shopping-cart/admin/views/orders.js +11 -3
  127. package/plugins/shopping-cart/plugin.json +2 -2
  128. package/plugins/shopping-cart/tests/email.test.js +69 -0
  129. package/plugins/support/CLAUDE.md +4 -0
  130. package/plugins/support/admin/lib/email.js +191 -0
  131. package/plugins/support/admin/views/desk.js +4 -1
  132. package/plugins/support/admin/views/inbox.js +6 -2
  133. package/plugins/support/plugin.json +2 -2
  134. package/plugins/support/tests/email.test.js +69 -0
  135. package/public/js/celebrations/core/canvas.js +1 -1
  136. package/public/js/celebrations/index.js +1 -1
  137. package/public/js/celebrations/licenses/ABeeZee-Regular-OFL.txt +93 -0
  138. package/public/js/celebrations/licenses/Allura-Regular-OFL.txt +93 -0
  139. package/public/js/celebrations/message-attrs.mjs +1 -0
  140. package/public/js/celebrations/themes/anniversary.js +1 -0
  141. package/public/js/celebrations/themes/autumn.js +1 -0
  142. package/public/js/celebrations/themes/birthday.js +1 -0
  143. package/public/js/celebrations/themes/butterfly-flight.js +1 -0
  144. package/public/js/celebrations/themes/calendar.js +1 -0
  145. package/public/js/celebrations/themes/chinese-new-year-dates.js +1 -0
  146. package/public/js/celebrations/themes/chinese-new-year.js +1 -0
  147. package/public/js/celebrations/themes/christmas.js +1 -1
  148. package/public/js/celebrations/themes/easter.js +1 -0
  149. package/public/js/celebrations/themes/garden-drawing.js +1 -0
  150. package/public/js/celebrations/themes/garden.js +1 -0
  151. package/public/js/celebrations/themes/graduation.js +1 -0
  152. package/public/js/celebrations/themes/greeting.js +1 -0
  153. package/public/js/celebrations/themes/make-it-rain.js +1 -0
  154. package/public/js/celebrations/themes/money-rain.js +1 -0
  155. package/public/js/celebrations/themes/new-year.js +1 -0
  156. package/public/js/celebrations/themes/occasion-drawing.js +1 -0
  157. package/public/js/celebrations/themes/occasions.js +1 -0
  158. package/public/js/celebrations/themes/rain-control.js +1 -0
  159. package/public/js/celebrations/themes/sparkler-font-data.js +1 -0
  160. package/public/js/celebrations/themes/sparkler-font.js +1 -0
  161. package/public/js/celebrations/themes/sparkler-writing.js +1 -0
  162. package/public/js/celebrations/themes/summer-garden.js +1 -0
  163. package/public/js/celebrations/themes/winter.js +1 -0
  164. package/public/js/celebrations/trait-data.mjs +1 -1
  165. package/public/js/effects.js +1 -1
  166. package/scripts/build.js +4 -0
  167. package/server/services/markdown.js +5 -2
  168. package/server/templates/page.html +1 -1
@@ -0,0 +1,624 @@
1
+ # Form Follow-Up: Success States & Action Triggers - Implementation Plan
2
+
3
+ > **For agentic workers:** REQUIRED: Use superpowers:subagent-driven-development (if subagents available) or
4
+ > superpowers:executing-plans to implement this plan. Steps use checkbox (`- [ ]`) syntax for tracking.
5
+
6
+ **Goal:** Wire CMS Actions to auto-execute on form submission, add configurable success redirect, and document the full
7
+ flow with a tutorial and example page.
8
+
9
+ **Architecture:** Two new optional fields (`settings.actionSlug`, `settings.successRedirect`) in each form's JSON. The
10
+ server-side submission pipeline executes the named Action after storing the entry (non-fatal, try/catch). The client
11
+ reads the `redirect` field from the response and either redirects or shows the inline success message.
12
+
13
+ **Tech Stack:** Fastify (ESM), Node.js file-based forms, Domma public site JS (`H.post`, `F.render`), static HTML
14
+ templates for admin docs/tutorials.
15
+
16
+ **Spec:** `docs/superpowers/specs/2026-03-12-form-followup-actions-design.md`
17
+
18
+ ---
19
+
20
+ ## Chunk 1: Server - action trigger + redirect response
21
+
22
+ ### Task 1: Add executeAction import and trigger to form submission
23
+
24
+ **Files:**
25
+
26
+ - Modify: `server/routes/api/forms.js`
27
+
28
+ The submission handler lives at `POST /forms/submit/:slug` (line 312). The webhook block ends around line 428 with a
29
+ `fastify.log.warn`. Add the action execution block immediately after it, then update the return value to include
30
+ `redirect`.
31
+
32
+ - [ ] **Step 1: Add import for executeAction**
33
+
34
+ At the top of `server/routes/api/forms.js`, add `executeAction` to the imports from collections. It actually lives in
35
+ `server/services/actions.js`. Add a new import line after the existing imports:
36
+
37
+ ```js
38
+ import {executeAction} from '../../services/actions.js';
39
+ ```
40
+
41
+ - [ ] **Step 2: Add action execution block after the webhook block**
42
+
43
+ Locate this line in the submission handler (around line 428):
44
+
45
+ ```js
46
+ // Webhook action
47
+ const webhookAction = form.actions?.webhook;
48
+ ```
49
+
50
+ After the entire webhook try/catch block (which ends around line 429), insert:
51
+
52
+ ```js
53
+ // CMS Action trigger
54
+ const actionSlug = form.settings?.actionSlug;
55
+ if (actionSlug && entry) {
56
+ try {
57
+ await executeAction(actionSlug, entry.id, { user: null });
58
+ } catch (err) {
59
+ fastify.log.warn(`[forms] Action "${actionSlug}" failed for form "${slug}": ${err.message}`);
60
+ }
61
+ }
62
+ ```
63
+
64
+ Note: `entry` is the result of `createEntry(...)` - currently it's not stored in a variable. You need to capture it.
65
+ Change this existing line:
66
+
67
+ ```js
68
+ // BEFORE:
69
+ await createEntry(targetSlug, data, { source: `form:${slug}` });
70
+
71
+ // AFTER:
72
+ entry = await createEntry(targetSlug, data, { source: `form:${slug}` });
73
+ ```
74
+
75
+ And declare `let entry = null;` before the collection try/catch block.
76
+
77
+ - [ ] **Step 3: Update the return value to include redirect**
78
+
79
+ Locate the final return at the bottom of the submit handler:
80
+
81
+ ```js
82
+ return { ok: true, message: settings.successMessage || 'Thank you for your submission.' };
83
+ ```
84
+
85
+ Replace with:
86
+
87
+ ```js
88
+ return {
89
+ ok: true,
90
+ message: settings.successMessage || 'Thank you for your submission.',
91
+ redirect: settings.successRedirect || null
92
+ };
93
+ ```
94
+
95
+ Also update the honeypot early-return to match:
96
+
97
+ ```js
98
+ // BEFORE:
99
+ return { ok: true, message: settings.successMessage };
100
+
101
+ // AFTER:
102
+ return { ok: true, message: settings.successMessage, redirect: settings.successRedirect || null };
103
+ ```
104
+
105
+ - [ ] **Step 4: Verify syntax**
106
+
107
+ ```bash
108
+ node --check server/routes/api/forms.js
109
+ ```
110
+
111
+ Expected: no output (no errors).
112
+
113
+ - [ ] **Step 5: Commit**
114
+
115
+ ```bash
116
+ git add server/routes/api/forms.js
117
+ git commit -m "feat: execute CMS action and return redirect on form submission"
118
+ ```
119
+
120
+ ---
121
+
122
+ ## Chunk 2: Admin form editor - new settings fields
123
+
124
+ ### Task 2: Add HTML fields to form-editor template
125
+
126
+ **Files:**
127
+
128
+ - Modify: `admin/js/templates/form-editor.html`
129
+
130
+ The Settings tab panel starts around line 102. The `setting-success-message` textarea is at line 113-115. Add two new
131
+ fields directly after it.
132
+
133
+ - [ ] **Step 1: Add Success Redirect URL field after success message**
134
+
135
+ Locate:
136
+
137
+ ```html
138
+ <textarea id="setting-success-message" class="form-input" rows="3"
139
+ placeholder="Thank you for your submission."></textarea>
140
+ </div>
141
+ ```
142
+
143
+ Insert immediately after the closing `</div>`:
144
+
145
+ ```html
146
+ <div class="mb-3">
147
+ <label class="form-label">Success Redirect URL</label>
148
+ <input id="setting-success-redirect" type="text" class="form-input"
149
+ placeholder="/thank-you">
150
+ <p class="form-hint text-muted" style="margin-top:.3rem;font-size:.8rem;">
151
+ If set, redirects the visitor to this URL on success. Takes priority over the success message.
152
+ </p>
153
+ </div>
154
+ ```
155
+
156
+ - [ ] **Step 2: Add CMS Action card to the Actions tab**
157
+
158
+ The Actions tab (Tab 4) begins around line 141. It has an Email card, Webhook card, and Spam Protection card. Add a
159
+ new "CMS Action" card as the third column card. Locate the closing `</div>` of the `<div class="row">` wrapper (around
160
+ line 190) and add a new card inside the row before it closes:
161
+
162
+ ```html
163
+ <div class="col-6">
164
+ <div class="card mb-4">
165
+ <div class="card-header"><h2>CMS Action</h2></div>
166
+ <div class="card-body">
167
+ <div class="mb-3">
168
+ <label class="form-label">Action on Submit</label>
169
+ <select id="action-cms-slug" class="form-input">
170
+ <option value="">- None -</option>
171
+ </select>
172
+ <p class="form-hint text-muted" style="margin-top:.3rem;font-size:.8rem;">
173
+ Run a CMS Action after the entry is stored. Requires Pro (MongoDB).
174
+ Failures are non-fatal - the submission is always saved first.
175
+ </p>
176
+ </div>
177
+ </div>
178
+ </div>
179
+ </div>
180
+ ```
181
+
182
+ - [ ] **Step 3: Verify the template renders (visual check only - no syntax errors)**
183
+
184
+ Check that the HTML is valid (no unclosed tags) by reading the modified section.
185
+
186
+ - [ ] **Step 4: Commit**
187
+
188
+ ```bash
189
+ git add admin/js/templates/form-editor.html
190
+ git commit -m "feat: add success redirect and CMS action fields to form editor template"
191
+ ```
192
+
193
+ ---
194
+
195
+ ### Task 3: Wire new fields in form-editor JS
196
+
197
+ **Files:**
198
+
199
+ - Modify: `admin/js/views/form-editor.js`
200
+
201
+ Three changes: collect, populate, and load actions into the select.
202
+
203
+ - [ ] **Step 1: Add fields to collectFormData()**
204
+
205
+ Locate `collectFormData()` (around line 1172). Inside the `settings` object (line 1180), add the two new fields after
206
+ `rateLimitPerMinute`:
207
+
208
+ ```js
209
+ // Add after:
210
+ rateLimitPerMinute: parseInt($container.find('#setting-rate-limit').val(), 10) || 3
211
+
212
+ // Insert:
213
+ ,
214
+ successRedirect: $container.find('#setting-success-redirect').val().trim() || null,
215
+ actionSlug: $container.find('#action-cms-slug').val() || null
216
+ ```
217
+
218
+ - [ ] **Step 2: Populate fields when loading an existing form**
219
+
220
+ Locate the populate section (around line 1320) where `const s = form.settings || {};` is. Add population after the
221
+ existing `rateLimitPerMinute` line:
222
+
223
+ ```js
224
+ // Add after:
225
+ $container.find('#setting-rate-limit').val(s.rateLimitPerMinute || 3);
226
+
227
+ // Insert:
228
+ $container.find('#setting-success-redirect').val(s.successRedirect || '');
229
+ // actionSlug select populated after actions are loaded (see loadActions below)
230
+ ```
231
+
232
+ - [ ] **Step 3: Add loadActions() call in onMount**
233
+
234
+ Find the `onMount` function (it calls `E.tabs(...)` and populates form data). After the tabs init and form population,
235
+ add a call to load available actions. Find where `$container.find('#action-email-enabled')` is populated (around line
236
+ 1330) and add after the webhook section:
237
+
238
+ ```js
239
+ // Load CMS Actions into select
240
+ const savedActionSlug = form?.settings?.actionSlug || '';
241
+ try {
242
+ const actions = await apiRequest('/actions').catch(() => []);
243
+ const sel = $container.find('#action-cms-slug').get(0);
244
+ (Array.isArray(actions) ? actions : []).forEach(a => {
245
+ const opt = document.createElement('option');
246
+ opt.value = a.slug;
247
+ opt.textContent = a.title || a.slug;
248
+ if (a.slug === savedActionSlug) opt.selected = true;
249
+ sel.appendChild(opt);
250
+ });
251
+ if (!actions.length) {
252
+ const opt = document.createElement('option');
253
+ opt.value = '';
254
+ opt.textContent = 'No actions available';
255
+ opt.disabled = true;
256
+ sel.appendChild(opt);
257
+ }
258
+ } catch {
259
+ // Actions endpoint unavailable (Pro feature not configured) - leave select empty
260
+ }
261
+ ```
262
+
263
+ - [ ] **Step 4: Verify syntax**
264
+
265
+ ```bash
266
+ node --check admin/js/views/form-editor.js
267
+ ```
268
+
269
+ Expected: no output.
270
+
271
+ - [ ] **Step 5: Commit**
272
+
273
+ ```bash
274
+ git add admin/js/views/form-editor.js
275
+ git commit -m "feat: collect, populate, and load CMS action + success redirect in form editor"
276
+ ```
277
+
278
+ ---
279
+
280
+ ## Chunk 3: Public site - handle redirect and inline success
281
+
282
+ ### Task 4: Update hydrateFormEmbeds to handle redirect
283
+
284
+ **Files:**
285
+
286
+ - Modify: `public/js/site.js`
287
+
288
+ The `onSubmit` handler is at line 497-506. Currently it awaits `H.post` without capturing the return value, then
289
+ immediately clears the form and calls `showResult`. Update it to check for `redirect` in the response.
290
+
291
+ - [ ] **Step 1: Capture response and handle redirect**
292
+
293
+ Locate this block (around line 497):
294
+
295
+ ```js
296
+ const onSubmit = async (formData) => {
297
+ try {
298
+ await H.post(`/api/forms/submit/${form.slug}`, formData);
299
+ while (el.firstChild) el.removeChild(el.firstChild);
300
+ showResult(el, s.successMessage || 'Thank you for your submission.', false);
301
+ } catch (err) {
302
+ showResult(el, err.message || 'Submission failed. Please try again.', true);
303
+ throw err; // re-throw so Domma knows the submit failed
304
+ }
305
+ };
306
+ ```
307
+
308
+ Replace with:
309
+
310
+ ```js
311
+ const onSubmit = async (formData) => {
312
+ try {
313
+ const result = await H.post(`/api/forms/submit/${form.slug}`, formData);
314
+ if (result?.redirect) {
315
+ window.location.href = result.redirect;
316
+ return;
317
+ }
318
+ while (el.firstChild) el.removeChild(el.firstChild);
319
+ showResult(el, result?.message || s.successMessage || 'Thank you for your submission.', false);
320
+ } catch (err) {
321
+ showResult(el, err.message || 'Submission failed. Please try again.', true);
322
+ throw err; // re-throw so Domma knows the submit failed
323
+ }
324
+ };
325
+ ```
326
+
327
+ - [ ] **Step 2: Verify syntax**
328
+
329
+ ```bash
330
+ node --check public/js/site.js
331
+ ```
332
+
333
+ Expected: no output.
334
+
335
+ - [ ] **Step 3: Commit**
336
+
337
+ ```bash
338
+ git add public/js/site.js
339
+ git commit -m "feat: handle redirect and success message response in form embed hydration"
340
+ ```
341
+
342
+ ---
343
+
344
+ ## Chunk 4: Example content + tutorial + documentation
345
+
346
+ ### Task 5: Create thank-you page and update enquiries form
347
+
348
+ **Files:**
349
+
350
+ - Create: `content/pages/thank-you.md`
351
+ - Modify: `content/forms/enquiries.json`
352
+
353
+ - [ ] **Step 1: Create thank-you page**
354
+
355
+ Create `content/pages/thank-you.md`:
356
+
357
+ ```markdown
358
+ ---
359
+ title: Thank You
360
+ slug: thank-you
361
+ description: Your message has been received
362
+ layout: default
363
+ status: published
364
+ sortOrder: 99
365
+ showInNav: false
366
+ sidebar: false
367
+ visibility: public
368
+ createdAt: '2026-03-12T00:00:00.000Z'
369
+ updatedAt: '2026-03-12T00:00:00.000Z'
370
+ ---
371
+
372
+ [hero variant="dark" size="md" fullwidth="true"]
373
+
374
+ # Thank You
375
+
376
+ [scribe]Your message has been received. We'll be in touch shortly.[/scribe]
377
+
378
+ [/hero]
379
+
380
+ [spacer /]
381
+
382
+ [card]
383
+
384
+ Thanks for getting in touch. We aim to respond within one business day.
385
+
386
+ [Back to Home](/)
387
+
388
+ [/card]
389
+ ```
390
+
391
+ - [ ] **Step 2: Update enquiries form to use successRedirect**
392
+
393
+ Read `content/forms/enquiries.json` first, then update the `settings` block to add:
394
+
395
+ ```json
396
+ "successRedirect": "/thank-you",
397
+ "actionSlug": null
398
+ ```
399
+
400
+ The `actionSlug` is `null` since this is a free-tier example (no MongoDB required). The redirect is the primary success
401
+ UX for this form.
402
+
403
+ - [ ] **Step 3: Update contact.md to note the form behaviour**
404
+
405
+ The contact page (`content/pages/contact.md`) already uses `<div data-form="enquiries"></div>`. No markup changes
406
+ needed - the redirect is configured in the form JSON. Optionally add a brief note in the page body:
407
+
408
+ ```markdown
409
+ [spacer /]
410
+
411
+ > **Note for developers:** This form is configured with a success redirect to `/thank-you`. To add a CMS Action trigger (Pro), set `actionSlug` in `content/forms/enquiries.json`.
412
+ ```
413
+
414
+ This is optional - remove if it feels too developer-facing for a public contact page.
415
+
416
+ - [ ] **Step 4: Commit**
417
+
418
+ ```bash
419
+ git add content/pages/thank-you.md content/forms/enquiries.json content/pages/contact.md
420
+ git commit -m "feat: add thank-you page and configure enquiries form with success redirect"
421
+ ```
422
+
423
+ ---
424
+
425
+ ### Task 6: Add tutorial card to tutorials.html
426
+
427
+ **Files:**
428
+
429
+ - Modify: `admin/js/templates/tutorials.html`
430
+
431
+ The file currently has one card: "Writing a Plugin". It ends around line 231-233. Add a new collapsible card after it.
432
+
433
+ - [ ] **Step 1: Add the tutorial card**
434
+
435
+ Locate the closing tags near the end of `tutorials.html`:
436
+
437
+ ```html
438
+ </div>
439
+ </div>
440
+ ```
441
+
442
+ Insert the new card before these closing tags:
443
+
444
+ ```html
445
+ <!-- Tutorial: Form Follow-Up -->
446
+ <div class="card card-collapsible mb-4">
447
+ <div class="card-header">
448
+ <h2>Form Follow-Up: Notifications &amp; Actions</h2>
449
+ </div>
450
+ <div class="card-body">
451
+
452
+ <p>Every form in Domma CMS can trigger up to four things after a submission is stored:</p>
453
+ <ol>
454
+ <li>Send an <strong>email notification</strong> to one or more recipients</li>
455
+ <li>POST to a <strong>webhook URL</strong></li>
456
+ <li>Execute a <strong>CMS Action</strong> (Pro)</li>
457
+ <li>Redirect the visitor to a <strong>success page</strong> (or show an inline message)</li>
458
+ </ol>
459
+ <p>These are configured per-form in the admin. Open any form in <a href="#/forms">Forms</a> and go to the
460
+ <strong>Settings</strong> and <strong>Actions</strong> tabs.</p>
461
+
462
+ <hr>
463
+
464
+ <h3>1. Email notification</h3>
465
+ <p>Go to the <strong>Actions</strong> tab of your form and enable <em>Send email on submit</em>. Enter one or
466
+ more comma-separated recipient addresses. This uses the SMTP settings configured in
467
+ <a href="#/settings">Site Settings → Email / SMTP</a>.</p>
468
+
469
+ <pre class="code-block"><code>Recipients: admin@example.com, team@example.com
470
+ Subject Prefix: [Contact Form]</code></pre>
471
+
472
+ <h3>2. Webhook</h3>
473
+ <p>Enable <em>POST to webhook on submit</em> and enter a URL. Domma will POST the following JSON body:</p>
474
+ <pre class="code-block"><code>{
475
+ "form": "enquiries",
476
+ "data": {
477
+ "full_name": "Jane Smith",
478
+ "email": "jane@example.com",
479
+ "message": "Hello!"
480
+ }
481
+ }</code></pre>
482
+ <p>Use this to integrate with Zapier, Make, Slack, or any HTTP endpoint.</p>
483
+
484
+ <h3>3. CMS Action (Pro)</h3>
485
+ <p>Actions are reusable workflow steps defined in <a href="#/actions">Actions</a>. A single action can chain
486
+ multiple steps: update a field, move an entry to another collection, send an email, call a webhook, or
487
+ delete an entry.</p>
488
+ <p>To wire an Action to a form:</p>
489
+ <ol>
490
+ <li>Create an Action in <a href="#/actions">Actions</a> targeting the same collection as your form.</li>
491
+ <li>Open the form in <a href="#/forms">Forms</a> → <strong>Actions</strong> tab → <strong>CMS Action</strong>
492
+ card.</li>
493
+ <li>Select the Action from the dropdown and save.</li>
494
+ </ol>
495
+ <p>The Action runs server-side, after the entry is saved. If it fails (e.g. MongoDB is not configured),
496
+ the submission is still stored - the action failure is non-fatal and logged as a warning.</p>
497
+
498
+ <h3>4. Success message vs. redirect</h3>
499
+ <p>After a successful submission the visitor sees one of two things:</p>
500
+ <ul>
501
+ <li><strong>Inline success message</strong> - the form is replaced by the text set in
502
+ <em>Settings → Success Message</em>. Good for simple acknowledgements.</li>
503
+ <li><strong>Page redirect</strong> - the visitor is sent to the URL set in
504
+ <em>Settings → Success Redirect URL</em>. Good for registration flows, checkouts, or when you want
505
+ a full thank-you page with additional content. <strong>Takes priority</strong> if both are set.</li>
506
+ </ul>
507
+ <p>Example: set <em>Success Redirect URL</em> to <code>/thank-you</code> and create a <code>thank-you.md</code>
508
+ page in the CMS with any content you like.</p>
509
+
510
+ <h3>Execution order</h3>
511
+ <p>On every submission, the pipeline runs in this fixed order:</p>
512
+ <ol>
513
+ <li>Validate fields + honeypot + rate limit</li>
514
+ <li>Store entry to collection</li>
515
+ <li>Send email (if enabled)</li>
516
+ <li>Call webhook (if enabled)</li>
517
+ <li>Execute CMS Action (if set)</li>
518
+ <li>Return success response → client redirects or shows message</li>
519
+ </ol>
520
+ <p>Steps 3-5 are non-fatal: a failure in any of them is logged as a warning but does not prevent the
521
+ submission from being stored or the success response from being returned.</p>
522
+
523
+ </div>
524
+ </div>
525
+ ```
526
+
527
+ - [ ] **Step 2: Commit**
528
+
529
+ ```bash
530
+ git add admin/js/templates/tutorials.html
531
+ git commit -m "docs: add form follow-up tutorial to admin tutorials page"
532
+ ```
533
+
534
+ ---
535
+
536
+ ### Task 7: Add documentation entry to documentation.html
537
+
538
+ **Files:**
539
+
540
+ - Modify: `admin/js/templates/documentation.html`
541
+
542
+ Find the section that covers Forms (search for `forms` or `submission` in the file). Add a concise reference block for
543
+ the two new fields.
544
+
545
+ - [ ] **Step 1: Locate the Forms section**
546
+
547
+ Search for the Forms card heading in `documentation.html`:
548
+
549
+ ```bash
550
+ grep -n "Forms\|submission\|Submit" admin/js/templates/documentation.html | head -20
551
+ ```
552
+
553
+ - [ ] **Step 2: Add new fields reference**
554
+
555
+ Within the Forms documentation card body, add after the existing content:
556
+
557
+ ```html
558
+ <h3>Form follow-up</h3>
559
+ <p>Two optional settings control what happens after a submission is stored:</p>
560
+ <table class="table" style="font-size:.9rem;">
561
+ <thead>
562
+ <tr><th>Field</th><th>Where</th><th>Description</th></tr>
563
+ </thead>
564
+ <tbody>
565
+ <tr>
566
+ <td><code>successMessage</code></td>
567
+ <td>Settings tab</td>
568
+ <td>Text shown inline after submission (replaces the form). Default: "Thank you for your submission."</td>
569
+ </tr>
570
+ <tr>
571
+ <td><code>successRedirect</code></td>
572
+ <td>Settings tab</td>
573
+ <td>URL to redirect the visitor on success. Takes priority over <code>successMessage</code> if set.</td>
574
+ </tr>
575
+ <tr>
576
+ <td><code>actionSlug</code></td>
577
+ <td>Actions tab → CMS Action</td>
578
+ <td>Slug of a CMS Action to execute after the entry is stored. Requires Pro (MongoDB). Non-fatal on failure.</td>
579
+ </tr>
580
+ </tbody>
581
+ </table>
582
+ <p>For the full walkthrough, see the
583
+ <a href="#/tutorials">Tutorials → Form Follow-Up: Notifications &amp; Actions</a> tutorial.</p>
584
+ ```
585
+
586
+ - [ ] **Step 3: Commit**
587
+
588
+ ```bash
589
+ git add admin/js/templates/documentation.html
590
+ git commit -m "docs: add form follow-up fields reference to documentation"
591
+ ```
592
+
593
+ ---
594
+
595
+ ## Verification
596
+
597
+ After a server restart, test the full flow:
598
+
599
+ ```bash
600
+ # 1. Submit the enquiries form via curl (no auth needed)
601
+ curl -s -X POST http://localhost:4096/api/forms/submit/enquiries \
602
+ -H 'Content-Type: application/json' \
603
+ -d '{"full_name":"Test User","email":"test@example.com","message":"Hello"}'
604
+
605
+ # Expected: {"ok":true,"message":"...","redirect":"/thank-you"}
606
+
607
+ # 2. Confirm the entry was stored
608
+ TOKEN=$(curl -s -X POST http://localhost:4096/api/auth/login \
609
+ -H 'Content-Type: application/json' \
610
+ -d '{"email":"darryl@dwit.uk","password":"YOUR_PASSWORD"}' | python3 -c "import sys,json; print(json.load(sys.stdin)['token'])")
611
+ curl -s http://localhost:4096/api/forms/enquiries/submissions \
612
+ -H "Authorization: Bearer $TOKEN" | python3 -m json.tool | head -20
613
+
614
+ # 3. Verify the thank-you page renders
615
+ curl -s http://localhost:4096/thank-you | grep -o '<title>[^<]*</title>'
616
+ ```
617
+
618
+ **Manual browser test:**
619
+
620
+ 1. Visit `/contact` - submit the enquiries form
621
+ 2. Should redirect to `/thank-you`
622
+ 3. Open the form editor for `enquiries` in the admin - verify the Success Redirect field shows `/thank-you`
623
+ 4. Admin tutorials page (`#/tutorials`) - verify the new card appears and is collapsible
624
+ 5. Admin documentation page (`#/documentation`) - verify the Forms section shows the new table