pict-section-recordset 1.0.15 → 1.0.16

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 (45) hide show
  1. package/example_applications/simple_entity/Simple-RecordSet-Application.js +113 -0
  2. package/package.json +3 -3
  3. package/source/providers/RecordSet-RecordProvider-Base.js +28 -0
  4. package/source/providers/RecordSet-RecordProvider-MeadowEndpoints.js +53 -3
  5. package/source/services/RecordsSet-MetaController.js +84 -1
  6. package/source/templates/Pict-Template-FilterView.js +172 -0
  7. package/source/views/RecordSet-Filter.js +46 -9
  8. package/source/views/dashboard/RecordSet-Dashboard-HeaderDashboard.js +17 -17
  9. package/source/views/dashboard/RecordSet-Dashboard-PaginationBottom.js +68 -0
  10. package/source/views/dashboard/RecordSet-Dashboard-PaginationTop.js +128 -0
  11. package/source/views/dashboard/RecordSet-Dashboard-RecordList.js +80 -0
  12. package/source/views/dashboard/RecordSet-Dashboard-RecordListEntry.js +121 -0
  13. package/source/views/dashboard/RecordSet-Dashboard-RecordListHeader.js +99 -0
  14. package/source/views/dashboard/RecordSet-Dashboard-Title.js +67 -0
  15. package/source/views/dashboard/RecordSet-Dashboard.js +354 -37
  16. package/source/views/list/RecordSet-List.js +11 -34
  17. package/types/providers/RecordSet-RecordProvider-Base.d.ts +19 -0
  18. package/types/providers/RecordSet-RecordProvider-Base.d.ts.map +1 -1
  19. package/types/providers/RecordSet-RecordProvider-MeadowEndpoints.d.ts +38 -5
  20. package/types/providers/RecordSet-RecordProvider-MeadowEndpoints.d.ts.map +1 -1
  21. package/types/services/RecordsSet-MetaController.d.ts +18 -1
  22. package/types/services/RecordsSet-MetaController.d.ts.map +1 -1
  23. package/types/templates/Pict-Template-FilterView.d.ts +18 -0
  24. package/types/templates/Pict-Template-FilterView.d.ts.map +1 -0
  25. package/types/views/RecordSet-Filter.d.ts +28 -2
  26. package/types/views/RecordSet-Filter.d.ts.map +1 -1
  27. package/types/views/dashboard/RecordSet-Dashboard-HeaderDashboard.d.ts +5 -5
  28. package/types/views/dashboard/RecordSet-Dashboard-HeaderDashboard.d.ts.map +1 -1
  29. package/types/views/dashboard/RecordSet-Dashboard-PaginationBottom.d.ts +11 -0
  30. package/types/views/dashboard/RecordSet-Dashboard-PaginationBottom.d.ts.map +1 -0
  31. package/types/views/dashboard/RecordSet-Dashboard-PaginationTop.d.ts +11 -0
  32. package/types/views/dashboard/RecordSet-Dashboard-PaginationTop.d.ts.map +1 -0
  33. package/types/views/dashboard/RecordSet-Dashboard-RecordList.d.ts +11 -0
  34. package/types/views/dashboard/RecordSet-Dashboard-RecordList.d.ts.map +1 -0
  35. package/types/views/dashboard/RecordSet-Dashboard-RecordListEntry.d.ts +11 -0
  36. package/types/views/dashboard/RecordSet-Dashboard-RecordListEntry.d.ts.map +1 -0
  37. package/types/views/dashboard/RecordSet-Dashboard-RecordListHeader.d.ts +11 -0
  38. package/types/views/dashboard/RecordSet-Dashboard-RecordListHeader.d.ts.map +1 -0
  39. package/types/views/dashboard/RecordSet-Dashboard-Title.d.ts +11 -0
  40. package/types/views/dashboard/RecordSet-Dashboard-Title.d.ts.map +1 -0
  41. package/types/views/dashboard/RecordSet-Dashboard.d.ts +18 -0
  42. package/types/views/dashboard/RecordSet-Dashboard.d.ts.map +1 -1
  43. package/types/views/list/RecordSet-List.d.ts +0 -1
  44. package/types/views/list/RecordSet-List.d.ts.map +1 -1
  45. package/source/views/dashboard/RecordSet-Dashboard-RecordSetDashboard.js +0 -64
@@ -12,6 +12,71 @@ module.exports.default_configuration.pict_configuration = (
12
12
  "AutoRenderMainViewportViewAfterInitialize": false
13
13
  },
14
14
 
