pict-section-recordset 1.0.27 → 1.0.29

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 (52) hide show
  1. package/example_applications/simple_entity/Simple-RecordSet-Application.js +1 -0
  2. package/package.json +2 -2
  3. package/source/providers/RecordSet-RecordProvider-Base.js +154 -0
  4. package/source/providers/RecordSet-RecordProvider-MeadowEndpoints.js +181 -1
  5. package/source/services/RecordsSet-MetaController.js +1 -0
  6. package/source/views/RecordSet-Filters.js +124 -1
  7. package/source/views/filters/RecordSet-Filter-Base.js +17 -3
  8. package/source/views/filters/RecordSet-Filter-DateMatch.js +5 -13
  9. package/source/views/filters/RecordSet-Filter-DateRange.js +5 -13
  10. package/source/views/filters/RecordSet-Filter-ExternalJoinDateMatch.js +5 -13
  11. package/source/views/filters/RecordSet-Filter-ExternalJoinDateRange.js +5 -13
  12. package/source/views/filters/RecordSet-Filter-ExternalJoinNumericMatch.js +5 -13
  13. package/source/views/filters/RecordSet-Filter-ExternalJoinNumericRange.js +5 -13
  14. package/source/views/filters/RecordSet-Filter-ExternalJoinStringMatch.js +5 -13
  15. package/source/views/filters/RecordSet-Filter-ExternalJoinStringRange.js +5 -13
  16. package/source/views/filters/RecordSet-Filter-InternalJoinDateMatch.js +5 -13
  17. package/source/views/filters/RecordSet-Filter-InternalJoinDateRange.js +5 -13
  18. package/source/views/filters/RecordSet-Filter-InternalJoinNumericMatch.js +5 -13
  19. package/source/views/filters/RecordSet-Filter-InternalJoinNumericRange.js +5 -13
  20. package/source/views/filters/RecordSet-Filter-InternalJoinStringMatch.js +5 -13
  21. package/source/views/filters/RecordSet-Filter-InternalJoinStringRange.js +5 -13
  22. package/source/views/filters/RecordSet-Filter-NumericMatch.js +5 -13
  23. package/source/views/filters/RecordSet-Filter-NumericRange.js +5 -13
  24. package/source/views/filters/RecordSet-Filter-StringMatch.js +5 -13
  25. package/source/views/filters/RecordSet-Filter-StringRange.js +5 -13
  26. package/types/providers/RecordSet-RecordProvider-Base.d.ts +39 -0
  27. package/types/providers/RecordSet-RecordProvider-Base.d.ts.map +1 -1
  28. package/types/providers/RecordSet-RecordProvider-MeadowEndpoints.d.ts +10 -0
  29. package/types/providers/RecordSet-RecordProvider-MeadowEndpoints.d.ts.map +1 -1
  30. package/types/services/RecordsSet-MetaController.d.ts.map +1 -1
  31. package/types/views/RecordSet-Filters.d.ts +8 -0
  32. package/types/views/RecordSet-Filters.d.ts.map +1 -1
  33. package/types/views/filters/RecordSet-Filter-Base.d.ts +1 -0
  34. package/types/views/filters/RecordSet-Filter-Base.d.ts.map +1 -1
  35. package/types/views/filters/RecordSet-Filter-DateMatch.d.ts.map +1 -1
  36. package/types/views/filters/RecordSet-Filter-DateRange.d.ts.map +1 -1
  37. package/types/views/filters/RecordSet-Filter-ExternalJoinDateMatch.d.ts.map +1 -1
  38. package/types/views/filters/RecordSet-Filter-ExternalJoinDateRange.d.ts.map +1 -1
  39. package/types/views/filters/RecordSet-Filter-ExternalJoinNumericMatch.d.ts.map +1 -1
  40. package/types/views/filters/RecordSet-Filter-ExternalJoinNumericRange.d.ts.map +1 -1
  41. package/types/views/filters/RecordSet-Filter-ExternalJoinStringMatch.d.ts.map +1 -1
  42. package/types/views/filters/RecordSet-Filter-ExternalJoinStringRange.d.ts.map +1 -1
  43. package/types/views/filters/RecordSet-Filter-InternalJoinDateMatch.d.ts.map +1 -1
  44. package/types/views/filters/RecordSet-Filter-InternalJoinDateRange.d.ts.map +1 -1
  45. package/types/views/filters/RecordSet-Filter-InternalJoinNumericMatch.d.ts.map +1 -1
  46. package/types/views/filters/RecordSet-Filter-InternalJoinNumericRange.d.ts.map +1 -1
  47. package/types/views/filters/RecordSet-Filter-InternalJoinStringMatch.d.ts.map +1 -1
  48. package/types/views/filters/RecordSet-Filter-InternalJoinStringRange.d.ts.map +1 -1
  49. package/types/views/filters/RecordSet-Filter-NumericMatch.d.ts.map +1 -1
  50. package/types/views/filters/RecordSet-Filter-NumericRange.d.ts.map +1 -1
  51. package/types/views/filters/RecordSet-Filter-StringMatch.d.ts.map +1 -1
  52. package/types/views/filters/RecordSet-Filter-StringRange.d.ts.map +1 -1
