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.
@@ -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/formDefinition.js
48
- this.get = function(req, res) {
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/formDefinition/get?formid={id}`
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/formDefinition/get?formid={id}
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
- formDefinition.js (new - serves JSON form definitions)
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
@@ -1,5 +1,13 @@
1
1
  {
2
- "cells": [],
2
+ "cells": [
3
+ {
4
+ "cell_type": "raw",
5
+ "metadata": {},
6
+ "source": [
7
+ "noop"
8
+ ]
9
+ }
10
+ ],
3
11
  "metadata": {
4
12
  "language_info": {
5
13
  "name": "python"
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 {