15
+ "DefaultDashboards":
16
+ [
17
+ {
18
+ "Scope": "Bookstore",
19
+ "CoreEntity": "Book",
20
+ "RecordDecorationConfiguration":
21
+ [
22
+ {
23
+ "Entity": "BookAuthorJoin",
24
+ "Filter": "FBL~IDBook~INN~{~PJU:,^IDBook^Record.State.CoreEntityRecordSubset~}",
25
+ "Destination": "State.BookAuthorJoins"
26
+ },
27
+ {
28
+ "Entity": "Author",
29
+ "Filter": "FBL~IDAuthor~INN~{~PJU:,^IDAuthor^Record.State.BookAuthorJoins~}",
30
+ "Destination": "State.Authors"
31
+ },
32
+ {
33
+ "Type": "MapJoin",
34
+ "DestinationRecordSetAddress": "State.CoreEntityRecordSubset",
35
+ "DestinationJoinValue": "IDBook",
36
+ "JoinJoinValueLHS": "IDBook",
37
+ "Joins": "State.BookAuthorJoins",
38
+ "JoinJoinValueRHS": "IDAuthor",
39
+ "JoinRecordSetAddress": "State.Authors",
40
+ "JoinValue": "IDAuthor",
41
+ "RecordDestinationAddress": "Authors"
42
+ }
43
+ ],
44
+ "Descriptors":
45
+ {
46
+ "Title":
47
+ {
48
+ "Name": "Title",
49
+ "Hash": "Title",
50
+ "DataType": "String"
51
+ },
52
+ "Authors":
53
+ {
54
+ "Name": "Authors",
55
+ "Hash": "Authors",
56
+ "PictForm":
57
+ {
58
+ "InputType": "ReadOnly",
59
+ }
60
+ },
61
+ "AuthorCount":
62
+ {
63
+ "Name": "Number of Authors",
64
+ "Hash": "AuthorCount",
65
+ "DataType": "Number",
66
+ "PictForm":
67
+ {
68
+ "InputType": "ReadOnly"
69
+ }
70
+ }
71
+ }
72
+ },
73
+ {
74
+ "Scope": "AuthorSummary",
75
+ "Descriptors":
76
+ {
77
+ }
78
+ }
79
+ ],
15
80
  "DefaultRecordSetConfigurations":