@@ -182,6 +182,7 @@ module.exports.default_configuration.pict_configuration = (
182
182
  {
183
183
  "ExternalJoinBookByAuthor":
184
184
  {
185
+ "DisplayName": "Author's Name",
185
186
  "Type": "ExternalJoinStringMatch",
186
187
  "ExternalFilterByColumns": [ "Name" ],
187
188
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pict-section-recordset",
3
- "version": "1.0.27",
3
+ "version": "1.0.29",
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.28.0",
35
35
  "jquery": "^3.7.1",
36
- "pict": "^1.0.288",
36
+ "pict": "^1.0.289",
37
37
  "pict-application": "^1.0.27",
38
38
  "pict-service-commandlineutility": "^1.0.15",
39
39
  "quackage": "^1.0.42",
@@ -68,6 +68,9 @@ class RecordSetProviderBase extends libPictProvider
68
68
  this.fable;
69
69
  /** @type {import('pict')} */
70
70
  this.pict;
71
+
72
+ /** @type {Record<string, any>} */
73
+ this._FilterSchema = { };
71
74
  }
72
75
 
73
76
  /**
@@ -246,6 +249,157 @@ class RecordSetProviderBase extends libPictProvider
246
249
  async decorateCoreRecords(pRecords)
247
250
  {
248
251
  }
252
+
253
+ getFilterSchemaKeys()
254
+ {
255
+ return Object.keys(this._FilterSchema);
256
+ }
257
+
258
+ /**
259
+ * @param {string} pFilterKey - The filter key to get the schema for.
260
+ *
261
+ * @return {Record<string, any>} The schema for the filter clause.
262
+ */
263
+ getFilterClauseSchemaForKey(pFilterKey)
264
+ {
265
+ return this._FilterSchema?.[pFilterKey];
266
+ }
267
+
268
+ /**
269
+ * @return {Record<string, Record<string, any>>} The schema for the filter clauses.
270
+ */
271
+ getFilterSchema()
272
+ {
273
+ /** @type {Record<string, Record<string, any>>} */
274
+ const tmpSchema = {};
275
+ for (const tmpKey of this.getFilterSchemaKeys())
276
+ {
277
+ tmpSchema[tmpKey] = this.getFilterClauseSchemaForKey(tmpKey);
278
+ }
279
+ return tmpSchema;
280
+ }
281
+
282
+ /**
283
+ * @param {string} pFilterKey - The filter key to add the clause for.
284
+ * @param {string} pClauseKey - The clause key to add.
285
+ */
286
+ addFilterClause(pFilterKey, pClauseKey)
287
+ {
288
+ let tmpClause = this.getFilterClauseSchemaForKey(pFilterKey)?.AvailableClauses?.find?.((c) => c.ClauseKey == pClauseKey);
289
+ if (!tmpClause)
290
+ {
291
+ this.pict.log.error(`RecordSetProviderBase.addFilterClause() - No filter clause schema found for key ${pFilterKey} and clause ${pClauseKey}.`);
292
+ return;
293
+ }
294
+ tmpClause = JSON.parse(JSON.stringify(tmpClause));
295
+ tmpClause.Hash = `${pFilterKey}-${pClauseKey}-${this.pict.getUUID()}`;
296
+ tmpClause.Label = tmpClause.Label || tmpClause.DisplayName;
297
+ const tmpClauses = this.getFilterClauses();
298
+ tmpClauses.push(tmpClause);
299
+ }
300
+
301
+ /**
302
+ */
303
+ clearFilterClauses()
304
+ {
305
+ const tmpClauses = this.pict.Bundle._ActiveFilterState?.[this.options.RecordSet]?.FilterClauses;
306
+ if (!Array.isArray(tmpClauses))
307
+ {
308
+ return;
309
+ }
310
+ tmpClauses.length = 0; // Clear the array
311
+ }
312
+
313
+ /**
314
+ * @param {string} pSpecificFilterClauseHash - The hash of the specific filter clause to remove.
315
+ */
316
+ removeFilterClause(pSpecificFilterClauseHash)
317
+ {
318
+ const tmpClauses = this.pict.Bundle._ActiveFilterState?.[this.options.RecordSet]?.FilterClauses;
319
+ if (!Array.isArray(tmpClauses))
320
+ {
321
+ this.pict.log.error('RecordSetProviderBase.removeFilterClause() - No filter clauses found.');
322
+ return;
323
+ }
324
+ const tmpClauseIndex = tmpClauses.findIndex((c) => pSpecificFilterClauseHash == c.Hash);
325
+ if (tmpClauseIndex < 0)
326
+ {
327
+ this.pict.log.error(`RecordSetProviderBase.removeFilterClause() - Filter clause with hash ${pSpecificFilterClauseHash} not found.`);
328
+ return;
329
+ }
330
+ tmpClauses.splice(tmpClauseIndex, 1);
331
+ }
332
+
333
+ /**
334
+ * @return {Array<Record<string, any>>} The filter clauses.
335
+ */
336
+ getFilterClauses()
337
+ {
338
+ let tmpClauses = this.pict.Bundle._ActiveFilterState?.[this.options.RecordSet]?.FilterClauses;
339
+ if (!Array.isArray(tmpClauses))
340
+ {
341
+ tmpClauses = [];
342
+ this.pict.Bundle._ActiveFilterState = this.pict.Bundle._ActiveFilterState || {};
343
+ this.pict.Bundle._ActiveFilterState[this.options.RecordSet] = this.pict.Bundle._ActiveFilterState[this.options.RecordSet] || {};
344
+ this.pict.Bundle._ActiveFilterState[this.options.RecordSet].FilterClauses = tmpClauses;
345
+ }
346
+ return tmpClauses;
347
+ }
348
+
349
+ /**
350
+ * @param {string} pSpecificFilterClauseHash - The hash of the specific filter clause to move.
351
+ * @param {number} pOrdinal - The ordinal position to move the filter clause to.
352
+ */
353
+ moveFilterClauseTo(pSpecificFilterClauseHash, pOrdinal)
354
+ {
355
+ const tmpClauses = this.pict.Bundle._ActiveFilterState?.[this.options.RecordSet]?.FilterClauses;
356
+ if (!Array.isArray(tmpClauses))
357
+ {
358
+ this.pict.log.error('RecordSetProviderBase.moveFilterClauseTo() - No filter clauses found.');
359
+ return;
360
+ }
361
+ if (pOrdinal < 0 || pOrdinal >= tmpClauses.length)
362
+ {
363
+ this.pict.log.error(`RecordSetProviderBase.moveFilterClauseTo() - Invalid ordinal ${pOrdinal}.`);
364
+ return;
365
+ }
366
+ const tmpClauseIndex = tmpClauses.indexOf((c) => pSpecificFilterClauseHash == c.Hash);
367
+ if (tmpClauseIndex < 0)
368
+ {
369
+ this.pict.log.error(`RecordSetProviderBase.moveFilterClauseTo() - Filter clause with hash ${pSpecificFilterClauseHash} not found.`);
370
+ return;
371
+ }
372
+ const tmpClause = tmpClauses.splice(tmpClauseIndex, 1)[0];
373
+ tmpClauses.splice(pOrdinal, 0, tmpClause);
374
+ }
375
+
376
+ /**
377
+ * @param {string} pSpecificFilterClauseHash - The hash of the specific filter clause to move.
378
+ * @param {number} pOrdinalOffset - The ordinal offset to move the filter clause by.
379
+ */
380
+ moveFilterClauseBy(pSpecificFilterClauseHash, pOrdinalOffset)
381
+ {
382
+ const tmpClauses = this.pict.Bundle._ActiveFilterState?.[this.options.RecordSet]?.FilterClauses;
383
+ if (!Array.isArray(tmpClauses))
384
+ {
385
+ this.pict.log.error('RecordSetProviderBase.moveFilterClauseBy() - No filter clauses found.');
386
+ return;
387
+ }
388
+ const tmpClauseIndex = tmpClauses.indexOf((c) => pSpecificFilterClauseHash == c.Hash);
389
+ if (tmpClauseIndex < 0)
390
+ {
391
+ this.pict.log.error(`RecordSetProviderBase.moveFilterClauseBy() - Filter clause with hash ${pSpecificFilterClauseHash} not found.`);
392
+ return;
393
+ }
394
+ const tmpNewOrdinal = tmpClauseIndex + pOrdinalOffset;
395
+ if (tmpNewOrdinal < 0 || tmpNewOrdinal >= tmpClauses.length)
396
+ {
397
+ this.pict.log.error(`RecordSetProviderBase.moveFilterClauseBy() - Invalid new ordinal ${tmpNewOrdinal} (offset of ${pOrdinalOffset} for original ordinal ${tmpClauseIndex}).`);
398
+ return;
399
+ }
400
+ const tmpClause = tmpClauses.splice(tmpClauseIndex, 1)[0];
401
+ tmpClauses.splice(tmpNewOrdinal, 0, tmpClause);
402
+ }
249
403
  }
250
404
 
251
405
  module.exports = RecordSetProviderBase;
@@ -424,11 +424,189 @@ class MeadowEndpointsRecordSetProvider extends libRecordSetProviderBase
424
424
  }
