@sonicjs-cms/core 2.7.0 → 2.8.0

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 (96) hide show
  1. package/dist/{app-DV27cjPy.d.cts → app-CYEm1ytG.d.cts} +1 -0
  2. package/dist/{app-DV27cjPy.d.ts → app-CYEm1ytG.d.ts} +1 -0
  3. package/dist/{chunk-DNHJS6RN.js → chunk-34QIAULP.js} +4 -4
  4. package/dist/{chunk-DNHJS6RN.js.map → chunk-34QIAULP.js.map} +1 -1
  5. package/dist/{chunk-Y3EWJQ4D.js → chunk-3E76TKR5.js} +3 -3
  6. package/dist/{chunk-Y3EWJQ4D.js.map → chunk-3E76TKR5.js.map} +1 -1
  7. package/dist/{chunk-YRFAQ6MI.cjs → chunk-5CENPGR2.cjs} +219 -14
  8. package/dist/chunk-5CENPGR2.cjs.map +1 -0
  9. package/dist/{chunk-MYB5RY7H.cjs → chunk-5HMR2SJW.cjs} +4 -4
  10. package/dist/{chunk-MYB5RY7H.cjs.map → chunk-5HMR2SJW.cjs.map} +1 -1
  11. package/dist/{chunk-YHW27CBV.cjs → chunk-6FHNRRJ3.cjs} +190 -2
  12. package/dist/chunk-6FHNRRJ3.cjs.map +1 -0
  13. package/dist/{chunk-UISZ2MBW.js → chunk-BAWMAS5S.js} +5438 -1443
  14. package/dist/chunk-BAWMAS5S.js.map +1 -0
  15. package/dist/{chunk-F332TENF.js → chunk-CJYFSKH7.js} +4 -190
  16. package/dist/chunk-CJYFSKH7.js.map +1 -0
  17. package/dist/{chunk-3YNNVSMC.js → chunk-G44QUVNM.js} +90 -2
  18. package/dist/chunk-G44QUVNM.js.map +1 -0
  19. package/dist/{chunk-E2BXLXPW.cjs → chunk-GPTMGUFN.cjs} +4 -4
  20. package/dist/{chunk-E2BXLXPW.cjs.map → chunk-GPTMGUFN.cjs.map} +1 -1
  21. package/dist/chunk-H7AMQWVI.js +2466 -0
  22. package/dist/chunk-H7AMQWVI.js.map +1 -0
  23. package/dist/{chunk-CLIH2T74.js → chunk-J5WGMRSU.js} +189 -3
  24. package/dist/chunk-J5WGMRSU.js.map +1 -0
  25. package/dist/{chunk-L2IDZI7F.js → chunk-JDFPB6UW.js} +219 -14
  26. package/dist/chunk-JDFPB6UW.js.map +1 -0
  27. package/dist/{chunk-Y72M3MVX.cjs → chunk-MNFY6DWY.cjs} +13 -200
  28. package/dist/chunk-MNFY6DWY.cjs.map +1 -0
  29. package/dist/chunk-S6K2H2TS.cjs +2470 -0
  30. package/dist/chunk-S6K2H2TS.cjs.map +1 -0
  31. package/dist/{chunk-EHSZ6TAN.cjs → chunk-SHCYIZAN.cjs} +9 -2
  32. package/dist/chunk-SHCYIZAN.cjs.map +1 -0
  33. package/dist/{chunk-GRN3GHUG.js → chunk-VCH6HXVP.js} +9 -2
  34. package/dist/chunk-VCH6HXVP.js.map +1 -0
  35. package/dist/{chunk-7FOAMNTI.cjs → chunk-VNLR35GO.cjs} +90 -2
  36. package/dist/chunk-VNLR35GO.cjs.map +1 -0
  37. package/dist/{chunk-J7F3NPAP.cjs → chunk-YE2MU7CN.cjs} +5192 -1194
  38. package/dist/chunk-YE2MU7CN.cjs.map +1 -0
  39. package/dist/index.cjs +201 -607
  40. package/dist/index.cjs.map +1 -1
  41. package/dist/index.d.cts +3 -3
  42. package/dist/index.d.ts +3 -3
  43. package/dist/index.js +67 -473
  44. package/dist/index.js.map +1 -1
  45. package/dist/middleware.cjs +23 -23
  46. package/dist/middleware.d.cts +1 -1
  47. package/dist/middleware.d.ts +1 -1
  48. package/dist/middleware.js +2 -2
  49. package/dist/migrations-7JGSFOCM.cjs +13 -0
  50. package/dist/{migrations-LEMFV2ND.cjs.map → migrations-7JGSFOCM.cjs.map} +1 -1
  51. package/dist/migrations-YB77VTVF.js +4 -0
  52. package/dist/{migrations-RKQES6XY.js.map → migrations-YB77VTVF.js.map} +1 -1
  53. package/dist/{plugin-bootstrap-CB-xaBfK.d.ts → plugin-bootstrap-C7Mj00Ud.d.ts} +2455 -1
  54. package/dist/{plugin-bootstrap-U-cw9jn3.d.cts → plugin-bootstrap-DKB5f8-E.d.cts} +2455 -1
  55. package/dist/plugins.cjs +14 -14
  56. package/dist/plugins.js +2 -2
  57. package/dist/routes.cjs +39 -27
  58. package/dist/routes.d.cts +126 -53
  59. package/dist/routes.d.ts +126 -53
  60. package/dist/routes.js +7 -7
  61. package/dist/services.cjs +14 -14
  62. package/dist/services.d.cts +1 -1
  63. package/dist/services.d.ts +1 -1
  64. package/dist/services.js +2 -2
  65. package/dist/templates.cjs +25 -17
  66. package/dist/templates.d.cts +21 -1
  67. package/dist/templates.d.ts +21 -1
  68. package/dist/templates.js +2 -2
  69. package/dist/utils.cjs +14 -14
  70. package/dist/utils.js +1 -1
  71. package/migrations/014_fix_plugin_registry.sql +1 -1
  72. package/migrations/020_add_email_plugin.sql +1 -1
  73. package/migrations/026_add_otp_login.sql +1 -1
  74. package/migrations/029_add_forms_system.sql +184 -0
  75. package/migrations/030_add_turnstile_to_forms.sql +14 -0
  76. package/package.json +2 -2
  77. package/dist/chunk-3YNNVSMC.js.map +0 -1
  78. package/dist/chunk-7FOAMNTI.cjs.map +0 -1
  79. package/dist/chunk-AYPF6C4D.cjs +0 -76
  80. package/dist/chunk-AYPF6C4D.cjs.map +0 -1
  81. package/dist/chunk-CLIH2T74.js.map +0 -1
  82. package/dist/chunk-EHSZ6TAN.cjs.map +0 -1
  83. package/dist/chunk-F332TENF.js.map +0 -1
  84. package/dist/chunk-GRN3GHUG.js.map +0 -1
  85. package/dist/chunk-J7F3NPAP.cjs.map +0 -1
  86. package/dist/chunk-L2IDZI7F.js.map +0 -1
  87. package/dist/chunk-UISZ2MBW.js.map +0 -1
  88. package/dist/chunk-V3KVSEG6.js +0 -74
  89. package/dist/chunk-V3KVSEG6.js.map +0 -1
  90. package/dist/chunk-Y72M3MVX.cjs.map +0 -1
  91. package/dist/chunk-YHW27CBV.cjs.map +0 -1
  92. package/dist/chunk-YRFAQ6MI.cjs.map +0 -1
  93. package/dist/migrations-LEMFV2ND.cjs +0 -13
  94. package/dist/migrations-RKQES6XY.js +0 -4
  95. package/migrations/025_rename_mdxeditor_to_easy_mdx.sql +0 -22
  96. /package/migrations/{029_ai_search_plugin.sql → 031_ai_search_plugin.sql} +0 -0