16
81
  [
17
82
  {
@@ -38,8 +103,56 @@ module.exports.default_configuration.pict_configuration = (
38
103
  "RecordSetListHasExtraColumns": true,
39
104
  "RecordSetListExtraColumnsHeaderTemplate": "<th style=\"border-bottom: 1px solid #ccc; padding: 5px; background-color: #f2f2f2; color: #333;\">Cover</th>",
40
105
  "RecordSetListExtraColumnRowTemplate": "<td><img src=\"{~D:Record.Data.ImageURL~}\"></td>",
106
+
107
+ "SearchFields": [ "Title" ],
108
+
109
+ "RecordSetFilterURLTemplate-Default": "/PSRS/{~D:Record.RecordSet~}/ListFilteredTo/{~D:Record.FilterString~}",
110
+ "RecordSetFilterURLTemplate-List": "/PSRS/{~D:Record.RecordSet~}/ListFilteredTo/{~D:Record.FilterString~}",
111
+ "RecordSetFilterURLTemplate-Dashboard": "/PSRS/{~D:Record.RecordSet~}/DashboardFilteredTo/{~D:Record.FilterString~}",
112
+
113
+ "RecordSetURLPrefix": "/1.0/"
114
+ },
115
+ {
116
+ "RecordSet": "BookstoreInventory",
117
+
118
+ "RecordSetType": "MeadowEndpoint", // Could be "Custom" which would require a provider to already be created for the record set.
119
+ "RecordSetMeadowEntity": "Book", // This leverages the /Schema endpoint to get the record set columns.
120
+ "RecordDecorationConfiguration":
121
+ [
122
+ {
123
+ "Entity": "BookAuthorJoin",
124
+ "Filter": "FBL~IDBook~INN~{~PJU:,^IDBook^Record.State.CoreEntityRecordSubset~}",
125
+ "Destination": "State.BookAuthorJoins"
126
+ },
127
+ {
128
+ "Entity": "Author",
129
+ "Filter": "FBL~IDAuthor~INN~{~PJU:,^IDAuthor^Record.State.BookAuthorJoins~}",
130
+ "Destination": "State.Authors"
131
+ },
132
+ {
133
+ "Type": "MapJoin",
134
+ "DestinationRecordSetAddress": "State.CoreEntityRecordSubset",
135
+ "DestinationJoinValue": "IDBook",
136
+ "JoinJoinValueLHS": "IDBook",
137
+ "Joins": "State.BookAuthorJoins",
138
+ "JoinJoinValueRHS": "IDAuthor",
139
+ "JoinRecordSetAddress": "State.Authors",
140
+ "JoinValue": "IDAuthor",
141
+ "RecordDestinationAddress": "Authors"
142
+ }
143
+ ],
144
+ "AvailableVerbs": [ "Dashboard" ],
145
+
146
+ "RecordSetListHasExtraColumns": true,
147
+ "RecordSetListExtraColumnsHeaderTemplate": "<th style=\"border-bottom: 1px solid #ccc; padding: 5px; background-color: #f2f2f2; color: #333;\">Cover</th>",
148
+ "RecordSetListExtraColumnRowTemplate": "<td><img src=\"{~D:Record.Data.ImageURL~}\"></td>",
149
+
41
150
  "SearchFields": [ "Title" ],
42
151
 
152
+ "RecordSetFilterURLTemplate-Default": "/PSRS/{~D:Record.RecordSet~}/ListFilteredTo/{~D:Record.FilterString~}",
153
+ "RecordSetFilterURLTemplate-List": "/PSRS/{~D:Record.RecordSet~}/ListFilteredTo/{~D:Record.FilterString~}",
154
+ "RecordSetFilterURLTemplate-Dashboard": "/PSRS/{~D:Record.RecordSet~}/DashboardFilteredTo/{~D:Record.FilterString~}",
155
+
43
156
  "RecordSetURLPrefix": "/1.0/"
44
157
  },
45
158
  {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pict-section-recordset",
3
- "version": "1.0.15",
3
+ "version": "1.0.16",
4
4
  "description": "Pict dynamic record set management views",
5
5
  "main": "source/Pict-Section-RecordSet.js",
6
6
  "directories": {
@@ -33,7 +33,7 @@
33
33
  "browser-env": "^3.3.0",
34
34
  "eslint": "^9.27.0",
35
35
  "jquery": "^3.7.1",
36
- "pict": "^1.0.257",
36
+ "pict": "^1.0.262",
37
37
  "pict-application": "^1.0.25",
38
38
  "pict-service-commandlineutility": "^1.0.15",
39
39
  "quackage": "^1.0.41",
@@ -43,7 +43,7 @@
43
43
  "fable-serviceproviderbase": "^3.0.15",
44
44
  "pict-provider": "^1.0.3",
45
45
  "pict-router": "^1.0.3",
46
- "pict-section-form": "^1.0.95",
46
+ "pict-section-form": "^1.0.97",
47
47
  "pict-template": "^1.0.10",
48
48
  "pict-view": "^1.0.60"
49
49
  },
@@ -104,6 +104,20 @@ class RecordSetProviderBase extends libPictProvider
104
104
  return { Records: [], Facets: { } };
105
105
  }
106
106
 
107
+ /**
108
+ * Read records from the provider.
109
+ *
110
+ * @param {RecordSetFilter} pOptions - Options for the read operation.
111
+ *
112
+ * @return {Promise<RecordSetResult>} - The result of the read operation.
113
+ */
114
+ async getDecoratedRecords(pOptions)
115
+ {
116
+ const tmpRecords = await this.getRecords(pOptions);
117
+ await this.decorateCoreRecords(tmpRecords.Records);
118
+ return tmpRecords;
119
+ }
120
+
107
121
  /**
108
122
  * Read records from the provider.
109
123
  *
@@ -133,6 +147,7 @@ class RecordSetProviderBase extends libPictProvider
133
147
  * Create a new record.
134
148
  *
135
149
  * @param {Record<string, any>} pRecord - The record to create.
150
+ * @return {Promise<Record<string, any>>} - The created record.
136
151
  */
137
152
  async createRecord(pRecord)
138
153
  {
@@ -144,6 +159,7 @@ class RecordSetProviderBase extends libPictProvider
144
159
  * Update a record.
145
160
  *
146
161
  * @param {Record<string, any>} pRecord - The record to update.
162
+ * @return {Promise<Record<string, any>>} - The updated record.
147
163
  */
148
164
  async updateRecord(pRecord)
149
165
  {
@@ -155,6 +171,7 @@ class RecordSetProviderBase extends libPictProvider
155
171
  * Delete a record.
156
172
  *
157
173
  * @param {Record<string, any>} pRecord - The record to delete.
174
+ * @return {Promise<void>}
158
175
  */
159
176
  async deleteRecord(pRecord)
160
177
  {
@@ -188,6 +205,7 @@ class RecordSetProviderBase extends libPictProvider
188
205
  * Clone a record.
189
206
  *
190
207
  * @param {Record<string, any>} pRecord - The record to clone.
208
+ * @return {Promise<Record<string, any>>} - The cloned record.
191
209
  */
192
210
  async cloneRecord(pRecord)
193
211
  {
@@ -211,6 +229,16 @@ class RecordSetProviderBase extends libPictProvider
211
229
  {
212
230
  return { };
213
231
  }
232
+
233
+ /**
234
+ * Abstract decoration method for core records. Subclasses should implement this method to decorate records with additional information.
235
+ *
236
+ * @param {Array<Record<string, any>>} pRecords - The records to decorate.
237
+ * @return {Promise<void>}
238
+ */
239
+ async decorateCoreRecords(pRecords)
240
+ {
241
+ }
214
242
  }
215
243
 
216
244
  module.exports = RecordSetProviderBase;
@@ -10,7 +10,7 @@ const libRecordSetProviderBase = require('./RecordSet-RecordProvider-Base.js');
10
10
  * Class representing a data change detection provider for Pict dynamic forms.
11
11
  * @extends libRecordSetProviderBase
12
12
  */
13
- class RecordSetProvider extends libRecordSetProviderBase
13
+ class MeadowEndpointsRecordSetProvider extends libRecordSetProviderBase
14
14
  {
15
15
  /**
16
16
  * Creates an instance of RecordSetProvider.
@@ -26,8 +26,21 @@ class RecordSetProvider extends libRecordSetProviderBase
26
26
  this.options;
27
27
  /** @type {import('fable')} */
28
28
  this.fable;
29
- /** @type {import('fable') & import('pict')} */
29
+ /** @type {import('pict') & {
30
+ * log: any,
31
+ * services:
32
+ * {
33
+ * PictSectionRecordSet: InstanceType<import('../Pict-Section-RecordSet.js')>,
34
+ * [key: string]: any,
35
+ * },
36
+ * instantiateServiceProviderWithoutRegistration: (hash: String) => any,
37
+ * PictSectionRecordSet: InstanceType<import('../Pict-Section-RecordSet.js')>
38
+ * }} */
30
39
  this.pict;
40
+ /** @type {string} */
41
+ this.Hash;
42
+ /** @type {string} */
43
+ this.UUID;
31
44
  //TODO: make this typedef better
32
45
  /** @type {Record<string, any>} */
33
46
  this._Schema = { };
@@ -359,6 +372,43 @@ class RecordSetProvider extends libRecordSetProviderBase
359
372
  }
360
373
  return this._Schema;
361
374
  }
375
+
376
+ /**
377
+ * Abstract decoration method for core records. Subclasses should implement this method to decorate records with additional information.
378
+ *
379
+ * @param {Array<Record<string, any>>} pRecords - The records to decorate.
380
+ * @return {Promise<void>}
381
+ */
382
+ async decorateCoreRecords(pRecords)
383
+ {
384
+ if (!this.options.RecordDecorationConfiguration)
385
+ {
386
+ return;
387
+ }
388
+ if (!Array.isArray(this.options.RecordDecorationConfiguration))
389
+ {
390
+ this.pict.log.error('RecordDecorationConfiguration is not an array', { RecordDecorationConfiguration: this.options.RecordDecorationConfiguration });
391
+ return;
392
+ }
393
+ this.pict.AppData[this.Hash] = { CoreEntityRecordSubset: pRecords };
394
+ const config = [{ Type: 'SetStateAddress', StateAddress: `AppData[${this.Hash}]` }].concat(this.options.RecordDecorationConfiguration);
395
+
396
+ try
397
+ {
398
+ await new Promise((resolve, reject) => this.pict.EntityProvider.gatherDataFromServer(config, (err) =>
399
+ {
400
+ if (err)
401
+ {
402
+ return reject(err);
403
+ }
404
+ resolve();
405
+ }));
406
+ }
407
+ catch (error)
408
+ {
409
+ this.pict.log.error(`MeadowEndpointsRecordSetProvider: Error gathering data from server for record decoration: ${error.message}`, { Stack: error.stack });
410
+ }
411
+ }
362
412
  }
363
413
 
364
- module.exports = RecordSetProvider;
414
+ module.exports = MeadowEndpointsRecordSetProvider;
@@ -28,6 +28,7 @@ class RecordSetMetacontroller extends libFableServiceProviderBase
28
28
 
29
29
  /** @type {import('pict') & { addAndInstantiateSingletonService: (hash: string, options: any, prototype: any) => any }} */
30
30
  this.fable;
31
+ this.pict = this.fable;
31
32
  /** @type {any} */
32
33
  this.log;
33
34
  /** @type {any} */
@@ -45,7 +46,8 @@ class RecordSetMetacontroller extends libFableServiceProviderBase
45
46
  this.recordSetProviders = {};
46
47
  this.recordSetProviderConfigurations = {};
47
48
 
48
- this.recordSetListConfigurations = {};
49
+ this.dashboardConfigurations = {};
50
+
49
51
  this.sessionProviders = [];
50
52
 
51
53
  this.has_initialized = false;
@@ -77,6 +79,15 @@ class RecordSetMetacontroller extends libFableServiceProviderBase
77
79
  }
78
80
  ]
79
81
  */
82
+
83
+ /**
84
+ * @return {Record<string, any>} - The registered configuration for the RecordSet
85
+ */
86
+ getRecordSetConfiguration(pRecordSet)
87
+ {
88
+ return this.recordSetProviderConfigurations[pRecordSet];
89
+ }
90
+
80
91
  loadRecordSetConfiguration(pRecordSetConfiguration)
81
92
  {
82
93
  if (typeof pRecordSetConfiguration !== 'object')
@@ -190,6 +201,72 @@ class RecordSetMetacontroller extends libFableServiceProviderBase
190
201
  }
191
202
  }
