pict-section-recordset 1.8.1 → 1.9.0

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pict-section-recordset",
3
- "version": "1.8.1",
3
+ "version": "1.9.0",
4
4
  "description": "Pict dynamic record set management views",
5
5
  "main": "source/Pict-Section-RecordSet.js",
6
6
  "files": [
@@ -37,9 +37,9 @@
37
37
  "browser-env": "^3.3.0",
38
38
  "eslint": "^9.28.0",
39
39
  "jquery": "^3.7.1",
40
- "pict": "^1.0.373",
40
+ "pict": "^1.0.372",
41
41
  "pict-application": "^1.0.34",
42
- "pict-docuserve": "^1.4.19",
42
+ "pict-docuserve": "^1.4.4",
43
43
  "pict-service-commandlineutility": "^1.0.19",
44
44
  "quackage": "^1.3.0",
45
45
  "typescript": "^5.9.3"
@@ -48,7 +48,7 @@
48
48
  "fable-serviceproviderbase": "^3.0.19",
49
49
  "pict-provider": "^1.0.13",
50
50
  "pict-router": "^1.0.10",
51
- "pict-section-form": "^1.1.9",
51
+ "pict-section-form": "^1.0.196",
52
52
  "pict-template": "^1.0.15",
53
53
  "pict-view": "^1.0.68",
54
54
  "sinon": "^21.0.1"
@@ -477,6 +477,43 @@ class MeadowEndpointsRecordSetProvider extends libRecordSetProviderBase
477
477
  return pRecord;
478
478
  }
479
479
 
