pict-section-recordset 1.0.52 → 1.0.54

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 (36) hide show
  1. package/example_applications/simple_entity/Simple-RecordSet-Application.js +221 -1
  2. package/example_applications/simple_entity/html/index.html +2 -0
  3. package/package.json +2 -2
  4. package/source/application/Pict-Application-RecordSet.js +2 -0
  5. package/source/providers/RecordSet-Link-Manager.js +1 -1
  6. package/source/providers/RecordSet-RecordProvider-Base.js +10 -0
  7. package/source/providers/RecordSet-RecordProvider-MeadowEndpoints.js +60 -15
  8. package/source/providers/RecordSet-Router.js +1 -1
  9. package/source/services/RecordsSet-MetaController.js +9 -6
  10. package/source/views/RecordSet-RecordBaseView.js +1 -1
  11. package/source/views/create/RecordSet-Create.js +344 -0
  12. package/source/views/dashboard/RecordSet-Dashboard.js +3 -2
  13. package/source/views/list/RecordSet-List.js +2 -2
  14. package/source/views/read/RecordSet-Read.js +760 -44
  15. package/types/application/Pict-Application-RecordSet.d.ts.map +1 -1
  16. package/types/providers/RecordSet-RecordProvider-Base.d.ts +2 -0
  17. package/types/providers/RecordSet-RecordProvider-Base.d.ts.map +1 -1
  18. package/types/providers/RecordSet-RecordProvider-MeadowEndpoints.d.ts +3 -5
  19. package/types/providers/RecordSet-RecordProvider-MeadowEndpoints.d.ts.map +1 -1
  20. package/types/services/RecordsSet-MetaController.d.ts +1 -1
  21. package/types/services/RecordsSet-MetaController.d.ts.map +1 -1
  22. package/types/views/create/RecordSet-Create.d.ts +39 -0
  23. package/types/views/create/RecordSet-Create.d.ts.map +1 -0
  24. package/types/views/dashboard/RecordSet-Dashboard.d.ts.map +1 -1
  25. package/types/views/read/RecordSet-Read.d.ts +65 -5
  26. package/types/views/read/RecordSet-Read.d.ts.map +1 -1
  27. package/source/views/edit/RecordSet-Edit-HeaderEdit.js +0 -64
  28. package/source/views/edit/RecordSet-Edit-RecordEdit.js +0 -64
  29. package/source/views/edit/RecordSet-Edit-RecordEditControls.js +0 -64
  30. package/source/views/edit/RecordSet-Edit-RecordEditExtra.js +0 -64
  31. package/source/views/edit/RecordSet-Edit-TabBarEdit.js +0 -64
  32. package/source/views/edit/RecordSet-Edit.js +0 -64
  33. package/source/views/read/RecordSet-Read-HeaderRead.js +0 -64
  34. package/source/views/read/RecordSet-Read-RecordRead.js +0 -78
  35. package/source/views/read/RecordSet-Read-RecordReadExtra.js +0 -64
  36. package/source/views/read/RecordSet-Read-TabBarRead.js +0 -64
@@ -1,11 +1,5 @@
1
1
  const libPictRecordSetRecordView = require('../RecordSet-RecordBaseView.js');
2
2
 
3
-
4
- const viewHeaderRead = require('./RecordSet-Read-HeaderRead.js');
5
- const viewRecordRead = require('./RecordSet-Read-RecordRead.js');
6
- const viewRecordReadExtra = require('./RecordSet-Read-RecordReadExtra.js');
7
- const viewTabBarRead = require('./RecordSet-Read-TabBarRead.js');
8
-
9
3
  /** @type {Record<string, any>} */