192
203
 
204
+ /**
205
+ * @param {Array<Record<string, any>>} pDashboardConfigurationArray - An array of dashboard configurations.
206
+ */
207
+ loadDashboardConfigurationArray(pDashboardConfigurationArray)
208
+ {
209
+ if (!Array.isArray(pDashboardConfigurationArray))
210
+ {
211
+ this.fable.log.error(`RecordSetMetacontroller: ${this.UUID} loadDashboardConfigurationArray called with invalid configuration.`);
212
+ return false;
213
+ }
214
+ if (pDashboardConfigurationArray.length === 0)
215
+ {
216
+ this.fable.log.warn(`RecordSetMetacontroller: ${this.UUID} loadDashboardConfigurationArray called with empty configuration.`);
217
+ return false;
218
+ }
219
+ for (const tmpDashboardConfiguration of pDashboardConfigurationArray)
220
+ {
221
+ if (tmpDashboardConfiguration.RecordDecorationConfiguration)
222
+ {
223
+ //TODO: register the record decoration configuration
224
+ }
225
+ }
226
+ }
227
+
228
+ /**
229
+ * TODO: This method is still incomplete.
230
+ *
231
+ * @param {Record<string, any>} pDashboardConfiguration - The dashboard configuration to add.
232
+ */
233
+ addDashboardConfiguration(pDashboardConfiguration)
234
+ {
235
+ let tmpProvider = false;
236
+
237
+ if (this.recordSetProviders[pDashboardConfiguration.RecordSet])
238
+ {
239
+ this.pict.log.error(`RecordSetMetacontroller: ${this.UUID} addDashboardConfiguration called with invalid configuration. RecordSet ${pDashboardConfiguration.RecordSet} already exists.`);
240
+ return null;
241
+ }
242
+ const providerConfiguration = Object.assign({}, {Hash: `RSP-Provider-${pDashboardConfiguration.RecordSet}`}, pDashboardConfiguration);
243
+ this.dashboardConfigurations[providerConfiguration.RecordSet] = providerConfiguration;
244
+
245
+ // Create a Meadow Endpoints provider
246
+ // Allow the Record Set to optionally point to a different entity
247
+ if ('RecordSetCoreMeadowEntity' in pDashboardConfiguration)
248
+ {
249
+ providerConfiguration.Entity = pDashboardConfiguration.RecordSetCoreMeadowEntity;
250
+ }
251
+ else
252
+ {
253
+ this.pict.log.error(`RecordSetMetacontroller: ${this.UUID} addDashboardConfiguration called with invalid configuration. Missing RecordSetCoreMeadowEntity.`);
254
+ return null;
255
+ }
256
+ // Default the URLPrefix to the base URLPrefix
257
+ if ('RecordSetURLPrefix' in pDashboardConfiguration)
258
+ {
259
+ providerConfiguration.URLPrefix = pDashboardConfiguration.RecordSetURLPrefix;
260
+ }
261
+ else
262
+ {
263
+ providerConfiguration.URLPrefix = '/1.0/';
264
+ }
265
+ tmpProvider = this.recordSetProviders[pDashboardConfiguration.RecordSet] = this.fable.addProvider(providerConfiguration.Hash, providerConfiguration, providerMeadowEndpoints);
266
+
267
+ return tmpProvider;
268
+ }
269
+
193
270
  loadRecordSetDynamcally(pRecordSet, pEntity, pDefaultFilter)