480
+ /**
481
+ * The "list entry" display template for an entity — how one of its records should read as a single line
482
+ * in a picker option / selected chip. Returns a pict template string (rendered against the raw record by
483
+ * the picker's TextTemplate), or null to fall back to a single display field.
484
+ *
485
+ * This is deliberately a small, overridable seam: today it hard-knows `User` (whose name lives across
486
+ * NameFull / NameFirst+NameLast and needs an Email/LoginID disambiguator), but the intent is that this
487
+ * eventually reads a per-entity template off the Stricture schema instead of branching here.
488
+ *
489
+ * @param {string} pEntityName - The entity (e.g. 'User').
490
+ * @return {string|null}
491
+ */
492
+ getEntityListEntryTemplate(pEntityName)
493
+ {
494
+ // Register the User branching fall-backs once. addTemplate is idempotent (keyed by hash), so the
495
+ // guard is just to avoid the churn of re-registering on every descriptor build.
496
+ if (!this._entityListEntryTemplatesRegistered)
497
+ {
498
+ this._entityListEntryTemplatesRegistered = true;
499
+ // Name fallback: when NameFull is empty, compose it from the parts.
500
+ this.pict.TemplateProvider.addTemplate('RSP-EntityListEntry-User-NameParts', '{~D:Record.NameFirst~} {~D:Record.NameLast~}');
501
+ // Disambiguator fallback: when Email is empty, use the LoginID.
502
+ this.pict.TemplateProvider.addTemplate('RSP-EntityListEntry-User-Login', '{~D:Record.LoginID~}');
503
+ }
504
+
505
+ switch (pEntityName)
506
+ {
507
+ case 'User':
508
+ // "<name> (<email>)" — name is NameFull, else NameFirst+NameLast; the parenthetical is Email,
509
+ // else LoginID. Stable across the three name-field variations and unique enough to tell apart
510
+ // many same-named users.
511
+ return '{~DWTF:Record.NameFull:RSP-EntityListEntry-User-NameParts~} ({~DWTF:Record.Email:RSP-EntityListEntry-User-Login~})';
512
+ default:
513
+ return null;
514
+ }
515
+ }
516
+
480
517
  /**
481
518
  * @param {string} pSchemaField - The schema field name.
482
519
  * @param {Record<string, any>} pColumn - The full column definition from the schema.
@@ -498,29 +535,23 @@ class MeadowEndpointsRecordSetProvider extends libRecordSetProviderBase
498
535
  const tmpFieldHumanName = this.getHumanReadableFieldName(pSchemaField);
499
536
  const isUserAuditField = ['CreatingIDUser', 'DeletingIDUser', 'UpdatingIDUser'].includes(pSchemaField);
500
537
  const customFilterClauses = this.options.Filters?.[pSchemaField];
501
- // The entity's own identity column (AutoIdentity / AutoGUID) i.e. the primary key.
502
- const isOwnIdentityField = pMeadowSchemaField && (pMeadowSchemaField.Type === 'AutoIdentity' || pMeadowSchemaField.Type === 'AutoGUID');
503
- const isForeignKeyLike = pSchemaField.startsWith('ID') || pSchemaField.startsWith('ParentID') || isUserAuditField || customFilterClauses;
504
- if (isForeignKeyLike)
538
+ if (pSchemaField.startsWith('ID') || pSchemaField.startsWith('ParentID') || isUserAuditField || customFilterClauses)
505
539
  {
506
540
  for (const customField of Array.isArray(customFilterClauses) ? customFilterClauses : [customFilterClauses])
507
541
  {
508
- // The table the picker pulls from: an explicit RemoteTable, else this
509
- // recordset's declared Entity when the column is our own primary key (the PK
510
- // references our own records), else the name peeled from the column for a
511
- // plain foreign key. Peeling alone is the defect — a lake PK like
512
- // `IDC182_HMA_MixDesign` peels to a table name that is not the entity name.
513
- const remoteTableName = customField?.RemoteTable || (isOwnIdentityField ? this.options.Entity : pSchemaField.split('ID')[1]);
542
+ const remoteTableName = customField?.RemoteTable || pSchemaField.split('ID')[1];
514
543
  const fieldName = this.getHumanReadableFieldName(pSchemaField);
515
544
  tmpFieldFilterClauses.push(Object.assign(
516
545
  {
517
546
  "Label": `${ fieldName }`,
518
547
  "Type": "InternalJoinSelectedValueList",
519
- "ExternalFilterByColumns": remoteTableName === 'User' ? [ 'NameFirst', 'NameLast' ] : [ 'Name' ],
548
+ "ExternalFilterByColumns": remoteTableName === 'User' ? [ 'NameFirst', 'NameLast', 'Email', 'LoginID' ] : [ 'Name' ],
520
549
  "ExternalRecordDisplayTemplate": remoteTableName === 'User' ? '{~D:Record.Data.NameFirst~} {~D:Record.Data.NameLast~}' : '{~D:Record.Name~}',
550
+ // The picker's option/selected display: a per-entity "list entry" template (the seam that
551
+ // will eventually live on the Stricture schema). Disambiguates beyond a single column.
552
+ "EntityListEntryTemplate": this.getEntityListEntryTemplate(remoteTableName),
521
553
  "CoreConnectionColumn": pSchemaField,
522
554
  "RemoteTable": `${ remoteTableName }`,
523
- "URLPrefix": this.options.URLPrefix,
524
555
  "JoinExternalConnectionColumn": `ID${ remoteTableName }`,
525
556
  "JoinInternalConnectionColumn": pSchemaField,
526
557
  'DisplayName': `Selected Records`,
@@ -530,10 +561,7 @@ class MeadowEndpointsRecordSetProvider extends libRecordSetProviderBase
530
561
  }, customField));
531
562
  }
532
563
  }
533
- // The primary key is also a plain integer key, so it ALSO falls through to the
534
- // Exact / In Range clauses below. A real foreign key gets only the picker; ordinary
535
- // columns are matched by type.
536
- if (!isForeignKeyLike || isOwnIdentityField)
564
+ else
537
565
  {
538
566
  switch (tmpFieldType)
539
567
  {
@@ -683,6 +683,9 @@ class ViewRecordSetSUBSETFilters extends libPictView
683
683
  ValueField: tmpDescriptor.JoinExternalConnectionColumn || `ID${tmpDescriptor.RemoteTable}`,
684
684
  SearchFields: tmpSearchFields,
685
685
  TextField: tmpSearchFields[0],
686
+ // Composed, disambiguated option/chip label (e.g. "Brian Smith (brian@…)") — falls back to
687
+ // TextField when the entity has no list-entry template.
688
+ TextTemplate: tmpDescriptor.EntityListEntryTemplate || undefined,
686
689
  Placeholder: `Select ${pMount.Label}…`,
687
690
  OnChange: (pValue) => this.applyQuickFilterEntity(pRecordSet, pViewContext, pMount.Field, pMount.ClauseKey, pValue),
688
691
  });
@@ -156,6 +156,9 @@ class ViewRecordSetSUBSETFilterEntityReferenceBase extends ViewRecordSetSUBSETFi
156
156
  pRecord.ClauseDescriptor.PictForm.Multiple = this.isMultiSelect();
157
157
  pRecord.ClauseDescriptor.PictForm.SearchFields = pRecord.ClauseDescriptor.PictForm.SearchFields
158
158
  || pRecord.ExternalFilterByColumns || (pRecord.ExternalFilterByColumn ? [ pRecord.ExternalFilterByColumn ] : [ 'Name' ]);
159
+ // Composed, disambiguated option/chip label (the entity's "list entry" template); falls back to a
160
+ // single display field when unset.
161
+ pRecord.ClauseDescriptor.PictForm.TextTemplate = pRecord.ClauseDescriptor.PictForm.TextTemplate || pRecord.EntityListEntryTemplate;
159
162
  pRecord.ClauseDescriptor.PictForm.ValueArrayAddress = pRecord.ClauseValuesAddress;
160
163
  pRecord.ClauseDescriptor.PictForm.GetContextScopeFilter = () => this.getContextScopeFilter(this.getInformaryScopedValue(pRecord.ClauseAddress) || pRecord);
161
164
  // JoinEntity compound display (host opt-in on the clause): show each searched row joined to a
@@ -230,7 +233,6 @@ class ViewRecordSetSUBSETFilterEntityReferenceBase extends ViewRecordSetSUBSETFi
230
233
  Destination: pOffset > 0 ? `${this.getInformaryAddressPrefix()}${pClauseInformaryAddress}.SearchResultsAppend` : `${this.getInformaryAddressPrefix()}${pClauseInformaryAddress}.SearchResults`,
231
234
  RecordStartCursor: pOffset,
232
235
  PageSize: this.options.PageSize,
233
- URLPrefix: tmpClause.URLPrefix,
234
236
  }
235
237
  ],
236
238
  () =>