425
425
  this.initializeEntitySchema(() =>
426
426
  {
427
+ const tmpSchema = this._Schema;
428
+ if (!tmpSchema || !tmpSchema.properties)
429
+ {
430
+ return fCallback(pError);
431
+ }
432
+ const tmpProperties = tmpSchema?.properties;
433
+ // loop throught the schema and add the columns to the tableCells
434
+ let tmpOrdinal = 0;
435
+ for (const tmpSchemaField in tmpProperties)
436
+ {
437
+ ++tmpOrdinal;
438
+ if (tmpSchemaField === 'Deleted' || tmpSchemaField === 'DeletingIDUser')
439
+ {
440
+ continue;
441
+ }
442
+ const tmpColumn = tmpProperties[tmpSchemaField];
443
+ let tmpFieldFilterSchema = this._FilterSchema[tmpSchemaField];
444
+ if (!tmpFieldFilterSchema)
445
+ {
446
+ this._FilterSchema[tmpSchemaField] = tmpFieldFilterSchema = { };
447
+ }
448
+ if (!tmpFieldFilterSchema.FilterKey)
449
+ {
450
+ tmpFieldFilterSchema.FilterKey = tmpSchemaField;
451
+ }
452
+ if (!tmpFieldFilterSchema.RecordSet)
453
+ {
454
+ tmpFieldFilterSchema.RecordSet = this.options.RecordSet;
455
+ }
456
+ if (!tmpFieldFilterSchema.DisplayName)
457
+ {
458
+ tmpFieldFilterSchema.DisplayName = this._getHumanReadableFieldName(tmpSchemaField);
459
+ }
460
+ if (!tmpFieldFilterSchema.Description)
461
+ {
462
+ tmpFieldFilterSchema.Description = `Filter by ${tmpFieldFilterSchema.DisplayName}`;
463
+ }
464
+ if (!tmpFieldFilterSchema.HelpText)
465
+ {
466
+ tmpFieldFilterSchema.HelpText = `Filter by ${tmpFieldFilterSchema.DisplayName} for the ${this._getHumanReadbleEntityName(this.options.Entity)} entity.`;
467
+ }
468
+ if (tmpFieldFilterSchema.Ordinal == null)
469
+ {
470
+ tmpFieldFilterSchema.Ordinal = tmpOrdinal;
471
+ }
472
+ if (!Array.isArray(tmpFieldFilterSchema.AvailableClauses))
473
+ {
474
+ tmpFieldFilterSchema.AvailableClauses = [];
475
+ }
476
+ const tmpFieldHumanName = this._getHumanReadableFieldName(tmpSchemaField);
477
+ switch (tmpColumn.type)
478
+ {
479
+ case 'string':
480
+ tmpFieldFilterSchema.AvailableClauses.push({ FilterKey: tmpSchemaField, ClauseKey: `${tmpSchemaField}_Match_Exact`, DisplayName: `${tmpFieldHumanName} Exact Match`, Type: 'StringMatch', FilterByColumn: tmpSchemaField, ExactMatch: true, Ordinal: tmpFieldFilterSchema.AvailableClauses.length + 1 });
481
+ tmpFieldFilterSchema.AvailableClauses.push({ FilterKey: tmpSchemaField, ClauseKey: `${tmpSchemaField}_Match_Fuzzy`, DisplayName: `${tmpFieldHumanName} Partial Match`, Type: 'StringMatch', FilterByColumn: tmpSchemaField, ExactMatch: false , Ordinal: tmpFieldFilterSchema.AvailableClauses.length + 1 });
482
+ tmpFieldFilterSchema.AvailableClauses.push({ FilterKey: tmpSchemaField, ClauseKey: `${tmpSchemaField}_Range`, DisplayName: `${tmpFieldHumanName} in Range`, Type: 'StringRange', FilterByColumn: tmpSchemaField , Ordinal: tmpFieldFilterSchema.AvailableClauses.length + 1 });
483
+ break;
484
+ case 'date':
485
+ case 'datetime':
486
+ tmpFieldFilterSchema.AvailableClauses.push({ FilterKey: tmpSchemaField, ClauseKey: `${tmpSchemaField}_Match_Exact`, DisplayName: `${tmpFieldHumanName} Exact Match`, Type: 'DateMatch', FilterByColumn: tmpSchemaField, ExactMatch: true , Ordinal: tmpFieldFilterSchema.AvailableClauses.length + 1 });
487
+ tmpFieldFilterSchema.AvailableClauses.push({ FilterKey: tmpSchemaField, ClauseKey: `${tmpSchemaField}_Match_Fuzzy`, DisplayName: `${tmpFieldHumanName} Partial Match`, Type: 'DateMatch', FilterByColumn: tmpSchemaField, ExactMatch: false , Ordinal: tmpFieldFilterSchema.AvailableClauses.length + 1 });
488
+ tmpFieldFilterSchema.AvailableClauses.push({ FilterKey: tmpSchemaField, ClauseKey: `${tmpSchemaField}_Range`, DisplayName: `${tmpFieldHumanName} in Range`, Type: 'DateRange', FilterByColumn: tmpSchemaField , Ordinal: tmpFieldFilterSchema.AvailableClauses.length + 1 });
489
+ break;
490
+ case 'boolean': //TODO: we didn't add filters for this - they are just numeric but it's weird for the user, maybe we should add views for this that account for the difference
491
+ case 'integer':
492
+ tmpFieldFilterSchema.AvailableClauses.push({ FilterKey: tmpSchemaField, ClauseKey: `${tmpSchemaField}_Match_Exact`, DisplayName: `${tmpFieldHumanName} Exact Match`, Type: 'NumericMatch', FilterByColumn: tmpSchemaField, ExactMatch: true , Ordinal: tmpFieldFilterSchema.AvailableClauses.length + 1 });
493
+ tmpFieldFilterSchema.AvailableClauses.push({ FilterKey: tmpSchemaField, ClauseKey: `${tmpSchemaField}_Match_Fuzzy`, DisplayName: `${tmpFieldHumanName} Partial Match`, Type: 'NumericMatch', FilterByColumn: tmpSchemaField, ExactMatch: false , Ordinal: tmpFieldFilterSchema.AvailableClauses.length + 1 });
494
+ tmpFieldFilterSchema.AvailableClauses.push({ FilterKey: tmpSchemaField, ClauseKey: `${tmpSchemaField}_Range`, DisplayName: `${tmpFieldHumanName} in Range`, Type: 'NumberRange', FilterByColumn: tmpSchemaField , Ordinal: tmpFieldFilterSchema.AvailableClauses.length + 1 });
495
+ break;
496
+ default:
497
+ this.pict.log.warn(`Unsupported field type ${tmpColumn.type} for field ${tmpSchemaField}`, { Schema: tmpColumn });
498
+ }
499
+ }
500
+ if (typeof this.pict.providers.FilterManager.filters === 'object')
501
+ {
502
+ for (const tmpFilterKey of Object.keys(this.pict.providers.FilterManager.filters))
503
+ {
504
+ const tmpFilterClause = this.pict.providers.FilterManager.filters[tmpFilterKey];
505
+ if (tmpFilterClause.CoreConnectionColumn === `ID${this.options.Entity}`)
506
+ {
507
+ //FIXME: I don't think using filter key is right here
508
+ let tmpFieldFilterSchema = this._FilterSchema[tmpFilterKey];
509
+ if (!tmpFieldFilterSchema)
510
+ {
511
+ this._FilterSchema[tmpFilterKey] = tmpFieldFilterSchema = { };
512
+ }
513
+ if (!tmpFieldFilterSchema.FilterKey)
514
+ {
515
+ tmpFieldFilterSchema.FilterKey = tmpFilterKey;
516
+ }
517
+ if (!tmpFieldFilterSchema.RecordSet)
518
+ {
519
+ tmpFieldFilterSchema.RecordSet = this.options.RecordSet;
520
+ }
521
+ if (!tmpFieldFilterSchema.DisplayName)
522
+ {
523
+ tmpFieldFilterSchema.DisplayName = tmpFilterClause.DisplayName || this._getHumanReadableFieldName(tmpFilterKey);
524
+ }
525
+ if (!tmpFieldFilterSchema.Description)
526
+ {
527
+ tmpFieldFilterSchema.Description = tmpFilterClause.Description || `Filter by ${tmpFieldFilterSchema.DisplayName}`;
528
+ }
529
+ if (!tmpFieldFilterSchema.HelpText)
530
+ {
531
+ tmpFieldFilterSchema.HelpText = tmpFilterClause.HelpText || `Filter by ${tmpFieldFilterSchema.DisplayName} for the ${this._getHumanReadbleEntityName(this.options.Entity)} entity.`;
532
+ }
533
+ if (tmpFieldFilterSchema.Ordinal == null)
534
+ {
535
+ tmpFieldFilterSchema.Ordinal = tmpOrdinal;
536
+ }
537
+ if (!Array.isArray(tmpFieldFilterSchema.AvailableClauses))
538
+ {
539
+ tmpFieldFilterSchema.AvailableClauses = [];
540
+ }
541
+ const tmpFieldHumanName = this._getHumanReadableFieldName(tmpFilterKey);
542
+ tmpFieldFilterSchema.AvailableClauses.push(tmpFilterClause);
543
+ if (!tmpFilterClause.FilterKey)
544
+ {
545
+ tmpFilterClause.FilterKey = tmpFilterKey;
546
+ }
547
+ if (!tmpFilterClause.ClauseKey)
548
+ {
549
+ tmpFilterClause.ClauseKey = tmpFilterKey;
550
+ }
551
+ if (!tmpFilterClause.DisplayName)
552
+ {
553
+ tmpFilterClause.DisplayName = tmpFieldHumanName;
554
+ }
555
+ tmpFilterClause.Ordinal = tmpFieldFilterSchema.AvailableClauses.length + 1;
556
+ }
557
+ }
558
+ }
427
559
  return fCallback(pError);
428
560
  });