194
271
  {
195
272
  if (typeof(pRecordSet) === 'object')
@@ -284,6 +361,7 @@ class RecordSetMetacontroller extends libFableServiceProviderBase
284
361
  this.fable.addProvider('RecordSetLinkManager', {}, providerLinkManager);
285
362
 
286
363
  // Add the subviews internally and externally
364
+ this.pict.addTemplate(require('../templates/Pict-Template-FilterView.js'));
287
365
  this.childViews.list = this.fable.addView('RSP-RecordSet-List', this.options, viewRecordSetList);
288
366
  this.childViews.edit = this.fable.addView('RSP-RecordSet-Edit', this.options, viewRecordSetEdit);
289
367
  this.childViews.read = this.fable.addView('RSP-RecordSet-Read', this.options, viewRecordSetRead);
@@ -302,6 +380,11 @@ class RecordSetMetacontroller extends libFableServiceProviderBase
302
380
  this.loadRecordSetConfigurationArray(this.fable.settings.DefaultRecordSetConfigurations);
303
381
  }
304
382
 
383
+ if (this.fable.settings.hasOwnProperty('DefaultDashboards'))
384
+ {
385
+ this.loadDashboardConfigurationArray(this.fable.settings.DefaultDashboards);
386
+ }
387
+
305
388
  this.has_initialized = true;
