ultravisor 1.0.0 → 1.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.
Files changed (96) hide show
  1. package/.babelrc +6 -0
  2. package/.browserslistrc +1 -0
  3. package/.browserslistrc-BACKUP +1 -0
  4. package/.gulpfile-quackage-config.json +7 -0
  5. package/.gulpfile-quackage.js +2 -0
  6. package/CONTRIBUTING.md +50 -0
  7. package/README.md +34 -0
  8. package/debug/Harness.js +2 -1
  9. package/docs/.nojekyll +0 -0
  10. package/docs/_sidebar.md +18 -0
  11. package/docs/_topbar.md +7 -0
  12. package/docs/architecture.md +103 -0
  13. package/docs/cover.md +15 -0
  14. package/docs/features/api.md +230 -0
  15. package/docs/features/cli.md +182 -0
  16. package/docs/features/configuration.md +245 -0
  17. package/docs/features/manifests.md +177 -0
  18. package/docs/features/operations.md +292 -0
  19. package/docs/features/scheduling.md +179 -0
  20. package/docs/features/tasks.md +1857 -0
  21. package/docs/index.html +39 -0
  22. package/docs/overview.md +75 -0
  23. package/docs/quickstart.md +167 -0
  24. package/docs/retold-catalog.json +24 -0
  25. package/docs/retold-keyword-index.json +19 -0
  26. package/package.json +5 -2
  27. package/source/Ultravisor.cjs +2 -2
  28. package/source/cli/Ultravisor-CLIProgram.cjs +38 -0
  29. package/source/cli/commands/Ultravisor-Command-ScheduleOperation.cjs +26 -2
  30. package/source/cli/commands/Ultravisor-Command-ScheduleTask.cjs +26 -2
  31. package/source/cli/commands/Ultravisor-Command-ScheduleView.cjs +22 -0
  32. package/source/cli/commands/Ultravisor-Command-SingleOperation.cjs +49 -1
  33. package/source/cli/commands/Ultravisor-Command-SingleTask.cjs +51 -1
  34. package/source/cli/commands/Ultravisor-Command-Stop.cjs +4 -0
  35. package/source/cli/commands/Ultravisor-Command-UpdateTask.cjs +91 -0
  36. package/source/config/Ultravisor-Default-Command-Configuration.cjs +6 -1
  37. package/source/services/Ultravisor-Hypervisor-Event-Base.cjs +18 -1
  38. package/source/services/Ultravisor-Hypervisor-State.cjs +213 -0
  39. package/source/services/Ultravisor-Hypervisor.cjs +225 -1
  40. package/source/services/Ultravisor-Operation-Manifest.cjs +150 -1
  41. package/source/services/Ultravisor-Operation.cjs +190 -1
  42. package/source/services/Ultravisor-Task.cjs +339 -1
  43. package/source/services/events/Ultravisor-Hypervisor-Event-Cron.cjs +71 -1
  44. package/source/services/tasks/Ultravisor-Task-Base.cjs +264 -0
  45. package/source/services/tasks/Ultravisor-Task-CollectValues.cjs +188 -0
  46. package/source/services/tasks/Ultravisor-Task-Command.cjs +65 -0
  47. package/source/services/tasks/Ultravisor-Task-CommandEach.cjs +190 -0
  48. package/source/services/tasks/Ultravisor-Task-Conditional.cjs +104 -0
  49. package/source/services/tasks/Ultravisor-Task-DateWindow.cjs +72 -0
  50. package/source/services/tasks/Ultravisor-Task-GeneratePagedOperation.cjs +336 -0
  51. package/source/services/tasks/Ultravisor-Task-LaunchOperation.cjs +143 -0
  52. package/source/services/tasks/Ultravisor-Task-LaunchTask.cjs +146 -0
  53. package/source/services/tasks/Ultravisor-Task-LineMatch.cjs +158 -0
  54. package/source/services/tasks/Ultravisor-Task-Request.cjs +56 -0
  55. package/source/services/tasks/Ultravisor-Task-Solver.cjs +89 -0
  56. package/source/services/tasks/Ultravisor-Task-TemplateString.cjs +93 -0
  57. package/source/services/tasks/rest/Ultravisor-Task-GetBinary.cjs +127 -0
  58. package/source/services/tasks/rest/Ultravisor-Task-GetJSON.cjs +119 -0
  59. package/source/services/tasks/rest/Ultravisor-Task-GetText.cjs +109 -0
  60. package/source/services/tasks/rest/Ultravisor-Task-GetXML.cjs +112 -0
  61. package/source/services/tasks/rest/Ultravisor-Task-RestRequest.cjs +499 -0
  62. package/source/services/tasks/rest/Ultravisor-Task-SendJSON.cjs +150 -0
  63. package/source/services/tasks/stagingfiles/Ultravisor-Task-CopyFile.cjs +110 -0
  64. package/source/services/tasks/stagingfiles/Ultravisor-Task-ListFiles.cjs +89 -0
  65. package/source/services/tasks/stagingfiles/Ultravisor-Task-ReadBinary.cjs +87 -0
  66. package/source/services/tasks/stagingfiles/Ultravisor-Task-ReadJSON.cjs +67 -0
  67. package/source/services/tasks/stagingfiles/Ultravisor-Task-ReadText.cjs +66 -0
  68. package/source/services/tasks/stagingfiles/Ultravisor-Task-ReadXML.cjs +69 -0
  69. package/source/services/tasks/stagingfiles/Ultravisor-Task-WriteBinary.cjs +95 -0
  70. package/source/services/tasks/stagingfiles/Ultravisor-Task-WriteJSON.cjs +96 -0
  71. package/source/services/tasks/stagingfiles/Ultravisor-Task-WriteText.cjs +99 -0
  72. package/source/services/tasks/stagingfiles/Ultravisor-Task-WriteXML.cjs +102 -0
  73. package/source/web_server/Ultravisor-API-Server.cjs +463 -3
  74. package/test/Ultravisor_tests.js +6097 -1
  75. package/webinterface/.babelrc +6 -0
  76. package/webinterface/.browserslistrc +1 -0
  77. package/webinterface/.browserslistrc-BACKUP +1 -0
  78. package/webinterface/.gulpfile-quackage-config.json +7 -0
  79. package/webinterface/.gulpfile-quackage.js +2 -0
  80. package/webinterface/css/ultravisor.css +121 -0
  81. package/webinterface/html/index.html +32 -0
  82. package/webinterface/package.json +39 -0
  83. package/webinterface/source/Pict-Application-Ultravisor-Configuration.json +15 -0
  84. package/webinterface/source/Pict-Application-Ultravisor.js +414 -0
  85. package/webinterface/source/providers/PictRouter-Ultravisor-Configuration.json +42 -0
  86. package/webinterface/source/views/PictView-Ultravisor-BottomBar.js +65 -0
  87. package/webinterface/source/views/PictView-Ultravisor-Dashboard.js +236 -0
  88. package/webinterface/source/views/PictView-Ultravisor-Layout.js +83 -0
  89. package/webinterface/source/views/PictView-Ultravisor-ManifestList.js +273 -0
  90. package/webinterface/source/views/PictView-Ultravisor-OperationEdit.js +243 -0
  91. package/webinterface/source/views/PictView-Ultravisor-OperationList.js +141 -0
  92. package/webinterface/source/views/PictView-Ultravisor-Schedule.js +280 -0
  93. package/webinterface/source/views/PictView-Ultravisor-TaskEdit.js +220 -0
  94. package/webinterface/source/views/PictView-Ultravisor-TaskList.js +248 -0
  95. package/webinterface/source/views/PictView-Ultravisor-TimingView.js +420 -0
  96. package/webinterface/source/views/PictView-Ultravisor-TopBar.js +147 -0
