@things-factory/labeling 9.1.19

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.
Files changed (55) hide show
  1. package/CHANGELOG.md +87 -0
  2. package/ENTITY_IMPLEMENTATION.md +351 -0
  3. package/INTEGRATION_COMPLETE.md +531 -0
  4. package/MIGRATION_GUIDE.md +310 -0
  5. package/README.md +551 -0
  6. package/REFACTORING_SUMMARY.md +212 -0
  7. package/UI_DOCUMENTATION.md +552 -0
  8. package/dist-client/index.d.ts +3 -0
  9. package/dist-client/index.js +9 -0
  10. package/dist-client/index.js.map +1 -0
  11. package/dist-client/pages/labeling-workflow-builder.d.ts +26 -0
  12. package/dist-client/pages/labeling-workflow-builder.js +636 -0
  13. package/dist-client/pages/labeling-workflow-builder.js.map +1 -0
  14. package/dist-client/pages/labeling-workflow-list.d.ts +24 -0
  15. package/dist-client/pages/labeling-workflow-list.js +495 -0
  16. package/dist-client/pages/labeling-workflow-list.js.map +1 -0
  17. package/dist-client/route.d.ts +1 -0
  18. package/dist-client/route.js +47 -0
  19. package/dist-client/route.js.map +1 -0
  20. package/dist-client/tsconfig.tsbuildinfo +1 -0
  21. package/dist-server/entities/index.d.ts +5 -0
  22. package/dist-server/entities/index.js +11 -0
  23. package/dist-server/entities/index.js.map +1 -0
  24. package/dist-server/index.d.ts +3 -0
  25. package/dist-server/index.js +7 -0
  26. package/dist-server/index.js.map +1 -0
  27. package/dist-server/route.d.ts +2 -0
  28. package/dist-server/route.js +6 -0
  29. package/dist-server/route.js.map +1 -0
  30. package/dist-server/service/index.d.ts +8 -0
  31. package/dist-server/service/index.js +21 -0
  32. package/dist-server/service/index.js.map +1 -0
  33. package/dist-server/service/labeling-workflow-service.d.ts +69 -0
  34. package/dist-server/service/labeling-workflow-service.js +521 -0
  35. package/dist-server/service/labeling-workflow-service.js.map +1 -0
  36. package/dist-server/service/labeling-workflow.d.ts +30 -0
  37. package/dist-server/service/labeling-workflow.js +119 -0
  38. package/dist-server/service/labeling-workflow.js.map +1 -0
  39. package/dist-server/service/workflow-execution-step.d.ts +28 -0
  40. package/dist-server/service/workflow-execution-step.js +115 -0
  41. package/dist-server/service/workflow-execution-step.js.map +1 -0
  42. package/dist-server/service/workflow-execution.d.ts +27 -0
  43. package/dist-server/service/workflow-execution.js +110 -0
  44. package/dist-server/service/workflow-execution.js.map +1 -0
  45. package/dist-server/tsconfig.tsbuildinfo +1 -0
  46. package/dist-server/types/workflow-types.d.ts +141 -0
  47. package/dist-server/types/workflow-types.js +488 -0
  48. package/dist-server/types/workflow-types.js.map +1 -0
  49. package/package.json +51 -0
  50. package/things-factory.config.js +11 -0
  51. package/translations/en.json +6 -0
  52. package/translations/ja.json +6 -0
  53. package/translations/ko.json +6 -0
  54. package/translations/ms.json +6 -0
  55. package/translations/zh.json +6 -0