306
389
 
307
390
  // Load pict-router if it isn't loaded
@@ -0,0 +1,172 @@
1
+ const libPictTemplate = require('pict-template');
2
+
3
+ /**
4
+ * Specialized instruction for rendering the filter view and plumbing in required context.
5
+ *
6
+ * Based on the Pict base {~V:...~} template instruction.
7
+ */
8
+ class PictTemplateFilterViewInstruction extends libPictTemplate
9
+ {
10
+ /**
11
+ * @param {Object} pFable - The Fable Framework instance
12
+ * @param {Object} pOptions - The options for the service
13
+ * @param {String} pServiceHash - The hash of the service
14
+ */
15
+ constructor(pFable, pOptions, pServiceHash)
16
+ {
17
+ super(pFable, pOptions, pServiceHash);
18
+
19
+ /** @type {any} */
20
+ this.log;
21
+
22
+ this.addPattern('{~FV:', '~}');
23
+ this.addPattern('{~FilterView:', '~}');
24
+
25
+
26
+ if (!('__TemplateOutputCache' in this.pict))
27
+ {
28
+ this.pict.__TemplateOutputCache = {};
29
+ }
30
+ }
31
+
32
+ /**
33
+ * Render a template expression, returning a string with the resulting content.
34
+ *
35
+ * @param {string} pTemplateHash - The hash contents of the template (what's between the template start and stop tags)
36
+ * @param {any} pRecord - The json object to be used as the Record for the template render
37
+ * @param {Array<any>} pContextArray - An array of context objects accessible from the template; safe to leave empty
38
+ *
39
+ * @return {string} The rendered template
40
+ */
41
+ render(pTemplateHash, pRecord, pContextArray)
42
+ {
43
+ let [ tmpViewHash, tmpViewContext ] = pTemplateHash.split(':');
44
+ tmpViewHash = tmpViewHash.trim();
45
+ const tmpRecordSet = pRecord.RecordSet || '';
46
+ if (!tmpRecordSet)
47
+ {
48
+ this.pict.log.error(`Pict: Filter View Template Render: No record set specified in template hash [${pTemplateHash}] for view [${tmpViewHash}]`);
49
+ return '';
50
+ }
51
+ const tmpRecordSetConfiguration = this.pict.PictSectionRecordSet.recordSetProviderConfigurations?.[tmpRecordSet];
52
+ if (!tmpRecordSetConfiguration)
53
+ {
54
+ this.pict.log.error(`Pict: Filter View Template Render: No record set configuration found for [${tmpRecordSet}] in template hash [${pTemplateHash}] for view [${tmpViewHash}]`);
55
+ return '';
56
+ }
57
+ if (tmpViewContext)
58
+ {
59
+ tmpViewContext = tmpViewContext.trim();
60
+ }
61
+ else
62
+ {
63
+ tmpViewContext = 'Default';
64
+ }
65
+ if (!(tmpViewHash in this.pict.views))
66
+ {
67
+ this.log.warn(`Pict: Filter View Template Render: View not found for [${tmpViewHash}]`);
68
+ return '';
69
+ }
70
+
71
+ pRecord = pRecord || {};
72
+ if (!pRecord.RecordSet)
73
+ {
74
+ pRecord.RecordSet = tmpRecordSet;
75
+ }
76
+ if (!pRecord.RecordSetConfiguration)
77
+ {
78
+ pRecord.RecordSetConfiguration = tmpRecordSetConfiguration;
79
+ }
80
+ if (!pRecord.ViewContext)
81
+ {
82
+ pRecord.ViewContext = tmpViewContext;
83
+ }
84
+
85
+ let tmpRenderGUID = this.pict.getUUID();
86
+
87
+ /** @type {import('pict-view')} */
88
+ const tmpView = this.pict.views[tmpViewHash];
89
+
90
+ tmpView.render('__Virtual', `__TemplateOutputCache.${tmpRenderGUID}`, pRecord);
91
+
92
+ let tmpResult = this.pict.__TemplateOutputCache[tmpRenderGUID];
93
+ // TODO: Uncomment this when we like how it's working
94
+ //delete this.pict.__TemplateOutputCache[tmpRenderGUID];
95
+
96
+ return tmpResult;
97
+ }
98
+
99
+ /**
100
+ * @param {string} pTemplateHash - The hash contents of the template (what's between the template start and stop tags)
101
+ * @param {any} pRecord - The json object to be used as the Record for the template
102
+ * @param {(pError?: Error, pResult?: string) => void} fCallback - The callback function to call with the result
103
+ * @param {Array<any>} pContextArray - An array of context objects accessible from the template; safe to leave empty
104
+ * @return {void}
105
+ */
106
+ renderAsync(pTemplateHash, pRecord, fCallback, pContextArray)
107
+ {
108
+ let [ tmpViewHash, tmpViewContext ] = pTemplateHash.split(':');
109
+ tmpViewHash = tmpViewHash.trim();
110
+ const tmpRecordSet = pRecord.RecordSet || '';
111
+ if (!tmpRecordSet)
112
+ {
113
+ this.pict.log.error(`Pict: Filter View Template Render: No record set specified in template hash [${pTemplateHash}] for view [${tmpViewHash}]`);
114
+ return fCallback(null, '');
115
+ }
116
+ const tmpRecordSetConfiguration = this.pict.PictSectionRecordSet.recordSetProviderConfigurations?.[tmpRecordSet];
117
+ if (!tmpRecordSetConfiguration)
118
+ {
119
+ this.pict.log.error(`Pict: Filter View Template Render: No record set configuration found for [${tmpRecordSet}] in template hash [${pTemplateHash}] for view [${tmpViewHash}]`);
120
+ return fCallback(null, '');
121
+ }
122
+ if (tmpViewContext)
123
+ {
124
+ tmpViewContext = tmpViewContext.trim();
125
+ }
126
+ else
127
+ {
128
+ tmpViewContext = 'Default';
129
+ }
130
+ if (!(tmpViewHash in this.pict.views))
131
+ {
132
+ this.log.warn(`Pict: Filter View Template Render: View not found for [${tmpViewHash}]`);
133
+ return fCallback(null, '');
134
+ }
135
+
136
+ pRecord = pRecord || {};
137
+ if (!pRecord.RecordSet)
138
+ {
139
+ pRecord.RecordSet = tmpRecordSet;
140
+ }
141
+ if (!pRecord.RecordSetConfiguration)
142
+ {
143
+ pRecord.RecordSetConfiguration = tmpRecordSetConfiguration;
144
+ }
145
+ if (!pRecord.ViewContext)
146
+ {
147
+ pRecord.ViewContext = tmpViewContext;
148
+ }
149
+ let tmpRenderGUID = this.pict.getUUID();
150
+
151
+ /** @type {import('pict-view')} */
152
+ const tmpView = this.pict.views[tmpViewHash];
153
+
154
+ return tmpView.renderAsync('__Virtual', `__TemplateOutputCache.${tmpRenderGUID}`, pRecord,
155
+ (pError, pResult) =>
156
+ {
157
+ if (pError)
158
+ {
159
+ this.log.warn(`Pict: Filter View Template Render: Error rendering view [${tmpViewHash}]`, pError);
160
+ return fCallback(pError, '');
161
+ }
162
+
163
+ let tmpResult = this.pict.__TemplateOutputCache[tmpRenderGUID];
164
+ // TODO: Uncomment this when we like how it's working
165
+ //delete this.pict.__TemplateOutputCache[tmpRenderGUID];
166
+
167
+ return fCallback(null, tmpResult);
168
+ });
169
+ }
170
+ }
171
+
172
+ module.exports = PictTemplateFilterViewInstruction;
@@ -32,7 +32,7 @@ const _DEFAULT_CONFIGURATION_SUBSET_Filter =
32
32
  Template: /*html*/`
33
33
  <!-- DefaultPackage pict view template: [PRSP-SUBSET-Filter-Template] -->
34
34
  <section id="PRSP_Filter_Container">
35
- <form id="PRSP_Filter_Form" onsubmit="_Pict.views['PRSP-Filters'].handleSearch(event)">
35
+ <form id="PRSP_Filter_Form" onsubmit="_Pict.views['PRSP-Filters'].handleSearch(event, '{~D:Record.RecordSet~}', '{~D:Record.ViewContext~}'); return false;">
36
36
  {~T:PRSP-SUBSET-Filter-Template-Input-Fieldset~}
37
37
  {~T:PRSP-SUBSET-Filter-Template-Button-Fieldset~}
38
38
  </form>
@@ -56,7 +56,7 @@ const _DEFAULT_CONFIGURATION_SUBSET_Filter =
56
56
  Template: /*html*/`