@@ -0,0 +1,2466 @@
1
+ import { init_admin_layout_catalyst_template, init_logo_template, renderAdminLayoutCatalyst } from './chunk-VCH6HXVP.js';
2
+
3
+ // src/templates/filter-bar.template.ts
4
+ function renderFilterBar(data) {
5
+ return `
6
+ <div class="rounded-xl bg-white dark:bg-zinc-900 shadow-sm ring-1 ring-zinc-950/5 dark:ring-white/10 p-6 mb-6">
7
+ <form id="filter-form" class="flex flex-wrap gap-4 items-center">
8
+ ${data.filters.map((filter) => `
9
+ <div class="flex items-center space-x-2">
10
+ <label class="text-sm font-medium text-zinc-500 dark:text-zinc-400">${filter.label}:</label>
11
+ <select
12
+ name="${filter.name}"
13
+ class="rounded-lg bg-white dark:bg-zinc-800 px-3 py-2 text-sm text-zinc-950 dark:text-white ring-1 ring-inset ring-zinc-950/10 dark:ring-white/10 focus:ring-2 focus:ring-blue-600 dark:focus:ring-blue-500 focus:outline-none transition-colors"
14
+ onchange="updateFilters()"
15
+ >
16
+ ${filter.options.map((option) => `
17
+ <option value="${option.value}" ${option.selected ? "selected" : ""}>
18
+ ${option.label}
19
+ </option>
20
+ `).join("")}
21
+ </select>
22
+ </div>
23
+ `).join("")}
24
+
25
+ ${data.actions && data.actions.length > 0 ? `
26
+ <div class="flex items-center space-x-2 ml-auto">
27
+ ${data.actions.map((action) => `
28
+ <button
29
+ type="button"
30
+ class="inline-flex items-center rounded-lg bg-white dark:bg-zinc-800 px-3 py-2 text-sm font-semibold text-zinc-950 dark:text-white ring-1 ring-inset ring-zinc-950/10 dark:ring-white/10 hover:bg-zinc-50 dark:hover:bg-zinc-700 transition-colors"
31
+ ${action.onclick ? `onclick="${action.onclick}"` : ""}
32
+ ${action.hxGet ? `hx-get="${action.hxGet}"` : ""}
33
+ ${action.hxTarget ? `hx-target="${action.hxTarget}"` : ""}
34
+ >
35
+ ${action.label}
36
+ </button>
37
+ `).join("")}
38
+ </div>
39
+ ` : ""}
40
+ </form>
41
+
42
+ <script>
43
+ function updateFilters() {
44
+ const form = document.getElementById('filter-form');
45
+ const formData = new FormData(form);
46
+ const params = new URLSearchParams(window.location.search);
47
+
48
+ // Update params with form values
49
+ for (const [key, value] of formData.entries()) {
50
+ if (value) {
51
+ params.set(key, value);
52
+ } else {
53
+ params.delete(key);
54
+ }
55
+ }
56
+
57
+ // Reset to page 1 when filters change
58
+ params.set('page', '1');
59
+
60
+ // Update URL and reload
61
+ window.location.href = window.location.pathname + '?' + params.toString();
62
+ }
63
+ </script>
64
+ </div>
65
+ `;
66
+ }
67
+
68
+ // src/templates/index.ts
69
+ init_admin_layout_catalyst_template();
70
+ init_logo_template();
71
+
72
+ // src/templates/pages/admin-forms-docs.template.ts
73
+ init_admin_layout_catalyst_template();
74
+ function renderFormsDocsPage(data) {
75
+ const pageContent = `
76
+ <style>
77
+ /* Light theme matching examples page */
78
+ .docs-container {
79
+ display: flex;
80
+ gap: 0;
81
+ min-height: calc(100vh - 200px);
82
+ background: #ffffff;
83
+ border-radius: 12px;
84
+ overflow: hidden;
85
+ box-shadow: 0 1px 3px rgba(0,0,0,0.1);
86
+ }
87
+
88
+ .docs-sidebar {
89
+ width: 280px;
90
+ background: #f8f9fa;
91
+ border-right: 1px solid #e0e0e0;
92
+ padding: 1.5rem 0;
93
+ overflow-y: auto;
94
+ }
95
+
96
+ .docs-sidebar h3 {
97
+ font-size: 0.75rem;
98
+ font-weight: 600;
99
+ text-transform: uppercase;
100
+ letter-spacing: 0.05em;
101
+ color: #6b7280;
102
+ padding: 0 1.5rem;
103
+ margin-bottom: 0.75rem;
104
+ }
105
+
106
+ .docs-nav {
107
+ list-style: none;
108
+ padding: 0;
109
+ margin: 0 0 2rem 0;
110
+ }
111
+
112
+ .docs-nav li {
113
+ margin: 0;
114
+ }
115
+
116
+ .docs-nav a {
117
+ display: block;
118
+ padding: 0.75rem 1.5rem;
119
+ color: #374151;
120
+ text-decoration: none;
121
+ font-size: 0.875rem;
122
+ transition: all 0.2s;
123
+ border-left: 3px solid transparent;
124
+ }
125
+
126
+ .docs-nav a:hover {
127
+ background: #e9ecef;
128
+ color: #1f2937;
129
+ }
130
+
131
+ .docs-nav a.active {
132
+ background: #e3f2fd;
133
+ color: #1976d2;
134
+ border-left-color: #1976d2;
135
+ font-weight: 500;
136
+ }
137
+
138
+ .docs-content {
139
+ flex: 1;
140
+ padding: 2rem;
141
+ background: #ffffff;
142
+ overflow-y: auto;
143
+ }
144
+
145
+ .doc-section {
146
+ display: none;
147
+ }
148
+
149
+ .doc-section.active {
150
+ display: block;
151
+ }
152
+
153
+ .doc-header {
154
+ margin-bottom: 2rem;
155
+ padding-bottom: 1rem;
156
+ border-bottom: 2px solid #e0e0e0;
157
+ }
158
+
159
+ .doc-header h2 {
160
+ font-size: 1.875rem;
161
+ font-weight: 700;
162
+ color: #1f2937;
163
+ margin-bottom: 0.5rem;
164
+ }
165
+
166
+ .doc-header p {
167
+ color: #6b7280;
168
+ font-size: 1rem;
169
+ }
170
+
171
+ .field-example {
172
+ background: #f8f9fa;
173
+ border: 1px solid #e0e0e0;
174
+ border-radius: 8px;
175
+ padding: 1.5rem;
176
+ margin-bottom: 1.5rem;
177
+ }
178
+
179
+ .field-example h3 {
180
+ font-size: 1.125rem;
181
+ font-weight: 600;
182
+ color: #1f2937;
183
+ margin-bottom: 0.75rem;
184
+ }
185
+
186
+ .field-example p {
187
+ color: #6b7280;
188
+ font-size: 0.875rem;
189
+ margin-bottom: 1rem;
190
+ }
191
+
192
+ .code-block {
193
+ background: #1e1e1e;
194
+ color: #d4d4d4;
195
+ padding: 1rem;
196
+ border-radius: 6px;
197
+ overflow-x: auto;
198
+ font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
199
+ font-size: 0.875rem;
200
+ line-height: 1.6;
201
+ }
202
+
203
+ .info-box {
204
+ background: #e3f2fd;
205
+ border: 1px solid #90caf9;
206
+ border-radius: 8px;
207
+ padding: 1rem;
208
+ margin-bottom: 1.5rem;
209
+ color: #1565c0;
210
+ font-size: 0.875rem;
211
+ }
212
+
213
+ .info-box strong {
214
+ font-weight: 600;
215
+ }
216
+ </style>
217
+
218
+ <div class="mb-6">
219
+ <div class="flex items-center gap-3 mb-4">
220
+ <a href="/admin/forms" class="inline-flex items-center text-sm text-zinc-600 dark:text-zinc-400 hover:text-zinc-900 dark:hover:text-white transition-colors">
221
+ <svg class="w-4 h-4 mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
222
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7"/>
223
+ </svg>
224
+ Back to Forms
225
+ </a>
226
+ </div>
227
+ <h1 class="text-3xl font-bold text-zinc-950 dark:text-white">Forms Quick Reference</h1>
228
+ <p class="mt-2 text-zinc-600 dark:text-zinc-400">Comprehensive guide to all Form.io field types and features</p>
229
+ </div>
230
+
231
+ <div class="docs-container">
232
+ <!-- Sidebar Navigation -->
233
+ <aside class="docs-sidebar">
234
+ <h3>Quick Start</h3>
235
+ <ul class="docs-nav">
236
+ <li><a href="#overview" class="doc-link active">Overview</a></li>
237
+ <li><a href="#getting-started" class="doc-link">Getting Started</a></li>
238
+ </ul>
239
+
240
+ <h3>Basic Fields</h3>
241
+ <ul class="docs-nav">
242
+ <li><a href="#textfield" class="doc-link">Text Field</a></li>
243
+ <li><a href="#textarea" class="doc-link">Text Area</a></li>
244
+ <li><a href="#number" class="doc-link">Number</a></li>
245
+ <li><a href="#password" class="doc-link">Password</a></li>
246
+ <li><a href="#email" class="doc-link">Email</a></li>
247
+ <li><a href="#url" class="doc-link">URL</a></li>
248
+ <li><a href="#phonenumber" class="doc-link">Phone Number</a></li>
249
+ </ul>
250
+
251
+ <h3>Date & Time</h3>
252
+ <ul class="docs-nav">
253
+ <li><a href="#datetime" class="doc-link">Date/Time</a></li>
254
+ <li><a href="#day" class="doc-link">Day</a></li>
255
+ <li><a href="#time" class="doc-link">Time</a></li>
256
+ </ul>
257
+
258
+ <h3>Selection Fields</h3>
259
+ <ul class="docs-nav">
260
+ <li><a href="#select" class="doc-link">Select Dropdown</a></li>
261
+ <li><a href="#selectboxes" class="doc-link">Select Boxes</a></li>
262
+ <li><a href="#radio" class="doc-link">Radio</a></li>
263
+ <li><a href="#checkbox" class="doc-link">Checkbox</a></li>
264
+ </ul>
265
+
266
+ <h3>Advanced Fields</h3>
267
+ <ul class="docs-nav">
268
+ <li><a href="#currency" class="doc-link">Currency</a></li>
269
+ <li><a href="#tags" class="doc-link">Tags</a></li>
270
+ <li><a href="#survey" class="doc-link">Survey</a></li>
271
+ <li><a href="#signature" class="doc-link">Signature</a></li>
272
+ <li><a href="#file" class="doc-link">File Upload</a></li>
273
+ <li><a href="#address" class="doc-link">Address</a></li>
274
+ <li><a href="#turnstile" class="doc-link">\u{1F6E1}\uFE0F Turnstile</a></li>
275
+ </ul>
276
+
277
+ <h3>Layout Components</h3>
278
+ <ul class="docs-nav">
279
+ <li><a href="#panel" class="doc-link">Panel</a></li>
280
+ <li><a href="#columns" class="doc-link">Columns</a></li>
281
+ <li><a href="#tabs" class="doc-link">Tabs</a></li>
282
+ <li><a href="#table" class="doc-link">Table</a></li>
283
+ <li><a href="#fieldset" class="doc-link">Fieldset</a></li>
284
+ </ul>
285
+
286
+ <h3>Data Components</h3>
287
+ <ul class="docs-nav">
288
+ <li><a href="#datagrid" class="doc-link">Data Grid</a></li>
289
+ <li><a href="#editgrid" class="doc-link">Edit Grid</a></li>
290
+ </ul>
291
+
292
+ <h3>Guides</h3>
293
+ <ul class="docs-nav">
294
+ <li><a href="#wizard" class="doc-link">Multi-Page Wizards</a></li>
295
+ <li><a href="#embedding" class="doc-link">Embedding Forms</a></li>
296
+ <li><a href="#validation" class="doc-link">Validation</a></li>
297
+ <li><a href="#conditional" class="doc-link">Conditional Logic</a></li>
298
+ </ul>
299
+ </aside>
300
+
301
+ <!-- Main Content Area -->
302
+ <main class="docs-content">
303
+
304
+ <!-- Overview Section -->
305
+ <section id="overview" class="doc-section active">
306
+ <div class="doc-header">
307
+ <h2>\u{1F4DA} Overview</h2>
308
+ <p>Complete reference for SonicJS Forms powered by Form.io</p>
309
+ </div>
310
+
311
+ <div class="info-box">
312
+ <strong>\u{1F4A1} New to SonicJS Forms?</strong> Start with "Getting Started" in the sidebar, then explore the field types you need.
313
+ </div>
314
+
315
+ <h3 style="font-size: 1.25rem; font-weight: 600; margin-bottom: 1rem; color: #1f2937;">Key Features</h3>
316
+ <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 1rem; margin-bottom: 2rem;">
317
+ <div style="padding: 1rem; background: #f0fdf4; border-radius: 8px;">
318
+ <strong style="color: #16a34a;">\u2713 Visual Builder</strong>
319
+ <p style="font-size: 0.875rem; color: #15803d; margin-top: 0.25rem;">Drag-and-drop interface</p>
320
+ </div>
321
+ <div style="padding: 1rem; background: #f0fdf4; border-radius: 8px;">
322
+ <strong style="color: #16a34a;">\u2713 40+ Field Types</strong>
323
+ <p style="font-size: 0.875rem; color: #15803d; margin-top: 0.25rem;">Text, date, file, signature, etc.</p>
324
+ </div>
325
+ <div style="padding: 1rem; background: #f0fdf4; border-radius: 8px;">
326
+ <strong style="color: #16a34a;">\u2713 Multi-Page Wizards</strong>
327
+ <p style="font-size: 0.875rem; color: #15803d; margin-top: 0.25rem;">Step-by-step forms</p>
328
+ </div>
329
+ <div style="padding: 1rem; background: #f0fdf4; border-radius: 8px;">
330
+ <strong style="color: #16a34a;">\u2713 Headless API</strong>
331
+ <p style="font-size: 0.875rem; color: #15803d; margin-top: 0.25rem;">JSON schema & REST API</p>
332
+ </div>
333
+ <div style="padding: 1rem; background: #f0fdf4; border-radius: 8px;">
334
+ <strong style="color: #16a34a;">\u2713 File Uploads</strong>
335
+ <p style="font-size: 0.875rem; color: #15803d; margin-top: 0.25rem;">Cloudflare R2 storage</p>
336
+ </div>
337
+ <div style="padding: 1rem; background: #f0fdf4; border-radius: 8px;">
338
+ <strong style="color: #16a34a;">\u2713 100% Open Source</strong>
339
+ <p style="font-size: 0.875rem; color: #15803d; margin-top: 0.25rem;">No vendor lock-in</p>
340
+ </div>
341
+ </div>
342
+
343
+ <h3 style="font-size: 1.25rem; font-weight: 600; margin-bottom: 1rem; color: #1f2937;">Quick Links</h3>
344
+ <div style="display: flex; gap: 1rem; flex-wrap: wrap;">
345
+ <a href="/admin/forms/examples" style="display: inline-flex; align-items: center; padding: 0.625rem 1.25rem; background: #3b82f6; color: white; border-radius: 6px; text-decoration: none; font-size: 0.875rem; font-weight: 500;">
346
+ <svg style="width: 1rem; height: 1rem; margin-right: 0.5rem;" fill="none" stroke="currentColor" viewBox="0 0 24 24">
347
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/>
348
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"/>
349
+ </svg>
350
+ View Examples
351
+ </a>
352
+ <a href="/admin/forms/new" style="display: inline-flex; align-items: center; padding: 0.625rem 1.25rem; background: #10b981; color: white; border-radius: 6px; text-decoration: none; font-size: 0.875rem; font-weight: 500;">
353
+ <svg style="width: 1rem; height: 1rem; margin-right: 0.5rem;" fill="currentColor" viewBox="0 0 20 20">
354
+ <path fill-rule="evenodd" d="M10 3a1 1 0 011 1v5h5a1 1 0 110 2h-5v5a1 1 0 11-2 0v-5H4a1 1 0 110-2h5V4a1 1 0 011-1z" clip-rule="evenodd"/>
355
+ </svg>
356
+ Create New Form
357
+ </a>
358
+ <a href="/admin/forms" style="display: inline-flex; align-items: center; padding: 0.625rem 1.25rem; background: #6b7280; color: white; border-radius: 6px; text-decoration: none; font-size: 0.875rem; font-weight: 500;">
359
+ <svg style="width: 1rem; height: 1rem; margin-right: 0.5rem;" fill="none" stroke="currentColor" viewBox="0 0 24 24">
360
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 10h16M4 14h16M4 18h16"/>
361
+ </svg>
362
+ View All Forms
363
+ </a>
364
+ </div>
365
+ </section>
366
+
367
+ <!-- Getting Started Section -->
368
+ <section id="getting-started" class="doc-section">
369
+ <div class="doc-header">
370
+ <h2>\u{1F680} Getting Started</h2>
371
+ <p>Create your first form in 3 easy steps</p>
372
+ </div>
373
+
374
+ <div class="field-example">
375
+ <h3>Step 1: Create a Form</h3>
376
+ <p>Navigate to <code>/admin/forms</code> and click "Create Form"</p>
377
+ </div>
378
+
379
+ <div class="field-example">
380
+ <h3>Step 2: Build Your Form</h3>
381
+ <p>Drag and drop field types from the sidebar to build your form visually</p>
382
+ </div>
383
+
384
+ <div class="field-example">
385
+ <h3>Step 3: Publish & Embed</h3>
386
+ <p>Save your form and access it via:</p>
387
+ <ul style="margin-left: 1.5rem; margin-top: 0.5rem; list-style-type: disc;">
388
+ <li><code>/forms/your-form-name</code> - Public form page</li>
389
+ <li><code>/forms/your-form-name/schema</code> - JSON schema API</li>
390
+ <li><code>/api/forms/:id/submit</code> - Submission endpoint</li>
391
+ </ul>
392
+ </div>
393
+ </section>
394
+
395
+ <!-- Text Field -->
396
+ <section id="textfield" class="doc-section">
397
+ <div class="doc-header">
398
+ <h2>\u{1F4DD} Text Field</h2>
399
+ <p>Single-line text input for short text values</p>
400
+ </div>
401
+
402
+ <div class="field-example">
403
+ <h3>Basic Usage</h3>
404
+ <p>Most common field type for names, titles, and short text</p>
405
+ <pre class="code-block">{
406
+ "type": "textfield",
407
+ "key": "firstName",
408
+ "label": "First Name",
409
+ "placeholder": "Enter your first name",
410
+ "validate": {
411
+ "required": true,
412
+ "minLength": 2,
413
+ "maxLength": 50
414
+ }
415
+ }</pre>
416
+ </div>
417
+
418
+ <div class="info-box">
419
+ <strong>\u{1F4A1} Pro Tip:</strong> Use <code>inputMask</code> for formatted input like SSN or custom patterns.
420
+ </div>
421
+ </section>
422
+
423
+ <!-- Text Area -->
424
+ <section id="textarea" class="doc-section">
425
+ <div class="doc-header">
426
+ <h2>\u{1F4C4} Text Area</h2>
427
+ <p>Multi-line text input for longer text content</p>
428
+ </div>
429
+
430
+ <div class="field-example">
431
+ <h3>Basic Usage</h3>
432
+ <p>Perfect for comments, descriptions, and multi-line text</p>
433
+ <pre class="code-block">{
434
+ "type": "textarea",
435
+ "key": "comments",
436
+ "label": "Additional Comments",
437
+ "placeholder": "Enter your comments here...",
438
+ "rows": 5,
439
+ "validate": {
440
+ "required": false,
441
+ "maxLength": 1000
442
+ }
443
+ }</pre>
444
+ </div>
445
+ </section>
446
+
447
+ <!-- Number Field -->
448
+ <section id="number" class="doc-section">
449
+ <div class="doc-header">
450
+ <h2>\u{1F522} Number</h2>
451
+ <p>Numeric input with validation</p>
452
+ </div>
453
+
454
+ <div class="field-example">
455
+ <h3>Basic Usage</h3>
456
+ <p>For ages, quantities, scores, and any numeric value</p>
457
+ <pre class="code-block">{
458
+ "type": "number",
459
+ "key": "age",
460
+ "label": "Age",
461
+ "placeholder": "18",
462
+ "validate": {
463
+ "required": true,
464
+ "min": 18,
465
+ "max": 120
466
+ }
467
+ }</pre>
468
+ </div>
469
+ </section>
470
+
471
+ <!-- Password Field -->
472
+ <section id="password" class="doc-section">
473
+ <div class="doc-header">
474
+ <h2>\u{1F512} Password</h2>
475
+ <p>Masked text input for sensitive data</p>
476
+ </div>
477
+
478
+ <div class="field-example">
479
+ <h3>Basic Usage</h3>
480
+ <p>Automatically masks input for security</p>
481
+ <pre class="code-block">{
482
+ "type": "password",
483
+ "key": "password",
484
+ "label": "Password",
485
+ "placeholder": "Enter password",
486
+ "validate": {
487
+ "required": true,
488
+ "minLength": 8,
489
+ "pattern": "^(?=.*[A-Za-z])(?=.*\\d)[A-Za-z\\d]{8,}$"
490
+ }
491
+ }</pre>
492
+ </div>
493
+ </section>
494
+
495
+ <!-- Email Field -->
496
+ <section id="email" class="doc-section">
497
+ <div class="doc-header">
498
+ <h2>\u{1F4E7} Email</h2>
499
+ <p>Email input with automatic validation</p>
500
+ </div>
501
+
502
+ <div class="field-example">
503
+ <h3>Basic Usage</h3>
504
+ <p>Validates email format automatically</p>
505
+ <pre class="code-block">{
506
+ "type": "email",
507
+ "key": "email",
508
+ "label": "Email Address",
509
+ "placeholder": "you@example.com",
510
+ "validate": {
511
+ "required": true
512
+ }
513
+ }</pre>
514
+ </div>
515
+ </section>
516
+
517
+ <!-- URL Field -->
518
+ <section id="url" class="doc-section">
519
+ <div class="doc-header">
520
+ <h2>\u{1F310} URL</h2>
521
+ <p>URL input with validation</p>
522
+ </div>
523
+
524
+ <div class="field-example">
525
+ <h3>Basic Usage</h3>
526
+ <p>Validates URL format (http/https)</p>
527
+ <pre class="code-block">{
528
+ "type": "url",
529
+ "key": "website",
530
+ "label": "Website",
531
+ "placeholder": "https://example.com",
532
+ "validate": {
533
+ "required": false
534
+ }
535
+ }</pre>
536
+ </div>
537
+ </section>
538
+
539
+ <!-- Phone Number Field -->
540
+ <section id="phonenumber" class="doc-section">
541
+ <div class="doc-header">
542
+ <h2>\u{1F4DE} Phone Number</h2>
543
+ <p>Phone number input with formatting</p>
544
+ </div>
545
+
546
+ <div class="field-example">
547
+ <h3>Basic Usage</h3>
548
+ <p>Automatically formats phone numbers</p>
549
+ <pre class="code-block">{
550
+ "type": "phoneNumber",
551
+ "key": "phone",
552
+ "label": "Phone Number",
553
+ "placeholder": "(555) 555-5555",
554
+ "validate": {
555
+ "required": true
556
+ }
557
+ }</pre>
558
+ </div>
559
+ </section>
560
+
561
+ <!-- DateTime Field -->
562
+ <section id="datetime" class="doc-section">
563
+ <div class="doc-header">
564
+ <h2>\u{1F4C5} Date/Time</h2>
565
+ <p>Date and time picker</p>
566
+ </div>
567
+
568
+ <div class="field-example">
569
+ <h3>Basic Usage</h3>
570
+ <p>Interactive date/time picker with format control</p>
571
+ <pre class="code-block">{
572
+ "type": "datetime",
573
+ "key": "appointmentDateTime",
574
+ "label": "Appointment Date & Time",
575
+ "format": "yyyy-MM-dd hh:mm a",
576
+ "enableTime": true,
577
+ "validate": {
578
+ "required": true
579
+ }
580
+ }</pre>
581
+ </div>
582
+ </section>
583
+
584
+ <!-- Day Field -->
585
+ <section id="day" class="doc-section">
586
+ <div class="doc-header">
587
+ <h2>\u{1F4C6} Day</h2>
588
+ <p>Day/Month/Year selector</p>
589
+ </div>
590
+
591
+ <div class="field-example">
592
+ <h3>Basic Usage</h3>
593
+ <p>Separate dropdowns for day, month, and year</p>
594
+ <pre class="code-block">{
595
+ "type": "day",
596
+ "key": "birthDate",
597
+ "label": "Date of Birth",
598
+ "fields": {
599
+ "day": { "placeholder": "Day" },
600
+ "month": { "placeholder": "Month" },
601
+ "year": { "placeholder": "Year" }
602
+ },
603
+ "validate": {
604
+ "required": true
605
+ }
606
+ }</pre>
607
+ </div>
608
+ </section>
609
+
610
+ <!-- Time Field -->
611
+ <section id="time" class="doc-section">
612
+ <div class="doc-header">
613
+ <h2>\u{1F550} Time</h2>
614
+ <p>Time picker (hours and minutes)</p>
615
+ </div>
616
+
617
+ <div class="field-example">
618
+ <h3>Basic Usage</h3>
619
+ <p>Select time in HH:MM format</p>
620
+ <pre class="code-block">{
621
+ "type": "time",
622
+ "key": "preferredTime",
623
+ "label": "Preferred Contact Time",
624
+ "validate": {
625
+ "required": false
626
+ }
627
+ }</pre>
628
+ </div>
629
+ </section>
630
+
631
+ <!-- Select Field -->
632
+ <section id="select" class="doc-section">
633
+ <div class="doc-header">
634
+ <h2>\u{1F53D} Select Dropdown</h2>
635
+ <p>Single selection dropdown</p>
636
+ </div>
637
+
638
+ <div class="field-example">
639
+ <h3>Basic Usage</h3>
640
+ <p>Choose one option from a list</p>
641
+ <pre class="code-block">{
642
+ "type": "select",
643
+ "key": "country",
644
+ "label": "Country",
645
+ "placeholder": "Select your country",
646
+ "data": {
647
+ "values": [
648
+ { "label": "United States", "value": "us" },
649
+ { "label": "Canada", "value": "ca" },
650
+ { "label": "United Kingdom", "value": "uk" },
651
+ { "label": "Australia", "value": "au" }
652
+ ]
653
+ },
654
+ "validate": {
655
+ "required": true
656
+ }
657
+ }</pre>
658
+ </div>
659
+ </section>
660
+
661
+ <!-- Select Boxes Field -->
662
+ <section id="selectboxes" class="doc-section">
663
+ <div class="doc-header">
664
+ <h2>\u2611\uFE0F Select Boxes</h2>
665
+ <p>Multiple checkbox selections</p>
666
+ </div>
667
+
668
+ <div class="field-example">
669
+ <h3>Basic Usage</h3>
670
+ <p>Select multiple options with checkboxes</p>
671
+ <pre class="code-block">{
672
+ "type": "selectboxes",
673
+ "key": "interests",
674
+ "label": "Areas of Interest",
675
+ "values": [
676
+ { "label": "Sports", "value": "sports" },
677
+ { "label": "Music", "value": "music" },
678
+ { "label": "Technology", "value": "tech" },
679
+ { "label": "Travel", "value": "travel" }
680
+ ]
681
+ }</pre>
682
+ </div>
683
+ </section>
684
+
685
+ <!-- Radio Field -->
686
+ <section id="radio" class="doc-section">
687
+ <div class="doc-header">
688
+ <h2>\u{1F518} Radio</h2>
689
+ <p>Single selection with radio buttons</p>
690
+ </div>
691
+
692
+ <div class="field-example">
693
+ <h3>Basic Usage</h3>
694
+ <p>Choose one option from radio buttons</p>
695
+ <pre class="code-block">{
696
+ "type": "radio",
697
+ "key": "gender",
698
+ "label": "Gender",
699
+ "values": [
700
+ { "label": "Male", "value": "male" },
701
+ { "label": "Female", "value": "female" },
702
+ { "label": "Non-binary", "value": "nonbinary" },
703
+ { "label": "Prefer not to say", "value": "other" }
704
+ ],
705
+ "validate": {
706
+ "required": true
707
+ }
708
+ }</pre>
709
+ </div>
710
+ </section>
711
+
712
+ <!-- Checkbox Field -->
713
+ <section id="checkbox" class="doc-section">
714
+ <div class="doc-header">
715
+ <h2>\u2705 Checkbox</h2>
716
+ <p>Single checkbox for boolean values</p>
717
+ </div>
718
+
719
+ <div class="field-example">
720
+ <h3>Basic Usage</h3>
721
+ <p>For agreements, subscriptions, and yes/no options</p>
722
+ <pre class="code-block">{
723
+ "type": "checkbox",
724
+ "key": "newsletter",
725
+ "label": "Subscribe to newsletter",
726
+ "validate": {
727
+ "required": false
728
+ }
729
+ }
730
+
731
+ // Required checkbox (terms agreement)
732
+ {
733
+ "type": "checkbox",
734
+ "key": "terms",
735
+ "label": "I agree to the terms and conditions",
736
+ "validate": {
737
+ "required": true
738
+ }
739
+ }</pre>
740
+ </div>
741
+ </section>
742
+
743
+ <!-- Currency Field -->
744
+ <section id="currency" class="doc-section">
745
+ <div class="doc-header">
746
+ <h2>\u{1F4B0} Currency</h2>
747
+ <p>Formatted currency input</p>
748
+ </div>
749
+
750
+ <div class="field-example">
751
+ <h3>Basic Usage</h3>
752
+ <p>Automatically formats with currency symbol</p>
753
+ <pre class="code-block">{
754
+ "type": "currency",
755
+ "key": "salary",
756
+ "label": "Expected Salary",
757
+ "currency": "USD",
758
+ "placeholder": "$50,000",
759
+ "validate": {
760
+ "required": true,
761
+ "min": 0
762
+ }
763
+ }</pre>
764
+ </div>
765
+ </section>
766
+
767
+ <!-- Tags Field -->
768
+ <section id="tags" class="doc-section">
769
+ <div class="doc-header">
770
+ <h2>\u{1F3F7}\uFE0F Tags</h2>
771
+ <p>Multi-value tag input</p>
772
+ </div>
773
+
774
+ <div class="field-example">
775
+ <h3>Basic Usage</h3>
776
+ <p>Type and press Enter to add tags</p>
777
+ <pre class="code-block">{
778
+ "type": "tags",
779
+ "key": "skills",
780
+ "label": "Skills",
781
+ "placeholder": "Type and press Enter (e.g. JavaScript, Python)",
782
+ "validate": {
783
+ "required": false
784
+ }
785
+ }</pre>
786
+ </div>
787
+ </section>
788
+
789
+ <!-- Survey Field -->
790
+ <section id="survey" class="doc-section">
791
+ <div class="doc-header">
792
+ <h2>\u{1F4CA} Survey</h2>
793
+ <p>Matrix-style rating questions</p>
794
+ </div>
795
+
796
+ <div class="field-example">
797
+ <h3>Basic Usage</h3>
798
+ <p>Multiple questions with rating scale</p>
799
+ <pre class="code-block">{
800
+ "type": "survey",
801
+ "key": "satisfaction",
802
+ "label": "Customer Satisfaction Survey",
803
+ "questions": [
804
+ { "label": "Product Quality", "value": "quality" },
805
+ { "label": "Customer Service", "value": "service" },
806
+ { "label": "Value for Money", "value": "value" }
807
+ ],
808
+ "values": [
809
+ { "label": "Poor", "value": "1" },
810
+ { "label": "Fair", "value": "2" },
811
+ { "label": "Good", "value": "3" },
812
+ { "label": "Excellent", "value": "4" }
813
+ ]
814
+ }</pre>
815
+ </div>
816
+ </section>
817
+
818
+ <!-- Signature Field -->
819
+ <section id="signature" class="doc-section">
820
+ <div class="doc-header">
821
+ <h2>\u270D\uFE0F Signature</h2>
822
+ <p>Digital signature pad</p>
823
+ </div>
824
+
825
+ <div class="field-example">
826
+ <h3>Basic Usage</h3>
827
+ <p>Capture signatures with mouse or touch</p>
828
+ <pre class="code-block">{
829
+ "type": "signature",
830
+ "key": "signature",
831
+ "label": "Signature",
832
+ "footer": "Sign above",
833
+ "width": "100%",
834
+ "height": "150px",
835
+ "validate": {
836
+ "required": true
837
+ }
838
+ }</pre>
839
+ </div>
840
+ </section>
841
+
842
+ <!-- File Upload Field -->
843
+ <section id="file" class="doc-section">
844
+ <div class="doc-header">
845
+ <h2>\u{1F4C1} File Upload</h2>
846
+ <p>File upload with storage options</p>
847
+ </div>
848
+
849
+ <div class="field-example">
850
+ <h3>Basic Usage</h3>
851
+ <p>Upload files to Cloudflare R2 or base64 encode</p>
852
+ <pre class="code-block">{
853
+ "type": "file",
854
+ "key": "resume",
855
+ "label": "Upload Resume",
856
+ "storage": "r2",
857
+ "filePattern": ".pdf,.doc,.docx",
858
+ "fileMaxSize": "5MB",
859
+ "multiple": false,
860
+ "validate": {
861
+ "required": true
862
+ }
863
+ }
864
+
865
+ // Multiple files
866
+ {
867
+ "type": "file",
868
+ "key": "attachments",
869
+ "label": "Attachments",
870
+ "storage": "base64",
871
+ "multiple": true,
872
+ "fileMaxSize": "10MB"
873
+ }</pre>
874
+ </div>
875
+
876
+ <div class="info-box">
877
+ <strong>\u{1F4A1} Storage Options:</strong> Use <code>storage: "r2"</code> for Cloudflare R2 (recommended) or <code>storage: "base64"</code> to encode in submission data.
878
+ </div>
879
+ </section>
880
+
881
+ <!-- Address Field -->
882
+ <section id="address" class="doc-section">
883
+ <div class="doc-header">
884
+ <h2>\u{1F4CD} Address</h2>
885
+ <p>Address autocomplete with Google Maps</p>
886
+ </div>
887
+
888
+ <div class="field-example">
889
+ <h3>Basic Usage</h3>
890
+ <p>Google Maps API-powered address autocomplete</p>
891
+ <pre class="code-block">{
892
+ "type": "address",
893
+ "key": "address",
894
+ "label": "Address",
895
+ "provider": "google",
896
+ "map": {
897
+ "key": "YOUR_GOOGLE_MAPS_API_KEY"
898
+ },
899
+ "validate": {
900
+ "required": true
901
+ }
902
+ }</pre>
903
+ </div>
904
+
905
+ <div class="info-box">
906
+ <strong>\u26A0\uFE0F API Key Required:</strong> Enable Google Maps Places API and Maps JavaScript API in Google Cloud Console.
907
+ </div>
908
+ </section>
909
+
910
+ <!-- Turnstile Component -->
911
+ <section id="turnstile" class="doc-section">
912
+ <div class="doc-header">
913
+ <h2>\u{1F6E1}\uFE0F Turnstile</h2>
914
+ <p>CAPTCHA-free bot protection by Cloudflare</p>
915
+ </div>
916
+
917
+ <div class="field-example">
918
+ <h3>Basic Usage</h3>
919
+ <p>Add invisible bot protection to your forms</p>
920
+ <pre class="code-block">{
921
+ "type": "turnstile",
922
+ "key": "turnstile",
923
+ "label": "Turnstile Verification",
924
+ "theme": "auto",
925
+ "size": "normal",
926
+ "appearance": "always",
927
+ "persistent": false,
928
+ "protected": true
929
+ }</pre>
930
+ </div>
931
+
932
+ <div class="field-example">
933
+ <h3>Configuration Options</h3>
934
+ <pre class="code-block">{
935
+ "type": "turnstile",
936
+ "key": "turnstile",
937
+ "label": "Security Check",
938
+ "theme": "light", // "light", "dark", "auto"
939
+ "size": "compact", // "normal", "compact"
940
+ "appearance": "always", // "always", "execute", "interaction-only"
941
+ "action": "submit-form", // Optional: action name for analytics
942
+ "errorMessage": "Please complete the security verification"
943
+ }</pre>
944
+ </div>
945
+
946
+ <div class="info-box">
947
+ <strong>\u{1F527} Setup Required:</strong> Enable the Turnstile plugin in Settings \u2192 Plugins and configure your site key and secret key from Cloudflare Dashboard.
948
+ </div>
949
+
950
+ <div class="info-box">
951
+ <strong>\u{1F4A1} Usage Tips:</strong>
952
+ <ul style="margin: 10px 0 0 20px; padding: 0;">
953
+ <li><strong>Invisible Mode:</strong> Use <code>"appearance": "interaction-only"</code> for seamless UX</li>
954
+ <li><strong>Dark Mode:</strong> Use <code>"theme": "auto"</code> to match user preferences</li>
955
+ <li><strong>Compact Size:</strong> Use <code>"size": "compact"</code> for mobile forms</li>
956
+ <li><strong>Backend Validation:</strong> Tokens are automatically validated server-side</li>
957
+ </ul>
958
+ </div>
959
+ </section>
960
+
961
+ <!-- Panel Component -->
962
+ <section id="panel" class="doc-section">
963
+ <div class="doc-header">
964
+ <h2>\u{1F4E6} Panel</h2>
965
+ <p>Group fields in collapsible panels</p>
966
+ </div>
967
+
968
+ <div class="field-example">
969
+ <h3>Basic Usage</h3>
970
+ <p>Used for wizards and grouping related fields</p>
971
+ <pre class="code-block">{
972
+ "type": "panel",
973
+ "key": "personalInfo",
974
+ "title": "Personal Information",
975
+ "collapsible": true,
976
+ "collapsed": false,
977
+ "components": [
978
+ { "type": "textfield", "key": "firstName", "label": "First Name" },
979
+ { "type": "textfield", "key": "lastName", "label": "Last Name" },
980
+ { "type": "email", "key": "email", "label": "Email" }
981
+ ]
982
+ }</pre>
983
+ </div>
984
+ </section>
985
+
986
+ <!-- Columns Component -->
987
+ <section id="columns" class="doc-section">
988
+ <div class="doc-header">
989
+ <h2>\u{1F4CA} Columns</h2>
990
+ <p>Multi-column layout</p>
991
+ </div>
992
+
993
+ <div class="field-example">
994
+ <h3>Basic Usage</h3>
995
+ <p>Create side-by-side fields (responsive)</p>
996
+ <pre class="code-block">{
997
+ "type": "columns",
998
+ "columns": [
999
+ {
1000
+ "components": [
1001
+ { "type": "textfield", "key": "firstName", "label": "First Name" }
1002
+ ],
1003
+ "width": 6
1004
+ },
1005
+ {
1006
+ "components": [
1007
+ { "type": "textfield", "key": "lastName", "label": "Last Name" }
1008
+ ],
1009
+ "width": 6
1010
+ }
1011
+ ]
1012
+ }</pre>
1013
+ </div>
1014
+
1015
+ <div class="info-box">
1016
+ <strong>\u{1F4A1} Width System:</strong> Width is based on 12-column grid. Use 6 for 50%, 4 for 33%, 3 for 25%, etc.
1017
+ </div>
1018
+ </section>
1019
+
1020
+ <!-- Tabs Component -->
1021
+ <section id="tabs" class="doc-section">
1022
+ <div class="doc-header">
1023
+ <h2>\u{1F4D1} Tabs</h2>
1024
+ <p>Organize fields in tabs</p>
1025
+ </div>
1026
+
1027
+ <div class="field-example">
1028
+ <h3>Basic Usage</h3>
1029
+ <p>Create tabbed interface for complex forms</p>
1030
+ <pre class="code-block">{
1031
+ "type": "tabs",
1032
+ "components": [
1033
+ {
1034
+ "label": "Personal Info",
1035
+ "key": "tab1",
1036
+ "components": [...]
1037
+ },
1038
+ {
1039
+ "label": "Contact Info",
1040
+ "key": "tab2",
1041
+ "components": [...]
1042
+ }
1043
+ ]
1044
+ }</pre>
1045
+ </div>
1046
+ </section>
1047
+
1048
+ <!-- Table Component -->
1049
+ <section id="table" class="doc-section">
1050
+ <div class="doc-header">
1051
+ <h2>\u{1F4CB} Table</h2>
1052
+ <p>Table layout for forms</p>
1053
+ </div>
1054
+
1055
+ <div class="field-example">
1056
+ <h3>Basic Usage</h3>
1057
+ <p>Create table-based layouts</p>
1058
+ <pre class="code-block">{
1059
+ "type": "table",
1060
+ "numRows": 3,
1061
+ "numCols": 2,
1062
+ "rows": [
1063
+ [
1064
+ { "components": [{ "type": "textfield", "key": "cell1" }] },
1065
+ { "components": [{ "type": "textfield", "key": "cell2" }] }
1066
+ ]
1067
+ ]
1068
+ }</pre>
1069
+ </div>
1070
+ </section>
1071
+
1072
+ <!-- Fieldset Component -->
1073
+ <section id="fieldset" class="doc-section">
1074
+ <div class="doc-header">
1075
+ <h2>\u{1F4E6} Fieldset</h2>
1076
+ <p>Group fields with border and legend</p>
1077
+ </div>
1078
+
1079
+ <div class="field-example">
1080
+ <h3>Basic Usage</h3>
1081
+ <p>HTML fieldset with legend label</p>
1082
+ <pre class="code-block">{
1083
+ "type": "fieldset",
1084
+ "legend": "Contact Information",
1085
+ "components": [
1086
+ { "type": "email", "key": "email", "label": "Email" },
1087
+ { "type": "phoneNumber", "key": "phone", "label": "Phone" }
1088
+ ]
1089
+ }</pre>
1090
+ </div>
1091
+ </section>
1092
+
1093
+ <!-- Data Grid Component -->
1094
+ <section id="datagrid" class="doc-section">
1095
+ <div class="doc-header">
1096
+ <h2>\u{1F5C3}\uFE0F Data Grid</h2>
1097
+ <p>Repeatable row data entry</p>
1098
+ </div>
1099
+
1100
+ <div class="field-example">
1101
+ <h3>Basic Usage</h3>
1102
+ <p>Add/remove rows of structured data</p>
1103
+ <pre class="code-block">{
1104
+ "type": "datagrid",
1105
+ "key": "items",
1106
+ "label": "Items",
1107
+ "addAnother": "Add Item",
1108
+ "components": [
1109
+ { "type": "textfield", "key": "name", "label": "Item Name" },
1110
+ { "type": "number", "key": "quantity", "label": "Quantity" },
1111
+ { "type": "currency", "key": "price", "label": "Price" }
1112
+ ]
1113
+ }</pre>
1114
+ </div>
1115
+ </section>
1116
+
1117
+ <!-- Edit Grid Component -->
1118
+ <section id="editgrid" class="doc-section">
1119
+ <div class="doc-header">
1120
+ <h2>\u270F\uFE0F Edit Grid</h2>
1121
+ <p>Editable table with modal editing</p>
1122
+ </div>
1123
+
1124
+ <div class="field-example">
1125
+ <h3>Basic Usage</h3>
1126
+ <p>Similar to Data Grid but with modal editing</p>
1127
+ <pre class="code-block">{
1128
+ "type": "editgrid",
1129
+ "key": "contacts",
1130
+ "label": "Contacts",
1131
+ "components": [
1132
+ { "type": "textfield", "key": "name", "label": "Name" },
1133
+ { "type": "email", "key": "email", "label": "Email" },
1134
+ { "type": "phoneNumber", "key": "phone", "label": "Phone" }
1135
+ ]
1136
+ }</pre>
1137
+ </div>
1138
+ </section>
1139
+
1140
+ <!-- Multi-Page Wizards Guide -->
1141
+ <section id="wizard" class="doc-section">
1142
+ <div class="doc-header">
1143
+ <h2>\u{1F9D9} Multi-Page Wizards</h2>
1144
+ <p>Create step-by-step forms with progress tracking</p>
1145
+ </div>
1146
+
1147
+ <div class="info-box">
1148
+ <strong>\u{1F4A1} How It Works:</strong> Set <code>display: "wizard"</code> and use Panel components for each step. Form.io automatically adds navigation buttons.
1149
+ </div>
1150
+
1151
+ <div class="field-example">
1152
+ <h3>Complete Wizard Example</h3>
1153
+ <pre class="code-block">{
1154
+ "display": "wizard",
1155
+ "components": [
1156
+ {
1157
+ "type": "panel",
1158
+ "key": "step1",
1159
+ "title": "Step 1: Personal Info",
1160
+ "components": [
1161
+ { "type": "textfield", "key": "firstName", "label": "First Name" },
1162
+ { "type": "textfield", "key": "lastName", "label": "Last Name" }
1163
+ ]
1164
+ },
1165
+ {
1166
+ "type": "panel",
1167
+ "key": "step2",
1168
+ "title": "Step 2: Contact Info",
1169
+ "components": [
1170
+ { "type": "email", "key": "email", "label": "Email" },
1171
+ { "type": "phoneNumber", "key": "phone", "label": "Phone" }
1172
+ ]
1173
+ },
1174
+ {
1175
+ "type": "panel",
1176
+ "key": "step3",
1177
+ "title": "Step 3: Review",
1178
+ "components": [
1179
+ { "type": "checkbox", "key": "terms", "label": "I agree" }
1180
+ ]
1181
+ }
1182
+ ]
1183
+ }</pre>
1184
+ </div>
1185
+ </section>
1186
+
1187
+ <!-- Embedding Forms Guide -->
1188
+ <section id="embedding" class="doc-section">
1189
+ <div class="doc-header">
1190
+ <h2>\u{1F310} Embedding Forms</h2>
1191
+ <p>Multiple ways to embed forms on your website</p>
1192
+ </div>
1193
+
1194
+ <div class="field-example">
1195
+ <h3>Method 1: JavaScript (Recommended)</h3>
1196
+ <p>Load form schema via API and render with Form.io</p>
1197
+ <pre class="code-block">&lt;div id="form"&gt;&lt;/div&gt;
1198
+ &lt;script src="https://cdn.form.io/formiojs/formio.full.min.js"&gt;&lt;/script&gt;
1199
+ &lt;script&gt;
1200
+ fetch('/forms/contact_form/schema')
1201
+ .then(r => r.json())
1202
+ .then(data => {
1203
+ Formio.createForm(
1204
+ document.getElementById('form'),
1205
+ data.schema
1206
+ ).then(form => {
1207
+ // Handle submission
1208
+ form.on('submitDone', (submission) => {
1209
+ console.log('Submitted:', submission);
1210
+ });
1211
+ });
1212
+ });
1213
+ &lt;/script&gt;</pre>
1214
+ </div>
1215
+
1216
+ <div class="field-example">
1217
+ <h3>Method 2: iFrame</h3>
1218
+ <p>Simple iframe embed (less flexible)</p>
1219
+ <pre class="code-block">&lt;iframe
1220
+ src="/forms/contact_form"
1221
+ width="100%"
1222
+ height="600"
1223
+ frameborder="0"
1224
+ &gt;&lt;/iframe&gt;</pre>
1225
+ </div>
1226
+
1227
+ <div class="field-example">
1228
+ <h3>Method 3: React Component</h3>
1229
+ <p>Use Form.io React library</p>
1230
+ <pre class="code-block">import { Form } from '@formio/react';
1231
+
1232
+ function MyForm() {
1233
+ const [schema, setSchema] = useState(null);
1234
+
1235
+ useEffect(() => {
1236
+ fetch('/forms/contact_form/schema')
1237
+ .then(r => r.json())
1238
+ .then(data => setSchema(data.schema));
1239
+ }, []);
1240
+
1241
+ const handleSubmit = (submission) => {
1242
+ fetch('/api/forms/123/submit', {
1243
+ method: 'POST',
1244
+ headers: { 'Content-Type': 'application/json' },
1245
+ body: JSON.stringify(submission.data)
1246
+ });
1247
+ };
1248
+
1249
+ return schema ? (
1250
+ &lt;Form form={schema} onSubmit={handleSubmit} /&gt;
1251
+ ) : null;
1252
+ }</pre>
1253
+ </div>
1254
+ </section>
1255
+
1256
+ <!-- Validation Guide -->
1257
+ <section id="validation" class="doc-section">
1258
+ <div class="doc-header">
1259
+ <h2>\u2705 Validation</h2>
1260
+ <p>Built-in validation rules</p>
1261
+ </div>
1262
+
1263
+ <div class="field-example">
1264
+ <h3>Common Validation Rules</h3>
1265
+ <pre class="code-block">{
1266
+ "validate": {
1267
+ "required": true, // Field must have value
1268
+ "minLength": 2, // Minimum characters
1269
+ "maxLength": 50, // Maximum characters
1270
+ "min": 0, // Minimum number value
1271
+ "max": 100, // Maximum number value
1272
+ "pattern": "^[A-Za-z]+$", // Regular expression
1273
+ "custom": "valid = (input === 'yes');" // Custom validation
1274
+ }
1275
+ }</pre>
1276
+ </div>
1277
+
1278
+ <div class="field-example">
1279
+ <h3>Custom Error Messages</h3>
1280
+ <pre class="code-block">{
1281
+ "validate": {
1282
+ "required": true,
1283
+ "customMessage": "Please provide your full name"
1284
+ }
1285
+ }</pre>
1286
+ </div>
1287
+ </section>
1288
+
1289
+ <!-- Conditional Logic Guide -->
1290
+ <section id="conditional" class="doc-section">
1291
+ <div class="doc-header">
1292
+ <h2>\u{1F500} Conditional Logic</h2>
1293
+ <p>Show/hide fields based on conditions</p>
1294
+ </div>
1295
+
1296
+ <div class="field-example">
1297
+ <h3>Simple Conditional</h3>
1298
+ <p>Show field only when condition is met</p>
1299
+ <pre class="code-block">{
1300
+ "type": "textfield",
1301
+ "key": "companyName",
1302
+ "label": "Company Name",
1303
+ "conditional": {
1304
+ "show": true,
1305
+ "when": "hasCompany",
1306
+ "eq": true
1307
+ }
1308
+ }</pre>
1309
+ </div>
1310
+
1311
+ <div class="field-example">
1312
+ <h3>Advanced Conditional (JavaScript)</h3>
1313
+ <p>Complex conditions using custom JavaScript</p>
1314
+ <pre class="code-block">{
1315
+ "type": "textfield",
1316
+ "key": "discount",
1317
+ "label": "Discount Code",
1318
+ "customConditional": "show = (data.total > 100 && data.memberType === 'premium');"
1319
+ }</pre>
1320
+ </div>
1321
+ </section>
1322
+
1323
+ </main>
1324
+ </div>
1325
+
1326
+ <script>
1327
+ // Navigation
1328
+ document.querySelectorAll('.doc-link').forEach(link => {
1329
+ link.addEventListener('click', function(e) {
1330
+ e.preventDefault();
1331
+ const targetId = this.getAttribute('href').substring(1);
1332
+
1333
+ // Update active link
1334
+ document.querySelectorAll('.doc-link').forEach(l => l.classList.remove('active'));
1335
+ this.classList.add('active');
1336
+
1337
+ // Update active section
1338
+ document.querySelectorAll('.doc-section').forEach(s => s.classList.remove('active'));
1339
+ document.getElementById(targetId).classList.add('active');
1340
+
1341
+ // Scroll to top
1342
+ document.querySelector('.docs-content').scrollTop = 0;
1343
+ });
1344
+ });
1345
+ </script>
1346
+ `;
1347
+ const layoutData = {
1348
+ title: "Forms Quick Reference",
1349
+ pageTitle: "Forms Quick Reference",
1350
+ content: pageContent,
1351
+ user: data.user,
1352
+ version: data.version
1353
+ };
1354
+ return renderAdminLayoutCatalyst(layoutData);
1355
+ }
1356
+
1357
+ // src/templates/pages/admin-forms-examples.template.ts
1358
+ init_admin_layout_catalyst_template();
1359
+ function renderFormsExamplesPage(data) {
1360
+ const pageContent = `
1361
+ <style>
1362
+ /* Light theme for examples page */
1363
+ .examples-container {
1364
+ display: flex;
1365
+ gap: 0;
1366
+ min-height: calc(100vh - 200px);
1367
+ background: #ffffff;
1368
+ border-radius: 12px;
1369
+ overflow: hidden;
1370
+ box-shadow: 0 1px 3px rgba(0,0,0,0.1);
1371
+ }
1372
+
1373
+ .examples-sidebar {
1374
+ width: 280px;
1375
+ background: #f8f9fa;
1376
+ border-right: 1px solid #e0e0e0;
1377
+ padding: 1.5rem 0;
1378
+ overflow-y: auto;
1379
+ }
1380
+
1381
+ .examples-sidebar h3 {
1382
+ font-size: 0.75rem;
1383
+ font-weight: 600;
1384
+ text-transform: uppercase;
1385
+ letter-spacing: 0.05em;
1386
+ color: #6b7280;
1387
+ padding: 0 1.5rem;
1388
+ margin-bottom: 0.75rem;
1389
+ }
1390
+
1391
+ .examples-nav {
1392
+ list-style: none;
1393
+ padding: 0;
1394
+ margin: 0 0 2rem 0;
1395
+ }
1396
+
1397
+ .examples-nav li {
1398
+ margin: 0;
1399
+ }
1400
+
1401
+ .examples-nav a {
1402
+ display: block;
1403
+ padding: 0.75rem 1.5rem;
1404
+ color: #374151;
1405
+ text-decoration: none;
1406
+ font-size: 0.875rem;
1407
+ transition: all 0.2s;
1408
+ border-left: 3px solid transparent;
1409
+ }
1410
+
1411
+ .examples-nav a:hover {
1412
+ background: #e9ecef;
1413
+ color: #1f2937;
1414
+ }
1415
+
1416
+ .examples-nav a.active {
1417
+ background: #e3f2fd;
1418
+ color: #1976d2;
1419
+ border-left-color: #1976d2;
1420
+ font-weight: 500;
1421
+ }
1422
+
1423
+ .examples-content {
1424
+ flex: 1;
1425
+ padding: 2rem;
1426
+ background: #ffffff;
1427
+ overflow-y: auto;
1428
+ }
1429
+
1430
+ .example-section {
1431
+ display: none;
1432
+ }
1433
+
1434
+ .example-section.active {
1435
+ display: block;
1436
+ }
1437
+
1438
+ .example-header {
1439
+ margin-bottom: 2rem;
1440
+ padding-bottom: 1rem;
1441
+ border-bottom: 2px solid #e0e0e0;
1442
+ }
1443
+
1444
+ .example-header h2 {
1445
+ font-size: 1.875rem;
1446
+ font-weight: 700;
1447
+ color: #1f2937;
1448
+ margin-bottom: 0.5rem;
1449
+ }
1450
+
1451
+ .example-header p {
1452
+ color: #6b7280;
1453
+ font-size: 1rem;
1454
+ }
1455
+
1456
+ .example-demo {
1457
+ background: #f8f9fa;
1458
+ border: 1px solid #e0e0e0;
1459
+ border-radius: 8px;
1460
+ padding: 2rem;
1461
+ margin-bottom: 2rem;
1462
+ }
1463
+
1464
+ .example-code {
1465
+ background: #1e1e1e;
1466
+ color: #d4d4d4;
1467
+ padding: 1.5rem;
1468
+ border-radius: 8px;
1469
+ overflow-x: auto;
1470
+ font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
1471
+ font-size: 0.875rem;
1472
+ line-height: 1.6;
1473
+ }
1474
+
1475
+ .code-header {
1476
+ display: flex;
1477
+ justify-content: space-between;
1478
+ align-items: center;
1479
+ margin-bottom: 1rem;
1480
+ }
1481
+
1482
+ .code-header h3 {
1483
+ font-size: 0.875rem;
1484
+ font-weight: 600;
1485
+ color: #374151;
1486
+ }
1487
+
1488
+ .copy-btn {
1489
+ padding: 0.375rem 0.75rem;
1490
+ font-size: 0.75rem;
1491
+ background: #f3f4f6;
1492
+ border: 1px solid #d1d5db;
1493
+ border-radius: 4px;
1494
+ cursor: pointer;
1495
+ transition: all 0.2s;
1496
+ }
1497
+
1498
+ .copy-btn:hover {
1499
+ background: #e5e7eb;
1500
+ }
1501
+
1502
+ /* Form.io overrides for lighter theme */
1503
+ .formio-component {
1504
+ margin-bottom: 1.25rem;
1505
+ }
1506
+
1507
+ .formio-component label {
1508
+ color: #374151;
1509
+ font-weight: 500;
1510
+ margin-bottom: 0.5rem;
1511
+ }
1512
+
1513
+ .formio-component input,
1514
+ .formio-component textarea,
1515
+ .formio-component select {
1516
+ border: 1px solid #d1d5db;
1517
+ border-radius: 6px;
1518
+ padding: 0.625rem 0.875rem;
1519
+ }
1520
+
1521
+ .formio-component input:focus,
1522
+ .formio-component textarea:focus,
1523
+ .formio-component select:focus {
1524
+ outline: none;
1525
+ border-color: #3b82f6;
1526
+ box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
1527
+ }
1528
+
1529
+ .btn-primary {
1530
+ background: #3b82f6 !important;
1531
+ border-color: #3b82f6 !important;
1532
+ padding: 0.625rem 1.25rem !important;
1533
+ border-radius: 6px !important;
1534
+ }
1535
+
1536
+ .btn-primary:hover {
1537
+ background: #2563eb !important;
1538
+ border-color: #2563eb !important;
1539
+ }
1540
+
1541
+ .alert-success {
1542
+ background: #10b981 !important;
1543
+ color: white !important;
1544
+ border: none !important;
1545
+ border-radius: 6px !important;
1546
+ }
1547
+ </style>
1548
+
1549
+ <div class="mb-6">
1550
+ <div class="flex items-center gap-3 mb-4">
1551
+ <a href="/admin/forms" class="inline-flex items-center text-sm text-zinc-600 dark:text-zinc-400 hover:text-zinc-900 dark:hover:text-white transition-colors">
1552
+ <svg class="w-4 h-4 mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
1553
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7"/>
1554
+ </svg>
1555
+ Back to Forms
1556
+ </a>
1557
+ </div>
1558
+ <h1 class="text-3xl font-bold text-zinc-950 dark:text-white">Form Examples</h1>
1559
+ <p class="mt-2 text-zinc-600 dark:text-zinc-400">Interactive examples showcasing Form.io capabilities</p>
1560
+ </div>
1561
+
1562
+ <div class="examples-container">
1563
+ <!-- Sidebar Navigation -->
1564
+ <aside class="examples-sidebar">
1565
+ <h3>Getting Started</h3>
1566
+ <ul class="examples-nav">
1567
+ <li><a href="#kitchen-sink" class="example-link active">Kitchen Sink</a></li>
1568
+ <li><a href="#simple-contact" class="example-link">Simple Contact Form</a></li>
1569
+ <li><a href="#thank-you" class="example-link">Thank You Page</a></li>
1570
+ </ul>
1571
+
1572
+ <h3>Advanced Forms</h3>
1573
+ <ul class="examples-nav">
1574
+ <li><a href="#wizard-form" class="example-link">Multi-Page Wizard</a></li>
1575
+ <li><a href="#conditional-logic" class="example-link">Conditional Logic</a></li>
1576
+ <li><a href="#file-upload" class="example-link">File Upload</a></li>
1577
+ </ul>
1578
+
1579
+ <h3>Components</h3>
1580
+ <ul class="examples-nav">
1581
+ <li><a href="#address-maps" class="example-link">Address with Maps</a></li>
1582
+ <li><a href="#signature" class="example-link">Signature Pad</a></li>
1583
+ <li><a href="#data-grid" class="example-link">Data Grid</a></li>
1584
+ <li><a href="#turnstile-protection" class="example-link">\u{1F6E1}\uFE0F Turnstile Protection</a></li>
1585
+ </ul>
1586
+ </aside>
1587
+
1588
+ <!-- Main Content Area -->
1589
+ <main class="examples-content">
1590
+
1591
+ <!-- Kitchen Sink Example -->
1592
+ <section id="kitchen-sink" class="example-section active">
1593
+ <div class="example-header">
1594
+ <h2>\u{1F373} Kitchen Sink</h2>
1595
+ <p>A comprehensive form showcasing all major field types and configurations.</p>
1596
+ </div>
1597
+
1598
+ <div class="example-demo">
1599
+ <div id="form-kitchen-sink"></div>
1600
+ </div>
1601
+
1602
+ <div class="code-header">
1603
+ <h3>Form Schema (JSON)</h3>
1604
+ <button class="copy-btn" onclick="copyCode('kitchen-sink-code')">Copy Code</button>
1605
+ </div>
1606
+ <pre class="example-code" id="kitchen-sink-code">{
1607
+ "display": "form",
1608
+ "components": [
1609
+ // Basic Text Fields
1610
+ { "type": "textfield", "key": "firstName", "label": "First Name" },
1611
+ { "type": "email", "key": "email", "label": "Email" },
1612
+ { "type": "phoneNumber", "key": "phone", "label": "Phone" },
1613
+ { "type": "password", "key": "password", "label": "Password" },
1614
+ { "type": "url", "key": "website", "label": "Website" },
1615
+ { "type": "textarea", "key": "bio", "label": "Biography" },
1616
+
1617
+ // Date & Time
1618
+ { "type": "datetime", "key": "appointmentDateTime", "label": "Appointment" },
1619
+ { "type": "day", "key": "birthDate", "label": "Birth Date" },
1620
+ { "type": "time", "key": "preferredTime", "label": "Time" },
1621
+
1622
+ // Selections
1623
+ { "type": "select", "key": "country", "label": "Country",
1624
+ "data": { "values": [{ "label": "USA", "value": "us" }] }},
1625
+ { "type": "selectboxes", "key": "interests", "label": "Interests",
1626
+ "values": [{ "label": "Sports", "value": "sports" }] },
1627
+ { "type": "radio", "key": "gender", "label": "Gender",
1628
+ "values": [{ "label": "Male", "value": "male" }] },
1629
+ { "type": "checkbox", "key": "newsletter", "label": "Newsletter" },
1630
+
1631
+ // Advanced
1632
+ { "type": "currency", "key": "salary", "label": "Salary" },
1633
+ { "type": "tags", "key": "skills", "label": "Skills" },
1634
+ { "type": "survey", "key": "satisfaction", "label": "Satisfaction" },
1635
+ { "type": "signature", "key": "signature", "label": "Signature" },
1636
+ { "type": "file", "key": "resume", "label": "Resume", "storage": "base64" }
1637
+ ]
1638
+ }</pre>
1639
+ </section>
1640
+
1641
+ <!-- Simple Contact Form Example -->
1642
+ <section id="simple-contact" class="example-section">
1643
+ <div class="example-header">
1644
+ <h2>\u{1F4E7} Simple Contact Form</h2>
1645
+ <p>A minimal contact form with validation.</p>
1646
+ </div>
1647
+
1648
+ <div class="example-demo">
1649
+ <div id="form-simple-contact"></div>
1650
+ </div>
1651
+
1652
+ <div class="code-header">
1653
+ <h3>Form Schema (JSON)</h3>
1654
+ <button class="copy-btn" onclick="copyCode('contact-code')">Copy Code</button>
1655
+ </div>
1656
+ <pre class="example-code" id="contact-code">{
1657
+ "display": "form",
1658
+ "components": [
1659
+ {
1660
+ "type": "textfield",
1661
+ "key": "name",
1662
+ "label": "Full Name",
1663
+ "validate": { "required": true }
1664
+ },
1665
+ {
1666
+ "type": "email",
1667
+ "key": "email",
1668
+ "label": "Email Address",
1669
+ "validate": { "required": true }
1670
+ },
1671
+ {
1672
+ "type": "textarea",
1673
+ "key": "message",
1674
+ "label": "Message",
1675
+ "rows": 5,
1676
+ "validate": { "required": true }
1677
+ }
1678
+ ]
1679
+ }</pre>
1680
+ </section>
1681
+
1682
+ <!-- Thank You Page Example -->
1683
+ <section id="thank-you" class="example-section">
1684
+ <div class="example-header">
1685
+ <h2>\u{1F389} Thank You Page</h2>
1686
+ <p>Handle form submission and redirect to a thank you message.</p>
1687
+ </div>
1688
+
1689
+ <div class="example-demo">
1690
+ <div id="form-thank-you"></div>
1691
+ <div id="thank-you-message" style="display: none; padding: 2rem; background: #10b981; color: white; border-radius: 8px; text-align: center;">
1692
+ <h3 style="font-size: 1.5rem; margin-bottom: 0.5rem;">\u2705 Thank You!</h3>
1693
+ <p>Your form has been submitted successfully.</p>
1694
+ </div>
1695
+ </div>
1696
+
1697
+ <div class="code-header">
1698
+ <h3>Form Schema (JSON)</h3>
1699
+ <button class="copy-btn" onclick="copyCode('thankyou-schema-code')">Copy Code</button>
1700
+ </div>
1701
+ <pre class="example-code" id="thankyou-schema-code">{
1702
+ "display": "form",
1703
+ "components": [
1704
+ { "type": "textfield", "key": "firstName", "label": "First Name",
1705
+ "validate": { "required": true }},
1706
+ { "type": "textfield", "key": "lastName", "label": "Last Name",
1707
+ "validate": { "required": true }},
1708
+ { "type": "email", "key": "email", "label": "Email Address",
1709
+ "validate": { "required": true }},
1710
+ { "type": "phoneNumber", "key": "phone", "label": "Phone Number" },
1711
+ { "type": "textarea", "key": "message", "label": "Message",
1712
+ "validate": { "required": true }},
1713
+ { "type": "button", "action": "submit", "label": "Submit Form" }
1714
+ ]
1715
+ }</pre>
1716
+
1717
+ <div class="code-header">
1718
+ <h3>JavaScript Code</h3>
1719
+ <button class="copy-btn" onclick="copyCode('thankyou-code')">Copy Code</button>
1720
+ </div>
1721
+ <pre class="example-code" id="thankyou-code">Formio.createForm(document.getElementById('formio'), formSchema)
1722
+ .then(function(form) {
1723
+ // Handle successful submission
1724
+ form.on('submitDone', function(submission) {
1725
+ console.log('Form submitted:', submission);
1726
+
1727
+ // Hide form and show thank you message
1728
+ form.element.style.display = 'none';
1729
+ document.getElementById('thank-you-message').style.display = 'block';
1730
+
1731
+ // Or redirect to another page:
1732
+ // window.location = '/thank-you';
1733
+ });
1734
+ });</pre>
1735
+ </section>
1736
+
1737
+ <!-- Wizard Form Example -->
1738
+ <section id="wizard-form" class="example-section">
1739
+ <div class="example-header">
1740
+ <h2>\u{1F9D9} Multi-Page Wizard</h2>
1741
+ <p>Step-by-step form with multiple pages and progress indicator.</p>
1742
+ </div>
1743
+
1744
+ <div class="example-demo">
1745
+ <div id="form-wizard"></div>
1746
+ </div>
1747
+
1748
+ <div class="code-header">
1749
+ <h3>Form Schema (JSON)</h3>
1750
+ <button class="copy-btn" onclick="copyCode('wizard-code')">Copy Code</button>
1751
+ </div>
1752
+ <pre class="example-code" id="wizard-code">{
1753
+ "display": "wizard",
1754
+ "components": [
1755
+ {
1756
+ "type": "panel",
1757
+ "key": "step1PersonalInfo",
1758
+ "title": "Step 1: Personal Information",
1759
+ "components": [
1760
+ { "type": "textfield", "key": "firstName", "label": "First Name" },
1761
+ { "type": "textfield", "key": "lastName", "label": "Last Name" },
1762
+ { "type": "datetime", "key": "birthDate", "label": "Date of Birth" },
1763
+ { "type": "select", "key": "gender", "label": "Gender" }
1764
+ ]
1765
+ },
1766
+ {
1767
+ "type": "panel",
1768
+ "key": "step2ContactInfo",
1769
+ "title": "Step 2: Contact Information",
1770
+ "components": [
1771
+ { "type": "email", "key": "email", "label": "Email" },
1772
+ { "type": "phoneNumber", "key": "phone", "label": "Phone" },
1773
+ { "type": "textfield", "key": "address", "label": "Address" },
1774
+ { "type": "select", "key": "country", "label": "Country" }
1775
+ ]
1776
+ },
1777
+ {
1778
+ "type": "panel",
1779
+ "key": "step3Preferences",
1780
+ "title": "Step 3: Preferences & Review",
1781
+ "components": [
1782
+ { "type": "selectboxes", "key": "interests", "label": "Interests" },
1783
+ { "type": "radio", "key": "contactMethod", "label": "Contact Method" },
1784
+ { "type": "textarea", "key": "comments", "label": "Comments" },
1785
+ { "type": "checkbox", "key": "terms", "label": "I agree to terms" }
1786
+ ]
1787
+ }
1788
+ ]
1789
+ }</pre>
1790
+ </section>
1791
+
1792
+ <!-- More examples... -->
1793
+ <section id="conditional-logic" class="example-section">
1794
+ <div class="example-header">
1795
+ <h2>\u{1F500} Conditional Logic</h2>
1796
+ <p>Show/hide fields based on user input.</p>
1797
+ </div>
1798
+ <div class="example-demo">
1799
+ <div id="form-conditional"></div>
1800
+ </div>
1801
+ </section>
1802
+
1803
+ <section id="file-upload" class="example-section">
1804
+ <div class="example-header">
1805
+ <h2>\u{1F4C1} File Upload</h2>
1806
+ <p>Upload files to Cloudflare R2 storage.</p>
1807
+ </div>
1808
+ <div class="example-demo">
1809
+ <div id="form-file-upload"></div>
1810
+ </div>
1811
+ </section>
1812
+
1813
+ <section id="address-maps" class="example-section">
1814
+ <div class="example-header">
1815
+ <h2>\u{1F4CD} Address with Maps</h2>
1816
+ <p>Google Maps autocomplete for address input.</p>
1817
+ </div>
1818
+ <div class="example-demo">
1819
+ <div id="form-address"></div>
1820
+ </div>
1821
+ </section>
1822
+
1823
+ <section id="signature" class="example-section">
1824
+ <div class="example-header">
1825
+ <h2>\u270D\uFE0F Signature Pad</h2>
1826
+ <p>Capture digital signatures.</p>
1827
+ </div>
1828
+ <div class="example-demo">
1829
+ <div id="form-signature"></div>
1830
+ </div>
1831
+ </section>
1832
+
1833
+ <section id="data-grid" class="example-section">
1834
+ <div class="example-header">
1835
+ <h2>\u{1F4CA} Data Grid</h2>
1836
+ <p>Repeatable data entry with add/remove rows.</p>
1837
+ </div>
1838
+ <div class="example-demo">
1839
+ <div id="form-data-grid"></div>
1840
+ </div>
1841
+ </section>
1842
+
1843
+ <section id="turnstile-protection" class="example-section">
1844
+ <div class="example-header">
1845
+ <h2>\u{1F6E1}\uFE0F Turnstile Protection</h2>
1846
+ <p>CAPTCHA-free bot protection by Cloudflare - drag and drop from the Premium section in the form builder.</p>
1847
+ </div>
1848
+
1849
+ <div class="info-box" style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 20px; border-radius: 8px; margin-bottom: 20px;">
1850
+ <h3 style="margin: 0 0 10px 0; font-size: 18px;">\u2728 Key Features</h3>
1851
+ <ul style="margin: 0; padding-left: 20px;">
1852
+ <li><strong>No CAPTCHA puzzles</strong> - Seamless user experience</li>
1853
+ <li><strong>Invisible protection</strong> - Works in the background</li>
1854
+ <li><strong>Auto-validated</strong> - Server-side token verification</li>
1855
+ <li><strong>Privacy-first</strong> - Cloudflare's secure infrastructure</li>
1856
+ </ul>
1857
+ </div>
1858
+
1859
+ <div class="example-demo">
1860
+ <div id="form-turnstile"></div>
1861
+ </div>
1862
+
1863
+ <div class="info-box" style="margin-top: 20px;">
1864
+ <strong>\u{1F527} Setup Instructions:</strong>
1865
+ <ol style="margin: 10px 0 0 20px; padding: 0;">
1866
+ <li>Go to <strong>Settings \u2192 Plugins</strong> and enable Turnstile plugin</li>
1867
+ <li>Get free API keys from <a href="https://dash.cloudflare.com/?to=/:account/turnstile" target="_blank" style="color: #3b82f6;">Cloudflare Dashboard</a></li>
1868
+ <li>Configure site key and secret key in plugin settings</li>
1869
+ <li>Drag Turnstile component from <strong>Premium</strong> section in form builder</li>
1870
+ </ol>
1871
+ </div>
1872
+
1873
+ <div class="info-box" style="margin-top: 15px; background: #fef3c7; border: 1px solid #fbbf24;">
1874
+ <strong>\u{1F4A1} Pro Tip:</strong> Use <code>"appearance": "interaction-only"</code> for invisible mode - the widget only appears when suspicious activity is detected!
1875
+ </div>
1876
+ </section>
1877
+
1878
+ </main>
1879
+ </div>
1880
+
1881
+ <!-- Load Form.io -->
1882
+ <script src="https://cdn.form.io/formiojs/formio.full.min.js"></script>
1883
+
1884
+ <!-- Register Turnstile Component -->
1885
+ <script>
1886
+ // Register custom Turnstile component (same as public forms)
1887
+ (function() {
1888
+ // Will register when Form.io loads
1889
+ function registerTurnstile() {
1890
+ if (!window.Formio || !window.Formio.Components) {
1891
+ return false;
1892
+ }
1893
+
1894
+ const FieldComponent = Formio.Components.components.field;
1895
+
1896
+ class TurnstileComponent extends FieldComponent {
1897
+ static schema(...extend) {
1898
+ return FieldComponent.schema({
1899
+ type: 'turnstile',
1900
+ label: 'Turnstile Verification',
1901
+ key: 'turnstile',
1902
+ input: true,
1903
+ persistent: false,
1904
+ protected: true
1905
+ }, ...extend);
1906
+ }
1907
+
1908
+ render() {
1909
+ return super.render(\`
1910
+ <div ref="turnstileContainer" class="formio-component-turnstile">
1911
+ <div ref="turnstileWidget" style="margin: 15px 0; padding: 20px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); border-radius: 8px; text-align: center; color: white;">
1912
+ <div style="font-size: 32px; margin-bottom: 10px;">\u{1F6E1}\uFE0F</div>
1913
+ <div style="font-weight: 600; font-size: 16px; margin-bottom: 5px;">Turnstile Verification</div>
1914
+ <div style="font-size: 13px; opacity: 0.9;">CAPTCHA-free bot protection by Cloudflare</div>
1915
+ <div style="font-size: 12px; margin-top: 10px; opacity: 0.8;">Enable Turnstile plugin in Settings \u2192 Plugins to activate</div>
1916
+ </div>
1917
+ </div>
1918
+ \`);
1919
+ }
1920
+
1921
+ attach(element) {
1922
+ this.loadRefs(element, { turnstileContainer: 'single', turnstileWidget: 'single' });
1923
+ return super.attach(element);
1924
+ }
1925
+ }
1926
+
1927
+ Formio.Components.addComponent('turnstile', TurnstileComponent);
1928
+ console.log('\u2705 Turnstile component registered on examples page');
1929
+ return true;
1930
+ }
1931
+
1932
+ // Try to register immediately
1933
+ if (!registerTurnstile()) {
1934
+ // If Form.io not loaded yet, try again after a delay
1935
+ setTimeout(registerTurnstile, 100);
1936
+ }
1937
+ })();
1938
+ </script>
1939
+
1940
+ <script>
1941
+ // Debug: Check if elements exist
1942
+ console.log('Script loaded');
1943
+
1944
+ // Navigation function
1945
+ function setupNavigation() {
1946
+ console.log('Setting up navigation...');
1947
+ const links = document.querySelectorAll('.example-link');
1948
+ console.log('Found', links.length, 'navigation links');
1949
+
1950
+ // Navigation
1951
+ links.forEach(link => {
1952
+ link.addEventListener('click', function(e) {
1953
+ e.preventDefault();
1954
+ const targetId = this.getAttribute('href').substring(1);
1955
+ console.log('Navigating to:', targetId);
1956
+
1957
+ // Update active link
1958
+ document.querySelectorAll('.example-link').forEach(l => l.classList.remove('active'));
1959
+ this.classList.add('active');
1960
+
1961
+ // Update active section
1962
+ document.querySelectorAll('.example-section').forEach(s => s.classList.remove('active'));
1963
+ const targetSection = document.getElementById(targetId);
1964
+ if (targetSection) {
1965
+ targetSection.classList.add('active');
1966
+ console.log('Activated section:', targetId);
1967
+ } else {
1968
+ console.error('Section not found:', targetId);
1969
+ }
1970
+
1971
+ // Scroll to top
1972
+ const content = document.querySelector('.examples-content');
1973
+ if (content) {
1974
+ content.scrollTop = 0;
1975
+ }
1976
+
1977
+ // Update URL hash
1978
+ window.location.hash = targetId;
1979
+ });
1980
+ });
1981
+
1982
+ // Handle initial hash on page load
1983
+ function handleHash() {
1984
+ const hash = window.location.hash.substring(1);
1985
+ console.log('Handling hash:', hash);
1986
+ if (hash) {
1987
+ const link = document.querySelector('.example-link[href="#' + hash + '"]');
1988
+ if (link) {
1989
+ link.click();
1990
+ }
1991
+ }
1992
+ }
1993
+
1994
+ // Call on load and hash change
1995
+ handleHash();
1996
+ window.addEventListener('hashchange', handleHash);
1997
+ }
1998
+
1999
+ // Copy code function
2000
+ window.copyCode = function(elementId) {
2001
+ const code = document.getElementById(elementId).textContent;
2002
+ navigator.clipboard.writeText(code).then(() => {
2003
+ const btn = event.target;
2004
+ const originalText = btn.textContent;
2005
+ btn.textContent = 'Copied!';
2006
+ setTimeout(() => btn.textContent = originalText, 2000);
2007
+ });
2008
+ };
2009
+
2010
+ // Initialize forms
2011
+ function initForms() {
2012
+ const kitchenSinkSchema = {
2013
+ display: 'form',
2014
+ components: [
2015
+ {
2016
+ type: 'htmlelement',
2017
+ tag: 'h3',
2018
+ content: 'Basic Fields',
2019
+ className: 'mb-3 text-lg font-semibold'
2020
+ },
2021
+ { type: 'textfield', key: 'firstName', label: 'First Name', placeholder: 'Enter your first name', validate: { required: true } },
2022
+ { type: 'textfield', key: 'lastName', label: 'Last Name', placeholder: 'Enter your last name', validate: { required: true } },
2023
+ { type: 'email', key: 'email', label: 'Email Address', placeholder: 'you@example.com', validate: { required: true } },
2024
+ { type: 'phoneNumber', key: 'phone', label: 'Phone Number', placeholder: '(555) 555-5555' },
2025
+ { type: 'number', key: 'age', label: 'Age', placeholder: '18', validate: { min: 18, max: 120 } },
2026
+ { type: 'password', key: 'password', label: 'Password', placeholder: 'Enter password', validate: { required: true } },
2027
+ { type: 'url', key: 'website', label: 'Website', placeholder: 'https://example.com' },
2028
+ { type: 'textarea', key: 'bio', label: 'Biography', rows: 4, placeholder: 'Tell us about yourself' },
2029
+
2030
+ {
2031
+ type: 'htmlelement',
2032
+ tag: 'h3',
2033
+ content: 'Date & Time Fields',
2034
+ className: 'mt-4 mb-3 text-lg font-semibold'
2035
+ },
2036
+ { type: 'datetime', key: 'appointmentDateTime', label: 'Appointment Date & Time', format: 'yyyy-MM-dd hh:mm a', enableTime: true },
2037
+ { type: 'day', key: 'birthDate', label: 'Birth Date (Day/Month/Year)' },
2038
+ { type: 'time', key: 'preferredTime', label: 'Preferred Contact Time' },
2039
+
2040
+ {
2041
+ type: 'htmlelement',
2042
+ tag: 'h3',
2043
+ content: 'Selection Fields',
2044
+ className: 'mt-4 mb-3 text-lg font-semibold'
2045
+ },
2046
+ {
2047
+ type: 'select',
2048
+ key: 'country',
2049
+ label: 'Country',
2050
+ placeholder: 'Select your country',
2051
+ data: {
2052
+ values: [
2053
+ { label: 'United States', value: 'us' },
2054
+ { label: 'Canada', value: 'ca' },
2055
+ { label: 'United Kingdom', value: 'uk' },
2056
+ { label: 'Australia', value: 'au' },
2057
+ { label: 'Germany', value: 'de' },
2058
+ { label: 'France', value: 'fr' }
2059
+ ]
2060
+ }
2061
+ },
2062
+ {
2063
+ type: 'selectboxes',
2064
+ key: 'interests',
2065
+ label: 'Interests (Multiple Selection)',
2066
+ values: [
2067
+ { label: 'Sports', value: 'sports' },
2068
+ { label: 'Music', value: 'music' },
2069
+ { label: 'Technology', value: 'tech' },
2070
+ { label: 'Travel', value: 'travel' },
2071
+ { label: 'Reading', value: 'reading' }
2072
+ ]
2073
+ },
2074
+ {
2075
+ type: 'radio',
2076
+ key: 'gender',
2077
+ label: 'Gender',
2078
+ values: [
2079
+ { label: 'Male', value: 'male' },
2080
+ { label: 'Female', value: 'female' },
2081
+ { label: 'Non-binary', value: 'nonbinary' },
2082
+ { label: 'Prefer not to say', value: 'prefer_not_to_say' }
2083
+ ]
2084
+ },
2085
+ { type: 'checkbox', key: 'newsletter', label: 'Subscribe to newsletter' },
2086
+ { type: 'checkbox', key: 'terms', label: 'I agree to the terms and conditions', validate: { required: true } },
2087
+
2088
+ {
2089
+ type: 'htmlelement',
2090
+ tag: 'h3',
2091
+ content: 'Advanced Fields',
2092
+ className: 'mt-4 mb-3 text-lg font-semibold'
2093
+ },
2094
+ {
2095
+ type: 'currency',
2096
+ key: 'salary',
2097
+ label: 'Expected Salary',
2098
+ currency: 'USD',
2099
+ placeholder: '$50,000'
2100
+ },
2101
+ {
2102
+ type: 'tags',
2103
+ key: 'skills',
2104
+ label: 'Skills (Type and press Enter)',
2105
+ placeholder: 'e.g. JavaScript, Python, React'
2106
+ },
2107
+ {
2108
+ type: 'survey',
2109
+ key: 'satisfaction',
2110
+ label: 'Satisfaction Survey',
2111
+ questions: [
2112
+ { label: 'Product Quality', value: 'quality' },
2113
+ { label: 'Customer Service', value: 'service' },
2114
+ { label: 'Value for Money', value: 'value' }
2115
+ ],
2116
+ values: [
2117
+ { label: 'Poor', value: '1' },
2118
+ { label: 'Fair', value: '2' },
2119
+ { label: 'Good', value: '3' },
2120
+ { label: 'Excellent', value: '4' }
2121
+ ]
2122
+ },
2123
+ {
2124
+ type: 'signature',
2125
+ key: 'signature',
2126
+ label: 'Signature',
2127
+ footer: 'Sign above',
2128
+ width: '100%',
2129
+ height: '150px'
2130
+ },
2131
+ {
2132
+ type: 'file',
2133
+ key: 'resume',
2134
+ label: 'Upload Resume (PDF, DOC)',
2135
+ storage: 'base64',
2136
+ filePattern: '.pdf,.doc,.docx',
2137
+ fileMaxSize: '5MB'
2138
+ },
2139
+
2140
+ { type: 'button', action: 'submit', label: 'Submit Kitchen Sink Form', theme: 'primary', className: 'mt-4' }
2141
+ ]
2142
+ };
2143
+ Formio.createForm(document.getElementById('form-kitchen-sink'), kitchenSinkSchema);
2144
+
2145
+ // Simple Contact
2146
+ const contactSchema = {
2147
+ display: 'form',
2148
+ components: [
2149
+ { type: 'textfield', key: 'name', label: 'Full Name', validate: { required: true } },
2150
+ { type: 'email', key: 'email', label: 'Email Address', validate: { required: true } },
2151
+ { type: 'textarea', key: 'message', label: 'Message', rows: 5, validate: { required: true } },
2152
+ { type: 'button', action: 'submit', label: 'Send Message', theme: 'primary' }
2153
+ ]
2154
+ };
2155
+ Formio.createForm(document.getElementById('form-simple-contact'), contactSchema);
2156
+
2157
+ // Thank You Page - Match Form.io's official example
2158
+ const thankYouSchema = {
2159
+ display: 'form',
2160
+ components: [
2161
+ {
2162
+ type: 'htmlelement',
2163
+ tag: 'p',
2164
+ content: 'Fill out this form and watch it redirect to a thank you page after submission.',
2165
+ className: 'mb-4 text-gray-600'
2166
+ },
2167
+ { type: 'textfield', key: 'firstName', label: 'First Name', placeholder: 'Enter your first name', validate: { required: true } },
2168
+ { type: 'textfield', key: 'lastName', label: 'Last Name', placeholder: 'Enter your last name', validate: { required: true } },
2169
+ { type: 'email', key: 'email', label: 'Email Address', placeholder: 'you@example.com', validate: { required: true } },
2170
+ { type: 'phoneNumber', key: 'phone', label: 'Phone Number', placeholder: '(555) 555-5555' },
2171
+ { type: 'textarea', key: 'message', label: 'Message', rows: 4, placeholder: 'Your message here...', validate: { required: true } },
2172
+ { type: 'button', action: 'submit', label: 'Submit Form', theme: 'primary' }
2173
+ ]
2174
+ };
2175
+ Formio.createForm(document.getElementById('form-thank-you'), thankYouSchema)
2176
+ .then(function(form) {
2177
+ form.on('submitDone', function(submission) {
2178
+ console.log('Form submitted:', submission);
2179
+ form.element.style.display = 'none';
2180
+ const thankYouMsg = document.getElementById('thank-you-message');
2181
+ thankYouMsg.style.display = 'block';
2182
+ // In a real application, you would redirect:
2183
+ // window.location = '/thank-you-page';
2184
+ });
2185
+ });
2186
+
2187
+ // Wizard - Proper 3-step multi-page wizard
2188
+ const wizardSchema = {
2189
+ display: 'wizard',
2190
+ components: [
2191
+ {
2192
+ type: 'panel',
2193
+ key: 'step1PersonalInfo',
2194
+ title: 'Step 1: Personal Information',
2195
+ components: [
2196
+ {
2197
+ type: 'htmlelement',
2198
+ tag: 'p',
2199
+ content: 'Please provide your personal information.',
2200
+ className: 'mb-3 text-gray-600'
2201
+ },
2202
+ { type: 'textfield', key: 'firstName', label: 'First Name', placeholder: 'John', validate: { required: true } },
2203
+ { type: 'textfield', key: 'lastName', label: 'Last Name', placeholder: 'Doe', validate: { required: true } },
2204
+ { type: 'datetime', key: 'birthDate', label: 'Date of Birth', format: 'yyyy-MM-dd', validate: { required: true } },
2205
+ {
2206
+ type: 'select',
2207
+ key: 'gender',
2208
+ label: 'Gender',
2209
+ data: {
2210
+ values: [
2211
+ { label: 'Male', value: 'male' },
2212
+ { label: 'Female', value: 'female' },
2213
+ { label: 'Non-binary', value: 'nonbinary' },
2214
+ { label: 'Prefer not to say', value: 'other' }
2215
+ ]
2216
+ }
2217
+ }
2218
+ ]
2219
+ },
2220
+ {
2221
+ type: 'panel',
2222
+ key: 'step2ContactInfo',
2223
+ title: 'Step 2: Contact Information',
2224
+ components: [
2225
+ {
2226
+ type: 'htmlelement',
2227
+ tag: 'p',
2228
+ content: 'How can we reach you?',
2229
+ className: 'mb-3 text-gray-600'
2230
+ },
2231
+ { type: 'email', key: 'email', label: 'Email Address', placeholder: 'john.doe@example.com', validate: { required: true } },
2232
+ { type: 'phoneNumber', key: 'phone', label: 'Phone Number', placeholder: '(555) 555-5555', validate: { required: true } },
2233
+ { type: 'textfield', key: 'address', label: 'Street Address', placeholder: '123 Main St' },
2234
+ { type: 'textfield', key: 'city', label: 'City', placeholder: 'New York' },
2235
+ {
2236
+ type: 'select',
2237
+ key: 'country',
2238
+ label: 'Country',
2239
+ data: {
2240
+ values: [
2241
+ { label: 'United States', value: 'us' },
2242
+ { label: 'Canada', value: 'ca' },
2243
+ { label: 'United Kingdom', value: 'uk' },
2244
+ { label: 'Australia', value: 'au' }
2245
+ ]
2246
+ },
2247
+ validate: { required: true }
2248
+ }
2249
+ ]
2250
+ },
2251
+ {
2252
+ type: 'panel',
2253
+ key: 'step3Preferences',
2254
+ title: 'Step 3: Preferences & Review',
2255
+ components: [
2256
+ {
2257
+ type: 'htmlelement',
2258
+ tag: 'p',
2259
+ content: 'Almost done! Tell us your preferences.',
2260
+ className: 'mb-3 text-gray-600'
2261
+ },
2262
+ {
2263
+ type: 'selectboxes',
2264
+ key: 'interests',
2265
+ label: 'Areas of Interest',
2266
+ values: [
2267
+ { label: 'Product Updates', value: 'products' },
2268
+ { label: 'Newsletter', value: 'newsletter' },
2269
+ { label: 'Special Offers', value: 'offers' },
2270
+ { label: 'Events & Webinars', value: 'events' }
2271
+ ]
2272
+ },
2273
+ {
2274
+ type: 'radio',
2275
+ key: 'contactMethod',
2276
+ label: 'Preferred Contact Method',
2277
+ values: [
2278
+ { label: 'Email', value: 'email' },
2279
+ { label: 'Phone', value: 'phone' },
2280
+ { label: 'SMS', value: 'sms' }
2281
+ ],
2282
+ validate: { required: true }
2283
+ },
2284
+ { type: 'textarea', key: 'comments', label: 'Additional Comments', rows: 3, placeholder: 'Any other information you would like to share...' },
2285
+ { type: 'checkbox', key: 'terms', label: 'I agree to the terms and conditions', validate: { required: true } }
2286
+ ]
2287
+ }
2288
+ ]
2289
+ };
2290
+ Formio.createForm(document.getElementById('form-wizard'), wizardSchema);
2291
+
2292
+ // Conditional Logic
2293
+ const conditionalSchema = {
2294
+ display: 'form',
2295
+ components: [
2296
+ { type: 'checkbox', key: 'hasCompany', label: 'I am registering on behalf of a company' },
2297
+ { type: 'textfield', key: 'companyName', label: 'Company Name',
2298
+ conditional: { show: true, when: 'hasCompany', eq: true }
2299
+ },
2300
+ { type: 'button', action: 'submit', label: 'Submit', theme: 'primary' }
2301
+ ]
2302
+ };
2303
+ Formio.createForm(document.getElementById('form-conditional'), conditionalSchema);
2304
+
2305
+ // File Upload - Proper example with actual file field
2306
+ const fileSchema = {
2307
+ display: 'form',
2308
+ components: [
2309
+ {
2310
+ type: 'htmlelement',
2311
+ tag: 'p',
2312
+ content: 'Upload files to Cloudflare R2 storage (or base64 encoding for demo).',
2313
+ className: 'mb-4 text-gray-600'
2314
+ },
2315
+ { type: 'textfield', key: 'name', label: 'Your Name', placeholder: 'John Doe', validate: { required: true } },
2316
+ { type: 'email', key: 'email', label: 'Email Address', placeholder: 'john@example.com', validate: { required: true } },
2317
+ {
2318
+ type: 'file',
2319
+ key: 'resume',
2320
+ label: 'Upload Resume (PDF, DOC, DOCX)',
2321
+ storage: 'base64',
2322
+ filePattern: '.pdf,.doc,.docx',
2323
+ fileMaxSize: '5MB',
2324
+ validate: { required: true }
2325
+ },
2326
+ {
2327
+ type: 'file',
2328
+ key: 'portfolio',
2329
+ label: 'Portfolio/Work Samples (Optional)',
2330
+ storage: 'base64',
2331
+ filePattern: '.pdf,.zip,.jpg,.png',
2332
+ fileMaxSize: '10MB',
2333
+ multiple: false
2334
+ },
2335
+ {
2336
+ type: 'file',
2337
+ key: 'attachments',
2338
+ label: 'Additional Attachments (Multiple files allowed)',
2339
+ storage: 'base64',
2340
+ multiple: true,
2341
+ fileMaxSize: '5MB'
2342
+ },
2343
+ { type: 'textarea', key: 'coverLetter', label: 'Cover Letter', rows: 5, placeholder: 'Tell us why you are a great fit...' },
2344
+ { type: 'button', action: 'submit', label: 'Upload & Submit', theme: 'primary' }
2345
+ ]
2346
+ };
2347
+ Formio.createForm(document.getElementById('form-file-upload'), fileSchema);
2348
+
2349
+ // Address (without API key for demo)
2350
+ const addressSchema = {
2351
+ display: 'form',
2352
+ components: [
2353
+ { type: 'textfield', key: 'street', label: 'Street Address' },
2354
+ { type: 'textfield', key: 'city', label: 'City' },
2355
+ { type: 'textfield', key: 'state', label: 'State' },
2356
+ { type: 'textfield', key: 'zip', label: 'ZIP Code' },
2357
+ { type: 'button', action: 'submit', label: 'Submit', theme: 'primary' }
2358
+ ]
2359
+ };
2360
+ Formio.createForm(document.getElementById('form-address'), addressSchema);
2361
+
2362
+ // Signature
2363
+ const signatureSchema = {
2364
+ display: 'form',
2365
+ components: [
2366
+ { type: 'textfield', key: 'name', label: 'Your Name' },
2367
+ { type: 'signature', key: 'signature', label: 'Sign Here', width: '100%', height: '150px' },
2368
+ { type: 'button', action: 'submit', label: 'Submit', theme: 'primary' }
2369
+ ]
2370
+ };
2371
+ Formio.createForm(document.getElementById('form-signature'), signatureSchema);
2372
+
2373
+ // Data Grid
2374
+ const dataGridSchema = {
2375
+ display: 'form',
2376
+ components: [
2377
+ {
2378
+ type: 'datagrid',
2379
+ key: 'items',
2380
+ label: 'Items',
2381
+ components: [
2382
+ { type: 'textfield', key: 'item', label: 'Item' },
2383
+ { type: 'number', key: 'quantity', label: 'Quantity' }
2384
+ ]
2385
+ },
2386
+ { type: 'button', action: 'submit', label: 'Submit', theme: 'primary' }
2387
+ ]
2388
+ };
2389
+ Formio.createForm(document.getElementById('form-data-grid'), dataGridSchema);
2390
+
2391
+ // Turnstile Protection Form
2392
+ const turnstileSchema = {
2393
+ components: [
2394
+ {
2395
+ type: 'textfield',
2396
+ key: 'fullName',
2397
+ label: 'Full Name',
2398
+ placeholder: 'Enter your full name',
2399
+ validate: { required: true }
2400
+ },
2401
+ {
2402
+ type: 'email',
2403
+ key: 'email',
2404
+ label: 'Email Address',
2405
+ placeholder: 'you@example.com',
2406
+ validate: { required: true }
2407
+ },
2408
+ {
2409
+ type: 'textarea',
2410
+ key: 'message',
2411
+ label: 'Message',
2412
+ placeholder: 'Tell us what you are thinking...',
2413
+ rows: 4,
2414
+ validate: { required: true }
2415
+ },
2416
+ {
2417
+ type: 'turnstile',
2418
+ key: 'turnstile',
2419
+ label: 'Security Verification',
2420
+ theme: 'auto',
2421
+ size: 'normal',
2422
+ appearance: 'always',
2423
+ persistent: false,
2424
+ protected: true
2425
+ },
2426
+ {
2427
+ type: 'button',
2428
+ action: 'submit',
2429
+ label: 'Send Secure Message',
2430
+ theme: 'primary',
2431
+ block: true
2432
+ }
2433
+ ]
2434
+ };
2435
+ Formio.createForm(document.getElementById('form-turnstile'), turnstileSchema);
2436
+ }
2437
+
2438
+ // Wait for Form.io to load
2439
+ if (typeof Formio !== 'undefined') {
2440
+ initForms();
2441
+ setupNavigation();
2442
+ } else {
2443
+ setTimeout(function checkFormio() {
2444
+ if (typeof Formio !== 'undefined') {
2445
+ initForms();
2446
+ setupNavigation();
2447
+ } else {
2448
+ setTimeout(checkFormio, 100);
2449
+ }
2450
+ }, 100);
2451
+ }
2452
+ </script>
2453
+ `;
2454
+ const layoutData = {
2455
+ title: "Forms Examples",
2456
+ pageTitle: "Forms Examples",
2457
+ content: pageContent,
2458
+ user: data.user,
2459
+ version: data.version
2460
+ };
2461
+ return renderAdminLayoutCatalyst(layoutData);
2462
+ }
2463
+
2464
+ export { renderFilterBar, renderFormsDocsPage, renderFormsExamplesPage };
2465
+ //# sourceMappingURL=chunk-H7AMQWVI.js.map
2466
+ //# sourceMappingURL=chunk-H7AMQWVI.js.map