retold-facto 0.0.4

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 (92) hide show
  1. package/.claude/launch.json +11 -0
  2. package/.dockerignore +8 -0
  3. package/.quackage.json +19 -0
  4. package/Dockerfile +26 -0
  5. package/bin/retold-facto.js +909 -0
  6. package/examples/facto-government-data.sqlite +0 -0
  7. package/examples/government-data-catalog.json +137 -0
  8. package/examples/government-data-loader.js +1432 -0
  9. package/package.json +91 -0
  10. package/scripts/facto-download.js +425 -0
  11. package/source/Retold-Facto.js +1042 -0
  12. package/source/services/Retold-Facto-BeaconProvider.js +511 -0
  13. package/source/services/Retold-Facto-CatalogManager.js +1252 -0
  14. package/source/services/Retold-Facto-DataLakeService.js +1642 -0
  15. package/source/services/Retold-Facto-DatasetManager.js +417 -0
  16. package/source/services/Retold-Facto-IngestEngine.js +1315 -0
  17. package/source/services/Retold-Facto-ProjectionEngine.js +3960 -0
  18. package/source/services/Retold-Facto-RecordManager.js +360 -0
  19. package/source/services/Retold-Facto-SchemaManager.js +1110 -0
  20. package/source/services/Retold-Facto-SourceFolderScanner.js +2243 -0
  21. package/source/services/Retold-Facto-SourceManager.js +730 -0
  22. package/source/services/Retold-Facto-StoreConnectionManager.js +441 -0
  23. package/source/services/Retold-Facto-ThroughputMonitor.js +478 -0
  24. package/source/services/web-app/codemirror-entry.js +7 -0
  25. package/source/services/web-app/pict-app/Pict-Application-Facto-Configuration.json +9 -0
  26. package/source/services/web-app/pict-app/Pict-Application-Facto.js +70 -0
  27. package/source/services/web-app/pict-app/Pict-Facto-Bundle.js +11 -0
  28. package/source/services/web-app/pict-app/providers/Pict-Provider-Facto-UI.js +66 -0
  29. package/source/services/web-app/pict-app/providers/Pict-Provider-Facto.js +69 -0
  30. package/source/services/web-app/pict-app/providers/facto-api/Facto-API-Catalog.js +93 -0
  31. package/source/services/web-app/pict-app/providers/facto-api/Facto-API-Connections.js +42 -0
  32. package/source/services/web-app/pict-app/providers/facto-api/Facto-API-Datasets.js +605 -0
  33. package/source/services/web-app/pict-app/providers/facto-api/Facto-API-Projections.js +188 -0
  34. package/source/services/web-app/pict-app/providers/facto-api/Facto-API-Scanner.js +80 -0
  35. package/source/services/web-app/pict-app/providers/facto-api/Facto-API-Schema.js +116 -0
  36. package/source/services/web-app/pict-app/providers/facto-api/Facto-API-Sources.js +104 -0
  37. package/source/services/web-app/pict-app/views/PictView-Facto-Catalog.js +526 -0
  38. package/source/services/web-app/pict-app/views/PictView-Facto-Datasets.js +173 -0
  39. package/source/services/web-app/pict-app/views/PictView-Facto-Ingest.js +259 -0
  40. package/source/services/web-app/pict-app/views/PictView-Facto-Layout.js +191 -0
  41. package/source/services/web-app/pict-app/views/PictView-Facto-Projections.js +231 -0
  42. package/source/services/web-app/pict-app/views/PictView-Facto-Records.js +326 -0
  43. package/source/services/web-app/pict-app/views/PictView-Facto-Scanner.js +624 -0
  44. package/source/services/web-app/pict-app/views/PictView-Facto-Sources.js +201 -0
  45. package/source/services/web-app/pict-app/views/PictView-Facto-Throughput.js +456 -0
  46. package/source/services/web-app/pict-app-full/Pict-Application-Facto-Full-Configuration.json +14 -0
  47. package/source/services/web-app/pict-app-full/Pict-Application-Facto-Full.js +391 -0
  48. package/source/services/web-app/pict-app-full/providers/PictRouter-Facto-Configuration.json +56 -0
  49. package/source/services/web-app/pict-app-full/views/PictView-Facto-Full-BottomBar.js +68 -0
  50. package/source/services/web-app/pict-app-full/views/PictView-Facto-Full-Connections.js +340 -0
  51. package/source/services/web-app/pict-app-full/views/PictView-Facto-Full-Dashboard.js +149 -0
  52. package/source/services/web-app/pict-app-full/views/PictView-Facto-Full-Dashboards.js +819 -0
  53. package/source/services/web-app/pict-app-full/views/PictView-Facto-Full-Datasets.js +178 -0
  54. package/source/services/web-app/pict-app-full/views/PictView-Facto-Full-IngestJobs.js +99 -0
  55. package/source/services/web-app/pict-app-full/views/PictView-Facto-Full-Layout.js +62 -0
  56. package/source/services/web-app/pict-app-full/views/PictView-Facto-Full-MappingEditor.js +158 -0
  57. package/source/services/web-app/pict-app-full/views/PictView-Facto-Full-ProjectionDetail.js +1120 -0
  58. package/source/services/web-app/pict-app-full/views/PictView-Facto-Full-Projections.js +172 -0
  59. package/source/services/web-app/pict-app-full/views/PictView-Facto-Full-QueryPanel.js +119 -0
  60. package/source/services/web-app/pict-app-full/views/PictView-Facto-Full-RecordViewer.js +663 -0
  61. package/source/services/web-app/pict-app-full/views/PictView-Facto-Full-Records.js +648 -0
  62. package/source/services/web-app/pict-app-full/views/PictView-Facto-Full-Scanner.js +1017 -0
  63. package/source/services/web-app/pict-app-full/views/PictView-Facto-Full-SchemaDetail.js +1404 -0
  64. package/source/services/web-app/pict-app-full/views/PictView-Facto-Full-SchemaDocEditor.js +1036 -0
  65. package/source/services/web-app/pict-app-full/views/PictView-Facto-Full-SchemaEditor.js +636 -0
  66. package/source/services/web-app/pict-app-full/views/PictView-Facto-Full-SchemaResearch.js +357 -0
  67. package/source/services/web-app/pict-app-full/views/PictView-Facto-Full-SourceDetail.js +822 -0
  68. package/source/services/web-app/pict-app-full/views/PictView-Facto-Full-SourceEditor.js +1036 -0
  69. package/source/services/web-app/pict-app-full/views/PictView-Facto-Full-SourceResearch.js +487 -0
  70. package/source/services/web-app/pict-app-full/views/PictView-Facto-Full-Sources.js +165 -0
  71. package/source/services/web-app/pict-app-full/views/PictView-Facto-Full-Throughput.js +439 -0
  72. package/source/services/web-app/pict-app-full/views/PictView-Facto-Full-TopBar.js +335 -0
  73. package/source/services/web-app/pict-app-full/views/projections/Facto-Projections-Constants.js +71 -0
  74. package/source/services/web-app/web/chart.min.js +20 -0
  75. package/source/services/web-app/web/codemirror-bundle.js +30099 -0
  76. package/source/services/web-app/web/css/facto-themes.css +467 -0
  77. package/source/services/web-app/web/css/facto.css +502 -0
  78. package/source/services/web-app/web/index.html +28 -0
  79. package/source/services/web-app/web/retold-facto.js +12138 -0
  80. package/source/services/web-app/web/retold-facto.js.map +1 -0
  81. package/source/services/web-app/web/retold-facto.min.js +2 -0
  82. package/source/services/web-app/web/retold-facto.min.js.map +1 -0
  83. package/source/services/web-app/web/simple/index.html +17 -0
  84. package/test/Facto_Browser_Integration_tests.js +798 -0
  85. package/test/RetoldFacto_tests.js +4117 -0
  86. package/test/fixtures/weather-readings.csv +17 -0
  87. package/test/fixtures/weather-stations.csv +9 -0
  88. package/test/model/MeadowModel-Extended.json +8497 -0
  89. package/test/model/MeadowModel-PICT.json +1 -0
  90. package/test/model/MeadowModel.json +1355 -0
  91. package/test/model/ddl/Facto.ddl +225 -0
  92. package/test/model/fable-configuration.json +14 -0