429
561
  });
430
562
  }
431
563
 
564
+ /**
565
+ * @param {string} pEntity - The schema field name.
566
+ * @return {string} - The human-readable name for the entity.
567
+ */
568
+ _getHumanReadbleEntityName(pEntity)
569
+ {
570
+ return pEntity.replace(/([a-z])([A-Z])/g, '$1 $2'); // Add space before capital letters
571
+ }
572
+
573
+ /**
574
+ * @param {string} pSchemaField - The schema field name.
575
+ * @return {string} - The human-readable name for the schema field.
576
+ */
577
+ _getHumanReadableFieldName(pSchemaField)
578
+ {
579
+ if (!this._Schema || !this._Schema.properties || !this._Schema.properties[pSchemaField])
580
+ {
581
+ return pSchemaField;
582
+ }
583
+ if (pSchemaField === `ID${this.options.Entity}`)
584
+ {
585
+ return `${this._getHumanReadbleEntityName(this.options.Entity)} Unique Database ID`;
586
+ }
587
+ if (pSchemaField === `GUID${this.options.Entity}`)
588
+ {
589
+ return `${this._getHumanReadbleEntityName(this.options.Entity)} Unique Identifier`;
590
+ }
591
+ if (pSchemaField === 'CreatingIDUser')
592
+ {
593
+ return 'Created By User';
594
+ }
595
+ if (pSchemaField === 'CreateDate')
596
+ {
597
+ return 'Date Created';
598
+ }
599
+ if (pSchemaField === 'UpdatingIDUser')
600
+ {
601
+ return 'Last Updated By User';
602
+ }
603
+ if (pSchemaField === 'UpdateDate')
604
+ {
605
+ return 'Date Last Updated';
606
+ }
607
+ return pSchemaField.replace(/([a-z])([A-Z])/g, '$1 $2') // Add space before capital letters
608
+ }
609
+
432
610
  /**
433
611
  * @param {(error?: Error) => void} fCallback - The callback function.
434
612
  */