@@ -0,0 +1,243 @@
1
+ const libPictView = require('pict-view');
2
+
3
+ const _ViewConfiguration =
4
+ {
5
+ ViewIdentifier: "Ultravisor-OperationEdit",
6
+
7
+ DefaultRenderable: "Ultravisor-OperationEdit-Content",
8
+ DefaultDestinationAddress: "#Ultravisor-Content-Container",
9
+
10
+ AutoRender: false,
11
+
12
+ CSS: /*css*/`
13
+ .ultravisor-operationedit {
14
+ padding: 2em;
15
+ max-width: 800px;
16
+ margin: 0 auto;
17
+ }
18
+ .ultravisor-operationedit-header {
19
+ margin-bottom: 1.5em;
20
+ padding-bottom: 1em;
21
+ border-bottom: 1px solid #2a2a4a;
22
+ }
23
+ .ultravisor-operationedit-header h1 {
24
+ margin: 0;
25
+ font-size: 2em;
26
+ font-weight: 300;
27
+ color: #e0e0e0;
28
+ }
29
+ .ultravisor-task-list-editor {
30
+ background: #16213e;
31
+ border: 1px solid #2a2a4a;
32
+ border-radius: 6px;
33
+ padding: 1em;
34
+ }
35
+ .ultravisor-task-list-item {
36
+ display: flex;
37
+ align-items: center;
38
+ justify-content: space-between;
39
+ padding: 0.5em 0.75em;
40
+ background: #1a1a2e;
41
+ border-radius: 4px;
42
+ margin-bottom: 0.5em;
43
+ }
44
+ .ultravisor-task-list-item code {
45
+ color: #4fc3f7;
46
+ font-size: 0.9em;
47
+ }
48
+ .ultravisor-task-list-add {
49
+ display: flex;
50
+ gap: 0.5em;
51
+ margin-top: 0.75em;
52
+ }
53
+ .ultravisor-task-list-add input {
54
+ flex: 1;
55
+ }
56
+ `,
57
+
58
+ Templates:
59
+ [
60
+ {
61
+ Hash: "Ultravisor-OperationEdit-Template",
62
+ Template: /*html*/`
63
+ <div class="ultravisor-operationedit">
64
+ <div class="ultravisor-operationedit-header">
65
+ <h1 id="Ultravisor-OperationEdit-Title">New Operation</h1>
66
+ </div>
67
+ <div id="Ultravisor-OperationEdit-Form"></div>
68
+ </div>
69
+ `
70
+ }
71
+ ],
72
+
73
+ Renderables:
74
+ [
75
+ {
76
+ RenderableHash: "Ultravisor-OperationEdit-Content",
77
+ TemplateHash: "Ultravisor-OperationEdit-Template",
78
+ DestinationAddress: "#Ultravisor-Content-Container",
79
+ RenderMethod: "replace"
80
+ }
81
+ ]
82
+ };
83
+
84
+ class UltravisorOperationEditView extends libPictView
85
+ {
86
+ constructor(pFable, pOptions, pServiceHash)
87
+ {
88
+ super(pFable, pOptions, pServiceHash);
89
+ }
90
+
91
+ onAfterRender(pRenderable, pRenderDestinationAddress, pRecord, pContent)
92
+ {
93
+ let tmpOp = this.pict.AppData.Ultravisor.CurrentEditOperation;
94
+ if (!tmpOp)
95
+ {
96
+ tmpOp = { GUIDOperation: '', Name: '', Description: '', Tasks: [] };
97
+ this.pict.AppData.Ultravisor.CurrentEditOperation = tmpOp;
98
+ }
99
+
100
+ let tmpIsNew = !tmpOp.GUIDOperation;
101
+ let tmpTitleEl = document.getElementById('Ultravisor-OperationEdit-Title');
102
+ if (tmpTitleEl)
103
+ {
104
+ tmpTitleEl.textContent = tmpIsNew ? 'New Operation' : ('Edit Operation: ' + (tmpOp.Name || tmpOp.GUIDOperation));
105
+ }
106
+
107
+ this.renderForm();
108
+
109
+ return super.onAfterRender(pRenderable, pRenderDestinationAddress, pRecord, pContent);
110
+ }
111
+
112
+ escapeAttr(pValue)
113
+ {
114
+ if (!pValue) return '';
115
+ return String(pValue).replace(/&/g, '&amp;').replace(/"/g, '&quot;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
116
+ }
117
+
118
+ escapeHTML(pValue)
119
+ {
120
+ if (!pValue) return '';
121
+ return String(pValue).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
122
+ }
123
+
124
+ renderForm()
125
+ {
126
+ let tmpOp = this.pict.AppData.Ultravisor.CurrentEditOperation;
127
+ let tmpIsNew = !tmpOp.GUIDOperation;
128
+ let tmpGlobalRef = '_Pict';
129
+ let tmpViewRef = tmpGlobalRef + ".views['Ultravisor-OperationEdit']";
130
+
131
+ let tmpHTML = '';
132
+ tmpHTML += '<div class="ultravisor-form-group"><label>GUID Operation</label>';
133
+ tmpHTML += '<input type="text" id="Ultravisor-OperationEdit-GUIDOperation" value="' + this.escapeAttr(tmpOp.GUIDOperation) + '" ' + (tmpIsNew ? '' : 'readonly') + '></div>';
134
+
135
+ tmpHTML += '<div class="ultravisor-form-group"><label>Name</label>';
136
+ tmpHTML += '<input type="text" id="Ultravisor-OperationEdit-Name" value="' + this.escapeAttr(tmpOp.Name || '') + '"></div>';
137
+
138
+ tmpHTML += '<div class="ultravisor-form-group"><label>Description</label>';
139
+ tmpHTML += '<textarea id="Ultravisor-OperationEdit-Description">' + this.escapeHTML(tmpOp.Description || '') + '</textarea></div>';
140
+
141
+ tmpHTML += '<div class="ultravisor-form-group"><label>Tasks (executed in order)</label>';
142
+ tmpHTML += '<div class="ultravisor-task-list-editor">';
143
+
144
+ let tmpTasks = tmpOp.Tasks || [];
145
+ if (tmpTasks.length === 0)
146
+ {
147
+ tmpHTML += '<div style="color: #607d8b; font-size: 0.9em; padding: 0.5em;">No tasks added yet.</div>';
148
+ }
149
+ else
150
+ {
151
+ for (let i = 0; i < tmpTasks.length; i++)
152
+ {
153
+ let tmpEscTask = tmpTasks[i].replace(/'/g, "\\'");
154
+ tmpHTML += '<div class="ultravisor-task-list-item">';
155
+ tmpHTML += '<code>' + this.escapeHTML(tmpTasks[i]) + '</code>';
156
+ tmpHTML += '<button class="ultravisor-btn-sm ultravisor-btn-delete" onclick="' + tmpViewRef + '.removeTaskFromOperation(' + i + ')">Remove</button>';
157
+ tmpHTML += '</div>';
158
+ }
159
+ }
160
+
161
+ tmpHTML += '<div class="ultravisor-task-list-add">';
162
+ tmpHTML += '<input type="text" id="Ultravisor-OperationEdit-NewTaskGUID" placeholder="Task GUID to add...">';
163
+ tmpHTML += '<button class="ultravisor-btn ultravisor-btn-secondary" onclick="' + tmpViewRef + '.addTaskToOperation()">Add</button>';
164
+ tmpHTML += '</div>';
165
+ tmpHTML += '</div></div>';
166
+
167
+ tmpHTML += '<div class="ultravisor-form-actions">';
168
+ tmpHTML += '<button class="ultravisor-btn ultravisor-btn-primary" onclick="' + tmpViewRef + '.saveOperation()">Save Operation</button>';
169
+ tmpHTML += '<button class="ultravisor-btn ultravisor-btn-secondary" onclick="' + tmpGlobalRef + '.PictApplication.navigateTo(\'/Operations\')">Cancel</button>';
170
+ tmpHTML += '</div>';
171
+
172
+ this.pict.ContentAssignment.assignContent('#Ultravisor-OperationEdit-Form', tmpHTML);
173
+ }
174
+
175
+ addTaskToOperation()
176
+ {
177
+ let tmpInput = document.getElementById('Ultravisor-OperationEdit-NewTaskGUID');
178
+ let tmpGUID = tmpInput ? tmpInput.value.trim() : '';
179
+ if (!tmpGUID) return;
180
+
181
+ let tmpOp = this.pict.AppData.Ultravisor.CurrentEditOperation;
182
+ if (!tmpOp.Tasks)
183
+ {
184
+ tmpOp.Tasks = [];
185
+ }
186
+ tmpOp.Tasks.push(tmpGUID);
187
+
188
+ // Preserve current form field values before re-render
189
+ tmpOp.GUIDOperation = document.getElementById('Ultravisor-OperationEdit-GUIDOperation').value.trim();
190
+ tmpOp.Name = document.getElementById('Ultravisor-OperationEdit-Name').value.trim();
191
+ tmpOp.Description = document.getElementById('Ultravisor-OperationEdit-Description').value.trim();
192
+
193
+ this.renderForm();
194
+ }
195
+
196
+ removeTaskFromOperation(pIndex)
197
+ {
198
+ let tmpOp = this.pict.AppData.Ultravisor.CurrentEditOperation;
199
+ if (tmpOp.Tasks && pIndex >= 0 && pIndex < tmpOp.Tasks.length)
200
+ {
201
+ tmpOp.Tasks.splice(pIndex, 1);
202
+ }
203
+
204
+ // Preserve current form field values before re-render
205
+ tmpOp.GUIDOperation = document.getElementById('Ultravisor-OperationEdit-GUIDOperation').value.trim();
206
+ tmpOp.Name = document.getElementById('Ultravisor-OperationEdit-Name').value.trim();
207
+ tmpOp.Description = document.getElementById('Ultravisor-OperationEdit-Description').value.trim();
208
+
209
+ this.renderForm();
210
+ }
211
+
212
+ saveOperation()
213
+ {
214
+ let tmpOpData =
215
+ {
216
+ GUIDOperation: document.getElementById('Ultravisor-OperationEdit-GUIDOperation').value.trim(),
217
+ Name: document.getElementById('Ultravisor-OperationEdit-Name').value.trim(),
218
+ Description: document.getElementById('Ultravisor-OperationEdit-Description').value.trim(),
219
+ Tasks: this.pict.AppData.Ultravisor.CurrentEditOperation.Tasks || []
220
+ };
221
+
222
+ if (!tmpOpData.GUIDOperation)
223
+ {
224
+ alert('GUID Operation is required.');
225
+ return;
226
+ }
227
+
228
+ this.pict.PictApplication.saveOperation(tmpOpData,
229
+ function (pError)
230
+ {
231
+ if (pError)
232
+ {
233
+ alert('Error saving operation: ' + pError.message);
234
+ return;
235
+ }
236
+ this.pict.PictApplication.navigateTo('/Operations');
237
+ }.bind(this));
238
+ }
239
+ }
240
+
241
+ module.exports = UltravisorOperationEditView;
242
+
243
+ module.exports.default_configuration = _ViewConfiguration;
@@ -0,0 +1,141 @@
1
+ const libPictView = require('pict-view');
2
+
3
+ const _ViewConfiguration =
4
+ {
5
+ ViewIdentifier: "Ultravisor-OperationList",
6
+
7
+ DefaultRenderable: "Ultravisor-OperationList-Content",
8
+ DefaultDestinationAddress: "#Ultravisor-Content-Container",
9
+
10
+ AutoRender: false,
11
+
12
+ CSS: /*css*/`
13
+ .ultravisor-operationlist {
14
+ padding: 2em;
15
+ max-width: 1200px;
16
+ margin: 0 auto;
17
+ }
18
+ .ultravisor-operationlist-header {
19
+ display: flex;
20
+ justify-content: space-between;
21
+ align-items: center;
22
+ margin-bottom: 1.5em;
23
+ padding-bottom: 1em;
24
+ border-bottom: 1px solid #2a2a4a;
25
+ }
26
+ .ultravisor-operationlist-header h1 {
27
+ margin: 0;
28
+ font-size: 2em;
29
+ font-weight: 300;
30
+ color: #e0e0e0;
31
+ }
32
+ .ultravisor-operation-table {
33
+ width: 100%;
34
+ border-collapse: collapse;
35
+ }
36
+ .ultravisor-operation-table th {
37
+ background-color: #16213e;
38
+ }
39
+ .ultravisor-operation-table tr:hover td {
40
+ background-color: #1a2744;
41
+ }
42
+ .ultravisor-operation-task-count {
43
+ display: inline-block;
44
+ padding: 0.15em 0.5em;
45
+ border-radius: 3px;
46
+ font-size: 0.8em;
47
+ font-weight: 600;
48
+ background-color: #2e7d32;
49
+ color: #c8e6c9;
50
+ }
51
+ `,
52
+
53
+ Templates:
54
+ [
55
+ {
56
+ Hash: "Ultravisor-OperationList-Template",
57
+ Template: /*html*/`
58
+ <div class="ultravisor-operationlist">
59
+ <div class="ultravisor-operationlist-header">
60
+ <h1>Operations</h1>
61
+ <button class="ultravisor-btn ultravisor-btn-primary" onclick="{~P~}.PictApplication.editOperation()">New Operation</button>
62
+ </div>
63
+ <div id="Ultravisor-OperationList-Body"></div>
64
+ </div>
65
+ `
66
+ }
67
+ ],
68
+
69
+ Renderables:
70
+ [
71
+ {
72
+ RenderableHash: "Ultravisor-OperationList-Content",
73
+ TemplateHash: "Ultravisor-OperationList-Template",
74
+ DestinationAddress: "#Ultravisor-Content-Container",
75
+ RenderMethod: "replace"
76
+ }
77
+ ]
78
+ };
79
+
80
+ class UltravisorOperationListView extends libPictView
81
+ {
82
+ constructor(pFable, pOptions, pServiceHash)
83
+ {
84
+ super(pFable, pOptions, pServiceHash);
85
+ }
86
+
87
+ onAfterRender(pRenderable, pRenderDestinationAddress, pRecord, pContent)
88
+ {
89
+ this.pict.PictApplication.loadOperations(
90
+ function ()
91
+ {
92
+ this.renderOperationTable();
93
+ }.bind(this));
94
+
95
+ return super.onAfterRender(pRenderable, pRenderDestinationAddress, pRecord, pContent);
96
+ }
97
+
98
+ renderOperationTable()
99
+ {
100
+ let tmpOpList = this.pict.AppData.Ultravisor.OperationList;
101
+ let tmpGlobalRef = '_Pict';
102
+
103
+ if (!tmpOpList || tmpOpList.length === 0)
104
+ {
105
+ this.pict.ContentAssignment.assignContent('#Ultravisor-OperationList-Body',
106
+ '<div class="ultravisor-empty-message">No operations defined. Click "New Operation" to create one.</div>');
107
+ return;
108
+ }
109
+
110
+ let tmpHTML = '<table class="ultravisor-operation-table">';
111
+ tmpHTML += '<thead><tr><th>GUID</th><th>Name</th><th>Tasks</th><th>Actions</th></tr></thead>';
112
+ tmpHTML += '<tbody>';
113
+
114
+ for (let i = 0; i < tmpOpList.length; i++)
115
+ {
116
+ let tmpOp = tmpOpList[i];
117
+ let tmpGUID = tmpOp.GUIDOperation || '';
118
+ let tmpName = tmpOp.Name || tmpGUID;
119
+ let tmpTaskCount = (tmpOp.Tasks && Array.isArray(tmpOp.Tasks)) ? tmpOp.Tasks.length : 0;
120
+ let tmpEscGUID = tmpGUID.replace(/'/g, "\\'");
121
+
122
+ tmpHTML += '<tr>';
123
+ tmpHTML += '<td><code>' + tmpGUID + '</code></td>';
124
+ tmpHTML += '<td>' + tmpName + '</td>';
125
+ tmpHTML += '<td><span class="ultravisor-operation-task-count">' + tmpTaskCount + ' task' + (tmpTaskCount !== 1 ? 's' : '') + '</span></td>';
126
+ tmpHTML += '<td><div class="ultravisor-task-actions">';
127
+ tmpHTML += '<button class="ultravisor-btn-sm ultravisor-btn-execute" onclick="' + tmpGlobalRef + '.PictApplication.executeOperation(\'' + tmpEscGUID + '\', function(pErr, pData){ alert(pErr ? \'Error: \'+pErr.message : \'Operation executed. Status: \'+(pData&&pData.Status||\'Done\')); })">Run</button>';
128
+ tmpHTML += '<button class="ultravisor-btn-sm ultravisor-btn-edit" onclick="' + tmpGlobalRef + '.PictApplication.editOperation(\'' + tmpEscGUID + '\')">Edit</button>';
129
+ tmpHTML += '<button class="ultravisor-btn-sm ultravisor-btn-delete" onclick="if(confirm(\'Delete operation ' + tmpEscGUID + '?\')){ ' + tmpGlobalRef + '.PictApplication.deleteOperation(\'' + tmpEscGUID + '\', function(){ ' + tmpGlobalRef + '.PictApplication.showView(\'Ultravisor-OperationList\'); }); }">Delete</button>';
130
+ tmpHTML += '</div></td>';
131
+ tmpHTML += '</tr>';
132
+ }
133
+
134
+ tmpHTML += '</tbody></table>';
135
+ this.pict.ContentAssignment.assignContent('#Ultravisor-OperationList-Body', tmpHTML);
136
+ }
137
+ }
138
+
139
+ module.exports = UltravisorOperationListView;
140
+
141
+ module.exports.default_configuration = _ViewConfiguration;
@@ -0,0 +1,280 @@
1
+ const libPictView = require('pict-view');
2
+
3
+ const _ViewConfiguration =
4
+ {
5
+ ViewIdentifier: "Ultravisor-Schedule",
6
+
7
+ DefaultRenderable: "Ultravisor-Schedule-Content",
8
+ DefaultDestinationAddress: "#Ultravisor-Content-Container",
9
+
10
+ AutoRender: false,
11
+
12
+ CSS: /*css*/`
13
+ .ultravisor-schedule {
14
+ padding: 2em;
15
+ max-width: 1200px;
16
+ margin: 0 auto;
17
+ }
18
+ .ultravisor-schedule-header {
19
+ display: flex;
20
+ justify-content: space-between;
21
+ align-items: center;
22
+ margin-bottom: 1.5em;
23
+ padding-bottom: 1em;
24
+ border-bottom: 1px solid #2a2a4a;
25
+ }
26
+ .ultravisor-schedule-header h1 {
27
+ margin: 0;
28
+ font-size: 2em;
29
+ font-weight: 300;
30
+ color: #e0e0e0;
31
+ }
32
+ .ultravisor-schedule-controls {
33
+ display: flex;
34
+ gap: 0.5em;
35
+ }
36
+ .ultravisor-schedule-table {
37
+ width: 100%;
38
+ border-collapse: collapse;
39
+ margin-bottom: 2em;
40
+ }
41
+ .ultravisor-schedule-table th {
42
+ background-color: #16213e;
43
+ }
44
+ .ultravisor-schedule-table tr:hover td {
45
+ background-color: #1a2744;
46
+ }
47
+ .ultravisor-schedule-active {
48
+ display: inline-block;
49
+ padding: 0.1em 0.5em;
50
+ border-radius: 3px;
51
+ font-size: 0.8em;
52
+ font-weight: 600;
53
+ }
54
+ .ultravisor-schedule-active.yes {
55
+ background-color: #2e7d32;
56
+ color: #c8e6c9;
57
+ }
58
+ .ultravisor-schedule-active.no {
59
+ background-color: #424242;
60
+ color: #9e9e9e;
61
+ }
62
+ .ultravisor-schedule-add-section {
63
+ background: #16213e;
64
+ border: 1px solid #2a2a4a;
65
+ border-radius: 8px;
66
+ padding: 1.5em;
67
+ margin-top: 1em;
68
+ }
69
+ .ultravisor-schedule-add-section h3 {
70
+ margin: 0 0 1em 0;
71
+ font-size: 1.1em;
72
+ font-weight: 600;
73
+ color: #b0bec5;
74
+ }
75
+ .ultravisor-schedule-add-form {
76
+ display: flex;
77
+ flex-wrap: wrap;
78
+ gap: 0.75em;
79
+ align-items: flex-end;
80
+ }
81
+ .ultravisor-schedule-add-form .ultravisor-form-group {
82
+ margin-bottom: 0;
83
+ }
84
+ .ultravisor-schedule-add-form .ultravisor-form-group label {
85
+ display: block;
86
+ margin-bottom: 0.35em;
87
+ font-size: 0.8em;
88
+ font-weight: 600;
89
+ color: #78909c;
90
+ text-transform: uppercase;
91
+ letter-spacing: 0.03em;
92
+ }
93
+ `,
94
+
95
+ Templates:
96
+ [
97
+ {
98
+ Hash: "Ultravisor-Schedule-Template",
99
+ Template: /*html*/`
100
+ <div class="ultravisor-schedule">
101
+ <div class="ultravisor-schedule-header">
102
+ <h1>Schedule</h1>
103
+ <div class="ultravisor-schedule-controls">
104
+ <button class="ultravisor-btn ultravisor-btn-primary" onclick="{~P~}.PictApplication.startSchedule(function(){ {~P~}.PictApplication.showView('Ultravisor-Schedule'); })">Start All</button>
105
+ <button class="ultravisor-btn ultravisor-btn-danger" onclick="{~P~}.PictApplication.stopSchedule(function(){ {~P~}.PictApplication.showView('Ultravisor-Schedule'); })">Stop All</button>
106
+ <button class="ultravisor-btn ultravisor-btn-secondary" onclick="{~P~}.PictApplication.showView('Ultravisor-Schedule')">Refresh</button>
107
+ </div>
108
+ </div>
109
+ <div id="Ultravisor-Schedule-Body"></div>
110
+ <div id="Ultravisor-Schedule-AddForms"></div>
111
+ </div>
112
+ `
113
+ }
114
+ ],
115
+
116
+ Renderables:
117
+ [
118
+ {
119
+ RenderableHash: "Ultravisor-Schedule-Content",
120
+ TemplateHash: "Ultravisor-Schedule-Template",
121
+ DestinationAddress: "#Ultravisor-Content-Container",
122
+ RenderMethod: "replace"
123
+ }
124
+ ]
125
+ };
126
+
127
+ class UltravisorScheduleView extends libPictView
128
+ {
129
+ constructor(pFable, pOptions, pServiceHash)
130
+ {
131
+ super(pFable, pOptions, pServiceHash);
132
+ }
133
+
134
+ onAfterRender(pRenderable, pRenderDestinationAddress, pRecord, pContent)
135
+ {
136
+ this.pict.PictApplication.loadSchedule(
137
+ function ()
138
+ {
139
+ this.renderScheduleTable();
140
+ this.renderAddForms();
141
+ }.bind(this));
142
+
143
+ return super.onAfterRender(pRenderable, pRenderDestinationAddress, pRecord, pContent);
144
+ }
145
+
146
+ renderScheduleTable()
147
+ {
148
+ let tmpSchedule = this.pict.AppData.Ultravisor.Schedule;
149
+ let tmpGlobalRef = '_Pict';
150
+
151
+ if (!tmpSchedule || tmpSchedule.length === 0)
152
+ {
153
+ this.pict.ContentAssignment.assignContent('#Ultravisor-Schedule-Body',
154
+ '<div class="ultravisor-empty-message">No schedule entries. Use the forms below to add tasks or operations to the schedule.</div>');
155
+ return;
156
+ }
157
+
158
+ let tmpHTML = '<table class="ultravisor-schedule-table">';
159
+ tmpHTML += '<thead><tr><th>GUID</th><th>Type</th><th>Target</th><th>Cron</th><th>Active</th><th>Actions</th></tr></thead>';
160
+ tmpHTML += '<tbody>';
161
+
162
+ for (let i = 0; i < tmpSchedule.length; i++)
163
+ {
164
+ let tmpEntry = tmpSchedule[i];
165
+ let tmpGUID = tmpEntry.GUID || '';
166
+ let tmpEscGUID = tmpGUID.replace(/'/g, "\\'");
167
+ let tmpActive = tmpEntry.Active ? 'yes' : 'no';
168
+
169
+ tmpHTML += '<tr>';
170
+ tmpHTML += '<td><code style="font-size:0.8em;">' + tmpGUID + '</code></td>';
171
+ tmpHTML += '<td>' + (tmpEntry.TargetType || '') + '</td>';
172
+ tmpHTML += '<td><code>' + (tmpEntry.TargetGUID || '') + '</code></td>';
173
+ tmpHTML += '<td><code>' + (tmpEntry.CronExpression || tmpEntry.Parameters || '') + '</code></td>';
174
+ tmpHTML += '<td><span class="ultravisor-schedule-active ' + tmpActive + '">' + (tmpEntry.Active ? 'Active' : 'Inactive') + '</span></td>';
175
+ tmpHTML += '<td>';
176
+ tmpHTML += '<button class="ultravisor-btn-sm ultravisor-btn-delete" onclick="if(confirm(\'Remove schedule entry?\')){ ' + tmpGlobalRef + '.PictApplication.removeScheduleEntry(\'' + tmpEscGUID + '\', function(){ ' + tmpGlobalRef + '.PictApplication.showView(\'Ultravisor-Schedule\'); }); }">Remove</button>';
177
+ tmpHTML += '</td>';
178
+ tmpHTML += '</tr>';
179
+ }
180
+
181
+ tmpHTML += '</tbody></table>';
182
+ this.pict.ContentAssignment.assignContent('#Ultravisor-Schedule-Body', tmpHTML);
183
+ }
184
+
185
+ renderAddForms()
186
+ {
187
+ let tmpGlobalRef = '_Pict';
188
+ let tmpViewRef = tmpGlobalRef + ".views['Ultravisor-Schedule']";
189
+
190
+ let tmpHTML = '';
191
+
192
+ // Schedule a task
193
+ tmpHTML += '<div class="ultravisor-schedule-add-section">';
194
+ tmpHTML += '<h3>Schedule a Task</h3>';
195
+ tmpHTML += '<div class="ultravisor-schedule-add-form">';
196
+ tmpHTML += '<div class="ultravisor-form-group"><label>Task GUID</label>';
197
+ tmpHTML += '<input type="text" id="Ultravisor-Schedule-TaskGUID" placeholder="e.g. MY-TASK-001"></div>';
198
+ tmpHTML += '<div class="ultravisor-form-group"><label>Schedule Type</label>';
199
+ tmpHTML += '<select id="Ultravisor-Schedule-TaskScheduleType">';
200
+ tmpHTML += '<option value="cron">Cron</option>';
201
+ tmpHTML += '<option value="daily">Daily</option>';
202
+ tmpHTML += '<option value="hourly">Hourly</option>';
203
+ tmpHTML += '</select></div>';
204
+ tmpHTML += '<div class="ultravisor-form-group"><label>Parameters (cron expression)</label>';
205
+ tmpHTML += '<input type="text" id="Ultravisor-Schedule-TaskParameters" placeholder="e.g. 0 * * * *"></div>';
206
+ tmpHTML += '<button class="ultravisor-btn ultravisor-btn-primary" onclick="' + tmpViewRef + '.addTaskSchedule()">Add</button>';
207
+ tmpHTML += '</div></div>';
208
+
209
+ // Schedule an operation
210
+ tmpHTML += '<div class="ultravisor-schedule-add-section" style="margin-top:1em;">';
211
+ tmpHTML += '<h3>Schedule an Operation</h3>';
212
+ tmpHTML += '<div class="ultravisor-schedule-add-form">';
213
+ tmpHTML += '<div class="ultravisor-form-group"><label>Operation GUID</label>';
214
+ tmpHTML += '<input type="text" id="Ultravisor-Schedule-OperationGUID" placeholder="e.g. MY-OP-001"></div>';
215
+ tmpHTML += '<div class="ultravisor-form-group"><label>Schedule Type</label>';
216
+ tmpHTML += '<select id="Ultravisor-Schedule-OperationScheduleType">';
217
+ tmpHTML += '<option value="cron">Cron</option>';
218
+ tmpHTML += '<option value="daily">Daily</option>';
219
+ tmpHTML += '<option value="hourly">Hourly</option>';
220
+ tmpHTML += '</select></div>';
221
+ tmpHTML += '<div class="ultravisor-form-group"><label>Parameters (cron expression)</label>';
222
+ tmpHTML += '<input type="text" id="Ultravisor-Schedule-OperationParameters" placeholder="e.g. 0 */6 * * *"></div>';
223
+ tmpHTML += '<button class="ultravisor-btn ultravisor-btn-primary" onclick="' + tmpViewRef + '.addOperationSchedule()">Add</button>';
224
+ tmpHTML += '</div></div>';
225
+
226
+ this.pict.ContentAssignment.assignContent('#Ultravisor-Schedule-AddForms', tmpHTML);
227
+ }
228
+
229
+ addTaskSchedule()
230
+ {
231
+ let tmpGUID = document.getElementById('Ultravisor-Schedule-TaskGUID').value.trim();
232
+ let tmpType = document.getElementById('Ultravisor-Schedule-TaskScheduleType').value;
233
+ let tmpParams = document.getElementById('Ultravisor-Schedule-TaskParameters').value.trim();
234
+
235
+ if (!tmpGUID)
236
+ {
237
+ alert('Task GUID is required.');
238
+ return;
239
+ }
240
+
241
+ this.pict.PictApplication.scheduleTask(tmpGUID, tmpType, tmpParams,
242
+ function (pError)
243
+ {
244
+ if (pError)
245
+ {
246
+ alert('Error scheduling task: ' + pError.message);
247
+ return;
248
+ }
249
+ this.pict.PictApplication.showView('Ultravisor-Schedule');
250
+ }.bind(this));
251
+ }
252
+
253
+ addOperationSchedule()
254
+ {
255
+ let tmpGUID = document.getElementById('Ultravisor-Schedule-OperationGUID').value.trim();
256
+ let tmpType = document.getElementById('Ultravisor-Schedule-OperationScheduleType').value;
257
+ let tmpParams = document.getElementById('Ultravisor-Schedule-OperationParameters').value.trim();
258
+
259
+ if (!tmpGUID)
260
+ {
261
+ alert('Operation GUID is required.');
262
+ return;
263
+ }
264
+
265
+ this.pict.PictApplication.scheduleOperation(tmpGUID, tmpType, tmpParams,
266
+ function (pError)
267
+ {
268
+ if (pError)
269
+ {
270
+ alert('Error scheduling operation: ' + pError.message);
271
+ return;
272
+ }
273
+ this.pict.PictApplication.showView('Ultravisor-Schedule');
274
+ }.bind(this));
275
+ }
276
+ }
277
+
278
+ module.exports = UltravisorScheduleView;
279
+
280
+ module.exports.default_configuration = _ViewConfiguration;