@@ -0,0 +1,340 @@
1
+ const libPictView = require('pict-view');
2
+
3
+ const _ViewConfiguration =
4
+ {
5
+ ViewIdentifier: "Facto-Full-Connections",
6
+
7
+ DefaultRenderable: "Facto-Full-Connections-Content",
8
+ DefaultDestinationAddress: "#Facto-Full-Content-Container",
9
+
10
+ AutoRender: false,
11
+
12
+ CSS: `
13
+ .facto-conn-form {
14
+ display: none;
15
+ padding: 1em;
16
+ background: var(--facto-bg-surface, #fcf8f0);
17
+ border: 1px solid var(--facto-border, #d6c8ae);
18
+ border-radius: 8px;
19
+ margin-bottom: 1em;
20
+ }
21
+ .facto-conn-form.active {
22
+ display: block;
23
+ }
24
+ .facto-conn-form-grid {
25
+ display: grid;
26
+ grid-template-columns: 1fr 1fr;
27
+ gap: 0.6em;
28
+ }
29
+ .facto-conn-form-grid label {
30
+ font-size: 0.78em;
31
+ font-weight: 600;
32
+ color: var(--facto-text-secondary, #786848);
33
+ display: block;
34
+ margin-bottom: 0.2em;
35
+ }
36
+ .facto-conn-form-grid input,
37
+ .facto-conn-form-grid select {
38
+ width: 100%;
39
+ padding: 0.4em 0.6em;
40
+ font-size: 0.85em;
41
+ border: 1px solid var(--facto-border, #d6c8ae);
42
+ border-radius: 4px;
43
+ background: var(--facto-bg-base, #f6f0e4);
44
+ color: var(--facto-text-primary, #3e2f1a);
45
+ }
46
+ .facto-conn-form-actions {
47
+ grid-column: span 2;
48
+ display: flex;
49
+ gap: 0.5em;
50
+ margin-top: 0.5em;
51
+ }
52
+ `,
53
+
54
+ Templates:
55
+ [
56
+ {
57
+ Hash: "Facto-Full-Connections-Template",
58
+ Template: /*html*/`
59
+ <div class="facto-content">
60
+ <div class="facto-content-header">
61
+ <h1>Store Connections</h1>
62
+ <p>Manage database and storage connections used by projections.</p>
63
+ </div>
64
+
65
+ <div class="facto-section">
66
+ <div style="display:flex; align-items:center; justify-content:space-between; margin-bottom:1em;">
67
+ <div class="facto-section-title" style="margin:0;">Connections</div>
68
+ <button class="facto-btn facto-btn-primary facto-btn-small" onclick="{~P~}.views['Facto-Full-Connections'].toggleConnectionForm()">+ Add Connection</button>
69
+ </div>
70
+
71
+ <div id="Facto-Conn-Form" class="facto-conn-form">
72
+ <div class="facto-conn-form-grid">
73
+ <div>
74
+ <label>Connection Name</label>
75
+ <input type="text" id="Facto-Conn-Name" placeholder="e.g. Production MySQL">
76
+ </div>
77
+ <div>
78
+ <label>Type</label>
79
+ <select id="Facto-Conn-Type" onchange="{~P~}.views['Facto-Full-Connections'].updateConnectionFormFields()">
80
+ <option value="MySQL">MySQL</option>
81
+ <option value="PostgreSQL">PostgreSQL</option>
82
+ <option value="MSSQL">MSSQL</option>
83
+ <option value="SQLite">SQLite</option>
84
+ <option value="Solr">Solr</option>
85
+ <option value="RocksDB">RocksDB</option>
86
+ </select>
87
+ </div>
88
+ <div id="Facto-Conn-FilePath-Wrap" style="display:none;">
89
+ <label>File Path</label>
90
+ <input type="text" id="Facto-Conn-FilePath" placeholder="/path/to/database.sqlite">
91
+ </div>
92
+ <div id="Facto-Conn-Host-Wrap">
93
+ <label>Host</label>
94
+ <input type="text" id="Facto-Conn-Host" placeholder="localhost">
95
+ </div>
96
+ <div id="Facto-Conn-Port-Wrap">
97
+ <label>Port</label>
98
+ <input type="number" id="Facto-Conn-Port" placeholder="3306">
99
+ </div>
100
+ <div id="Facto-Conn-User-Wrap">
101
+ <label>User</label>
102
+ <input type="text" id="Facto-Conn-User" placeholder="root">
103
+ </div>
104
+ <div id="Facto-Conn-Password-Wrap">
105
+ <label>Password</label>
106
+ <input type="password" id="Facto-Conn-Password" placeholder="password">
107
+ </div>
108
+ <div id="Facto-Conn-Database-Wrap">
109
+ <label>Database</label>
110
+ <input type="text" id="Facto-Conn-Database" placeholder="my_database">
111
+ </div>
112
+ <div class="facto-conn-form-actions">
113
+ <button class="facto-btn facto-btn-primary facto-btn-small" onclick="{~P~}.views['Facto-Full-Connections'].addConnection()">Save Connection</button>
114
+ <button class="facto-btn facto-btn-secondary facto-btn-small" onclick="{~P~}.views['Facto-Full-Connections'].toggleConnectionForm()">Cancel</button>
115
+ </div>
116
+ </div>
117
+ </div>
118
+
119
+ <div id="Facto-Conn-List"></div>
120
+ </div>
121
+ </div>
122
+ `
123
+ }
124
+ ],
125
+
126
+ Renderables:
127
+ [
128
+ {
129
+ RenderableHash: "Facto-Full-Connections-Content",
130
+ TemplateHash: "Facto-Full-Connections-Template",
131
+ DestinationAddress: "#Facto-Full-Content-Container",
132
+ RenderMethod: "replace"
133
+ }
134
+ ]
135
+ };
136
+
137
+ class FactoFullConnectionsView extends libPictView
138
+ {
139
+ constructor(pFable, pOptions, pServiceHash)
140
+ {
141
+ super(pFable, pOptions, pServiceHash);
142
+
143
+ }
144
+
145
+ onAfterRender()
146
+ {
147
+ // Load connections from the API
148
+ this.pict.providers.Facto.loadStoreConnections().then(
149
+ () =>
150
+ {
151
+ this.refreshConnectionList();
152
+ }).catch(
153
+ (pError) =>
154
+ {
155
+ this.pict.views['Pict-Section-Modal'].toast('Error loading connections: ' + pError.message, {type: 'error'});
156
+ });
157
+
158
+ return super.onAfterRender();
159
+ }
160
+
161
+ toggleConnectionForm()
162
+ {
163
+ let tmpForm = document.getElementById('Facto-Conn-Form');
164
+ if (tmpForm)
165
+ {
166
+ tmpForm.classList.toggle('active');
167
+ if (tmpForm.classList.contains('active'))
168
+ {
169
+ this.updateConnectionFormFields();
170
+ }
171
+ }
172
+ }
173
+
174
+ updateConnectionFormFields()
175
+ {
176
+ let tmpType = this.pict.providers.FactoUI.getVal('Facto-Conn-Type');
177
+ let tmpIsFileBased = (tmpType === 'SQLite' || tmpType === 'RocksDB');
178
+
179
+ let tmpFilePathWrap = document.getElementById('Facto-Conn-FilePath-Wrap');
180
+ let tmpHostWrap = document.getElementById('Facto-Conn-Host-Wrap');
181
+ let tmpPortWrap = document.getElementById('Facto-Conn-Port-Wrap');
182
+ let tmpUserWrap = document.getElementById('Facto-Conn-User-Wrap');
183
+ let tmpPasswordWrap = document.getElementById('Facto-Conn-Password-Wrap');
184
+ let tmpDatabaseWrap = document.getElementById('Facto-Conn-Database-Wrap');
185
+
186
+ if (tmpFilePathWrap) tmpFilePathWrap.style.display = tmpIsFileBased ? '' : 'none';
187
+ if (tmpHostWrap) tmpHostWrap.style.display = tmpIsFileBased ? 'none' : '';
188
+ if (tmpPortWrap) tmpPortWrap.style.display = tmpIsFileBased ? 'none' : '';
189
+ if (tmpUserWrap) tmpUserWrap.style.display = tmpIsFileBased ? 'none' : '';
190
+ if (tmpPasswordWrap) tmpPasswordWrap.style.display = tmpIsFileBased ? 'none' : '';
191
+ if (tmpDatabaseWrap) tmpDatabaseWrap.style.display = tmpIsFileBased ? 'none' : '';
192
+ }
193
+
194
+ refreshConnectionList()
195
+ {
196
+ let tmpContainer = document.getElementById('Facto-Conn-List');
197
+ if (!tmpContainer) return;
198
+
199
+ if (this.pict.AppData.Facto.StoreConnections.length === 0)
200
+ {
201
+ tmpContainer.innerHTML = '<div class="facto-card" style="text-align:center; padding:2em; color:var(--facto-text-tertiary);">No connections configured. Add one to get started.</div>';
202
+ return;
203
+ }
204
+
205
+ let tmpHtml = '<table class="facto-table"><thead><tr>';
206
+ tmpHtml += '<th>Name</th><th>Type</th><th>Status</th><th>Actions</th>';
207
+ tmpHtml += '</tr></thead><tbody>';
208
+
209
+ for (let i = 0; i < this.pict.AppData.Facto.StoreConnections.length; i++)
210
+ {
211
+ let tmpConn = this.pict.AppData.Facto.StoreConnections[i];
212
+ let tmpStatusClass = (tmpConn.Status || 'Untested').toLowerCase();
213
+
214
+ tmpHtml += '<tr>';
215
+ tmpHtml += '<td><strong>' + (tmpConn.Name || '\u2014') + '</strong></td>';
216
+ tmpHtml += '<td>' + (tmpConn.Type || '\u2014') + '</td>';
217
+ tmpHtml += '<td><span class="facto-status-badge ' + tmpStatusClass + '">' + (tmpConn.Status || 'Untested') + '</span></td>';
218
+ tmpHtml += '<td>';
219
+ tmpHtml += '<button class="facto-btn facto-btn-primary facto-btn-small" onclick="pict.views[\'Facto-Full-Connections\'].testConnection(' + tmpConn.IDStoreConnection + ')">Test</button> ';
220
+ tmpHtml += '<button class="facto-btn facto-btn-secondary facto-btn-small" onclick="pict.views[\'Facto-Full-Connections\'].deleteConnection(' + tmpConn.IDStoreConnection + ')">Delete</button>';
221
+ tmpHtml += '</td>';
222
+ tmpHtml += '</tr>';
223
+ }
224
+
225
+ tmpHtml += '</tbody></table>';
226
+ tmpContainer.innerHTML = tmpHtml;
227
+ }
228
+
229
+ addConnection()
230
+ {
231
+ let tmpName = this.pict.providers.FactoUI.getVal('Facto-Conn-Name');
232
+ let tmpType = this.pict.providers.FactoUI.getVal('Facto-Conn-Type');
233
+
234
+ if (!tmpName)
235
+ {
236
+ this.pict.views['Pict-Section-Modal'].toast('Connection name is required.', {type: 'warning'});
237
+ return;
238
+ }
239
+
240
+ let tmpIsFileBased = (tmpType === 'SQLite' || tmpType === 'RocksDB');
241
+ let tmpConfig = {};
242
+
243
+ if (tmpIsFileBased)
244
+ {
245
+ tmpConfig.SQLiteFilePath = this.pict.providers.FactoUI.getVal('Facto-Conn-FilePath');
246
+ if (!tmpConfig.SQLiteFilePath)
247
+ {
248
+ this.pict.views['Pict-Section-Modal'].toast('File path is required for ' + tmpType + ' connections.', {type: 'warning'});
249
+ return;
250
+ }
251
+ }
252
+ else
253
+ {
254
+ tmpConfig.host = this.pict.providers.FactoUI.getVal('Facto-Conn-Host');
255
+ tmpConfig.server = tmpConfig.host;
256
+ tmpConfig.port = parseInt(this.pict.providers.FactoUI.getVal('Facto-Conn-Port')) || 0;
257
+ tmpConfig.user = this.pict.providers.FactoUI.getVal('Facto-Conn-User');
258
+ tmpConfig.password = this.pict.providers.FactoUI.getVal('Facto-Conn-Password');
259
+ tmpConfig.database = this.pict.providers.FactoUI.getVal('Facto-Conn-Database');
260
+ }
261
+
262
+ this.pict.providers.Facto.createStoreConnection(
263
+ {
264
+ Name: tmpName,
265
+ Type: tmpType,
266
+ Config: tmpConfig
267
+ }).then(
268
+ (pResponse) =>
269
+ {
270
+ if (pResponse && pResponse.Error)
271
+ {
272
+ this.pict.views['Pict-Section-Modal'].toast('Error: ' + pResponse.Error, {type: 'error'});
273
+ return;
274
+ }
275
+
276
+ // Reload connections
277
+ this.pict.providers.Facto.loadStoreConnections().then(
278
+ (pResult) =>
279
+ {
280
+
281
+ this.refreshConnectionList();
282
+ this.toggleConnectionForm();
283
+
284
+ // Clear form
285
+ let tmpFields = ['Name', 'Host', 'Port', 'User', 'Password', 'Database', 'FilePath'];
286
+ for (let i = 0; i < tmpFields.length; i++)
287
+ {
288
+ let tmpEl = document.getElementById('Facto-Conn-' + tmpFields[i]);
289
+ if (tmpEl) tmpEl.value = '';
290
+ }
291
+ });
292
+ });
293
+ }
294
+
295
+ testConnection(pIDStoreConnection)
296
+ {
297
+ this.pict.providers.Facto.testStoreConnection(pIDStoreConnection).then(
298
+ (pResponse) =>
299
+ {
300
+ // Reload connections to get updated status
301
+ this.pict.providers.Facto.loadStoreConnections().then(
302
+ (pResult) =>
303
+ {
304
+
305
+ this.refreshConnectionList();
306
+
307
+ if (pResponse && pResponse.Success)
308
+ {
309
+ this.pict.views['Pict-Section-Modal'].toast('Connection test succeeded!', {type: 'success'});
310
+ }
311
+ else
312
+ {
313
+ this.pict.views['Pict-Section-Modal'].toast('Connection test failed: ' + (pResponse && pResponse.Error ? pResponse.Error : 'Unknown error'), {type: 'error'});
314
+ }
315
+ });
316
+ });
317
+ }
318
+
319
+ async deleteConnection(pIDStoreConnection)
320
+ {
321
+ let tmpConfirmed = await this.pict.views['Pict-Section-Modal'].confirm('Delete this connection?', { title: 'Delete Connection', confirmLabel: 'Delete', dangerous: true });
322
+ if (!tmpConfirmed) return;
323
+
324
+ this.pict.providers.Facto.deleteStoreConnection(pIDStoreConnection).then(
325
+ () =>
326
+ {
327
+ this.pict.providers.Facto.loadStoreConnections().then(
328
+ (pResult) =>
329
+ {
330
+
331
+ this.refreshConnectionList();
332
+ });
333
+ });
334
+ }
335
+
336
+ }
337
+
338
+ module.exports = FactoFullConnectionsView;
339
+
340
+ module.exports.default_configuration = _ViewConfiguration;
@@ -0,0 +1,149 @@
1
+ const libPictView = require('pict-view');
2
+
3
+ const _ViewConfiguration =
4
+ {
5
+ ViewIdentifier: "Facto-Full-Dashboard",
6
+
7
+ DefaultRenderable: "Facto-Full-Dashboard-Content",
8
+ DefaultDestinationAddress: "#Facto-Full-Content-Container",
9
+
10
+ AutoRender: false,
11
+
12
+ CSS: /*css*/`
13
+ .facto-dashboard-stat {
14
+ text-align: center;
15
+ }
16
+
17
+ .facto-dashboard-stat-value {
18
+ font-size: 2em;
19
+ font-weight: 700;
20
+ color: var(--facto-text-heading);
21
+ line-height: 1.2;
22
+ }
23
+
24
+ .facto-dashboard-stat-label {
25
+ font-size: 0.85em;
26
+ color: var(--facto-text-secondary);
27
+ margin-top: 0.3em;
28
+ }
29
+
30
+ .facto-dashboard-actions {
31
+ display: flex;
32
+ gap: 0.75em;
33
+ flex-wrap: wrap;
34
+ }
35
+ `,
36
+
37
+ Templates:
38
+ [
39
+ {
40
+ Hash: "Facto-Full-Dashboard-Template",
41
+ Template: /*html*/`
42
+ <div class="facto-content">
43
+ <div class="facto-content-header">
44
+ <h1>Dashboard</h1>
45
+ <p>Retold Facto data warehouse overview.</p>
46
+ </div>
47
+
48
+ <div class="facto-card-grid" id="Facto-Full-Dashboard-Cards">
49
+ <div class="facto-card facto-dashboard-stat">
50
+ <div class="facto-dashboard-stat-value" id="Facto-Full-Dash-SourceCount">--</div>
51
+ <div class="facto-dashboard-stat-label">Sources</div>
52
+ </div>
53
+ <div class="facto-card facto-dashboard-stat">
54
+ <div class="facto-dashboard-stat-value" id="Facto-Full-Dash-DatasetCount">--</div>
55
+ <div class="facto-dashboard-stat-label">Datasets</div>
56
+ </div>
57
+ <div class="facto-card facto-dashboard-stat">
58
+ <div class="facto-dashboard-stat-value" id="Facto-Full-Dash-RecordCount">--</div>
59
+ <div class="facto-dashboard-stat-label">Records</div>
60
+ </div>
61
+ <div class="facto-card facto-dashboard-stat">
62
+ <div class="facto-dashboard-stat-value" id="Facto-Full-Dash-IngestCount">--</div>
63
+ <div class="facto-dashboard-stat-label">Ingest Jobs</div>
64
+ </div>
65
+ <div class="facto-card facto-dashboard-stat">
66
+ <div class="facto-dashboard-stat-value" id="Facto-Full-Dash-CatalogCount">--</div>
67
+ <div class="facto-dashboard-stat-label">Catalog Entries</div>
68
+ </div>
69
+ </div>
70
+
71
+ <div class="facto-section" style="margin-top:2em;">
72
+ <div class="facto-section-title">Quick Actions</div>
73
+ <div class="facto-dashboard-actions">
74
+ <button class="facto-btn facto-btn-primary" onclick="{~P~}.PictApplication.navigateTo('/SourceResearch')">Source Research</button>
75
+ <button class="facto-btn facto-btn-primary" onclick="{~P~}.PictApplication.navigateTo('/Sources')">Manage Sources</button>
76
+ <button class="facto-btn facto-btn-primary" onclick="{~P~}.PictApplication.navigateTo('/Records')">Browse Records</button>
77
+ <button class="facto-btn facto-btn-secondary" onclick="window.location.href='/simple/'">Simple View</button>
78
+ </div>
79
+ </div>
80
+ </div>
81
+ `
82
+ }
83
+ ],
84
+
85
+ Renderables:
86
+ [
87
+ {
88
+ RenderableHash: "Facto-Full-Dashboard-Content",
89
+ TemplateHash: "Facto-Full-Dashboard-Template",
90
+ DestinationAddress: "#Facto-Full-Content-Container",
91
+ RenderMethod: "replace"
92
+ }
93
+ ]
94
+ };
95
+
96
+ class FactoFullDashboardView extends libPictView
97
+ {
98
+ constructor(pFable, pOptions, pServiceHash)
99
+ {
100
+ super(pFable, pOptions, pServiceHash);
101
+ }
102
+
103
+ onAfterRender(pRenderable, pRenderDestinationAddress, pRecord, pContent)
104
+ {
105
+ let tmpProvider = this.pict.providers.Facto;
106
+
107
+ // Load counts in parallel
108
+ tmpProvider.loadSources().then(
109
+ () =>
110
+ {
111
+ let tmpEl = document.getElementById('Facto-Full-Dash-SourceCount');
112
+ if (tmpEl) tmpEl.textContent = (this.pict.AppData.Facto.Sources || []).length;
113
+ });
114
+
115
+ tmpProvider.loadDatasets().then(
116
+ () =>
117
+ {
118
+ let tmpEl = document.getElementById('Facto-Full-Dash-DatasetCount');
119
+ if (tmpEl) tmpEl.textContent = (this.pict.AppData.Facto.Datasets || []).length;
120
+ });
121
+
122
+ tmpProvider.loadRecords().then(
123
+ () =>
124
+ {
125
+ let tmpEl = document.getElementById('Facto-Full-Dash-RecordCount');
126
+ if (tmpEl) tmpEl.textContent = (this.pict.AppData.Facto.Records || []).length;
127
+ });
128
+
129
+ tmpProvider.loadIngestJobs().then(
130
+ () =>
131
+ {
132
+ let tmpEl = document.getElementById('Facto-Full-Dash-IngestCount');
133
+ if (tmpEl) tmpEl.textContent = (this.pict.AppData.Facto.IngestJobs || []).length;
134
+ });
135
+
136
+ tmpProvider.loadCatalogEntries().then(
137
+ () =>
138
+ {
139
+ let tmpEl = document.getElementById('Facto-Full-Dash-CatalogCount');
140
+ if (tmpEl) tmpEl.textContent = (this.pict.AppData.Facto.CatalogEntries || []).length;
141
+ });
142
+
143
+ return super.onAfterRender(pRenderable, pRenderDestinationAddress, pRecord, pContent);
144
+ }
145
+ }
146
+
147
+ module.exports = FactoFullDashboardView;
148
+
149
+ module.exports.default_configuration = _ViewConfiguration;