10
4
  const _DEFAULT_CONFIGURATION__Read = (
11
5
  {
@@ -34,16 +28,195 @@ const _DEFAULT_CONFIGURATION__Read = (
34
28
  Templates:
35
29
  [
36
30
  {
37
- Hash: 'PRSP-Read-Template',
31
+ Hash: 'PRSP-Read-RecordRead-Template',
32
+ Template: /*html*/`
33
+ <!-- DefaultPackage pict view template: [PRSP-Read-RecordRead-Template] -->
34
+ <div>
35
+ {~TSWP:PRSP-Read-RecordRead-Template-Row:Record.DisplayFields:Record.Record~}
36
+ </div>
37
+ {~T:PRSP-Read-RecordButtonBar-Template~}
38
+ <!-- DefaultPackage end view template: [PRSP-Read-RecordRead-Template] -->
39
+ `
40
+ },
41
+ {
42
+ Hash: 'PRSP-Read-RecordRead-Template-Row',
43
+ Template: /*html*/`
44
+ <!-- DefaultPackage pict view template: [PRSP-Read-RecordRead-Template] -->
45
+ <div>
46
+ <span style="font-style: italic;">{~D:Record.Data.DisplayName~}</span>
47
+ <span style="font-weight: bold;">{~DVBK:Record.Payload:Record.Data.Key~}</span>
48
+ </div>
49
+ <!-- DefaultPackage end view template: [PRSP-Read-RecordRead-Template] -->
50
+ `
51
+ },
52
+ {
53
+ Hash: 'PRSP-Read-RecordButtonBar-Template',
38
54
  Template: /*html*/`
39
- <!-- DefaultPackage pict view template: [PRSP-Read-Template] -->
40
- <h1>{~D:Record.RecordSet~} {~D:Record.GUIDAddress~} [{~D:Record.RecordConfiguration.GUIDRecord~}]</h1>
41
- <!--
42
- {~DJ:Record~}
43
- -->
44
- {~T:PRSP-Read-RecordRead-Template~}
45
- <!-- DefaultPackage end view template: [PRSP-Read-Template] -->
46
- `
55
+ <style>
56
+ .record-button-bar-hidden
57
+ {
58
+ display: none;
59
+ }
60
+ .record-button-bar
61
+ {
62
+ display: flex;
63
+ margin-top: 15px;
64
+ }
65
+ .record-button-bar > button
66
+ {
67
+ margin-right: 10px;
68
+ }
69
+ </style>
70
+ <div class="record-button-bar">
71
+ <button id="PRSP-Read-CancelButton" type="button" onclick="_Pict.views['RSP-RecordSet-Read'].cancel()">Cancel</button>
72
+ <button id="PRSP-Read-SaveButton" type="button" onclick="_Pict.views['RSP-RecordSet-Read'].save()">Save</button>
73
+ <button id="PRSP-Read-EditButton" type="button" onclick="_Pict.views['RSP-RecordSet-Read'].edit()">Edit</button>
74
+ </div>
75
+ `
76
+ },
77
+ {
78
+ Hash: 'PRSP-Read-RecordTab-Template',
79
+ Template: /*html*/`<!-- Placeholder for tabs, something has gone wrong if this comment is rendered. -->`
80
+ },
81
+ {
82
+ Hash: 'PRSP-Read-RecordTabNav-Template',
83
+ Template: /*html*/`<!-- Placeholder for tabs, something has gone wrong if this comment is rendered. -->`
84
+ },
85
+ {
86
+ Hash: 'PRSP-Read-Basic-Template',
87
+ Template: /*html*/`
88
+ <!-- DefaultPackage pict view template: [PRSP-Read-Basic-Template] -->
89
+ <h1>{~D:Record.RecordSet~} {~D:Record.GUIDAddress~} [{~D:Record.RecordConfiguration.GUIDRecord~}]</h1>
90
+ <!--
91
+ {~DJ:Record~}
92
+ -->
93
+ <div>
94
+ {~T:PRSP-Read-RecordRead-Template~}
95
+ </div>
96
+ <!-- DefaultPackage end view template: [PRSP-Read-Basic-Template] -->
97
+ `
98
+ },
99
+ {
100
+ Hash: 'PRSP-Read-Split-Template',
101
+ Template: /*html*/`
102
+ <!-- DefaultPackage pict view template: [PRSP-Read-Split-Template] -->
103
+ <h1>{~D:Record.RecordSet~} {~D:Record.GUIDAddress~} [{~D:Record.RecordConfiguration.GUIDRecord~}]</h1>
104
+ <!--
105
+ {~DJ:Record~}
106
+ -->
107
+ <style>
108
+ .psrs-split-view
109
+ {
110
+ display: flex;
111
+ height: 100%;
112
+ }
113
+ .psrs-left-panel
114
+ {
115
+ overflow: scroll;
116
+ }
117
+ .psrs-right-panel
118
+ {
119
+ overflow: scroll;
120
+ }
121
+ #psrs-resize
122
+ {
123
+ width: 1px;
124
+ padding-left: 10px;
125
+ padding-right: 10px;
126
+ cursor: col-resize;
127
+ }
128
+ #psrs-resize > div
129
+ {
130
+ width: 1px;
131
+ height: 100%;
132
+ background-color: rgba(0,0,0,0.5);
133
+ }
134
+ #PRSP-Read-Tab-Nav
135
+ {
136
+ display: flex;
137
+ border-bottom: 1px solid rgba(0,0,0,0.5);
138
+ margin-bottom: 20px;
139
+ width: 100%;
140
+ }
141
+ .psrs-tab.is-active
142
+ {
143
+ border: 1px solid rgba(0,0,0,0.5);
144
+ }
145
+ .psrs-tab
146
+ {
147
+ border-right: 1px solid rgba(0,0,0,0.5);
148
+ border-left: 1px solid rgba(0,0,0,0.5);
149
+ padding: 10px;
150
+ }
151
+ .psrs-tab-body
152
+ {
153
+ display: none;
154
+ }
155
+ .psrs-tab-body.is-active
156
+ {
157
+ display: inherit;
158
+ }
159
+ </style>
160
+ <div class="psrs-split-view">
161
+ <div class="psrs-left-panel" style="min-width: 50%;">
162
+ {~T:PRSP-Read-RecordRead-Template~}
163
+ </div>
164
+ <div id="psrs-resize">
165
+ <div></div>
166
+ </div>
167
+ <div class="psrs-right-panel" style="width: 100%;">
168
+ <div id="PRSP-Read-Tabs-Container">
169
+ {~T:PRSP-Read-RecordTabNav-Template~}
170
+ {~T:PRSP-Read-RecordTab-Template~}
171
+ </div>
172
+ </div>
173
+ </div>
174
+ <!-- DefaultPackage end view template: [PRSP-Read-Split-Template] -->
175
+ `
176
+ },
177
+ {
178
+ Hash: 'PRSP-Read-Tab-Template',
179
+ Template: /*html*/`
180
+ <!-- DefaultPackage pict view template: [PRSP-Read-Tab-Template] -->
181
+ <h1>{~D:Record.RecordSet~} {~D:Record.GUIDAddress~} [{~D:Record.RecordConfiguration.GUIDRecord~}]</h1>
182
+ <!--
183
+ {~DJ:Record~}
184
+ -->
185
+ <style>
186
+ #PRSP-Read-Tab-Nav
187
+ {
188
+ display: flex;
189
+ border-bottom: 1px solid rgba(0,0,0,0.5);
190
+ margin-bottom: 20px;
191
+ width: 100%;
192
+ }
193
+ .psrs-tab.is-active
194
+ {
195
+ border: 1px solid rgba(0,0,0,0.5);
196
+ }
197
+ .psrs-tab
198
+ {
199
+ padding: 10px;
200
+ border-right: 1px solid rgba(0,0,0,0.5);
201
+ border-left: 1px solid rgba(0,0,0,0.5);
202
+ }
203
+ .psrs-tab-body
204
+ {
205
+ display: none;
206
+ }
207
+ .psrs-tab-body.is-active
208
+ {
209
+ display: inherit;
210
+ }
211
+ </style>
212
+ <div class="psrs-tab-view">
213
+ <div id="PRSP-Read-Tabs-Container">
214
+ {~T:PRSP-Read-RecordTabNav-Template~}
215
+ {~T:PRSP-Read-RecordTab-Template~}
216
+ </div>
217
+ </div>
218
+ <!-- DefaultPackage end view template: [PRSP-Read-Tab-Template] -->
219
+ `
47
220
  },
48
221
  {
49
222
  Hash: 'PRSP-Read-Link-Name-Template',
@@ -52,15 +225,27 @@ const _DEFAULT_CONFIGURATION__Read = (
52
225
  {
53
226
  Hash: 'PRSP-Read-Link-URL-Template',
54
227
  // TODO: Double payload pattern...
55
- Template: `#/PSRS/{~D:Record.Payload.Payload.RecordSet~}/Read/{~DVBK:Record.Payload.Data:Record.Payload.Payload.GUIDAddress~}`
228
+ Template: `#/PSRS/{~D:Record.Payload.Payload.RecordSet~}/View/{~DVBK:Record.Payload.Data:Record.Payload.Payload.GUIDAddress~}`
56
229
  },
57
230
  ],
58
231
 
59
232
  Renderables:
60
233
  [
61
234
  {
62
- RenderableHash: 'PRSP_Renderable_Read',
63
- TemplateHash: 'PRSP-Read-Template',
235
+ RenderableHash: 'PRSP_Renderable_Read_Basic',
236
+ TemplateHash: 'PRSP-Read-Basic-Template',
237
+ DestinationAddress: '#PRSP_Container',
238
+ RenderMethod: 'replace'
239
+ },
240
+ {
241
+ RenderableHash: 'PRSP_Renderable_Read_Split',
242
+ TemplateHash: 'PRSP-Read-Split-Template',
243
+ DestinationAddress: '#PRSP_Container',
244
+ RenderMethod: 'replace'
245
+ },
246
+ {
247
+ RenderableHash: 'PRSP_Renderable_Read_Tab',
248
+ TemplateHash: 'PRSP-Read-Tab-Template',
64
249
  DestinationAddress: '#PRSP_Container',
65
250
  RenderMethod: 'replace'
66
251
  }
@@ -76,14 +261,66 @@ class viewRecordSetRead extends libPictRecordSetRecordView
76
261
  let tmpOptions = Object.assign({}, _DEFAULT_CONFIGURATION__Read, pOptions);
77
262
  super(pFable, tmpOptions, pServiceHash);
78
263
 
79
- this.childViews = {
80
- viewHeaderRead: null,
81
- viewTabBarRead: null,
82
- viewRecordRead: null,
83
- viewRecordReadExtra: null
84
- };
264
+ this.layoutType = 'Basic';
265
+ this.action = 'View';
266
+ this.tabs = [];
267
+ this.RecordSet = null;
268
+ this.providerHash = null;
269
+ this.activeTab = null;
270
+ this.mouseHandler = null;
271
+ this.manifest = null;
272
+ this.defaultManifest = null;
273
+ this.GUID = null;
274
+ }
275
+
276
+ initializeDragListener()
277
+ {
278
+ let isResizing = false;
279
+ const handleMouseMove = (event) =>
280
+ {
281
+ const resizer = document.getElementById("psrs-resize");
282
+ const leftPanel = document.querySelector(".psrs-left-panel");
283
+ const rightPanel = document.querySelector(".psrs-right-panel");
284
+ if (!isResizing || !(resizer.parentNode instanceof HTMLElement && leftPanel instanceof HTMLElement && rightPanel instanceof HTMLElement))
285
+ {
286
+ return;
287
+ }
288
+ const containerRect = resizer.parentNode.getBoundingClientRect();
289
+ const newLeftWidth = event.clientX - containerRect.left;
290
+ const newRightWidth = containerRect.width - newLeftWidth - resizer.offsetWidth;
291
+ leftPanel.style.width = `${newLeftWidth}px`;
292
+ leftPanel.style.minWidth = `${newLeftWidth}px`;
293
+ rightPanel.style.width = `${newRightWidth}px`;
294
+ }
295
+ if (!this.mouseHandler)
296
+ {
297
+ this.mouseHandler = (event) =>
298
+ {
299
+ isResizing = true;
300
+ document.body.style.userSelect = "none";
301
+ document.addEventListener("mousemove", handleMouseMove);
302
+ document.addEventListener("mouseup", () =>
303
+ {
304
+ isResizing = false;
305
+ document.body.style.userSelect = "";
306
+ document.removeEventListener("mousemove", handleMouseMove);
307
+ });
308
+ };
309
+ }
310
+ const resizer = document.getElementById("psrs-resize");
311
+ resizer.removeEventListener('mousedown', this.mouseHandler);
312
+ resizer.addEventListener("mousedown", this.mouseHandler);
85
313
  }
86
314
 
315
+ onAfterRender(pRenderable)
316
+ {
317
+ if (this.layoutType == 'Split')
318
+ {
319
+ this.initializeDragListener();
320
+ }
321
+
322
+ return super.onAfterRender(pRenderable);
323
+ }
87
324
 
88
325
  handleRecordSetReadRoute(pRoutePayload)
89
326
  {
@@ -92,7 +329,28 @@ class viewRecordSetRead extends libPictRecordSetRecordView
92
329
  throw new Error(`Pict RecordSet Read view route handler called with invalid route payload.`);
93
330
  }
94
331
 
332
+ this.action = 'View';
95
333
  const tmpProviderConfiguration = this.pict.PictSectionRecordSet.recordSetProviderConfigurations[pRoutePayload.data.RecordSet];
334
+ this.layoutType = tmpProviderConfiguration?.ReadLayout || 'Basic';
335
+ const tmpProviderHash = `RSP-Provider-${pRoutePayload.data.RecordSet}`;
336
+
337
+ tmpProviderConfiguration.RoutePayload = pRoutePayload;
338
+ tmpProviderConfiguration.RecordSet = pRoutePayload.data.RecordSet;
339
+ tmpProviderConfiguration.GUIDRecord = pRoutePayload.data.GUIDRecord;
340
+
341
+ return this.renderRead(tmpProviderConfiguration, tmpProviderHash, pRoutePayload.data.GUIDRecord);
342
+ }
343
+
344
+ handleRecordSetEditRoute(pRoutePayload)
345
+ {
346
+ if (typeof(pRoutePayload) != 'object')
347
+ {
348
+ throw new Error(`Pict RecordSet Read view route handler called with invalid route payload.`);
349
+ }
350
+
351
+ this.action = 'Edit';
352
+ const tmpProviderConfiguration = this.pict.PictSectionRecordSet.recordSetProviderConfigurations[pRoutePayload.data.RecordSet];
353
+ this.layoutType = tmpProviderConfiguration?.ReadLayout || 'Basic';
96
354
  const tmpProviderHash = `RSP-Provider-${pRoutePayload.data.RecordSet}`;
97
355
 
98
356
  tmpProviderConfiguration.RoutePayload = pRoutePayload;
@@ -104,10 +362,31 @@ class viewRecordSetRead extends libPictRecordSetRecordView
104
362
 
105
363
  addRoutes(pPictRouter)
106
364
  {
107
- pPictRouter.addRoute('/PSRS/:RecordSet/Read/:GUIDRecord', this.handleRecordSetReadRoute.bind(this));
365
+ pPictRouter.addRoute('/PSRS/:RecordSet/View/:GUIDRecord', this.handleRecordSetReadRoute.bind(this));
366
+ pPictRouter.addRoute('/PSRS/:RecordSet/Edit/:GUIDRecord', this.handleRecordSetEditRoute.bind(this));
108
367
  return true;
109
368
  }
110
369
 
370
+ async cancel()
371
+ {
372
+ await this.onBeforeView();
373
+ this.fable.providers.RecordSetRouter.pictRouter.navigate(`/PSRS/${ this.RecordSet }/View/${ this.GUID }`);
374
+ }
375
+
376
+ async save()
377
+ {
378
+ await this.onBeforeSave();
379
+ await this.pict.providers[this.providerHash].updateRecord(this.pict.AppData[`${ this.RecordSet }Details`]);
380
+ await this.onBeforeView();
381
+ this.fable.providers.RecordSetRouter.pictRouter.navigate(`/PSRS/${ this.RecordSet }/View/${ this.GUID }`);
382
+ }
383
+
384
+ async edit()
385
+ {
386
+ await this.onBeforeEdit();
387
+ this.fable.providers.RecordSetRouter.pictRouter.navigate(`/PSRS/${ this.RecordSet }/Edit/${ this.GUID }`);
388
+ }
389
+
111
390
  onBeforeRenderRead(pRecordReadData)
112
391
  {
113
392
  this.formatDisplayData(pRecordReadData);
@@ -130,6 +409,26 @@ class viewRecordSetRead extends libPictRecordSetRecordView
130
409
  return pRecordListData;
131
410
  }
132
411
 
412
+ async onBeforeEdit()
413
+ {
414
+ // Hook that is called when transitioning to edit mode.
415
+ }
416
+
417
+ async onBeforeSave()
418
+ {
419
+ // Hook that is called before saving.
420
+ }
421
+
422
+ async onBeforeView()
423
+ {
424
+ // Hook that is called when transitioning to view mode.
425
+ }
426
+
427
+ async onBeforeTabChange()
428
+ {
429
+ // Hook that is called when transitioning between tabs.
430
+ }
431
+
133
432
  async renderRead(pRecordConfiguration, pProviderHash, pRecordGUID)
134
433
  {
135
434
  // Get the records
@@ -140,27 +439,76 @@ class viewRecordSetRead extends libPictRecordSetRecordView
140
439
  }
141
440
 
142
441
  let tmpRecordReadData =
143
- {
144
- "RecordSet": pRecordConfiguration.RecordSet,
442
+ {
443
+ "RecordSet": pRecordConfiguration.RecordSet,
145
444
 
146
- "RecordConfiguration": pRecordConfiguration,
445
+ "RecordConfiguration": pRecordConfiguration,
147
446
 
148
- "RenderDestination": this.options.DefaultDestinationAddress,
447
+ "RenderDestination": this.options.DefaultDestinationAddress,
149
448
 
150
- "Record": false,
151
- };
449
+ "Record": false,
450
+ };
451
+
452
+ this.GUID = pRecordGUID;
453
+
454
+ if (pRecordConfiguration.RecordSet !== this.RecordSet)
455
+ {
456
+ this.RecordSet = pRecordConfiguration.RecordSet;
457
+ this.activeTab = null;
458
+ }
459
+
460
+ this.providerHash = pProviderHash;
152
461
 
153
462
  // If the record configuration does not have a GUID, try to infer one from the RecordSet name
154
463
  // TODO: This should be coming from the schema but that can come after we discuss how we deal with default routing
155
- tmpRecordReadData.GUIDAddress = `GUID${this.pict.providers[pProviderHash].options.Entity}`;
464
+ tmpRecordReadData.GUIDAddress = this.pict.providers[pProviderHash].getGUIDField();
156
465
 
157
466
  tmpRecordReadData.Record = await this.pict.providers[pProviderHash].getRecordByGUID(pRecordGUID);
158
467
  tmpRecordReadData.RecordSchema = await this.pict.providers[pProviderHash].getRecordSchema();
468
+ this.pict.AppData[`${ tmpRecordReadData.RecordSet }Details`] = tmpRecordReadData.Record;
469
+
470
+ if (pRecordConfiguration.RecordSetReadManifestOnly)
471
+ {
472
+ this.pict.TemplateProvider.addTemplate(`PRSP-Read-RecordRead-Template`, /*html*/`
473
+ <!-- Manifest dynamic pict template: [PRSP-Read-RecordRead-Template] -->
474
+ <div>${ this._generateManifestTemplate(pRecordConfiguration, 'RecordRead', null, true) }</div>
475
+ {~T:PRSP-Read-RecordButtonBar-Template~}
476
+ <!-- Manifest dynamic pict end template: [PRSP-Read-RecordRead-Template] -->
477
+ `);
478
+ }
479
+ else
480
+ {
481
+ // Construct a default manifest based on the RecordSchema:
482
+ this.defaultManifest = await this._buildDefaultManifest(tmpRecordReadData.RecordSet);
483
+ this.pict.TemplateProvider.addTemplate(`PRSP-Read-RecordRead-Template`, /*html*/`
484
+ <!-- Manifest dynamic pict template: [PRSP-Read-RecordRead-Template] -->
485
+ <div>${ this._generateManifestTemplate(pRecordConfiguration, 'RecordRead', null, true, this.action, this.defaultManifest) }</div>
486
+ {~T:PRSP-Read-RecordButtonBar-Template~}
487
+ <!-- Manifest dynamic pict end template: [PRSP-Read-RecordRead-Template] -->
488
+ `);
489
+ }
490
+
491
+ if (this.layoutType !== 'Basic')
492
+ {
493
+ this.tabs = await this._structureTabs(pRecordConfiguration, tmpRecordReadData.Record);
494
+ this.pict.TemplateProvider.addTemplate('PRSP-Read-RecordTab-Template', /*html*/`
495
+ <!-- DefaultPackage pict view template: [PRSP-Read-RecordTab-Template] -->
496
+ ${ this.tabs.map((t) => t.Template).join('') }
497
+ <!-- DefaultPackage end view template: [PRSP-Read-RecordTab-Template] -->
498
+ `);
499
+ this.pict.TemplateProvider.addTemplate('PRSP-Read-RecordTabNav-Template', /*html*/`
500
+ <!-- DefaultPackage pict view template: [PRSP-Read-RecordTabNav-Template] -->
501
+ <div id="PRSP-Read-Tab-Nav">
502
+ ${ this.tabs.map((t) => t.TabTemplate).join('') }
503
+ </div>
504
+ <!-- DefaultPackage end view template: [PRSP-Read-RecordTabNav-Template] -->
505
+ `);
506
+ }
159
507
 
160
508
  tmpRecordReadData = this.onBeforeRenderRead(tmpRecordReadData);
161
509
 
162
- this.renderAsync('PRSP_Renderable_Read', tmpRecordReadData.RenderDestination, tmpRecordReadData,
163
- function (pError)
510
+ this.renderAsync(`PRSP_Renderable_Read_${ this.layoutType }`, tmpRecordReadData.RenderDestination, tmpRecordReadData,
511
+ async function (pError)
164
512
  {
165
513
  if (pError)
166
514
  {
@@ -176,22 +524,390 @@ class viewRecordSetRead extends libPictRecordSetRecordView
176
524
  {
177
525
  this.pict.log.info(`RecordSetRead: Rendered read ${tmpRecordReadData.RecordSet} with ${tmpRecordReadData.RecordConfiguration.GUIDRecord} []`);
178
526
  }
527
+ for (const s of this.manifest?.Sections || [])
528
+ {
529
+ this.pict.views[`PictSectionForm-${ s.Hash }`].render();
530
+ this.pict.views[`PictSectionForm-${ s.Hash }`].marshalToView();
531
+ }
532
+ for (const t of this.tabs)
533
+ {
534
+ if (t.renderAsync)
535
+ {
536
+ await t.renderAsync();
537
+ }
538
+ else
539
+ {
540
+ t.render();
541
+ }
542
+ }
543
+ if (this.action == 'Edit')
544
+ {
545
+ document.getElementById('PRSP-Read-EditButton').classList.add('record-button-bar-hidden');
546
+ document.getElementById('PRSP-Read-SaveButton').classList.remove('record-button-bar-hidden');
547
+ document.getElementById('PRSP-Read-CancelButton').classList.remove('record-button-bar-hidden');
548
+ }
549
+ else
550
+ {
551
+ document.getElementById('PRSP-Read-EditButton').classList.remove('record-button-bar-hidden');
552
+ document.getElementById('PRSP-Read-SaveButton').classList.add('record-button-bar-hidden');
553
+ document.getElementById('PRSP-Read-CancelButton').classList.add('record-button-bar-hidden');
554
+ }
555
+ this.setTab(this.activeTab || this.tabs?.[0]?.Hash);
179
556
  return true;
180
557
  }.bind(this));
181
558
  }
182
559
 
183
- onInitialize()
560
+ async setTab(t)
184
561
  {
185
- this.childViews.headerRead = this.pict.addView('PRSP-Read-HeaderRead', viewHeaderRead.default_configuration, viewHeaderRead);
186
- this.childViews.recordRead = this.pict.addView('PRSP-Read-RecordRead', viewRecordRead.default_configuration, viewRecordRead);
187
- this.childViews.recordReadExtra = this.pict.addView('PRSP-Read-RecordReadExtra', viewRecordReadExtra.default_configuration, viewRecordReadExtra);
188
- this.childViews.tabBarRead = this.pict.addView('PRSP-Read-TabBarRead', viewTabBarRead.default_configuration, viewTabBarRead);
562
+ if (this.activeTab !== t)
563
+ {
564
+ await this.onBeforeTabChange();
565
+ }
566
+ this.activeTab = t;
567
+ const tabSet = document.querySelectorAll('.psrs-tab');
568
+ const tabBodySet = document.querySelectorAll('.psrs-tab-body');
569
+ for (const tb of tabSet)
570
+ {
571
+ tb.classList.remove('is-active');
572
+ if (tb.id == `PSRS-TabNav-${ t }`)
573
+ {
574
+ tb.classList.add('is-active');
575
+ }
576
+ }
577
+ for (const tb of tabBodySet)
578
+ {
579
+ tb.classList.remove('is-active');
580
+ if (tb.id == `PSRS-Tab-${ t }`)
581
+ {
582
+ tb.classList.add('is-active');
583
+ }
584
+ }
585
+ }
586
+
587
+ async _buildDefaultManifest(recordSet)
588
+ {
589
+ // Construct a default manifest based on the RecordSchema:
590
+ const defaultManifest =
591
+ {
592
+ "Form": "DefaultManifest",
593
+ "Scope": "Default",
594
+ "Descriptors": {},
595
+ "Sections":
596
+ [
597
+ {
598
+ "Name": "",
599
+ "Hash": "DefaultSection",
600
+ "Solvers": [],
601
+ "ShowTitle": false,
602
+ "Groups": [
603
+ {
604
+ "Name": "",
605
+ "Hash": "DefaultGroup",
606
+ "Rows": [],
607
+ "RecordSetSolvers": [],
608
+ "ShowTitle": false
609
+ }
610
+ ]
611
+ }
612
+ ]
613
+ }
614
+ let rowCounter = 1;
615
+ const providerHash = `RSP-Provider-${ recordSet }`;
616
+ const schema = await this.pict.providers[providerHash].getRecordSchema();
617
+ for (const p of Object.keys(schema.properties))
618
+ {
619
+ const exclusionSet = [this.pict.providers[this.providerHash].getIDField(), this.pict.providers[this.providerHash].getGUIDField(), 'CreatingIDUser', 'UpdatingIDUser', 'DeletingIDUser', 'Deleted', 'CreateDate', 'UpdateDate', 'DeleteDate', 'Deleted'];
620
+ if (exclusionSet.includes(p))
621
+ {
622
+ continue;
623
+ }
624
+ const tmpDescriptor =
625
+ {
626
+ "Name": `${ this.pict.providers[providerHash].getHumanReadableFieldName?.() || p }`,
627
+ "Hash": `${ this.pict.providers[this.providerHash].options.Entity }-${ p }`,
628
+ "DataType": "String",
629
+ "PictForm":
630
+ {
631
+ "Row": `${ rowCounter }`,
632
+ "Section": "DefaultSection",
633
+ "Group": "DefaultGroup"
634
+ }
635
+ };
636
+ rowCounter += 1;
637
+ switch (schema.properties[p].type)
638
+ {
639
+ case 'string':
640
+ case 'autoguid':
641
+ tmpDescriptor.DataType = 'String';
642
+ break;
643
+ case 'datetime':
644
+ case 'date':
645
+ case 'createdate':
646
+ case 'updatedate':
647
+ tmpDescriptor.DataType = 'String';
648
+ tmpDescriptor.PictForm.InputType = 'DateTime'
649
+ break;
650
+ case 'boolean':
651
+ case 'deleted':
652
+ tmpDescriptor.DataType = 'Boolean';
653
+ case 'integer':
654
+ case 'decimal':
655
+ case 'autoidentity':
656
+ case 'createiduser':
657
+ case 'updateiduser':
658
+ case 'deleteiduser':
659
+ tmpDescriptor.DataType = 'Number';
660
+ break;
661
+ default:
662
+ tmpDescriptor.DataType = 'String';
663
+ }
664
+
665
+ defaultManifest.Descriptors[`${ recordSet }Details.${ p }`] = tmpDescriptor;
666
+ }
667
+ return defaultManifest;
668
+ }
189
669
 
190
- // // Initialize the subviews
191
- this.childViews.headerRead.initialize();
192
- this.childViews.recordRead.initialize();
193
- this.childViews.recordReadExtra.initialize();
194
- this.childViews.tabBarRead.initialize();
670
+ _generateManifestTemplate(config, section, specificManifest, setBaseManifest, action = this.action, defaultManifest)
671
+ {
672
+ // Look for a manifest by the action (if there is no specific manifest passed) and fallback to view if the action manifest isn't present.
673
+ const tmpManifestHash = specificManifest || config[`RecordSetReadDefaultManifest${ action }`] || config[`RecordSetReadManifests${ action }`]?.[0] || config[`RecordSetReadDefaultManifestView`] || config[`RecordSetReadManifestsView`]?.[0];
674
+ // Make sure the copy of the manifest doesn't mutate the original (for read only handling).
675
+ const tmpManifest = JSON.parse(JSON.stringify(defaultManifest ? defaultManifest : this.pict.PictSectionRecordSet.getManifest(tmpManifestHash)));
676
+ if (!tmpManifest)
677
+ {
678
+ this.pict.log.error(`RecordSetRead: No manifest found for ${ config.RecordSet }. Read Render failed.`);
679
+ return '';
680
+ }
681
+ if (!tmpManifest.Descriptors)
682
+ {
683
+ this.pict.log.error(`RecordSetRead: No manifest descriptors found for manifest ${ tmpManifestHash }. Read Render failed.`);
684
+ return '';
685
+ }
686
+ else if (action == 'View' && !config.RecordSetReadOverrideReadOnly)
687
+ {
688
+ // If we are in view mode, apply the read only type unless overriden.
689
+ for (const x of Object.keys(tmpManifest.Descriptors))
690
+ {
691
+ if (!tmpManifest.Descriptors[x].PictForm)
692
+ {
693
+ tmpManifest.Descriptors[x].PictForm = {};
694
+ }
695
+ tmpManifest.Descriptors[x].PictForm.InputType = 'ReadOnly';
696
+ }
697
+ }
698
+ if (setBaseManifest)
699
+ {
700
+ this.manifest = tmpManifest;
701
+ }
702
+ let sectionsTemplate = '';
703
+ // Clear any stale views in PictSectionForm before reinstantiating (if we don't the read only state doesn't get cleared).
704
+ for (const s of tmpManifest?.Sections || [])
705
+ {
706
+ delete this.pict.views[`PictSectionForm-${ s.Hash }`]
707
+ }
708
+ this.pict.views.PictFormMetacontroller.bootstrapPictFormViewsFromManifest(tmpManifest);
709
+
710
+ for (const pickList of tmpManifest?.PickLists || [])
711
+ {
712
+ this.pict.providers.DynamicMetaLists.rebuildListByHash(pickList.Hash);
713
+ }
714
+
715
+ for (const s of tmpManifest?.Sections || [])
716
+ {
717
+ const viewSectionID = `PSRS-Read-${ section }-Section-${ s.Hash }`;
718
+ sectionsTemplate += /*html*/`<div id="${ viewSectionID }"></div>`;
719
+ this.pict.views[`PictSectionForm-${ s.Hash }`].viewMarshalDestination = 'AppData';
720
+ this.pict.views[`PictSectionForm-${ s.Hash }`].options.DefaultDestinationAddress = `#${ viewSectionID }`;
721
+ this.pict.views[`PictSectionForm-${ s.Hash }`].rebuildCustomTemplate();
722
+ }
723
+ return sectionsTemplate
724
+ }
725
+
726
+ async _structureTabs(config, record)
727
+ {
728
+ const validTabs = config.ReadLayout == 'Tab' ?
729
+ [
730
+ {
731
+ Type: 'Record',
732
+ RecordSet: config.RecordSet,
733
+ Title: config.RecordSetReadTabTitle || 'Record',
734
+ Hash: 'Record',
735
+ Template: /*html*/`
736
+ <div id="PSRS-Tab-Record" class="psrs-tab-body">{~T:PRSP-Read-RecordRead-Template~}</div>
737
+ `,
738
+ TabTemplate: /*html*/`
739
+ <div class="psrs-tab" id="PSRS-TabNav-Record" onclick="_Pict.views['RSP-RecordSet-Read'].setTab('Record')">${ config.RecordSetReadTabTitle || 'Record' }</div>
740
+ `,
741
+ render: () => {}
742
+ }
743
+ ] : [];
744
+
745
+ for (const t of config.RecordSetReadTabs)
746
+ {
747
+ if (!t.Title)
748
+ {
749
+ this.pict.log.info('Skipping tab that is missing title.');
750
+ continue;
751
+ }
752
+ if (!t.Hash)
753
+ {
754
+ t.Hash = t.Title.replaceAll(' ', '');
755
+ }
756
+ if (t.Type == 'Manifest' || t.Type == 'AttachedRecord')
757
+ {
758
+ let recordSetConfig = null;
759
+ let tmpManifest = null;
760
+ if (t.Type == 'AttachedRecord')
761
+ {
762
+ const getMethod = async (remote, id) => {
763
+ if (this.pict.providers[`RSP-Provider-${ remote }`])
764
+ {
765
+ return await this.pict.providers[`RSP-Provider-${ remote }`].getRecord(id);
766
+ }
767
+ else
768
+ {
769
+ return await new Promise((resolve, reject) =>
770
+ {
771
+ this.pict.EntityProvider.getEntity(remote, id, (pError, pResult) =>
772
+ {
773
+ if (pError)
774
+ {
775
+ resolve(null);
776
+ }
777
+ resolve(pResult);
778
+ });
779
+ });
780
+ }
781
+ };
782
+
783
+ const getJoin = async (remote, field, value) => {
784
+ let result = null;
785
+ if (this.pict.providers[`RSP-Provider-${ remote }`])
786
+ {
787
+ result = await this.pict.providers[`RSP-Provider-${ remote }`].getRecordsInline(`FBV~${ field }~EQ~${ value }`);
788
+ }
789
+ else
790
+ {
791
+ result = await new Promise((resolve, reject) =>
792
+ {
793
+ this.pict.EntityProvider.getEntitySet(remote, `FBV~${ field }~EQ~${ value }`, (pError, pResult) =>
794
+ {
795
+ if (pError)
796
+ {
797
+ resolve(null);
798
+ }
799
+ resolve(pResult);
800
+ });
801
+ });
802
+ }
803
+ return result?.[0] || null;
804
+ };
805
+
806
+ if (!t.RecordSet)
807
+ {
808
+ this.pict.log.info(`Skipping attached record tab because no recordset was included.`);
809
+ }
810
+ recordSetConfig = this.pict.PictSectionRecordSet.recordSetProviderConfigurations[t.RecordSet];
811
+ if (!recordSetConfig)
812
+ {
813
+ this.pict.log.info(`Skipping attached record tab because recordset ${ t.RecordSet } is not registered.`);
814
+ continue;
815
+ }
816
+ t.Manifest = recordSetConfig.RecordSetReadDefaultManifestView || recordSetConfig.RecordSetReadManifestsView?.[0];
817
+ tmpManifest = recordSetConfig.RecordSetReadManifestOnly ? this.pict.PictSectionRecordSet.getManifest(t.Manifest) : await this._buildDefaultManifest(recordSetConfig.RecordSetMeadowEntity || recordSetConfig.RecordSet);
818
+ if (!tmpManifest)
819
+ {
820
+ this.pict.log.info(`Skipping manifest tab because manifest ${ t.Manifest } is not registered or default manifest could not be constructed.`);
821
+ continue;
822
+ }
823
+ if (!t.JoinField)
824
+ {
825
+ t.JoinField = this.pict.providers[`RSP-Provider-${ t.RecordSet }`].getIDField();
826
+ }
827
+ if (t.JoiningRecordSet)
828
+ {
829
+ if (!t.BaseField)
830
+ {
831
+ t.BaseField = this.pict.providers[`RSP-Provider-${ config.RecordSet }`].getIDField();
832
+ }
833
+ if (!record[t.BaseField])
834
+ {
835
+ this.pict.log.info(`Skipping attached record tab because field ${ t.BaseField } does not exist on this record.`);
836
+ continue;
837
+ }
838
+ const tempJoin = await getJoin(t.JoiningRecordSet, t.BaseField, record[t.BaseField]);
839
+ if (!tempJoin?.[t.JoinField])
840
+ {
841
+ this.pict.log.info(`Skipping attached record tab because joining field ${ t.JoinField } does not exist on this record.`);
842
+ continue;
843
+ }
844
+ const tempRecord = await getMethod(recordSetConfig.RecordSetMeadowEntity || recordSetConfig.RecordSet, tempJoin[t.JoinField]);
845
+ this.pict.AppData[`${ t.RecordSet }Details`] = tempRecord;
846
+ }
847
+ else
848
+ {
849
+ if (!record[t.Joinfield])
850
+ {
851
+ this.pict.log.info(`Skipping attached record tab because joining field ${ t.JoinField } does not exist on this record.`);
852
+ continue;
853
+ }
854
+ const tempRecord = await getMethod(recordSetConfig.RecordSetMeadowEntity || recordSetConfig.RecordSet, record[t.Joinfield]);
855
+ this.pict.AppData[`${ t.RecordSet }Details`] = tempRecord;
856
+ }
857
+ }
858
+ if (!tmpManifest)
859
+ {
860
+ tmpManifest = this.pict.PictSectionRecordSet.getManifest(t.Manifest);
861
+ }
862
+ t.Template = /*html*/`
863
+ <div id="PSRS-Tab-${ t.Hash }" class="psrs-tab-body">${ this._generateManifestTemplate(config, 'RecordTab', t.Manifest, false, 'View', recordSetConfig && !recordSetConfig.RecordSetReadManifestOnly ? tmpManifest : null) }</div>
864
+ `;
865
+ t.TabTemplate = /*html*/`
866
+ <div class="psrs-tab" id="PSRS-TabNav-${ t.Hash }" onclick="_Pict.views['RSP-RecordSet-Read'].setTab('${ t.Hash }')">${ t.Title }</div>
867
+ `;
868
+ t.render = () =>
869
+ {
870
+ // @ts-ignore
871
+ for (const s of tmpManifest?.Sections || [])
872
+ {
873
+ this.pict.views[`PictSectionForm-${ s.Hash }`].render();
874
+ this.pict.views[`PictSectionForm-${ s.Hash }`].marshalToView();
875
+ }
876
+ };
877
+ validTabs.push(t);
878
+ }
879
+ else if (t.Type == 'View')
880
+ {
881
+ if (!t.View)
882
+ {
883
+ this.pict.log.info(`Skipping view tab because no view was included.`);
884
+ continue;
885
+ }
886
+ const tmpView = this.pict.views[t.View];
887
+ if (!tmpView)
888
+ {
889
+ this.pict.log.info(`Skipping view tab because view ${ t.View } is not registered.`);
890
+ continue;
891
+ }
892
+ t.Template = /*html*/`
893
+ <div id="PSRS-Tab-${ t.Hash }" class="psrs-tab-body"></div>
894
+ `;
895
+ t.TabTemplate = /*html*/`
896
+ <div class="psrs-tab" id="PSRS-TabNav-${ t.Hash }" onclick="_Pict.views['RSP-RecordSet-Read'].setTab('${ t.Hash }')">${ t.Title }</div>
897
+ `;
898
+ tmpView.options.DefaultDestinationAddress = `#PSRS-Tab-${ t.Hash }`;
899
+ t.render = () =>
900
+ {
901
+ this.pict.views[`${ t.View }`].renderAsync();
902
+ };
903
+ validTabs.push(t);
904
+ }
905
+ }
906
+ return validTabs;
907
+ }
908
+
909
+ onInitialize()
910
+ {
195
911
 
196
912
  // Add the route templates for the read view
197
913
  this.pict.PictSectionRecordSet.addRecordLinkTemplate('PRSP-Read-Link-Name-Template', 'PRSP-Read-Link-URL-Template', true);