@@ -446,7 +624,9 @@ class MeadowEndpointsRecordSetProvider extends libRecordSetProviderBase
446
624
  {
447
625
  if (error)
448
626
  {
449
- throw error;
627
+ this.fable.log.error('Error fetching schema', error);
628
+ this._Schema = null;
629
+ return fCallback(error);
450
630
  }
451
631
  this._Schema = result;
452
632
  return fCallback(null);
@@ -312,6 +312,7 @@ class RecordSetMetacontroller extends libFableServiceProviderBase
312
312
  // Add the subviews internally and externally
313
313
  this.pict.addTemplate(require('../templates/Pict-Template-FilterView.js'));
314
314
  this.pict.addTemplate(require('../templates/Pict-Template-FilterInstanceViews.js'));
315
+ this.pict.addTemplate(require('../views/filters').Base);
315
316
  this.childViews.errorNotFound = this.fable.addView('RSP-RecordSet-Error-NotFound', ViewDefinitionRecordSetErrorNotFound);
316
317
  this.childViews.list = this.fable.addView('RSP-RecordSet-List', this.options, ViewRecordSetList);
317
318
  this.childViews.edit = this.fable.addView('RSP-RecordSet-Edit', this.options, ViewRecordSetEdit);
@@ -38,6 +38,7 @@ const _DEFAULT_CONFIGURATION_SUBSET_Filter =
38
38
  {~FIV:Record~}
39
39
  </div>
40
40
  {~T:PRSP-SUBSET-Filters-Template-Button-Fieldset~}
41
+ {~T:PRSP-SUBSET-Filters-Template-AddFilter-Fieldset~}
41
42
  </form>
42
43
  </section>
43
44
  <!-- DefaultPackage end view template: [PRSP-SUBSET-Filters-Template] -->
@@ -63,6 +64,60 @@ const _DEFAULT_CONFIGURATION_SUBSET_Filter =
63
64
  <button type="submit" id="PRSP_Filter_Button_Apply">Apply</button>
64
65
  </fieldset>
65
66
  <!-- DefaultPackage end view template: [PRSP-SUBSET-Filters-Template-Button-Fieldset] -->
67
+ `
68
+ },
69
+ {
70
+ Hash: 'PRSP-SUBSET-Filters-Template-AddFilter-Fieldset',
71
+ Template: /*html*/`
72
+ <!-- DefaultPackage pict view template: [PRSP-SUBSET-Filters-Template-AddFilter-Fieldset] -->
73
+ <fieldset>
74
+ <button type="button" id="PRSP_Filter_Button_Add" onclick="_Pict.views['PRSP-Filters'].selectFilterToAdd(event, '{~D:Record.RecordSet~}', '{~D:Record.ViewContext~}')">+</button>
75
+ <div id="PRSP-SUBSET-Filters-Template-AddFilter-Dropdown"></div>
76
+ </fieldset>
77
+ <!-- DefaultPackage end view template: [PRSP-SUBSET-Filters-Template-AddFilter-Fieldset] -->
78
+ `
79
+ },
80
+ {
81
+ Hash: 'PRSP-SUBSET-Filters-Template-AddFilter-Dropdown',
82
+ Template: /*html*/`
83
+ <!-- DefaultPackage pict view template: [PRSP-SUBSET-Filters-Template-AddFilter-Dropdown] -->
84
+ <div>
85
+ <select id="PRSP-SUBSET-Filters-Template-AddFilter-Dropdown-Select" onchange="event.preventDefault(); _Pict.views['PRSP-Filters'].render('PRSP-SUBSET-Filters-Template-AddFilter-Dropdown-AddFilterClauseDropdown', undefined,
86
+ {
87
+ RecordSet: event.target.querySelector('option:checked').getAttribute('data-i-recordset'),
88
+ FilterKey: event.target.querySelector('option:checked').getAttribute('data-i-filter-key'),
89
+ AvailableClauses: _Pict.providers[\`RSP-Provider-\${event.target.querySelector('option:checked').getAttribute('data-i-recordset')}\`].getFilterClauseSchemaForKey(event.target.querySelector('option:checked').getAttribute('data-i-filter-key')).AvailableClauses,
90
+ });">
91
+ {~TS:PRSP-SUBSET-Filters-Template-AddFilter-Dropdown-Entry:Scope.getFilterSchema()~}
92
+ </select>
93
+ <div id="PRSP-SUBSET-Filters-Template-AddFilter-Dropdown-AddFilterClauseDropdown">
94
+ </div>
95
+ </div>
96
+ <!-- DefaultPackage end view template: [PRSP-SUBSET-Filters-Template-AddFilter-Dropdown] -->
97
+ `
98
+ },
99
+ {
100
+ Hash: 'PRSP-SUBSET-Filters-Template-AddFilter-Dropdown-AddFilterClauseDropdown',
101
+ Template: /*html*/`
102
+ <!-- DefaultPackage pict view template: [PRSP-SUBSET-Filters-Template-AddFilter-Dropdown-AddFilterClauseDropdown] -->
103
+ <select id="PRSP-SUBSET-Filters-Template-AddFilter-Dropdown-AddFilterClauseDropdown-Select">
104
+ {~TS:PRSP-SUBSET-Filters-Template-AddFilter-Dropdown-Entry:Record.AvailableClauses~}
105
+ </select>
106
+ <button type="button" id="PRSP_Filter_Button_ConfirmAdd" onclick="_Pict.views['PRSP-Filters'].addFilter(event, '{~D:Record.RecordSet~}', '{~D:Record.ViewContext~}',
107
+ document.getElementById('PRSP-SUBSET-Filters-Template-AddFilter-Dropdown-AddFilterClauseDropdown').querySelector('option:checked').getAttribute('data-i-filter-key'),
108
+ document.getElementById('PRSP-SUBSET-Filters-Template-AddFilter-Dropdown-AddFilterClauseDropdown').querySelector('option:checked').getAttribute('data-i-clause-key'),
109
+ )">Add Filter</button>
110
+ <!-- DefaultPackage end view template: [PRSP-SUBSET-Filters-Template-AddFilter-Dropdown-AddFilterClauseDropdown] -->
111
+ `
112
+ },
113
+ {
114
+ Hash: 'PRSP-SUBSET-Filters-Template-AddFilter-Dropdown-Entry',
115
+ Template: /*html*/`
116
+ <!-- DefaultPackage pict view template: [PRSP-SUBSET-Filters-Template-AddFilter-Dropdown-Entry] -->
117
+ <option value="{~D:Record.FilterKey~}|{~D:Record.ClauseKey~}" data-i-recordset="{~D:Record.RecordSet~}" data-i-filter-key="{~D:Record.FilterKey~}" data-i-clause-key="{~D:Record.ClauseKey~}">
118
+ {~D:Record.DisplayName~}
119
+ </option>
120
+ <!-- DefaultPackage end view template: [PRSP-SUBSET-Filters-Template-AddFilter-Dropdown-Entry] -->
66
121
  `
67
122
  },
68
123
  ],
@@ -75,6 +130,18 @@ const _DEFAULT_CONFIGURATION_SUBSET_Filter =
75
130
  DestinationAddress: '#PRSP_Filters_Container',
76
131
  RenderMethod: 'replace'
77
132
  },
