json-object-editor 0.10.657 → 0.10.662
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.
- package/CHANGELOG.md +15 -1
- package/css/joe-styles.css +1 -1
- package/css/joe.css +2 -2
- package/css/joe.min.css +1 -1
- package/docs/JOE_AI_Overview.md +73 -17
- package/docs/React_Form_Integration_Example.md +299 -0
- package/docs/React_Form_Integration_Strategy.md +397 -0
- package/dummy +9 -1
- package/form-qs.json +1007 -0
- package/js/joe-ai.js +257 -31
- package/js/joe-react-form.js +608 -0
- package/js/joe.js +1 -1
- package/package.json +1 -1
- package/react-form-spa-ex.js +570 -0
- package/readme.md +13 -1
- package/server/fields/core.js +52 -2
- package/server/modules/MCP.js +4 -0
- package/server/modules/Sites.js +28 -2
- package/server/plugins/chatgpt.js +304 -24
- package/server/plugins/formBuilder.js +98 -0
- package/server/schemas/ai_assistant.js +58 -45
- package/server/schemas/ai_prompt.js +4 -58
- package/server/schemas/ai_widget_conversation.js +31 -0
- package/server/schemas/include.js +8 -3
- package/server/schemas/page.js +6 -0
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
# React Form Integration - Usage Example
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
This document shows how to set up a JOE page with a React-powered form using JSON form definitions.
|
|
6
|
+
|
|
7
|
+
## Setup Steps
|
|
8
|
+
|
|
9
|
+
### 1. Create JSON Form Definition Include
|
|
10
|
+
|
|
11
|
+
1. In JOE, go to **Includes** schema
|
|
12
|
+
2. Create a new include:
|
|
13
|
+
- **Name**: "Harmonious Wellness Form Definition"
|
|
14
|
+
- **Filetype**: `json`
|
|
15
|
+
- **Content**: Paste your `form-qs.json` content
|
|
16
|
+
- **Fill Template**: `false` (or `true` if you want to use `${this.}` variables)
|
|
17
|
+
3. Save and note the `_id` of the include
|
|
18
|
+
|
|
19
|
+
### 2. Create JOE Form Record
|
|
20
|
+
|
|
21
|
+
1. Go to **Forms** schema
|
|
22
|
+
2. Create a new form:
|
|
23
|
+
- **Name**: "Harmonious Wellness Health Questionnaire"
|
|
24
|
+
- **Save Submission**: `true`
|
|
25
|
+
- **Meta** (optional): Add `json_definition_include: "{include_id}"` if you want to link it
|
|
26
|
+
3. Save and note the `_id` of the form
|
|
27
|
+
|
|
28
|
+
### 3. Create React Form JS Include (if not already created)
|
|
29
|
+
|
|
30
|
+
1. Go to **Includes** schema
|
|
31
|
+
2. Create a new include:
|
|
32
|
+
- **Name**: "JOE React Form Renderer"
|
|
33
|
+
- **Filetype**: `js`
|
|
34
|
+
- **Content**: Copy content from `js/joe-react-form.js`
|
|
35
|
+
3. Save and note the `_id` of the include
|
|
36
|
+
|
|
37
|
+
### 4. Create JOE Page
|
|
38
|
+
|
|
39
|
+
1. Go to **Pages** schema
|
|
40
|
+
2. Create a new page:
|
|
41
|
+
- **Name**: "Health Questionnaire"
|
|
42
|
+
- **Site**: Select your site
|
|
43
|
+
- **Path**: `/health-questionnaire` (or your desired path)
|
|
44
|
+
- **Content Type**: `code` (or `module` for dynamic generation - see examples below)
|
|
45
|
+
- **Form**: Select the form you created in step 2
|
|
46
|
+
- **Includes**:
|
|
47
|
+
- Add the React Form JS include from step 3
|
|
48
|
+
- Add the JSON Form Definition include from step 1 (optional - formDefinition plugin can find it automatically)
|
|
49
|
+
- **Content**: Use the template below
|
|
50
|
+
- **Layout**: Select a layout (or create one with minimal structure)
|
|
51
|
+
|
|
52
|
+
**Note:** When using `${INCLUDES}` in your page content, JOE will automatically generate `<script>` and `<link>` tags for all JS and CSS includes you add here. The JSON include is accessed via the formDefinition API endpoint, not as a direct include.
|
|
53
|
+
|
|
54
|
+
### 5. Page Content Template
|
|
55
|
+
|
|
56
|
+
**Option A: Using Code Content Type with Template Variables (Recommended)**
|
|
57
|
+
|
|
58
|
+
Set `content_type: 'code'` and use JOE template variables:
|
|
59
|
+
|
|
60
|
+
```html
|
|
61
|
+
<!DOCTYPE html>
|
|
62
|
+
<html>
|
|
63
|
+
<head>
|
|
64
|
+
<meta charset="utf-8">
|
|
65
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
66
|
+
<title>${this.PAGE.name}</title>
|
|
67
|
+
|
|
68
|
+
<!-- Tailwind CSS (for styling) - Optional -->
|
|
69
|
+
<script src="https://cdn.tailwindcss.com"></script>
|
|
70
|
+
|
|
71
|
+
<!-- React from CDN - MUST load before ${INCLUDES} (which contains joe-react-form.js) -->
|
|
72
|
+
<script crossorigin src="https://unpkg.com/react@18/umd/react.production.min.js"></script>
|
|
73
|
+
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"></script>
|
|
74
|
+
|
|
75
|
+
<!-- JOE automatically includes all JS/CSS from page.includes here -->
|
|
76
|
+
<!-- Note: joe-react-form.js will wait for React if needed, but loading React first is recommended -->
|
|
77
|
+
${INCLUDES}
|
|
78
|
+
</head>
|
|
79
|
+
<body>
|
|
80
|
+
<div id="react-form-root"></div>
|
|
81
|
+
<script>
|
|
82
|
+
// Wait for joeReactForm to be available (script may still be loading)
|
|
83
|
+
function initForm() {
|
|
84
|
+
if (typeof joeReactForm !== 'undefined') {
|
|
85
|
+
joeReactForm.init({
|
|
86
|
+
rootId: 'react-form-root',
|
|
87
|
+
formDefinitionUrl: '/API/plugin/formBuilder/definition?formid=${this.PAGE.form}&pageid=${this.PAGE._id}',
|
|
88
|
+
formId: '${this.PAGE.form}'
|
|
89
|
+
});
|
|
90
|
+
} else {
|
|
91
|
+
// If not ready, wait for DOMContentLoaded or check again shortly
|
|
92
|
+
if (document.readyState === 'loading') {
|
|
93
|
+
document.addEventListener('DOMContentLoaded', initForm);
|
|
94
|
+
} else {
|
|
95
|
+
setTimeout(initForm, 50);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
initForm();
|
|
100
|
+
</script>
|
|
101
|
+
</body>
|
|
102
|
+
</html>
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
**Benefits:**
|
|
106
|
+
- `${INCLUDES}` automatically includes all JS/CSS from page's includes array
|
|
107
|
+
- `${this.PAGE.form}` and `${this.PAGE._id}` automatically use linked form and page IDs
|
|
108
|
+
- No hardcoded IDs to maintain!
|
|
109
|
+
|
|
110
|
+
**Option B: Using Module Content Type**
|
|
111
|
+
|
|
112
|
+
Set `content_type: 'module'` - see the "Alternative: Using Module Content Type" section below.
|
|
113
|
+
|
|
114
|
+
### Alternative: Using Module Content Type (Recommended)
|
|
115
|
+
|
|
116
|
+
You can use `content_type: 'module'` to generate HTML dynamically using JOE's template patterns:
|
|
117
|
+
|
|
118
|
+
```javascript
|
|
119
|
+
module.exports = function(content) {
|
|
120
|
+
var page = content.PAGE;
|
|
121
|
+
var form = page.form ? JOE.Cache.findByID('form', page.form) : null;
|
|
122
|
+
|
|
123
|
+
if (!form) {
|
|
124
|
+
return '<div>Error: No form linked to this page</div>';
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Use formDefinition plugin to auto-find JSON include from page
|
|
128
|
+
// This will find the JSON include from page.includes automatically
|
|
129
|
+
var formDefUrl = '/API/plugin/formBuilder/definition?formid=' + form._id + '&pageid=' + page._id;
|
|
130
|
+
|
|
131
|
+
return `<!DOCTYPE html>
|
|
132
|
+
<html>
|
|
133
|
+
<head>
|
|
134
|
+
<meta charset="utf-8">
|
|
135
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
136
|
+
<title>${page.name}</title>
|
|
137
|
+
<script src="https://cdn.tailwindcss.com"></script>
|
|
138
|
+
<script crossorigin src="https://unpkg.com/react@18/umd/react.production.min.js"></script>
|
|
139
|
+
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"></script>
|
|
140
|
+
${content.INCLUDES}
|
|
141
|
+
</head>
|
|
142
|
+
<body>
|
|
143
|
+
<div id="react-form-root"></div>
|
|
144
|
+
<script>
|
|
145
|
+
// Wait for joeReactForm to be available (script may still be loading)
|
|
146
|
+
function initForm() {
|
|
147
|
+
if (typeof joeReactForm !== 'undefined') {
|
|
148
|
+
joeReactForm.init({
|
|
149
|
+
rootId: 'react-form-root',
|
|
150
|
+
formDefinitionUrl: '${formDefUrl}',
|
|
151
|
+
formId: '${form._id}'
|
|
152
|
+
});
|
|
153
|
+
} else {
|
|
154
|
+
if (document.readyState === 'loading') {
|
|
155
|
+
document.addEventListener('DOMContentLoaded', initForm);
|
|
156
|
+
} else {
|
|
157
|
+
setTimeout(initForm, 50);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
initForm();
|
|
162
|
+
</script>
|
|
163
|
+
</body>
|
|
164
|
+
</html>`;
|
|
165
|
+
};
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
**Key improvements:**
|
|
169
|
+
- Uses `${content.INCLUDES}` - automatically includes all JS/CSS from page's includes array
|
|
170
|
+
- Uses `formBuilder.definition` endpoint with `pageid` - automatically finds JSON include from page
|
|
171
|
+
- No need to manually specify include IDs
|
|
172
|
+
|
|
173
|
+
### Alternative: Using Code Content Type with Template Variables
|
|
174
|
+
|
|
175
|
+
You can also use `content_type: 'code'` with JOE template variables:
|
|
176
|
+
|
|
177
|
+
```html
|
|
178
|
+
<!DOCTYPE html>
|
|
179
|
+
<html>
|
|
180
|
+
<head>
|
|
181
|
+
<meta charset="utf-8">
|
|
182
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
183
|
+
<title>${this.PAGE.name}</title>
|
|
184
|
+
<script src="https://cdn.tailwindcss.com"></script>
|
|
185
|
+
<script crossorigin src="https://unpkg.com/react@18/umd/react.production.min.js"></script>
|
|
186
|
+
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"></script>
|
|
187
|
+
${INCLUDES}
|
|
188
|
+
</head>
|
|
189
|
+
<body>
|
|
190
|
+
<div id="react-form-root"></div>
|
|
191
|
+
<script>
|
|
192
|
+
// Wait for joeReactForm to be available (script may still be loading)
|
|
193
|
+
function initForm() {
|
|
194
|
+
if (typeof joeReactForm !== 'undefined') {
|
|
195
|
+
joeReactForm.init({
|
|
196
|
+
rootId: 'react-form-root',
|
|
197
|
+
formDefinitionUrl: '/API/plugin/formBuilder/definition?formid=${this.PAGE.form}&pageid=${this.PAGE._id}',
|
|
198
|
+
formId: '${this.PAGE.form}'
|
|
199
|
+
});
|
|
200
|
+
} else {
|
|
201
|
+
if (document.readyState === 'loading') {
|
|
202
|
+
document.addEventListener('DOMContentLoaded', initForm);
|
|
203
|
+
} else {
|
|
204
|
+
setTimeout(initForm, 50);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
initForm();
|
|
209
|
+
</script>
|
|
210
|
+
</body>
|
|
211
|
+
</html>
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
This automatically:
|
|
215
|
+
- Uses `${INCLUDES}` to inject all page includes (JS/CSS)
|
|
216
|
+
- Uses `${this.PAGE.form}` and `${this.PAGE._id}` to pass form and page IDs
|
|
217
|
+
- No hardcoded IDs needed!
|
|
218
|
+
|
|
219
|
+
## API Endpoints
|
|
220
|
+
|
|
221
|
+
### Get Form Definition
|
|
222
|
+
|
|
223
|
+
```
|
|
224
|
+
GET /API/plugin/formBuilder/definition?include_id={include_id}
|
|
225
|
+
GET /API/plugin/formBuilder/definition?formid={form_id}&field=json_definition_include
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
Returns: JSON form definition (form-qs.json format)
|
|
229
|
+
|
|
230
|
+
### Submit Form
|
|
231
|
+
|
|
232
|
+
```
|
|
233
|
+
POST /API/plugin/formBuilder/submission
|
|
234
|
+
Content-Type: application/json
|
|
235
|
+
|
|
236
|
+
{
|
|
237
|
+
"formid": "{form_id}",
|
|
238
|
+
"submission": {
|
|
239
|
+
"first_name": "John",
|
|
240
|
+
"last_name": "Doe",
|
|
241
|
+
...
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
Returns: Submission confirmation or error
|
|
247
|
+
|
|
248
|
+
## How It Works
|
|
249
|
+
|
|
250
|
+
1. **Page loads** → Includes React CDN and form renderer JS
|
|
251
|
+
2. **Form renderer initializes** → Fetches JSON form definition from include
|
|
252
|
+
3. **React renders form** → Based on JSON structure (sections, fields, visibility rules)
|
|
253
|
+
4. **User fills form** → React manages state and conditional visibility
|
|
254
|
+
5. **User submits** → Data sent to `/API/plugin/formBuilder/submission`
|
|
255
|
+
6. **JOE saves submission** → Creates `submission` record linked to form
|
|
256
|
+
|
|
257
|
+
## Customization
|
|
258
|
+
|
|
259
|
+
### Styling
|
|
260
|
+
|
|
261
|
+
The form uses Tailwind CSS classes. You can:
|
|
262
|
+
- Include Tailwind CDN (as shown above)
|
|
263
|
+
- Add custom CSS via include
|
|
264
|
+
- Modify classes in `joe-react-form.js`
|
|
265
|
+
|
|
266
|
+
### Form Definition Template Variables
|
|
267
|
+
|
|
268
|
+
If you enable `fillTemplate` on the JSON include, you can use variables like:
|
|
269
|
+
```json
|
|
270
|
+
{
|
|
271
|
+
"formName": "${this.SITE.name} - Health Questionnaire",
|
|
272
|
+
"version": "${this.WEBCONFIG.version}"
|
|
273
|
+
}
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
### Form Callbacks
|
|
277
|
+
|
|
278
|
+
The JOE form record supports:
|
|
279
|
+
- **Callback**: Client-side function called after submission
|
|
280
|
+
- **Server Action**: Server-side function called during submission
|
|
281
|
+
- **Validate**: Client-side validation function
|
|
282
|
+
|
|
283
|
+
These can be added to the form record and will be executed as configured.
|
|
284
|
+
|
|
285
|
+
## Troubleshooting
|
|
286
|
+
|
|
287
|
+
### Form not loading
|
|
288
|
+
- Check browser console for errors
|
|
289
|
+
- Verify all include IDs are correct
|
|
290
|
+
- Ensure React CDN is loading (check Network tab)
|
|
291
|
+
|
|
292
|
+
### Submission errors
|
|
293
|
+
- Verify form ID is correct
|
|
294
|
+
- Check that form has `save_submission: true`
|
|
295
|
+
- Check server logs for submission errors
|
|
296
|
+
|
|
297
|
+
### Styling issues
|
|
298
|
+
- Ensure Tailwind CSS is loaded (or adjust classes)
|
|
299
|
+
- Check that Tailwind CDN version supports all classes used
|
|
@@ -0,0 +1,397 @@
|
|
|
1
|
+
# React Form Integration Strategy for JOE
|
|
2
|
+
|
|
3
|
+
## Executive Summary
|
|
4
|
+
|
|
5
|
+
This document outlines strategy options for integrating:
|
|
6
|
+
1. **JSON-based form definitions** (from `form-qs.json` format)
|
|
7
|
+
2. **React Single Page Application** as a page type in JOE
|
|
8
|
+
3. **Form submission storage** into JOE's submission system
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## Current State Analysis
|
|
13
|
+
|
|
14
|
+
### JOE Form System (Current)
|
|
15
|
+
- **Schema**: `form` schema with `sections` (objectList), `questions` (references to question schema)
|
|
16
|
+
- **Rendering**: `formBuilder` plugin renders HTML forms server-side
|
|
17
|
+
- **Submission**: POST to `/API/plugin/formBuilder/submission` with `{formid, submission: {...}}`
|
|
18
|
+
- **Storage**: Creates `submission` items with `data`, `form_name`, `form` (reference), `visitor`, `submission_date`
|
|
19
|
+
|
|
20
|
+
### JOE Page System (Current)
|
|
21
|
+
- **Content Types**: `wysiwyg`, `code`, `plugin`, `module`
|
|
22
|
+
- **Module Type**: Server-side JS function that returns HTML string
|
|
23
|
+
- **Includes**: Can load JS/CSS files via `include` schema (served at `/_include/{id}`)
|
|
24
|
+
- **Layouts**: Templates with `${this.PAGE.content}` placeholder
|
|
25
|
+
|
|
26
|
+
### Target Requirements
|
|
27
|
+
1. Use JSON file (`form-qs.json`) for form structure and rules
|
|
28
|
+
2. Embed React SPA as a page in JOE
|
|
29
|
+
3. Submit to JOE submission storage on form completion
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## Strategy Options
|
|
34
|
+
|
|
35
|
+
### **Option 1: React SPA as Code Page with API Bridge** ⭐ **RECOMMENDED**
|
|
36
|
+
|
|
37
|
+
#### Architecture
|
|
38
|
+
- **Page Type**: `content_type: 'code'`
|
|
39
|
+
- **React App**: Compiled/bundled React app loaded via includes or inline
|
|
40
|
+
- **JSON Loading**: React app fetches form definition JSON via API endpoint
|
|
41
|
+
- **Submission**: React app posts to JOE submission API endpoint
|
|
42
|
+
|
|
43
|
+
#### Implementation Details
|
|
44
|
+
|
|
45
|
+
**1. Create Form Definition API Endpoint**
|
|
46
|
+
```javascript
|
|
47
|
+
// server/plugins/formBuilder.js
|
|
48
|
+
this.definition = function(req, res) {
|
|
49
|
+
var formId = req.query.formid || req.params.formid;
|
|
50
|
+
// Load form-qs.json format, return as JSON
|
|
51
|
+
// Could be stored as include or in a new form_definition schema
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
**2. Create Submission API Endpoint** (extend existing or new)
|
|
56
|
+
```javascript
|
|
57
|
+
// Extend server/plugins/formBuilder.js or create formSubmission.js
|
|
58
|
+
this.submit = function(data, req) {
|
|
59
|
+
// Accept JSON form submission, map to JOE submission format
|
|
60
|
+
// Create submission record with form reference
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
**3. React App Integration**
|
|
65
|
+
- React app bundled (webpack/vite) and stored as an `include` (JS file)
|
|
66
|
+
- Page content: `<div id="react-form-root"></div><script src="/_include/{react-bundle-id}"></script>`
|
|
67
|
+
- React app:
|
|
68
|
+
- Fetches form definition from `/API/plugin/formBuilder/definition?formid={id}`
|
|
69
|
+
- Renders form using `react-form-spa-ex.js` logic
|
|
70
|
+
- Submits to `/API/plugin/formBuilder/submission` (or new endpoint)
|
|
71
|
+
|
|
72
|
+
#### Pros
|
|
73
|
+
- ✅ Leverages existing JOE infrastructure
|
|
74
|
+
- ✅ React app is independent and can be versioned/updated separately
|
|
75
|
+
- ✅ Works with existing submission storage
|
|
76
|
+
- ✅ Minimal changes to JOE core
|
|
77
|
+
- ✅ Can reuse existing form validation/storage logic
|
|
78
|
+
|
|
79
|
+
#### Cons
|
|
80
|
+
- ⚠️ Requires bundling React app separately
|
|
81
|
+
- ⚠️ Two API endpoints to maintain (form definition + submission)
|
|
82
|
+
- ⚠️ React app needs to know JOE API structure
|
|
83
|
+
|
|
84
|
+
#### Estimated Effort
|
|
85
|
+
- **Low-Medium**: ~2-3 days
|
|
86
|
+
- API endpoint for form definitions: 4-6 hours
|
|
87
|
+
- Submission endpoint extension: 2-4 hours
|
|
88
|
+
- React app integration/hooking: 4-6 hours
|
|
89
|
+
- Testing: 4-6 hours
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
### **Option 2: New "spa" Content Type with Form Plugin**
|
|
94
|
+
|
|
95
|
+
#### Architecture
|
|
96
|
+
- **Page Type**: New `content_type: 'spa'` option
|
|
97
|
+
- **Form Plugin**: New `reactForm` plugin that handles React app bootstrapping
|
|
98
|
+
- **Configuration**: Page stores React component path and form definition reference
|
|
99
|
+
|
|
100
|
+
#### Implementation Details
|
|
101
|
+
|
|
102
|
+
**1. Extend Page Schema**
|
|
103
|
+
```javascript
|
|
104
|
+
// server/schemas/page.js
|
|
105
|
+
{ name:'content_type', type:'string', enumValues:['wysiwyg','code','plugin','module','spa'] }
|
|
106
|
+
// Add fields: spa_component, form_definition_id
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
**2. Create React Form Plugin**
|
|
110
|
+
```javascript
|
|
111
|
+
// server/plugins/reactForm.js
|
|
112
|
+
this.render = function(data, req) {
|
|
113
|
+
var page = data.page;
|
|
114
|
+
var formDef = loadFormDefinition(page.form_definition_id);
|
|
115
|
+
// Return HTML with:
|
|
116
|
+
// - React libraries (CDN or includes)
|
|
117
|
+
// - Form definition as JSON in <script> tag
|
|
118
|
+
// - React component mount point
|
|
119
|
+
// - Submission handler that calls JOE API
|
|
120
|
+
}
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
**3. Submission Handling**
|
|
124
|
+
- Plugin generates submission handler that posts to existing formBuilder endpoint
|
|
125
|
+
- Maps JSON form format to JOE submission format
|
|
126
|
+
|
|
127
|
+
#### Pros
|
|
128
|
+
- ✅ Native JOE integration
|
|
129
|
+
- ✅ Form definitions can be stored as JOE items
|
|
130
|
+
- ✅ Consistent with other page content types
|
|
131
|
+
- ✅ Can be configured through JOE UI
|
|
132
|
+
|
|
133
|
+
#### Cons
|
|
134
|
+
- ⚠️ Requires schema changes to page
|
|
135
|
+
- ⚠️ More complex implementation
|
|
136
|
+
- ⚠️ Need to handle React bundling/delivery
|
|
137
|
+
|
|
138
|
+
#### Estimated Effort
|
|
139
|
+
- **Medium**: ~4-5 days
|
|
140
|
+
- Schema extensions: 2-4 hours
|
|
141
|
+
- React form plugin: 1-2 days
|
|
142
|
+
- React bundling strategy: 4-8 hours
|
|
143
|
+
- UI for configuring SPA pages: 4-6 hours
|
|
144
|
+
- Testing: 1 day
|
|
145
|
+
|
|
146
|
+
---
|
|
147
|
+
|
|
148
|
+
### **Option 3: Module Type with Server-Side React Rendering**
|
|
149
|
+
|
|
150
|
+
#### Architecture
|
|
151
|
+
- **Page Type**: `content_type: 'module'`
|
|
152
|
+
- **SSR**: Use React server-side rendering (via `react-dom/server`)
|
|
153
|
+
- **Hydration**: Client-side React app for interactivity
|
|
154
|
+
|
|
155
|
+
#### Implementation Details
|
|
156
|
+
|
|
157
|
+
**1. Module Function**
|
|
158
|
+
```javascript
|
|
159
|
+
// Page content (module type)
|
|
160
|
+
module.exports = async function(content) {
|
|
161
|
+
var React = require('react');
|
|
162
|
+
var ReactDOMServer = require('react-dom/server');
|
|
163
|
+
var FormApp = require('./path/to/react-form-spa-ex.js');
|
|
164
|
+
var formDef = loadFormDefinition(content.PAGE.meta.form_definition_id);
|
|
165
|
+
|
|
166
|
+
var html = ReactDOMServer.renderToString(
|
|
167
|
+
React.createElement(FormApp, { formDefinition: formDef })
|
|
168
|
+
);
|
|
169
|
+
|
|
170
|
+
return `<div id="root">${html}</div>
|
|
171
|
+
<script>window.__FORM_DEF__ = ${JSON.stringify(formDef)};</script>
|
|
172
|
+
<script src="/_include/{react-bundle-id}"></script>`;
|
|
173
|
+
}
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
**2. Client Hydration**
|
|
177
|
+
- React app mounts and hydrates the server-rendered HTML
|
|
178
|
+
- Handles form submission to JOE API
|
|
179
|
+
|
|
180
|
+
#### Pros
|
|
181
|
+
- ✅ SEO-friendly (server-rendered)
|
|
182
|
+
- ✅ Faster initial render
|
|
183
|
+
- ✅ Uses existing module type
|
|
184
|
+
|
|
185
|
+
#### Cons
|
|
186
|
+
- ⚠️ Requires React SSR dependencies on server
|
|
187
|
+
- ⚠️ More complex build/deployment
|
|
188
|
+
- ⚠️ Node.js React dependencies add overhead
|
|
189
|
+
- ⚠️ Hydration complexity
|
|
190
|
+
|
|
191
|
+
#### Estimated Effort
|
|
192
|
+
- **Medium-High**: ~5-7 days
|
|
193
|
+
- SSR setup: 1-2 days
|
|
194
|
+
- Hydration logic: 1 day
|
|
195
|
+
- Build configuration: 1 day
|
|
196
|
+
- Testing/debugging: 2-3 days
|
|
197
|
+
|
|
198
|
+
---
|
|
199
|
+
|
|
200
|
+
### **Option 4: Hybrid - Form Definition Schema + React Include**
|
|
201
|
+
|
|
202
|
+
#### Architecture
|
|
203
|
+
- **New Schema**: `form_definition` schema to store JSON form definitions
|
|
204
|
+
- **Form Mapping**: Bridge between JSON form format and JOE form/question schemas
|
|
205
|
+
- **Page**: Code or module type that loads React app and form definition
|
|
206
|
+
|
|
207
|
+
#### Implementation Details
|
|
208
|
+
|
|
209
|
+
**1. Create Form Definition Schema**
|
|
210
|
+
```javascript
|
|
211
|
+
// server/schemas/form_definition.js
|
|
212
|
+
{
|
|
213
|
+
name: 'form_definition',
|
|
214
|
+
fields: [
|
|
215
|
+
{ name: 'form_id', type: 'string' }, // matches form-qs.json formId
|
|
216
|
+
{ name: 'definition', type: 'object' }, // full form-qs.json structure
|
|
217
|
+
{ name: 'form', type: 'string', isReference: true } // link to JOE form
|
|
218
|
+
]
|
|
219
|
+
}
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
**2. Form Bridge Service**
|
|
223
|
+
```javascript
|
|
224
|
+
// server/plugins/formBridge.js
|
|
225
|
+
// Convert form-qs.json format to JOE form/questions
|
|
226
|
+
// Or: Store JSON format, map on-the-fly for submissions
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
**3. React App**
|
|
230
|
+
- Loads form definition from API
|
|
231
|
+
- Renders form
|
|
232
|
+
- Submits in format compatible with JOE submissions
|
|
233
|
+
|
|
234
|
+
#### Pros
|
|
235
|
+
- ✅ JSON form definitions stored as first-class JOE items
|
|
236
|
+
- ✅ Can sync/import from external JSON files
|
|
237
|
+
- ✅ Separation of concerns (definition vs. rendering)
|
|
238
|
+
- ✅ Can potentially auto-generate JOE forms from JSON
|
|
239
|
+
|
|
240
|
+
#### Cons
|
|
241
|
+
- ⚠️ Additional schema to maintain
|
|
242
|
+
- ⚠️ Need mapping/bridge logic
|
|
243
|
+
- ⚠️ More moving parts
|
|
244
|
+
|
|
245
|
+
#### Estimated Effort
|
|
246
|
+
- **Medium**: ~4-5 days
|
|
247
|
+
- Form definition schema: 4-6 hours
|
|
248
|
+
- Bridge/mapping logic: 1-2 days
|
|
249
|
+
- React integration: 1 day
|
|
250
|
+
- Testing: 1 day
|
|
251
|
+
|
|
252
|
+
---
|
|
253
|
+
|
|
254
|
+
## Recommended Approach: **Option 1 with Option 4 Enhancements**
|
|
255
|
+
|
|
256
|
+
### Phase 1: Quick Win (Option 1)
|
|
257
|
+
1. Create API endpoint to serve form definitions (store JSON in includes or files)
|
|
258
|
+
2. Extend formBuilder submission endpoint to accept JSON format submissions
|
|
259
|
+
3. Create React app that:
|
|
260
|
+
- Loads form definition from API
|
|
261
|
+
- Renders form (based on react-form-spa-ex.js)
|
|
262
|
+
- Submits to JOE endpoint
|
|
263
|
+
4. Embed React app in a JOE page via includes
|
|
264
|
+
|
|
265
|
+
### Phase 2: Enhanced (Option 4 additions)
|
|
266
|
+
1. Create `form_definition` schema to store JSON form definitions in JOE
|
|
267
|
+
2. Add UI to import/manage form definitions
|
|
268
|
+
3. Optionally: Auto-create JOE forms from JSON definitions for reporting/admin
|
|
269
|
+
|
|
270
|
+
### Why This Approach?
|
|
271
|
+
- ✅ **Fastest to implement**: Leverages existing infrastructure
|
|
272
|
+
- ✅ **Low risk**: Minimal changes to core JOE systems
|
|
273
|
+
- ✅ **Flexible**: Can evolve to Option 4 later without breaking changes
|
|
274
|
+
- ✅ **Testable**: Can test React app independently
|
|
275
|
+
|
|
276
|
+
---
|
|
277
|
+
|
|
278
|
+
## Technical Implementation Details
|
|
279
|
+
|
|
280
|
+
### JSON Form Format Mapping
|
|
281
|
+
|
|
282
|
+
The `form-qs.json` format has:
|
|
283
|
+
- `formId`, `formName`, `version`
|
|
284
|
+
- `sections[]` with `fields[]`
|
|
285
|
+
- `visibility` rules with `whenAll`/`whenAny` conditions
|
|
286
|
+
- `dataExport` settings
|
|
287
|
+
|
|
288
|
+
**Mapping to JOE Submissions:**
|
|
289
|
+
```javascript
|
|
290
|
+
{
|
|
291
|
+
itemtype: 'submission',
|
|
292
|
+
form: {joe_form_id}, // reference to JOE form
|
|
293
|
+
form_name: formDef.formName,
|
|
294
|
+
data: {
|
|
295
|
+
// Flattened field values
|
|
296
|
+
first_name: "...",
|
|
297
|
+
last_name: "...",
|
|
298
|
+
// ... all field values
|
|
299
|
+
},
|
|
300
|
+
submission_date: new Date().toISOString(),
|
|
301
|
+
visitor: visitor_id, // from form if configured
|
|
302
|
+
source: req.headers.referer
|
|
303
|
+
}
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
### React App Submission Handler
|
|
307
|
+
|
|
308
|
+
```javascript
|
|
309
|
+
async function submitForm(formData, formId) {
|
|
310
|
+
const response = await fetch('/API/plugin/formBuilder/submission', {
|
|
311
|
+
method: 'POST',
|
|
312
|
+
headers: { 'Content-Type': 'application/json' },
|
|
313
|
+
body: JSON.stringify({
|
|
314
|
+
formid: formId, // JOE form ID
|
|
315
|
+
submission: formData // { first_name: "...", ... }
|
|
316
|
+
})
|
|
317
|
+
});
|
|
318
|
+
return response.json();
|
|
319
|
+
}
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
### Form Definition API
|
|
323
|
+
|
|
324
|
+
```javascript
|
|
325
|
+
// GET /API/plugin/formBuilder/definition?formid={id}
|
|
326
|
+
// Returns: form-qs.json format
|
|
327
|
+
{
|
|
328
|
+
formId: "...",
|
|
329
|
+
formName: "...",
|
|
330
|
+
sections: [...],
|
|
331
|
+
settings: {...}
|
|
332
|
+
}
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
---
|
|
336
|
+
|
|
337
|
+
## Migration Path
|
|
338
|
+
|
|
339
|
+
1. **Week 1**: Implement Option 1 (API endpoints + React integration)
|
|
340
|
+
2. **Week 2**: Test with example form, refine submission mapping
|
|
341
|
+
3. **Week 3** (Optional): Implement Option 4 enhancements (form_definition schema)
|
|
342
|
+
4. **Week 4**: Documentation and production deployment
|
|
343
|
+
|
|
344
|
+
---
|
|
345
|
+
|
|
346
|
+
## Open Questions
|
|
347
|
+
|
|
348
|
+
1. **Form Definition Storage**: Store JSON in includes, files, or new schema?
|
|
349
|
+
- **Recommendation**: Start with includes/files, move to schema in Phase 2
|
|
350
|
+
|
|
351
|
+
2. **React Bundling**: Use CDN, bundle via build process, or serve from includes?
|
|
352
|
+
- **Recommendation**: Bundle React app, store as include for versioning
|
|
353
|
+
|
|
354
|
+
3. **Form ID Mapping**: How to map `form-qs.json` `formId` to JOE `form._id`?
|
|
355
|
+
- **Recommendation**: Add `form_json_id` field to form schema, or store mapping in form_definition
|
|
356
|
+
|
|
357
|
+
4. **Visitor Tracking**: How to identify visitors in React SPA context?
|
|
358
|
+
- **Recommendation**: Use existing visitor_id_field mechanism, pass via form definition
|
|
359
|
+
|
|
360
|
+
5. **Validation**: Use React-side validation only, or also JOE-side?
|
|
361
|
+
- **Recommendation**: Client-side for UX, server-side for security (both)
|
|
362
|
+
|
|
363
|
+
---
|
|
364
|
+
|
|
365
|
+
## Next Steps
|
|
366
|
+
|
|
367
|
+
1. **Decision**: Review options and select approach
|
|
368
|
+
2. **Prototype**: Build minimal POC of selected option
|
|
369
|
+
3. **Refine**: Iterate based on prototype feedback
|
|
370
|
+
4. **Implement**: Full implementation
|
|
371
|
+
5. **Test**: Comprehensive testing with example forms
|
|
372
|
+
6. **Deploy**: Production deployment
|
|
373
|
+
|
|
374
|
+
---
|
|
375
|
+
|
|
376
|
+
## Appendix: File Structure
|
|
377
|
+
|
|
378
|
+
```
|
|
379
|
+
server/
|
|
380
|
+
plugins/
|
|
381
|
+
formBuilder.js (extend - serves JSON form definitions via .definition() method, handles JSON submissions)
|
|
382
|
+
|
|
383
|
+
schemas/
|
|
384
|
+
form_definition.js (optional - Phase 2)
|
|
385
|
+
|
|
386
|
+
includes/
|
|
387
|
+
react-form-bundle.js (bundled React app)
|
|
388
|
+
|
|
389
|
+
pages/
|
|
390
|
+
react-form-example (page with React form)
|
|
391
|
+
```
|
|
392
|
+
|
|
393
|
+
---
|
|
394
|
+
|
|
395
|
+
**Document Version**: 1.0
|
|
396
|
+
**Date**: 2026-01-04
|
|
397
|
+
**Author**: AI Assistant + User Collaboration
|