@juggernautlabs/views 0.0.1 → 0.0.2

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/README.md CHANGED
@@ -0,0 +1,195 @@
1
+
2
+ # Juggernaut Labs React View SDK
3
+
4
+ The **Juggernaut Labs React View SDK** (`@juggernautlabs/views`) is the bridge between your platform’s backend logic and rich, interactive user experiences.
5
+
6
+ It empowers developers to inject custom React interfaces directly into specific platform contexts—such as **Process Job Results** or **Database Records**. Instead of viewing raw JSON or static tables, users interact with dynamic UIs that can visualize data and trigger complex downstream workflows.
7
+
8
+ ## Core Value Proposition
9
+
10
+ * **Contextual Leverage:** Build views that are instantly aware of where they are running. Automatically hydrate your UI with data from a specific Job or Database Record without complex API fetching logic.
11
+ * **Human-in-the-Loop Interactivity:** Transform static outputs into active decision points. Review AI-generated content, validate data, and trigger follow-up processes directly from the view.
12
+ * **Dynamic Visualization:** Create meaningful visual layers for your data. Turn a standard database row into a rich profile, dashboard, or media player based on the record type.
13
+
14
+ ---
15
+
16
+ ## Installation & Setup
17
+
18
+ ### 1. Install the Package
19
+
20
+ ```bash
21
+ npm install @juggernautlabs/views
22
+
23
+ ```
24
+
25
+ ### 2. Configure Your Project
26
+
27
+ Add the following scripts to your `package.json`. The SDK includes a CLI that handles the heavy lifting for local development and deployment.
28
+
29
+ ```json
30
+ "scripts": {
31
+ "dev": "npx @juggernautlabs/views dev index.tsx",
32
+ "build": "vite build",
33
+ "deploy": "npx @juggernautlabs/views deploy ./index.tsx"
34
+ }
35
+
36
+ ```
37
+
38
+ ---
39
+
40
+ ## Usage: Process Jobs
41
+
42
+ Build interactive interfaces for the results of a Process Job. This is essential for workflows requiring manual review or distinct "next steps" based on the job's output.
43
+
44
+ ### The Hooks
45
+
46
+ * **`useJuggernautJob()`**: Returns the context of the current job, including all tasks and generated outputs.
47
+ * **`useJuggernautActions()`**: Returns an object containing available follow-up actions.
48
+
49
+ ### Implementation Example
50
+
51
+ Below is an example of a "Curriculum Review" interface where a user reviews generated content and triggers an approval process.
52
+
53
+ ```tsx
54
+ import { useJuggernautJob, useJuggernautActions } from '@juggernautlabs/views';
55
+
56
+ export const JobResultView = () => {
57
+ const job = useJuggernautJob();
58
+ const actions = useJuggernautActions();
59
+
60
+ // "approve_curriculum" corresponds to the API Name of the action
61
+ // defined in your Juggernaut output settings.
62
+ const handleApprove = () => {
63
+ if (actions.approve_curriculum) {
64
+ actions.approve_curriculum.execute({
65
+ // 'inputs' are passed to the triggered process
66
+ inputs: {
67
+ jobId: job.id,
68
+ status: 'approved',
69
+ reviewerNotes: 'Curriculum meets all standards.'
70
+ },
71
+ // 'metadata' (optional) helps with logging and audit trails
72
+ metadata: {
73
+ source: 'reviewer_dashboard_v1'
74
+ }
75
+ });
76
+ }
77
+ };
78
+
79
+ return (
80
+ <div className="review-container">
81
+ <h1>Reviewing: {job.title}</h1>
82
+ <pre>{JSON.stringify(job.output, null, 2)}</pre>
83
+
84
+ <div className="footer">
85
+ <button onClick={handleApprove}>
86
+ Approve & Publish
87
+ </button>
88
+ </div>
89
+ </div>
90
+ );
91
+ };
92
+
93
+ ```
94
+
95
+ > **Configuration Note:** For an action to appear in the `useJuggernautActions` hook, it must be explicitly added to the **Trigger Actions** section within the **Outputs** tab of your Process configuration.
96
+
97
+ ---
98
+
99
+ ## Usage: Database Records
100
+
101
+ Create a custom visual layer for your database records. This allows you to render distinct UIs based on the record's content.
102
+
103
+ ### The Hook
104
+
105
+ * **`useJuggernautRecord()`**: Returns the full data object for the current database record context.
106
+
107
+ ### Implementation Example
108
+
109
+ Transforming a standard "Movie" database row into a rich media profile.
110
+
111
+ ```tsx
112
+ import { useJuggernautRecord } from '@juggernautlabs/views';
113
+
114
+ export const MovieRecordView = () => {
115
+ const movie = useJuggernautRecord();
116
+
117
+ return (
118
+ <div className="movie-card">
119
+ <div className="header" style={{ backgroundImage: `url(${movie.backdrop})` }}>
120
+ <h1>{movie.title}</h1>
121
+ </div>
122
+ <div className="content">
123
+ <p>{movie.description}</p>
124
+ {/* Conditional rendering based on record data */}
125
+ {movie.trailerId && (
126
+ <iframe src={`https://youtube.com/embed/${movie.trailerId}`} />
127
+ )}
128
+ </div>
129
+ </div>
130
+ );
131
+ };
132
+
133
+ ```
134
+
135
+ ---
136
+
137
+ ## Local Development (Mocking Data)
138
+
139
+ You cannot effectively develop UI without data. The SDK allows you to simulate the platform environment locally using a JSON file.
140
+
141
+ 1. Create a `.demo.json` file in the root of your component directory.
142
+ 2. Populate it with mock data. You can copy JSON from a successful job run or database query in the platform.
143
+
144
+ **`.demo.json` Structure:**
145
+
146
+ ```json
147
+ {
148
+ "job": {
149
+ "id": "job_123",
150
+ "status": "completed",
151
+ "output": { "module_1": "Intro to React" }
152
+ },
153
+ "record": {
154
+ "id": "rec_456",
155
+ "title": "Inception",
156
+ "trailerId": "8hP9D6kZseM"
157
+ },
158
+ "actions": {
159
+ "approve_curriculum": {
160
+ "type": "process_trigger",
161
+ "label": "Approve"
162
+ }
163
+ }
164
+ }
165
+
166
+ ```
167
+
168
+ When you run `npm run dev`, the CLI will read this file and automatically hydrate `useJuggernautJob`, `useJuggernautRecord`, and `useJuggernautActions` with your mock data.
169
+
170
+ ---
171
+
172
+ ## Deployment
173
+
174
+ When your view is ready, deploy it directly to the Juggernaut Labs platform using the CLI command configured in your scripts:
175
+
176
+ ```bash
177
+ npm run deploy
178
+
179
+ ```
180
+
181
+ This bundles your React code and uploads it to the view configuration specified in your `index.tsx`.
182
+
183
+ ---
184
+
185
+ ### API Reference: Action Execution
186
+
187
+ When using `useJuggernautActions`, the returned object keys match the **API Name** of the action. To trigger an action, call the `.execute()` method on that key.
188
+
189
+ **Method Signature:**
190
+ `action.execute(payload: { inputs: object, metadata?: object })`
191
+
192
+ | Property | Type | Description |
193
+ | --- | --- | --- |
194
+ | **inputs** | `object` | Key-value pairs matching the input variables defined in the target process. |
195
+ | **metadata** | `object` | *(Optional)* Additional context data useful for logging or auditing. |
package/dist/cli.js CHANGED
@@ -77,6 +77,7 @@ class TemplateUploader {
77
77
  try {
78
78
  const fileContent = await fs2.readFileSync(filePath, "utf-8");
79
79
  const metadata = JSON52.parse(fileContent) || {};
80
+ const isValidSettings = metadata.settings && typeof metadata.settings === "object";
80
81
  return {
81
82
  name: metadata.name || "Untitled Template",
82
83
  description: metadata.description || "",
@@ -85,7 +86,8 @@ class TemplateUploader {
85
86
  role: metadata.target?.role,
86
87
  resource: metadata.target?.resource,
87
88
  resourceType: metadata.target?.resourceType
88
- }
89
+ },
90
+ settings: isValidSettings ? metadata.settings : undefined
89
91
  };
90
92
  } catch (e) {
91
93
  throw new Error("Invalid JSON in metadata file");
package/dist/index.js CHANGED
@@ -16,6 +16,56 @@ class ContextManager {
16
16
  };
17
17
  }
18
18
 
19
+ // src/events.js
20
+ var ActionEvents = {
21
+ START_JOB: "start:job",
22
+ SCHEDULE_JOB: "schedule:job",
23
+ OPEN_RESOURCE: "open:resource",
24
+ START_PLUGIN: "start:plugin",
25
+ DATABASE_OPERATION: "database:operation",
26
+ TRIGGER_ACTION: "trigger:action",
27
+ CONTEXT_REQUEST: "juggernaut:context:request",
28
+ CONTEXT_RESPONSE: "juggernaut:context:response",
29
+ CONTEXT_UPDATE: "juggernaut:context:update"
30
+ };
31
+ var generateTriggerActionEventKey = (eventKey) => {
32
+ return `${ActionEvents.TRIGGER_ACTION}-${eventKey}`;
33
+ };
34
+
35
+ // src/resource.js
36
+ class JuggernautAction {
37
+ constructor(options = { name: "", label: "", apiName: "", description: "", allowUserInput }, handler) {
38
+ this.name = options.name;
39
+ this.label = options.label;
40
+ this.apiName = options.apiName;
41
+ this.description = options.description;
42
+ this.handler = handler;
43
+ this.allowUserInput = options.allowUserInput || true;
44
+ }
45
+ get resourceType() {
46
+ return "Action";
47
+ }
48
+ execute(params) {
49
+ if (this.handler) {
50
+ return this.handler(params);
51
+ }
52
+ }
53
+ static resolveActions(actionsData = []) {
54
+ let actions = {};
55
+ for (let actionData of actionsData) {
56
+ actions[actionData.apiName] = new JuggernautAction({
57
+ name: actionData.name,
58
+ label: actionData.label,
59
+ apiName: actionData.apiName,
60
+ description: actionData.description
61
+ }, (params) => {
62
+ return sendJuggernautActionEvent(generateTriggerActionEventKey(actionData.key), params);
63
+ });
64
+ }
65
+ return actions;
66
+ }
67
+ }
68
+
19
69
  // src/hooks.jsx
20
70
  var IS_DEV_MODE = import.meta.env ? import.meta.env.DEV : process?.env.NODE_ENV === "development";
21
71
  var DEMO_FILE_PATH = "/.demo.json";
@@ -25,7 +75,7 @@ var handleContextWindowEvents = (event) => {
25
75
  const { type, payload } = event.data || {};
26
76
  if (!type || !payload)
27
77
  return;
28
- const { resourceType, resource } = payload;
78
+ const { resourceType, resource, actions = [] } = payload;
29
79
  switch (type) {
30
80
  case "juggernaut:context:request":
31
81
  if (ContextManager[STORE_KEY] && ContextManager[STORE_KEY].hasOwnProperty(resourceType)) {
@@ -39,6 +89,7 @@ var handleContextWindowEvents = (event) => {
39
89
  case "juggernaut:context:update":
40
90
  if (ContextManager[STORE_KEY]) {
41
91
  ContextManager[STORE_KEY][resourceType] = resource;
92
+ ContextManager[STORE_KEY]["actions"] = JuggernautAction.resolveActions(actions);
42
93
  subscribers.forEach((callback) => callback(resourceType, resource));
43
94
  }
44
95
  break;
@@ -76,6 +127,10 @@ var useJuggernautResource = (resourceType) => {
76
127
  if (ContextManager[STORE_KEY]) {
77
128
  ContextManager[STORE_KEY][resourceType] = demoData[resourceType];
78
129
  }
130
+ if (resourceType === "actions") {
131
+ const actions = JuggernautAction.resolveActions(demoData.actions || []);
132
+ ContextManager[STORE_KEY]["actions"] = actions;
133
+ }
79
134
  setResource(demoData[resourceType]);
80
135
  return true;
81
136
  }
@@ -133,7 +188,9 @@ var useJuggernautRecord = () => useJuggernautResource("record");
133
188
  var useJuggernautData = () => useJuggernautResource("data");
134
189
  var useJuggernautPlugin = () => useJuggernautResource("plugin");
135
190
  var useJuggernautProcess = () => useJuggernautResource("process");
136
- var useJuggernautActions = () => useJuggernautResource("actions");
191
+ var useJuggernautActions = () => {
192
+ return ContextManager[STORE_KEY]["actions"] || {};
193
+ };
137
194
  var useJuggernautSystem = () => useJuggernautResource("system");
138
195
  var FILE_EVENTS = {
139
196
  GET_FILE: "juggernaut:file:get",
@@ -203,7 +260,7 @@ var useJuggernautFileSystem = () => {
203
260
  };
204
261
  // src/actions.jsx
205
262
  import { useEffect as useEffect2, useCallback as useCallback2, useRef as useRef2 } from "react";
206
- var ActionEvents = {
263
+ var ActionEvents2 = {
207
264
  START_JOB: "start:job",
208
265
  SCHEDULE_JOB: "schedule:job",
209
266
  OPEN_RESOURCE: "open:resource",
@@ -262,22 +319,22 @@ var useBridge = () => {
262
319
  });
263
320
  }, []);
264
321
  const startJob = useCallback2((processId, inputs, metadata = {}) => {
265
- return sendEvent(ActionEvents.START_JOB, { processId, inputs, metadata });
322
+ return sendEvent(ActionEvents2.START_JOB, { processId, inputs, metadata });
266
323
  }, [sendEvent]);
267
324
  const scheduleJob = useCallback2((processId, inputs, schedule, metadata = {}) => {
268
- return sendEvent(ActionEvents.SCHEDULE_JOB, { processId, inputs, schedule, metadata });
325
+ return sendEvent(ActionEvents2.SCHEDULE_JOB, { processId, inputs, schedule, metadata });
269
326
  }, [sendEvent]);
270
327
  const openResource = useCallback2((type, id, extra = {}) => {
271
- return sendEvent(ActionEvents.OPEN_RESOURCE, { resourceType: type, resourceId: id, ...extra });
328
+ return sendEvent(ActionEvents2.OPEN_RESOURCE, { resourceType: type, resourceId: id, ...extra });
272
329
  }, [sendEvent]);
273
- const triggerAction = useCallback2((payload) => sendEvent(ActionEvents.TRIGGER_ACTION, payload), [sendEvent]);
330
+ const triggerAction = useCallback2((eventKey, payload) => sendEvent(`${ActionEvents2.TRIGGER_ACTION}-${eventKey}`, payload), [sendEvent]);
274
331
  const launchProcess = (id, options = {}) => openResource("process", id, options);
275
332
  const launchJob = (id, options = {}) => openResource("job", id, options);
276
333
  const launchPlugin = (id, options = {}) => openResource("plugin", id, options);
277
334
  const launchDatabase = (id, options = {}) => openResource("database", id, options);
278
335
  const launchRecord = (dbId, recordId, options = {}) => openResource("database:record", recordId, { database: dbId, ...options });
279
- const startPlugin = useCallback2((payload) => sendEvent(ActionEvents.START_PLUGIN, payload), [sendEvent]);
280
- const databaseOperation = useCallback2((payload) => sendEvent(ActionEvents.DATABASE_OPERATION, payload), [sendEvent]);
336
+ const startPlugin = useCallback2((payload) => sendEvent(ActionEvents2.START_PLUGIN, payload), [sendEvent]);
337
+ const databaseOperation = useCallback2((payload) => sendEvent(ActionEvents2.DATABASE_OPERATION, payload), [sendEvent]);
281
338
  return {
282
339
  startJob,
283
340
  scheduleJob,
@@ -304,5 +361,5 @@ export {
304
361
  useBridge,
305
362
  sendJuggernautActionEvent,
306
363
  listenForJuggernautPlatformEvents,
307
- ActionEvents
364
+ ActionEvents2 as ActionEvents
308
365
  };
package/package.json CHANGED
@@ -1,7 +1,8 @@
1
1
  {
2
2
  "name": "@juggernautlabs/views",
3
- "version": "0.0.1",
3
+ "version": "0.0.2",
4
4
  "description": "A way to build Juggernaut view templates using React.",
5
+ "readme": "README.md",
5
6
  "type": "module",
6
7
  "bin": {
7
8
  "juggernauttemplates": "./dist/cli.js"