57
57
  <!-- DefaultPackage pict view template: [PRSP-SUBSET-Filter-Template-Button-Fieldset] -->
58
58
  <fieldset>
59
- <button type="button" id="PRSP_Filter_Button_Reset" onclick="_Pict.views['PRSP-Filters'].handleReset(event)">Reset</button>
59
+ <button type="button" id="PRSP_Filter_Button_Reset" onclick="_Pict.views['PRSP-Filters'].handleReset(event, '{~D:Record.RecordSet~}', '{~D:Record.ViewContext~}')">Reset</button>
60
60
  <button type="submit" id="PRSP_Filter_Button_Apply">Apply</button>
61
61
  </fieldset>
62
62
  <!-- DefaultPackage end view template: [PRSP-SUBSET-Filter-Template-Button-Fieldset] -->
@@ -83,20 +83,57 @@ class viewRecordSetSUBSETFilter extends libPictView
83
83
  {
84
84
  let tmpOptions = Object.assign({}, _DEFAULT_CONFIGURATION_SUBSET_Filter, pOptions);
85
85
  super(pFable, tmpOptions, pServiceHash);
86
+ /** @type {import('fable') & import('pict') & { PictSectionRecordSet: import('../Pict-Section-RecordSet.js') }} */
87
+ this.pict;
86
88
  }