133
+ {
134
+ RenderableHash: 'PRSP-SUBSET-Filters-Template-AddFilter-Dropdown',
135
+ TemplateHash: 'PRSP-SUBSET-Filters-Template-AddFilter-Dropdown',
136
+ ContentDestinationAddress: '#PRSP-SUBSET-Filters-Template-AddFilter-Dropdown',
137
+ RenderMethod: 'replace',
138
+ },
139
+ {
140
+ RenderableHash: 'PRSP-SUBSET-Filters-Template-AddFilter-Dropdown-AddFilterClauseDropdown',
141
+ TemplateHash: 'PRSP-SUBSET-Filters-Template-AddFilter-Dropdown-AddFilterClauseDropdown',
142
+ ContentDestinationAddress: '#PRSP-SUBSET-Filters-Template-AddFilter-Dropdown-AddFilterClauseDropdown',
143
+ RenderMethod: 'replace',
144
+ },
78
145
  ],
79
146
 
80
147
  Manifests: {},
@@ -275,6 +342,33 @@ class ViewRecordSetSUBSETFilters extends libPictView
275
342
  this.performSearch(pRecordSet, pViewContext);
276
343
  }
277
344
 
345
+ /**
346
+ * @param {Event} pEvent - The DOM event that triggered the search
347
+ * @param {string} pRecordSet - The record set being filtered
348
+ * @param {string} pViewContext - The view context for the filter (ex. List, Dashboard)
349
+ */
350
+ selectFilterToAdd(pEvent, pRecordSet, pViewContext)
351
+ {
352
+ pEvent.preventDefault();
353
+ //const tmpRecordsetProvider = this.pict.providers['RSP-Provider-' + pRecordSet];
354
+ //this.pict.log.info(`Selecting filter to add for record set: ${pRecordSet} in view context: ${pViewContext}`, tmpRecordsetProvider.getFilterSchema())
355
+ this.renderWithScope(this.pict.providers[`RSP-Provider-${pRecordSet}`], 'PRSP-SUBSET-Filters-Template-AddFilter-Dropdown');
356
+ }
357
+
358
+ addFilter(pEvent, pRecordSet, pViewContext, pFilterKey, pClauseKey)
359
+ {
360
+ this.pict.log.info(`Adding filter: ${pFilterKey} with clause: ${pClauseKey} to record set: ${pRecordSet} in view context: ${pViewContext}`);
361
+ this.pict.providers[`RSP-Provider-${pRecordSet}`].addFilterClause(pFilterKey, pClauseKey);
362
+ //FIXME: we need the record from the original render here but no longer have it...
363
+ //this.render();
364
+ }
365
+
366
+ getFilterSchema(pRecordSet)
367
+ {
368
+ const tmpRecordsetProvider = this.pict.providers['RSP-Provider-' + pRecordSet];
369
+ return Object.values(tmpRecordsetProvider.getFilterSchema()).flatMap((pFilter) => pFilter.AvailableClauses || []);
370
+ }
371
+
278
372
  /**
279
373
  * Lifecycle hook that triggers after the view is rendered.
280
374
  *
@@ -288,7 +382,36 @@ class ViewRecordSetSUBSETFilters extends libPictView
288
382
  //FIXME: since this is rendering to the DOM indirectly, can't marshal right after render; need to fix this better, if this even works
289
383
  setTimeout(() =>
290
384
  {
291
- this.onMarshalToView();
385
+ if (!pRecord)
386
+ {
387
+ pRecord = {};
388
+ }
389
+ if (!Array.isArray(pRecord.AvailableClauses))
390
+ {
391
+ const tmpSelect = document.getElementById('PRSP-SUBSET-Filters-Template-AddFilter-Dropdown-Select');
392
+ if (tmpSelect)
393
+ {
394
+ const tmpActiveOption = document.getElementById('PRSP-SUBSET-Filters-Template-AddFilter-Dropdown-Select')?.querySelector('option:checked')
395
+ const tmpRecordSet = tmpActiveOption?.getAttribute('data-i-recordset');
396
+ const tmpFilterKey = tmpActiveOption?.getAttribute('data-i-filter-key');
397
+ if (tmpRecordSet && tmpFilterKey)
398
+ {
399
+ const tmpProvider = this.pict.providers[`RSP-Provider-${tmpRecordSet}`];
400
+ if (tmpProvider)
401
+ {
402
+ pRecord.AvailableClauses = tmpProvider.getFilterClauseSchemaForKey(tmpFilterKey).AvailableClauses;
403
+ if (Array.isArray(pRecord.AvailableClauses))
404
+ {
405
+ this.render('PRSP-SUBSET-Filters-Template-AddFilter-Dropdown-AddFilterClauseDropdown', undefined, pRecord);
406
+ }
407
+ }
408
+ }
409
+ }
410
+ }
411
+ setTimeout(() =>
412
+ {
413
+ this.onMarshalToView();
414
+ }, 1);
292
415
  }, 1);
293
416
  return super.onAfterRender(pRenderable, pRenderDestinationAddress, pRecord, pContent);
294
417
  }
@@ -30,13 +30,23 @@ const _DEFAULT_CONFIGURATION_SUBSET_Filter =
30
30
  Templates:
31
31
  [
32
32
  {
33
- Hash: 'PRSP-Filter-DateRange-Template',
33
+ Hash: 'PRSP-Filter-Base-Form',
34
34
  Template: /*html*/`
35
- <!-- DefaultPackage pict view template: [PRSP-Filter-Base-Template] -->
35
+ <!-- DefaultPackage pict view template: [PRSP-Filter-Base-Form] -->
36
36
  {~JD:Record~}
37
+ <!-- DefaultPackage end view template: [PRSP-Filter-Base-Form] -->
38
+ `
39
+ },
40
+ {
41
+ Hash: 'PRSP-Filter-Base-Template',
42
+ Template: /*html*/`
43
+ <!-- DefaultPackage pict view template: [PRSP-Filter-Base-Template] -->
44
+ <div>
45
+ {~TBR:Context[0].getFilterFormTemplate()~}
46
+ </div>
37
47
  <!-- DefaultPackage end view template: [PRSP-Filter-Base-Template] -->
38
48
  `
39
- }
49
+ },
40
50
  ],
