json-object-editor 0.10.668 → 0.10.671

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.
@@ -69,8 +69,15 @@ var schema = {
69
69
  return _joe.Filter.Options.status();
70
70
  },
71
71
  filters:function(i){
72
-
73
- return _joe.Filter.Options.tags();
72
+ var schemas = [];
73
+ var subs = [];
74
+ _joe.current.list.map(function(status){
75
+ schemas = schemas.concat(status.datasets||[]);
76
+ });
77
+ (new Set(schemas)).map(function(schema){
78
+ subs.push({name:schema,filter:{datasets:{$in:[schema]}}})
79
+ });
80
+ return subs.concat(_joe.Filter.Options.tags({group:'tags',collapsed:true}));
74
81
  },
75
82
  stripeColor: function(item) {
76
83
  //use the stripe color from ai_assistant status object if it has one
@@ -113,7 +113,7 @@ var schema = {
113
113
  }},
114
114
  {section_end:'Template'},
115
115
  {section_start:'preview'},
116
- {name:'examples', type:'content',run:function(item){
116
+ {name:'examples', reloadable:true,type:'content',run:function(item){
117
117
  return $J.schema('report').methods.renderPreviews(item);
118
118
  }
119
119
  /*run:function(item){
@@ -193,6 +193,7 @@ var task = function(){return{
193
193
  'name',
194
194
  'info',
195
195
  {extend:'description',specs:{
196
+ queryJobs:true,
196
197
  ai:{prompt:'Summarize the task in a few sentences. Take into account the project the task is associated with if there is one.'}}
197
198
  },
198
199
  {section_end:'overview'},
@@ -0,0 +1,224 @@
1
+ class FieldJobsContainer extends HTMLElement {
2
+ constructor() {
3
+ super();
4
+ this.objectId = null;
5
+ this.fieldName = null;
6
+ this.jobs = [];
7
+ this.updateInterval = null;
8
+ }
9
+
10
+ static get observedAttributes() {
11
+ return ['data-object-id', 'data-field-name'];
12
+ }
13
+
14
+ connectedCallback() {
15
+ this.objectId = this.getAttribute('data-object-id');
16
+ this.fieldName = this.getAttribute('data-field-name');
17
+ this.render();
18
+ this.startElapsedTimeUpdates();
19
+
20
+ // Immediately poll for jobs on connect
21
+ if (this.objectId && this.fieldName) {
22
+ var self = this;
23
+ $.get('/API/aijobs/' + encodeURIComponent(this.objectId) + '/' + encodeURIComponent(this.fieldName))
24
+ .then(function(data) {
25
+ if (data && data.jobs && Array.isArray(data.jobs)) {
26
+ self.updateJobs(data.jobs);
27
+ } else {
28
+ self.updateJobs([]);
29
+ }
30
+ })
31
+ .fail(function(err) {
32
+ // Silently fail - will retry on next poll
33
+ if (window.DEBUG_MODE || (window.$c && $c.DEBUG_MODE)) {
34
+ console.warn('[field-jobs-container] initial poll failed:', err);
35
+ }
36
+ });
37
+ }
38
+ }
39
+
40
+ disconnectedCallback() {
41
+ this.stopElapsedTimeUpdates();
42
+ }
43
+
44
+ attributeChangedCallback(name, oldValue, newValue) {
45
+ if (name === 'data-object-id') {
46
+ this.objectId = newValue;
47
+ } else if (name === 'data-field-name') {
48
+ this.fieldName = newValue;
49
+ }
50
+ if (this.objectId && this.fieldName) {
51
+ this.render();
52
+ }
53
+ }
54
+
55
+ /**
56
+ * Update jobs from server response
57
+ */
58
+ updateJobs(jobs) {
59
+ if (!Array.isArray(jobs)) {
60
+ jobs = [];
61
+ }
62
+ this.jobs = jobs;
63
+ this.render();
64
+ }
65
+
66
+ /**
67
+ * Calculate elapsed seconds from startTime
68
+ */
69
+ calculateElapsed(startTime) {
70
+ if (!startTime) return 0;
71
+ try {
72
+ var start = new Date(startTime);
73
+ var now = new Date();
74
+ var elapsedMs = now - start;
75
+ return Math.floor(elapsedMs / 1000);
76
+ } catch (e) {
77
+ return 0;
78
+ }
79
+ }
80
+
81
+ /**
82
+ * Start elapsed time updates (every second)
83
+ */
84
+ startElapsedTimeUpdates() {
85
+ if (this.updateInterval) return; // Already running
86
+
87
+ this.updateInterval = setInterval(() => {
88
+ // Check if we have any active jobs
89
+ var hasActive = this.jobs.some(function(job) {
90
+ return (job.status === 'running' || job.status === 'starting') &&
91
+ (job.total == null || job.progress == null || job.progress < job.total);
92
+ });
93
+
94
+ if (hasActive) {
95
+ // Force re-render to update elapsed times
96
+ this.render();
97
+ } else {
98
+ // Stop if no active jobs
99
+ this.stopElapsedTimeUpdates();
100
+ }
101
+ }, 1000);
102
+ }
103
+
104
+ /**
105
+ * Stop elapsed time updates
106
+ */
107
+ stopElapsedTimeUpdates() {
108
+ if (this.updateInterval) {
109
+ clearInterval(this.updateInterval);
110
+ this.updateInterval = null;
111
+ }
112
+ }
113
+
114
+ /**
115
+ * Render the component
116
+ */
117
+ render() {
118
+ // Build endpoint URL for token link (if we have both objectId and fieldName)
119
+ var endpointUrl = null;
120
+ var tokenLinkHtml = '';
121
+ var fullToken = null;
122
+
123
+ // Try to get a sample token from jobs, or construct lookup key
124
+ if (this.jobs.length > 0 && this.jobs[0].token) {
125
+ fullToken = this.jobs[0].token;
126
+ } else if (this.objectId && this.fieldName) {
127
+ // If no jobs, show lookup key format
128
+ fullToken = this.objectId + '_' + this.fieldName;
129
+ }
130
+
131
+ if (this.objectId && this.fieldName) {
132
+ endpointUrl = '/API/aijobs/' + encodeURIComponent(this.objectId) + '/' + encodeURIComponent(this.fieldName);
133
+ var titleAttr = fullToken ? 'title="' + fullToken.replace(/"/g, '&quot;') + '"' : 'title="View endpoint"';
134
+ tokenLinkHtml = '<a href="' + endpointUrl + '" target="_blank" class="field-jobs-token-link" ' + titleAttr + '>[' + this.objectId.substring(0, 8) + '...|' + this.fieldName + ']</a>';
135
+ }
136
+
137
+ if (!this.objectId || !this.fieldName) {
138
+ var html = '<div class="field-jobs-title">';
139
+ html += '<span class="field-jobs-title-text">0 active jobs</span>';
140
+ html += tokenLinkHtml;
141
+ html += '</div>';
142
+ this.innerHTML = html;
143
+ return;
144
+ }
145
+
146
+ // Filter active jobs (for count)
147
+ var activeJobs = this.jobs.filter(function(job) {
148
+ return job.status !== 'complete' && job.status !== 'error';
149
+ });
150
+
151
+ // Update token link with full token if we have jobs (recalculate in case jobs were added)
152
+ if (this.jobs.length > 0 && this.jobs[0].token) {
153
+ fullToken = this.jobs[0].token;
154
+ if (endpointUrl) {
155
+ var titleAttr = 'title="' + fullToken.replace(/"/g, '&quot;') + '"';
156
+ tokenLinkHtml = '<a href="' + endpointUrl + '" target="_blank" class="field-jobs-token-link" ' + titleAttr + '>[' + this.objectId.substring(0, 8) + '...|' + this.fieldName + ']</a>';
157
+ }
158
+ }
159
+
160
+ // Build HTML
161
+ var html = '';
162
+
163
+ // Title with active job count and token link on the right
164
+ html += '<div class="field-jobs-title">';
165
+ html += '<span class="field-jobs-title-text">' + activeJobs.length + ' active job' + (activeJobs.length !== 1 ? 's' : '') + '</span>';
166
+ html += tokenLinkHtml;
167
+ html += '</div>';
168
+
169
+ // Job rows (show all jobs, including completed)
170
+ if (this.jobs.length > 0) {
171
+ var self = this;
172
+ this.jobs.forEach(function(job) {
173
+ var jobName = job.promptName || job.promptId || 'Job';
174
+ var elapsedSeconds = job.elapsedSeconds != null ? job.elapsedSeconds : self.calculateElapsed(job.startTime);
175
+ var elapsedText = elapsedSeconds > 0 ? ' (' + elapsedSeconds + 's)' : '';
176
+
177
+ // Status text (capitalized)
178
+ var statusText = '';
179
+ if (job.status) {
180
+ statusText = ' - ' + job.status.charAt(0).toUpperCase() + job.status.slice(1);
181
+ }
182
+
183
+ var percent = null;
184
+ if (job.total != null && job.progress != null) {
185
+ percent = Math.round((job.progress / job.total) * 100);
186
+ }
187
+
188
+ var statusClass = '';
189
+ if (job.status === 'complete') {
190
+ statusClass = 'field-jobs-complete';
191
+ percent = 100;
192
+ } else if (job.status === 'error') {
193
+ statusClass = 'field-jobs-error';
194
+ }
195
+
196
+ html += '<div class="field-jobs-row ' + statusClass + '">';
197
+ html += '<div class="field-jobs-row-content">';
198
+ html += '<div class="field-jobs-row-title">' + jobName + statusText + elapsedText + '</div>';
199
+ html += '<div class="field-jobs-row-message">' + (job.message || '') + '</div>';
200
+ html += '</div>';
201
+ if (percent != null) {
202
+ html += '<div class="field-jobs-row-percent">' + percent + '%</div>';
203
+ } else {
204
+ html += '<div class="field-jobs-row-percent">—</div>';
205
+ }
206
+ html += '</div>';
207
+ });
208
+ }
209
+
210
+ this.innerHTML = html;
211
+
212
+ // Restart elapsed time updates if we have active jobs
213
+ var hasActive = this.jobs.some(function(job) {
214
+ return (job.status === 'running' || job.status === 'starting') &&
215
+ (job.total == null || job.progress == null || job.progress < job.total);
216
+ });
217
+
218
+ if (hasActive) {
219
+ this.startElapsedTimeUpdates();
220
+ }
221
+ }
222
+ }
223
+
224
+ window.customElements.define('field-jobs-container', FieldJobsContainer);