87
89
 
88
- handleSearch(event)
90
+ /**
91
+ * @param {Event} pEvent - The DOM event that triggered the search
92
+ * @param {string} pRecordSet - The record set being filtered
93
+ * @param {string} pViewContext - The view context for the filter (ex. List, Dashboard)
94
+ */
95
+ handleSearch(pEvent, pRecordSet, pViewContext)
89
96
  {
90
- event.preventDefault(); // don't submit the form
91
- event.stopPropagation();
92
- this.pict.views['RSP-RecordSet-List']?.handleSearch?.(this.pict.ContentAssignment.readContent('input[name="filter"]'));
97
+ pEvent.preventDefault(); // don't submit the form
98
+ pEvent.stopPropagation();
99
+ const tmpSearchString = this.pict.ContentAssignment.readContent(`input[name="filter"]`);
100
+ this.performSearch(pRecordSet, pViewContext, tmpSearchString ? String(tmpSearchString) : ' ');
93
101
  }
94
102
 
95
- handleReset(event)
103
+ /**
104
+ * @param {string} pRecordSet - The record set being filtered
105
+ * @param {string} pViewContext - The view context for the filter (ex. List, Dashboard)
106
+ * @param {string} [pFilterString] - The filter string to apply, defaults to a single space if not provided
107
+ */
108
+ performSearch(pRecordSet, pViewContext, pFilterString)
96
109
  {
97
- event.stopPropagation();
110
+ const tmpPictRouter = this.pict.providers.PictRouter;
111
+ const tmpProviderConfiguration = this.pict.PictSectionRecordSet.recordSetProviderConfigurations[pRecordSet];
112
+ let filterExpr = ' ';
113
+ if (pFilterString)
114
+ {
115
+ const searchFields = tmpProviderConfiguration?.SearchFields ?? [ 'Name' ];
116
+ filterExpr = searchFields.map((filterField) => `FBVOR~${filterField}~LK~${encodeURIComponent(`%${pFilterString}%`)}`).join('~');
117
+ }
118
+ let tmpURLTemplate = tmpProviderConfiguration[`RecordSetFilterURLTemplate-${pViewContext}`] || tmpProviderConfiguration[`RecordSetFilterURLTemplate-Default`];
119
+ const tmpURL = this.pict.parseTemplate(tmpURLTemplate,
120
+ {
121
+ RecordSet: pRecordSet,
122
+ FilterString: filterExpr,
123
+ });
124
+ tmpPictRouter.router.navigate(tmpURL);
125
+ }
126
+
127
+ /**
128
+ * @param {Event} pEvent - The DOM event that triggered the search
129
+ * @param {string} pRecordSet - The record set being filtered
130
+ * @param {string} pViewContext - The view context for the filter (ex. List, Dashboard)
131
+ */
132
+ handleReset(pEvent, pRecordSet, pViewContext)
133
+ {
134
+ pEvent.preventDefault();
98
135
  this.pict.ContentAssignment.assignContent('input[name="filter"]', '');
99
- this.pict.views['RSP-RecordSet-List']?.handleSearch?.('');
136
+ this.performSearch(pRecordSet, pViewContext);
100
137
  }
101
138
  }
102
139