41
51
 
42
52
  Renderables:
@@ -78,7 +88,11 @@ class ViewRecordSetSUBSETFilterBase extends libPictView
78
88
  Name: pRecord.Label || pRecord.ExternalFilterByColumn || pRecord.ExternalFilterByColumns?.[0] || pRecord.FilterByColumn || pRecord.FilterByColumns?.[0] || 'Value',
79
89
  DataType: 'String',
80
90
  };
91
+ }
81
92
 
93
+ getFilterFormTemplate()
94
+ {
95
+ return 'PRSP-Filter-Base-Form';
82
96
  }
83
97
  }
84
98
 
@@ -6,9 +6,6 @@ const _DEFAULT_CONFIGURATION_Filter_DateMatch =
6
6
  {
7
7
  ViewIdentifier: 'PRSP-Filter-DateMatch',
8
8
 
9
- DefaultRenderable: 'PRSP_Renderable_Filter_DateMatch',
10
- DefaultDestinationAddress: '#PRSP_Renderable_Filter_DateMatch',
11
-
12
9
  Templates:
13
10
  [
14
11
  {
@@ -20,16 +17,6 @@ const _DEFAULT_CONFIGURATION_Filter_DateMatch =
20
17
  `
21
18
  }
22
19
  ],
23
-
24
- Renderables:
25
- [
26
- {
27
- RenderableHash: 'PRSP_Renderable_Filter_DateMatch',
28
- TemplateHash: 'PRSP-Filter-DateMatch-Template',
29
- DestinationAddress: '#PRSP_Filter_DateMatch_Container',
30
- RenderMethod: 'replace'
31
- }
32
- ],
33
20
  };
34
21
 
35
22
  class ViewRecordSetSUBSETFilterDateMatch extends ViewRecordSetSUBSETFilterBase
@@ -49,6 +36,11 @@ class ViewRecordSetSUBSETFilterDateMatch extends ViewRecordSetSUBSETFilterBase
49
36
  //{~IWVDA:PSRSFilterProxyView:Record.ClauseDescriptor~}
50
37
  pRecord.ClauseDescriptor.DataType = 'DateTime';
51
38
  }
39
+
40
+ getFilterFormTemplate()
41
+ {
42
+ return 'PRSP-Filter-DateMatch-Template';
43
+ }
52
44
  }
53
45
 
54
46
  module.exports = ViewRecordSetSUBSETFilterDateMatch;
@@ -6,9 +6,6 @@ const _DEFAULT_CONFIGURATION_Filter_DateRange =
6
6
  {
7
7
  ViewIdentifier: 'PRSP-Filter-DateRange',
8
8
 
9
- DefaultRenderable: 'PRSP_Renderable_Filter_DateRange',
10
- DefaultDestinationAddress: '#PRSP_Renderable_Filter_DateRange',
11
-
12
9
  Templates:
13
10
  [
14
11
  {
@@ -21,16 +18,6 @@ const _DEFAULT_CONFIGURATION_Filter_DateRange =
21
18
  `
22
19
  }
23
20
  ],
24
-
25
- Renderables:
26
- [
27
- {
28
- RenderableHash: 'PRSP_Renderable_Filter_DateRange',
29
- TemplateHash: 'PRSP-Filter-DateRange-Template',
30
- DestinationAddress: '#PRSP_Filter_DateRange_Container',
31
- RenderMethod: 'replace'
32
- }
33
- ],
34
21
  };
35
22
 
36
23
  class ViewRecordSetSUBSETFilterDateRange extends ViewRecordSetSUBSETFilterBaseRange
@@ -67,6 +54,11 @@ class ViewRecordSetSUBSETFilterDateRange extends ViewRecordSetSUBSETFilterBaseRa
67
54
  return fCallback(pError);
68
55
  });
69
56
  }
57
+
58
+ getFilterFormTemplate()
59
+ {
60
+ return 'PRSP-Filter-DateRange-Template';
61
+ }
70
62
  }
71
63
 
72
64
  module.exports = ViewRecordSetSUBSETFilterDateRange;