@@ -0,0 +1,636 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.LabelingWorkflowBuilderPage = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const lit_1 = require("lit");
6
+ const decorators_js_1 = require("lit/decorators.js");
7
+ const shell_1 = require("@operato/shell");
8
+ const graphql_1 = require("@operato/graphql");
9
+ const graphql_tag_1 = tslib_1.__importDefault(require("graphql-tag"));
10
+ /**
11
+ * Workflow Builder Page
12
+ *
13
+ * Generic workflow builder UI for creating and editing labeling workflows
14
+ */
15
+ let LabelingWorkflowBuilderPage = class LabelingWorkflowBuilderPage extends shell_1.PageView {
16
+ constructor() {
17
+ super(...arguments);
18
+ this.workflow = {
19
+ name: '',
20
+ description: '',
21
+ projectId: null,
22
+ triggerType: 'manual',
23
+ triggerConfig: '',
24
+ steps: [],
25
+ autoStart: false
26
+ };
27
+ this.selectedStepIndex = -1;
28
+ this.loading = false;
29
+ this.error = '';
30
+ }
31
+ static { this.styles = [
32
+ (0, lit_1.css) `
33
+ :host {
34
+ display: flex;
35
+ flex-direction: column;
36
+ height: 100%;
37
+ background-color: var(--md-sys-color-surface);
38
+ }
39
+
40
+ .header {
41
+ display: flex;
42
+ justify-content: space-between;
43
+ align-items: center;
44
+ padding: 20px;
45
+ background-color: var(--md-sys-color-surface-variant);
46
+ border-bottom: 1px solid var(--md-sys-color-outline);
47
+ }
48
+
49
+ .header h1 {
50
+ margin: 0;
51
+ font-size: 24px;
52
+ color: var(--md-sys-color-on-surface);
53
+ }
54
+
55
+ .header-actions {
56
+ display: flex;
57
+ gap: 10px;
58
+ }
59
+
60
+ .content {
61
+ display: flex;
62
+ flex: 1;
63
+ overflow: hidden;
64
+ }
65
+
66
+ .sidebar {
67
+ width: 300px;
68
+ border-right: 1px solid var(--md-sys-color-outline);
69
+ overflow-y: auto;
70
+ padding: 20px;
71
+ }
72
+
73
+ .main-area {
74
+ flex: 1;
75
+ overflow-y: auto;
76
+ padding: 20px;
77
+ }
78
+
79
+ .form-group {
80
+ margin-bottom: 20px;
81
+ }
82
+
83
+ .form-group label {
84
+ display: block;
85
+ margin-bottom: 8px;
86
+ font-weight: 500;
87
+ color: var(--md-sys-color-on-surface);
88
+ }
89
+
90
+ .form-group input,
91
+ .form-group textarea,
92
+ .form-group select {
93
+ width: 100%;
94
+ padding: 10px;
95
+ border: 1px solid var(--md-sys-color-outline);
96
+ border-radius: 4px;
97
+ font-size: 14px;
98
+ background-color: var(--md-sys-color-surface);
99
+ color: var(--md-sys-color-on-surface);
100
+ }
101
+
102
+ .form-group textarea {
103
+ min-height: 80px;
104
+ resize: vertical;
105
+ }
106
+
107
+ button {
108
+ padding: 10px 20px;
109
+ background-color: var(--md-sys-color-primary);
110
+ color: var(--md-sys-color-on-primary);
111
+ border: none;
112
+ border-radius: 4px;
113
+ cursor: pointer;
114
+ font-size: 14px;
115
+ }
116
+
117
+ button:hover {
118
+ opacity: 0.9;
119
+ }
120
+
121
+ button.secondary {
122
+ background-color: var(--md-sys-color-secondary);
123
+ color: var(--md-sys-color-on-secondary);
124
+ }
125
+
126
+ .steps-container {
127
+ margin-top: 20px;
128
+ }
129
+
130
+ .steps-header {
131
+ display: flex;
132
+ justify-content: space-between;
133
+ align-items: center;
134
+ margin-bottom: 15px;
135
+ }
136
+
137
+ .steps-header h2 {
138
+ margin: 0;
139
+ font-size: 18px;
140
+ }
141
+
142
+ .step-list {
143
+ display: flex;
144
+ flex-direction: column;
145
+ gap: 10px;
146
+ }
147
+
148
+ .step-item {
149
+ background-color: var(--md-sys-color-surface-variant);
150
+ border: 1px solid var(--md-sys-color-outline);
151
+ border-radius: 8px;
152
+ padding: 15px;
153
+ cursor: pointer;
154
+ transition:
155
+ transform 0.2s,
156
+ box-shadow 0.2s;
157
+ }
158
+
159
+ .step-item:hover {
160
+ transform: translateY(-2px);
161
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
162
+ }
163
+
164
+ .step-item.selected {
165
+ border-color: var(--md-sys-color-primary);
166
+ border-width: 2px;
167
+ }
168
+
169
+ .step-header {
170
+ display: flex;
171
+ justify-content: space-between;
172
+ align-items: center;
173
+ margin-bottom: 8px;
174
+ }
175
+
176
+ .step-title {
177
+ font-weight: bold;
178
+ font-size: 16px;
179
+ }
180
+
181
+ .step-actions {
182
+ display: flex;
183
+ gap: 5px;
184
+ }
185
+
186
+ .step-actions button {
187
+ padding: 5px 10px;
188
+ font-size: 12px;
189
+ }
190
+
191
+ .step-type {
192
+ display: inline-block;
193
+ padding: 4px 8px;
194
+ background-color: var(--md-sys-color-primary-container);
195
+ color: var(--md-sys-color-on-primary-container);
196
+ border-radius: 4px;
197
+ font-size: 12px;
198
+ margin-bottom: 8px;
199
+ }
200
+
201
+ .step-config {
202
+ font-size: 12px;
203
+ color: var(--md-sys-color-on-surface-variant);
204
+ margin-top: 8px;
205
+ }
206
+
207
+ .empty-state {
208
+ text-align: center;
209
+ padding: 40px;
210
+ color: var(--md-sys-color-on-surface-variant);
211
+ }
212
+
213
+ .add-step-button {
214
+ width: 100%;
215
+ padding: 15px;
216
+ border: 2px dashed var(--md-sys-color-outline);
217
+ background-color: transparent;
218
+ color: var(--md-sys-color-on-surface);
219
+ cursor: pointer;
220
+ border-radius: 8px;
221
+ margin-top: 10px;
222
+ }
223
+
224
+ .add-step-button:hover {
225
+ border-color: var(--md-sys-color-primary);
226
+ background-color: var(--md-sys-color-surface-variant);
227
+ }
228
+
229
+ .loading {
230
+ text-align: center;
231
+ padding: 40px;
232
+ }
233
+
234
+ .error {
235
+ padding: 15px;
236
+ background-color: #ffebee;
237
+ color: #c62828;
238
+ border-radius: 4px;
239
+ margin-bottom: 20px;
240
+ }
241
+ `
242
+ ]; }
243
+ async firstUpdated() {
244
+ if (this.workflowId) {
245
+ await this.loadWorkflow();
246
+ }
247
+ }
248
+ async loadWorkflow() {
249
+ this.loading = true;
250
+ this.error = '';
251
+ try {
252
+ const response = await graphql_1.client.query({
253
+ query: (0, graphql_tag_1.default) `
254
+ query GetWorkflow($workflowId: String!) {
255
+ labelingWorkflow(workflowId: $workflowId) {
256
+ id
257
+ name
258
+ description
259
+ projectId
260
+ triggerType
261
+ triggerConfig
262
+ steps {
263
+ name
264
+ type
265
+ order
266
+ config
267
+ continueOnError
268
+ }
269
+ status
270
+ autoStart
271
+ }
272
+ }
273
+ `,
274
+ variables: { workflowId: this.workflowId }
275
+ });
276
+ this.workflow = response.data.labelingWorkflow;
277
+ }
278
+ catch (error) {
279
+ console.error('Failed to load workflow:', error);
280
+ this.error = 'Failed to load workflow. Please try again.';
281
+ }
282
+ finally {
283
+ this.loading = false;
284
+ }
285
+ }
286
+ async saveWorkflow() {
287
+ this.loading = true;
288
+ this.error = '';
289
+ try {
290
+ const mutation = this.workflowId
291
+ ? (0, graphql_tag_1.default) `
292
+ mutation UpdateWorkflow($workflowId: String!, $input: UpdateWorkflowRequest!) {
293
+ updateLabelingWorkflow(workflowId: $workflowId, input: $input) {
294
+ id
295
+ name
296
+ }
297
+ }
298
+ `
299
+ : (0, graphql_tag_1.default) `
300
+ mutation CreateWorkflow($input: CreateWorkflowRequest!) {
301
+ createLabelingWorkflow(input: $input) {
302
+ id
303
+ name
304
+ }
305
+ }
306
+ `;
307
+ const variables = {
308
+ input: {
309
+ name: this.workflow.name,
310
+ description: this.workflow.description,
311
+ projectId: parseInt(this.workflow.projectId),
312
+ triggerType: this.workflow.triggerType,
313
+ triggerConfig: this.workflow.triggerConfig,
314
+ steps: this.workflow.steps.map((step, index) => ({
315
+ name: step.name,
316
+ type: step.type,
317
+ order: index + 1,
318
+ config: step.config,
319
+ continueOnError: step.continueOnError || false
320
+ })),
321
+ autoStart: this.workflow.autoStart
322
+ }
323
+ };
324
+ if (this.workflowId) {
325
+ variables.workflowId = this.workflowId;
326
+ }
327
+ await graphql_1.client.mutate({ mutation, variables });
328
+ alert('Workflow saved successfully!');
329
+ history.back();
330
+ }
331
+ catch (error) {
332
+ console.error('Failed to save workflow:', error);
333
+ this.error = 'Failed to save workflow. Please check your inputs and try again.';
334
+ }
335
+ finally {
336
+ this.loading = false;
337
+ }
338
+ }
339
+ addStep() {
340
+ const newStep = {
341
+ name: `Step ${this.workflow.steps.length + 1}`,
342
+ type: 'import_data',
343
+ config: '{}',
344
+ continueOnError: false
345
+ };
346
+ this.workflow = {
347
+ ...this.workflow,
348
+ steps: [...this.workflow.steps, newStep]
349
+ };
350
+ this.selectedStepIndex = this.workflow.steps.length - 1;
351
+ }
352
+ removeStep(index) {
353
+ if (confirm('Are you sure you want to remove this step?')) {
354
+ const steps = [...this.workflow.steps];
355
+ steps.splice(index, 1);
356
+ this.workflow = {
357
+ ...this.workflow,
358
+ steps
359
+ };
360
+ if (this.selectedStepIndex === index) {
361
+ this.selectedStepIndex = -1;
362
+ }
363
+ else if (this.selectedStepIndex > index) {
364
+ this.selectedStepIndex--;
365
+ }
366
+ }
367
+ }
368
+ moveStepUp(index) {
369
+ if (index === 0)
370
+ return;
371
+ const steps = [...this.workflow.steps];
372
+ const temp = steps[index];
373
+ steps[index] = steps[index - 1];
374
+ steps[index - 1] = temp;
375
+ this.workflow = {
376
+ ...this.workflow,
377
+ steps
378
+ };
379
+ if (this.selectedStepIndex === index) {
380
+ this.selectedStepIndex = index - 1;
381
+ }
382
+ else if (this.selectedStepIndex === index - 1) {
383
+ this.selectedStepIndex = index;
384
+ }
385
+ }
386
+ moveStepDown(index) {
387
+ if (index === this.workflow.steps.length - 1)
388
+ return;
389
+ const steps = [...this.workflow.steps];
390
+ const temp = steps[index];
391
+ steps[index] = steps[index + 1];
392
+ steps[index + 1] = temp;
393
+ this.workflow = {
394
+ ...this.workflow,
395
+ steps
396
+ };
397
+ if (this.selectedStepIndex === index) {
398
+ this.selectedStepIndex = index + 1;
399
+ }
400
+ else if (this.selectedStepIndex === index + 1) {
401
+ this.selectedStepIndex = index;
402
+ }
403
+ }
404
+ selectStep(index) {
405
+ this.selectedStepIndex = index;
406
+ }
407
+ updateStep(index, field, value) {
408
+ const steps = [...this.workflow.steps];
409
+ steps[index] = {
410
+ ...steps[index],
411
+ [field]: value
412
+ };
413
+ this.workflow = {
414
+ ...this.workflow,
415
+ steps
416
+ };
417
+ }
418
+ updateWorkflow(field, value) {
419
+ this.workflow = {
420
+ ...this.workflow,
421
+ [field]: value
422
+ };
423
+ }
424
+ renderStepEditor() {
425
+ if (this.selectedStepIndex === -1 || !this.workflow.steps[this.selectedStepIndex]) {
426
+ return (0, lit_1.html) ` <div class="empty-state">Select a step to edit its configuration</div> `;
427
+ }
428
+ const step = this.workflow.steps[this.selectedStepIndex];
429
+ return (0, lit_1.html) `
430
+ <div class="form-group">
431
+ <label>Step Name</label>
432
+ <input
433
+ type="text"
434
+ .value=${step.name}
435
+ @input=${(e) => this.updateStep(this.selectedStepIndex, 'name', e.target.value)}
436
+ />
437
+ </div>
438
+
439
+ <div class="form-group">
440
+ <label>Step Type</label>
441
+ <select
442
+ .value=${step.type}
443
+ @change=${(e) => this.updateStep(this.selectedStepIndex, 'type', e.target.value)}
444
+ >
445
+ <option value="import_data">Import Data</option>
446
+ <option value="generate_predictions">Generate Predictions</option>
447
+ <option value="wait_for_annotations">Wait for Annotations</option>
448
+ <option value="sync_annotations">Sync Annotations</option>
449
+ <option value="validate_quality">Validate Quality</option>
450
+ <option value="export_results">Export Results</option>
451
+ <option value="notification">Notification</option>
452
+ </select>
453
+ </div>
454
+
455
+ <div class="form-group">
456
+ <label>Configuration (JSON)</label>
457
+ <textarea
458
+ .value=${step.config}
459
+ @input=${(e) => this.updateStep(this.selectedStepIndex, 'config', e.target.value)}
460
+ placeholder='{"key": "value"}'
461
+ ></textarea>
462
+ </div>
463
+
464
+ <div class="form-group">
465
+ <label>
466
+ <input
467
+ type="checkbox"
468
+ .checked=${step.continueOnError}
469
+ @change=${(e) => this.updateStep(this.selectedStepIndex, 'continueOnError', e.target.checked)}
470
+ />
471
+ Continue on Error
472
+ </label>
473
+ </div>
474
+ `;
475
+ }
476
+ render() {
477
+ if (this.loading) {
478
+ return (0, lit_1.html) `<div class="loading">Loading...</div>`;
479
+ }
480
+ return (0, lit_1.html) `
481
+ <div class="header">
482
+ <h1>${this.workflowId ? 'Edit Workflow' : 'Create Workflow'}</h1>
483
+ <div class="header-actions">
484
+ <button class="secondary" @click=${() => history.back()}>Cancel</button>
485
+ <button @click=${this.saveWorkflow}>Save Workflow</button>
486
+ </div>
487
+ </div>
488
+
489
+ ${this.error ? (0, lit_1.html) `<div class="error">${this.error}</div>` : ''}
490
+
491
+ <div class="content">
492
+ <div class="sidebar">
493
+ <div class="form-group">
494
+ <label>Workflow Name *</label>
495
+ <input
496
+ type="text"
497
+ .value=${this.workflow.name}
498
+ @input=${(e) => this.updateWorkflow('name', e.target.value)}
499
+ placeholder="Enter workflow name"
500
+ required
501
+ />
502
+ </div>
503
+
504
+ <div class="form-group">
505
+ <label>Description</label>
506
+ <textarea
507
+ .value=${this.workflow.description || ''}
508
+ @input=${(e) => this.updateWorkflow('description', e.target.value)}
509
+ placeholder="Enter workflow description"
510
+ ></textarea>
511
+ </div>
512
+
513
+ <div class="form-group">
514
+ <label>Project ID *</label>
515
+ <input
516
+ type="number"
517
+ .value=${this.workflow.projectId || ''}
518
+ @input=${(e) => this.updateWorkflow('projectId', e.target.value)}
519
+ placeholder="Label Studio project ID"
520
+ required
521
+ />
522
+ </div>
523
+
524
+ <div class="form-group">
525
+ <label>Trigger Type</label>
526
+ <select
527
+ .value=${this.workflow.triggerType}
528
+ @change=${(e) => this.updateWorkflow('triggerType', e.target.value)}
529
+ >
530
+ <option value="manual">Manual</option>
531
+ <option value="scheduled">Scheduled</option>
532
+ <option value="event">Event-based</option>
533
+ </select>
534
+ </div>
535
+
536
+ <div class="form-group">
537
+ <label>
538
+ <input
539
+ type="checkbox"
540
+ .checked=${this.workflow.autoStart}
541
+ @change=${(e) => this.updateWorkflow('autoStart', e.target.checked)}
542
+ />
543
+ Auto Start
544
+ </label>
545
+ </div>
546
+
547
+ <div class="steps-container">
548
+ <div class="steps-header">
549
+ <h2>Workflow Steps</h2>
550
+ </div>
551
+
552
+ <div class="step-list">
553
+ ${this.workflow.steps.length === 0
554
+ ? (0, lit_1.html) `<div class="empty-state">No steps added yet</div>`
555
+ : this.workflow.steps.map((step, index) => (0, lit_1.html) `
556
+ <div
557
+ class="step-item ${this.selectedStepIndex === index ? 'selected' : ''}"
558
+ @click=${() => this.selectStep(index)}
559
+ >
560
+ <div class="step-header">
561
+ <div class="step-title">${index + 1}. ${step.name}</div>
562
+ <div class="step-actions">
563
+ ${index > 0
564
+ ? (0, lit_1.html) `<button
565
+ @click=${(e) => {
566
+ e.stopPropagation();
567
+ this.moveStepUp(index);
568
+ }}
569
+ >
570
+
571
+ </button>`
572
+ : ''}
573
+ ${index < this.workflow.steps.length - 1
574
+ ? (0, lit_1.html) `<button
575
+ @click=${(e) => {
576
+ e.stopPropagation();
577
+ this.moveStepDown(index);
578
+ }}
579
+ >
580
+
581
+ </button>`
582
+ : ''}
583
+ <button
584
+ class="secondary"
585
+ @click=${(e) => {
586
+ e.stopPropagation();
587
+ this.removeStep(index);
588
+ }}
589
+ >
590
+ ×
591
+ </button>
592
+ </div>
593
+ </div>
594
+ <div class="step-type">${step.type}</div>
595
+ ${step.continueOnError ? (0, lit_1.html) `<div class="step-config">Continue on error: Yes</div>` : ''}
596
+ </div>
597
+ `)}
598
+ </div>
599
+
600
+ <button class="add-step-button" @click=${this.addStep}>+ Add Step</button>
601
+ </div>
602
+ </div>
603
+
604
+ <div class="main-area">
605
+ <h2>Step Configuration</h2>
606
+ ${this.renderStepEditor()}
607
+ </div>
608
+ </div>
609
+ `;
610
+ }
611
+ };
612
+ exports.LabelingWorkflowBuilderPage = LabelingWorkflowBuilderPage;
613
+ tslib_1.__decorate([
614
+ (0, decorators_js_1.property)({ type: String }),
615
+ tslib_1.__metadata("design:type", String)
616
+ ], LabelingWorkflowBuilderPage.prototype, "workflowId", void 0);
617
+ tslib_1.__decorate([
618
+ (0, decorators_js_1.state)(),
619
+ tslib_1.__metadata("design:type", Object)
620
+ ], LabelingWorkflowBuilderPage.prototype, "workflow", void 0);
621
+ tslib_1.__decorate([
622
+ (0, decorators_js_1.state)(),
623
+ tslib_1.__metadata("design:type", Number)
624
+ ], LabelingWorkflowBuilderPage.prototype, "selectedStepIndex", void 0);
625
+ tslib_1.__decorate([
626
+ (0, decorators_js_1.state)(),
627
+ tslib_1.__metadata("design:type", Boolean)
628
+ ], LabelingWorkflowBuilderPage.prototype, "loading", void 0);
629
+ tslib_1.__decorate([
630
+ (0, decorators_js_1.state)(),
631
+ tslib_1.__metadata("design:type", String)
632
+ ], LabelingWorkflowBuilderPage.prototype, "error", void 0);
633
+ exports.LabelingWorkflowBuilderPage = LabelingWorkflowBuilderPage = tslib_1.__decorate([
634
+ (0, decorators_js_1.customElement)('labeling-workflow-builder-page')
635
+ ], LabelingWorkflowBuilderPage);
636
+ //# sourceMappingURL=labeling-workflow-builder.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"labeling-workflow-builder.js","sourceRoot":"","sources":["../../client/pages/labeling-workflow-builder.ts"],"names":[],"mappings":"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","sourcesContent":["import { html, css } from 'lit'\nimport { customElement, state, property } from 'lit/decorators.js'\nimport { PageView } from '@operato/shell'\nimport { client } from '@operato/graphql'\nimport gql from 'graphql-tag'\n\n/**\n * Workflow Builder Page\n *\n * Generic workflow builder UI for creating and editing labeling workflows\n */\n@customElement('labeling-workflow-builder-page')\nexport class LabelingWorkflowBuilderPage extends PageView {\n static styles = [\n css`\n :host {\n display: flex;\n flex-direction: column;\n height: 100%;\n background-color: var(--md-sys-color-surface);\n }\n\n .header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 20px;\n background-color: var(--md-sys-color-surface-variant);\n border-bottom: 1px solid var(--md-sys-color-outline);\n }\n\n .header h1 {\n margin: 0;\n font-size: 24px;\n color: var(--md-sys-color-on-surface);\n }\n\n .header-actions {\n display: flex;\n gap: 10px;\n }\n\n .content {\n display: flex;\n flex: 1;\n overflow: hidden;\n }\n\n .sidebar {\n width: 300px;\n border-right: 1px solid var(--md-sys-color-outline);\n overflow-y: auto;\n padding: 20px;\n }\n\n .main-area {\n flex: 1;\n overflow-y: auto;\n padding: 20px;\n }\n\n .form-group {\n margin-bottom: 20px;\n }\n\n .form-group label {\n display: block;\n margin-bottom: 8px;\n font-weight: 500;\n color: var(--md-sys-color-on-surface);\n }\n\n .form-group input,\n .form-group textarea,\n .form-group select {\n width: 100%;\n padding: 10px;\n border: 1px solid var(--md-sys-color-outline);\n border-radius: 4px;\n font-size: 14px;\n background-color: var(--md-sys-color-surface);\n color: var(--md-sys-color-on-surface);\n }\n\n .form-group textarea {\n min-height: 80px;\n resize: vertical;\n }\n\n button {\n padding: 10px 20px;\n background-color: var(--md-sys-color-primary);\n color: var(--md-sys-color-on-primary);\n border: none;\n border-radius: 4px;\n cursor: pointer;\n font-size: 14px;\n }\n\n button:hover {\n opacity: 0.9;\n }\n\n button.secondary {\n background-color: var(--md-sys-color-secondary);\n color: var(--md-sys-color-on-secondary);\n }\n\n .steps-container {\n margin-top: 20px;\n }\n\n .steps-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin-bottom: 15px;\n }\n\n .steps-header h2 {\n margin: 0;\n font-size: 18px;\n }\n\n .step-list {\n display: flex;\n flex-direction: column;\n gap: 10px;\n }\n\n .step-item {\n background-color: var(--md-sys-color-surface-variant);\n border: 1px solid var(--md-sys-color-outline);\n border-radius: 8px;\n padding: 15px;\n cursor: pointer;\n transition:\n transform 0.2s,\n box-shadow 0.2s;\n }\n\n .step-item:hover {\n transform: translateY(-2px);\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);\n }\n\n .step-item.selected {\n border-color: var(--md-sys-color-primary);\n border-width: 2px;\n }\n\n .step-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin-bottom: 8px;\n }\n\n .step-title {\n font-weight: bold;\n font-size: 16px;\n }\n\n .step-actions {\n display: flex;\n gap: 5px;\n }\n\n .step-actions button {\n padding: 5px 10px;\n font-size: 12px;\n }\n\n .step-type {\n display: inline-block;\n padding: 4px 8px;\n background-color: var(--md-sys-color-primary-container);\n color: var(--md-sys-color-on-primary-container);\n border-radius: 4px;\n font-size: 12px;\n margin-bottom: 8px;\n }\n\n .step-config {\n font-size: 12px;\n color: var(--md-sys-color-on-surface-variant);\n margin-top: 8px;\n }\n\n .empty-state {\n text-align: center;\n padding: 40px;\n color: var(--md-sys-color-on-surface-variant);\n }\n\n .add-step-button {\n width: 100%;\n padding: 15px;\n border: 2px dashed var(--md-sys-color-outline);\n background-color: transparent;\n color: var(--md-sys-color-on-surface);\n cursor: pointer;\n border-radius: 8px;\n margin-top: 10px;\n }\n\n .add-step-button:hover {\n border-color: var(--md-sys-color-primary);\n background-color: var(--md-sys-color-surface-variant);\n }\n\n .loading {\n text-align: center;\n padding: 40px;\n }\n\n .error {\n padding: 15px;\n background-color: #ffebee;\n color: #c62828;\n border-radius: 4px;\n margin-bottom: 20px;\n }\n `\n ]\n\n @property({ type: String }) workflowId?: string\n @state() workflow: any = {\n name: '',\n description: '',\n projectId: null,\n triggerType: 'manual',\n triggerConfig: '',\n steps: [],\n autoStart: false\n }\n @state() selectedStepIndex: number = -1\n @state() loading: boolean = false\n @state() error: string = ''\n\n async firstUpdated() {\n if (this.workflowId) {\n await this.loadWorkflow()\n }\n }\n\n async loadWorkflow() {\n this.loading = true\n this.error = ''\n\n try {\n const response = await client.query({\n query: gql`\n query GetWorkflow($workflowId: String!) {\n labelingWorkflow(workflowId: $workflowId) {\n id\n name\n description\n projectId\n triggerType\n triggerConfig\n steps {\n name\n type\n order\n config\n continueOnError\n }\n status\n autoStart\n }\n }\n `,\n variables: { workflowId: this.workflowId }\n })\n\n this.workflow = response.data.labelingWorkflow\n } catch (error) {\n console.error('Failed to load workflow:', error)\n this.error = 'Failed to load workflow. Please try again.'\n } finally {\n this.loading = false\n }\n }\n\n async saveWorkflow() {\n this.loading = true\n this.error = ''\n\n try {\n const mutation = this.workflowId\n ? gql`\n mutation UpdateWorkflow($workflowId: String!, $input: UpdateWorkflowRequest!) {\n updateLabelingWorkflow(workflowId: $workflowId, input: $input) {\n id\n name\n }\n }\n `\n : gql`\n mutation CreateWorkflow($input: CreateWorkflowRequest!) {\n createLabelingWorkflow(input: $input) {\n id\n name\n }\n }\n `\n\n const variables: any = {\n input: {\n name: this.workflow.name,\n description: this.workflow.description,\n projectId: parseInt(this.workflow.projectId),\n triggerType: this.workflow.triggerType,\n triggerConfig: this.workflow.triggerConfig,\n steps: this.workflow.steps.map((step: any, index: number) => ({\n name: step.name,\n type: step.type,\n order: index + 1,\n config: step.config,\n continueOnError: step.continueOnError || false\n })),\n autoStart: this.workflow.autoStart\n }\n }\n\n if (this.workflowId) {\n variables.workflowId = this.workflowId\n }\n\n await client.mutate({ mutation, variables })\n\n alert('Workflow saved successfully!')\n history.back()\n } catch (error) {\n console.error('Failed to save workflow:', error)\n this.error = 'Failed to save workflow. Please check your inputs and try again.'\n } finally {\n this.loading = false\n }\n }\n\n addStep() {\n const newStep = {\n name: `Step ${this.workflow.steps.length + 1}`,\n type: 'import_data',\n config: '{}',\n continueOnError: false\n }\n\n this.workflow = {\n ...this.workflow,\n steps: [...this.workflow.steps, newStep]\n }\n\n this.selectedStepIndex = this.workflow.steps.length - 1\n }\n\n removeStep(index: number) {\n if (confirm('Are you sure you want to remove this step?')) {\n const steps = [...this.workflow.steps]\n steps.splice(index, 1)\n\n this.workflow = {\n ...this.workflow,\n steps\n }\n\n if (this.selectedStepIndex === index) {\n this.selectedStepIndex = -1\n } else if (this.selectedStepIndex > index) {\n this.selectedStepIndex--\n }\n }\n }\n\n moveStepUp(index: number) {\n if (index === 0) return\n\n const steps = [...this.workflow.steps]\n const temp = steps[index]\n steps[index] = steps[index - 1]\n steps[index - 1] = temp\n\n this.workflow = {\n ...this.workflow,\n steps\n }\n\n if (this.selectedStepIndex === index) {\n this.selectedStepIndex = index - 1\n } else if (this.selectedStepIndex === index - 1) {\n this.selectedStepIndex = index\n }\n }\n\n moveStepDown(index: number) {\n if (index === this.workflow.steps.length - 1) return\n\n const steps = [...this.workflow.steps]\n const temp = steps[index]\n steps[index] = steps[index + 1]\n steps[index + 1] = temp\n\n this.workflow = {\n ...this.workflow,\n steps\n }\n\n if (this.selectedStepIndex === index) {\n this.selectedStepIndex = index + 1\n } else if (this.selectedStepIndex === index + 1) {\n this.selectedStepIndex = index\n }\n }\n\n selectStep(index: number) {\n this.selectedStepIndex = index\n }\n\n updateStep(index: number, field: string, value: any) {\n const steps = [...this.workflow.steps]\n steps[index] = {\n ...steps[index],\n [field]: value\n }\n\n this.workflow = {\n ...this.workflow,\n steps\n }\n }\n\n updateWorkflow(field: string, value: any) {\n this.workflow = {\n ...this.workflow,\n [field]: value\n }\n }\n\n renderStepEditor() {\n if (this.selectedStepIndex === -1 || !this.workflow.steps[this.selectedStepIndex]) {\n return html` <div class=\"empty-state\">Select a step to edit its configuration</div> `\n }\n\n const step = this.workflow.steps[this.selectedStepIndex]\n\n return html`\n <div class=\"form-group\">\n <label>Step Name</label>\n <input\n type=\"text\"\n .value=${step.name}\n @input=${(e: any) => this.updateStep(this.selectedStepIndex, 'name', e.target.value)}\n />\n </div>\n\n <div class=\"form-group\">\n <label>Step Type</label>\n <select\n .value=${step.type}\n @change=${(e: any) => this.updateStep(this.selectedStepIndex, 'type', e.target.value)}\n >\n <option value=\"import_data\">Import Data</option>\n <option value=\"generate_predictions\">Generate Predictions</option>\n <option value=\"wait_for_annotations\">Wait for Annotations</option>\n <option value=\"sync_annotations\">Sync Annotations</option>\n <option value=\"validate_quality\">Validate Quality</option>\n <option value=\"export_results\">Export Results</option>\n <option value=\"notification\">Notification</option>\n </select>\n </div>\n\n <div class=\"form-group\">\n <label>Configuration (JSON)</label>\n <textarea\n .value=${step.config}\n @input=${(e: any) => this.updateStep(this.selectedStepIndex, 'config', e.target.value)}\n placeholder='{\"key\": \"value\"}'\n ></textarea>\n </div>\n\n <div class=\"form-group\">\n <label>\n <input\n type=\"checkbox\"\n .checked=${step.continueOnError}\n @change=${(e: any) => this.updateStep(this.selectedStepIndex, 'continueOnError', e.target.checked)}\n />\n Continue on Error\n </label>\n </div>\n `\n }\n\n render() {\n if (this.loading) {\n return html`<div class=\"loading\">Loading...</div>`\n }\n\n return html`\n <div class=\"header\">\n <h1>${this.workflowId ? 'Edit Workflow' : 'Create Workflow'}</h1>\n <div class=\"header-actions\">\n <button class=\"secondary\" @click=${() => history.back()}>Cancel</button>\n <button @click=${this.saveWorkflow}>Save Workflow</button>\n </div>\n </div>\n\n ${this.error ? html`<div class=\"error\">${this.error}</div>` : ''}\n\n <div class=\"content\">\n <div class=\"sidebar\">\n <div class=\"form-group\">\n <label>Workflow Name *</label>\n <input\n type=\"text\"\n .value=${this.workflow.name}\n @input=${(e: any) => this.updateWorkflow('name', e.target.value)}\n placeholder=\"Enter workflow name\"\n required\n />\n </div>\n\n <div class=\"form-group\">\n <label>Description</label>\n <textarea\n .value=${this.workflow.description || ''}\n @input=${(e: any) => this.updateWorkflow('description', e.target.value)}\n placeholder=\"Enter workflow description\"\n ></textarea>\n </div>\n\n <div class=\"form-group\">\n <label>Project ID *</label>\n <input\n type=\"number\"\n .value=${this.workflow.projectId || ''}\n @input=${(e: any) => this.updateWorkflow('projectId', e.target.value)}\n placeholder=\"Label Studio project ID\"\n required\n />\n </div>\n\n <div class=\"form-group\">\n <label>Trigger Type</label>\n <select\n .value=${this.workflow.triggerType}\n @change=${(e: any) => this.updateWorkflow('triggerType', e.target.value)}\n >\n <option value=\"manual\">Manual</option>\n <option value=\"scheduled\">Scheduled</option>\n <option value=\"event\">Event-based</option>\n </select>\n </div>\n\n <div class=\"form-group\">\n <label>\n <input\n type=\"checkbox\"\n .checked=${this.workflow.autoStart}\n @change=${(e: any) => this.updateWorkflow('autoStart', e.target.checked)}\n />\n Auto Start\n </label>\n </div>\n\n <div class=\"steps-container\">\n <div class=\"steps-header\">\n <h2>Workflow Steps</h2>\n </div>\n\n <div class=\"step-list\">\n ${this.workflow.steps.length === 0\n ? html`<div class=\"empty-state\">No steps added yet</div>`\n : this.workflow.steps.map(\n (step: any, index: number) => html`\n <div\n class=\"step-item ${this.selectedStepIndex === index ? 'selected' : ''}\"\n @click=${() => this.selectStep(index)}\n >\n <div class=\"step-header\">\n <div class=\"step-title\">${index + 1}. ${step.name}</div>\n <div class=\"step-actions\">\n ${index > 0\n ? html`<button\n @click=${(e: Event) => {\n e.stopPropagation()\n this.moveStepUp(index)\n }}\n >\n ↑\n </button>`\n : ''}\n ${index < this.workflow.steps.length - 1\n ? html`<button\n @click=${(e: Event) => {\n e.stopPropagation()\n this.moveStepDown(index)\n }}\n >\n ↓\n </button>`\n : ''}\n <button\n class=\"secondary\"\n @click=${(e: Event) => {\n e.stopPropagation()\n this.removeStep(index)\n }}\n >\n ×\n </button>\n </div>\n </div>\n <div class=\"step-type\">${step.type}</div>\n ${step.continueOnError ? html`<div class=\"step-config\">Continue on error: Yes</div>` : ''}\n </div>\n `\n )}\n </div>\n\n <button class=\"add-step-button\" @click=${this.addStep}>+ Add Step</button>\n </div>\n </div>\n\n <div class=\"main-area\">\n <h2>Step Configuration</h2>\n ${this.renderStepEditor()}\n </div>\n </div>\n `\n }\n}\n"]}