json-object-editor 0.10.660 → 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 +10 -0
- 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 +5 -6
- package/dummy +9 -1
- package/js/joe-ai.js +26 -0
- package/js/joe-react-form.js +608 -0
- package/js/joe.js +1 -1
- package/package.json +1 -1
- package/readme.md +8 -0
- package/server/fields/core.js +48 -1
- package/server/modules/MCP.js +4 -0
- package/server/modules/Sites.js +28 -2
- package/server/plugins/chatgpt.js +169 -21
- package/server/plugins/formBuilder.js +98 -0
- package/server/schemas/ai_assistant.js +43 -44
- package/server/schemas/ai_prompt.js +4 -58
- 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
|
|
@@ -44,8 +44,8 @@ This document outlines strategy options for integrating:
|
|
|
44
44
|
|
|
45
45
|
**1. Create Form Definition API Endpoint**
|
|
46
46
|
```javascript
|
|
47
|
-
// server/plugins/
|
|
48
|
-
this.
|
|
47
|
+
// server/plugins/formBuilder.js
|
|
48
|
+
this.definition = function(req, res) {
|
|
49
49
|
var formId = req.query.formid || req.params.formid;
|
|
50
50
|
// Load form-qs.json format, return as JSON
|
|
51
51
|
// Could be stored as include or in a new form_definition schema
|
|
@@ -65,7 +65,7 @@ this.submit = function(data, req) {
|
|
|
65
65
|
- React app bundled (webpack/vite) and stored as an `include` (JS file)
|
|
66
66
|
- Page content: `<div id="react-form-root"></div><script src="/_include/{react-bundle-id}"></script>`
|
|
67
67
|
- React app:
|
|
68
|
-
- Fetches form definition from `/API/plugin/
|
|
68
|
+
- Fetches form definition from `/API/plugin/formBuilder/definition?formid={id}`
|
|
69
69
|
- Renders form using `react-form-spa-ex.js` logic
|
|
70
70
|
- Submits to `/API/plugin/formBuilder/submission` (or new endpoint)
|
|
71
71
|
|
|
@@ -322,7 +322,7 @@ async function submitForm(formData, formId) {
|
|
|
322
322
|
### Form Definition API
|
|
323
323
|
|
|
324
324
|
```javascript
|
|
325
|
-
// GET /API/plugin/
|
|
325
|
+
// GET /API/plugin/formBuilder/definition?formid={id}
|
|
326
326
|
// Returns: form-qs.json format
|
|
327
327
|
{
|
|
328
328
|
formId: "...",
|
|
@@ -378,8 +378,7 @@ async function submitForm(formData, formId) {
|
|
|
378
378
|
```
|
|
379
379
|
server/
|
|
380
380
|
plugins/
|
|
381
|
-
|
|
382
|
-
formBuilder.js (extend - handle JSON submissions)
|
|
381
|
+
formBuilder.js (extend - serves JSON form definitions via .definition() method, handles JSON submissions)
|
|
383
382
|
|
|
384
383
|
schemas/
|
|
385
384
|
form_definition.js (optional - Phase 2)
|
package/dummy
CHANGED
package/js/joe-ai.js
CHANGED
|
@@ -27,6 +27,28 @@
|
|
|
27
27
|
.replace(/\n/g, '<br>');
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
+
// Load MCP manifest once per page and expose tool names via Ai.mcpToolNames
|
|
31
|
+
Ai.mcpToolNames = [];
|
|
32
|
+
(function initMcpToolNames(){
|
|
33
|
+
try{
|
|
34
|
+
if (typeof fetch !== 'function') { return; }
|
|
35
|
+
fetch('/.well-known/mcp/manifest.json')
|
|
36
|
+
.then(function(r){
|
|
37
|
+
if (!r.ok){ throw new Error('HTTP '+r.status); }
|
|
38
|
+
return r.json();
|
|
39
|
+
})
|
|
40
|
+
.then(function(man){
|
|
41
|
+
try{
|
|
42
|
+
var tools = (man && man.tools) || [];
|
|
43
|
+
Ai.mcpToolNames = tools.map(function(t){ return t && t.name; }).filter(Boolean).sort();
|
|
44
|
+
}catch(_e){}
|
|
45
|
+
})
|
|
46
|
+
.catch(function(e){
|
|
47
|
+
console.warn('[joe-ai] MCP manifest load failed', e);
|
|
48
|
+
});
|
|
49
|
+
}catch(_e){}
|
|
50
|
+
})();
|
|
51
|
+
|
|
30
52
|
class JoeAIChatbox extends HTMLElement {
|
|
31
53
|
constructor() {
|
|
32
54
|
super();
|
|
@@ -873,7 +895,11 @@
|
|
|
873
895
|
conversation_id: this.conversation_id,
|
|
874
896
|
content: text,
|
|
875
897
|
role: 'user',
|
|
898
|
+
// Legacy OpenAI Assistants id (if present) plus the current JOE
|
|
899
|
+
// ai_assistant id. The backend prefers the JOE id (ai_assistant_id)
|
|
900
|
+
// and falls back to assistant_id only when needed.
|
|
876
901
|
assistant_id: this.assistant_id || undefined,
|
|
902
|
+
ai_assistant_id: this.getAttribute('ai_assistant_id') || undefined,
|
|
877
903
|
model: this.model || undefined
|
|
878
904
|
};
|
|
879
905
|
try {
|