@memberjunction/ng-entity-card 5.30.1 → 5.32.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/dist/lib/entity-card.component.d.ts +2 -1
- package/dist/lib/entity-card.component.d.ts.map +1 -1
- package/dist/lib/entity-card.component.js +7 -7
- package/dist/lib/entity-card.component.js.map +1 -1
- package/dist/lib/entity-card.types.d.ts +2 -2
- package/dist/lib/entity-card.types.d.ts.map +1 -1
- package/dist/lib/entity-card.types.js +3 -3
- package/dist/lib/entity-card.types.js.map +1 -1
- package/package.json +4 -3
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { EventEmitter, OnChanges, SimpleChanges } from '@angular/core';
|
|
2
2
|
import { EntityInfo } from '@memberjunction/core';
|
|
3
|
+
import { BaseAngularComponent } from '@memberjunction/ng-base-types';
|
|
3
4
|
import { EntityCardVariant, CardTemplate, CardDisplayField, CancelableCardEvent, CardRecordEvent } from './entity-card.types';
|
|
4
5
|
import * as i0 from "@angular/core";
|
|
5
6
|
/**
|
|
@@ -46,7 +47,7 @@ import * as i0 from "@angular/core";
|
|
|
46
47
|
*
|
|
47
48
|
* @selector mj-entity-card
|
|
48
49
|
*/
|
|
49
|
-
export declare class MJEntityCardComponent implements OnChanges {
|
|
50
|
+
export declare class MJEntityCardComponent extends BaseAngularComponent implements OnChanges {
|
|
50
51
|
private cdr;
|
|
51
52
|
/**
|
|
52
53
|
* The entity name (e.g., "Members"). Used to look up `EntityInfo` from
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"entity-card.component.d.ts","sourceRoot":"","sources":["../../src/lib/entity-card.component.ts"],"names":[],"mappings":"AAAA,OAAO,EACuB,YAAY,EACtC,SAAS,EAAE,aAAa,EAC3B,MAAM,eAAe,CAAC;AACvB,OAAO,
|
|
1
|
+
{"version":3,"file":"entity-card.component.d.ts","sourceRoot":"","sources":["../../src/lib/entity-card.component.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Component, Input, Output, EventEmitter, ChangeDetectionStrategy, ChangeDetectorRef, inject } from '@angular/core';
|
|
2
|
-
import {
|
|
2
|
+
import { BaseAngularComponent } from '@memberjunction/ng-base-types';
|
|
3
3
|
import { GenerateCardTemplate, GenerateCardTemplateFromMetadata, CombineTitleFields, FormatKeyAsLabel, } from './entity-card.types';
|
|
4
4
|
import * as i0 from "@angular/core";
|
|
5
5
|
const _forTrack0 = ($index, $item) => $item.Name;
|
|
@@ -169,7 +169,7 @@ function MJEntityCardComponent_Conditional_10_Template(rf, ctx) { if (rf & 1) {
|
|
|
169
169
|
*
|
|
170
170
|
* @selector mj-entity-card
|
|
171
171
|
*/
|
|
172
|
-
export class MJEntityCardComponent {
|
|
172
|
+
export class MJEntityCardComponent extends BaseAngularComponent {
|
|
173
173
|
cdr = inject(ChangeDetectorRef);
|
|
174
174
|
// ================================================================
|
|
175
175
|
// Data Inputs
|
|
@@ -418,7 +418,7 @@ export class MJEntityCardComponent {
|
|
|
418
418
|
if (!this.EntityName)
|
|
419
419
|
return null;
|
|
420
420
|
try {
|
|
421
|
-
const md =
|
|
421
|
+
const md = this.ProviderToUse;
|
|
422
422
|
return md.Entities.find(e => e.Name === this.EntityName) ?? null;
|
|
423
423
|
}
|
|
424
424
|
catch {
|
|
@@ -446,7 +446,7 @@ export class MJEntityCardComponent {
|
|
|
446
446
|
BadgeField: full.BadgeField && keySet.has(full.BadgeField) ? full.BadgeField : null,
|
|
447
447
|
};
|
|
448
448
|
}
|
|
449
|
-
return GenerateCardTemplateFromMetadata(entityName, metadataKeys, this.ResolvedMaxFields);
|
|
449
|
+
return GenerateCardTemplateFromMetadata(entityName, metadataKeys, this.ResolvedMaxFields, this.ProviderToUse);
|
|
450
450
|
}
|
|
451
451
|
resolveTitle() {
|
|
452
452
|
if (!this.EffectiveTemplate || this.EffectiveTemplate.TitleFields.length === 0) {
|
|
@@ -512,8 +512,8 @@ export class MJEntityCardComponent {
|
|
|
512
512
|
RecordID: String(this.Record['RecordID'] ?? this.Record['ID'] ?? ''),
|
|
513
513
|
};
|
|
514
514
|
}
|
|
515
|
-
static ɵfac = function MJEntityCardComponent_Factory(__ngFactoryType__) { return
|
|
516
|
-
static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: MJEntityCardComponent, selectors: [["mj-entity-card"]], inputs: { EntityName: "EntityName", Entity: "Entity", Record: "Record", Variant: "Variant", Template: "Template", MaxDisplayFields: "MaxDisplayFields", ShowAvatar: "ShowAvatar", ShowOpenButton: "ShowOpenButton", ShowFieldLabels: "ShowFieldLabels", CssClass: "CssClass" }, outputs: { BeforeCardClick: "BeforeCardClick", CardClicked: "CardClicked", BeforeOpen: "BeforeOpen", OpenRequested: "OpenRequested" }, features: [i0.ɵɵNgOnChangesFeature], decls: 11, vars: 9, consts: [[1, "mj-entity-card", 3, "click"], [1, "mj-ec-avatar-area"], [1, "mj-ec-content"], [1, "mj-ec-header"], [1, "mj-ec-title"], [1, "mj-ec-entity"], [1, "mj-ec-subtitle"], [1, "mj-ec-description"], [1, "mj-ec-fields", 3, "mj-ec-fields--labeled"], ["title", "Open record", 1, "mj-ec-open-btn"], [1, "mj-ec-thumbnail"], [1, "mj-ec-avatar", 3, "background"], ["alt", "", 3, "src"], [1, "mj-ec-avatar"], [1, "mj-ec-fields"], [1, "mj-ec-field"], [1, "mj-ec-field-label"], [1, "mj-ec-field-value"], ["title", "Open record", 1, "mj-ec-open-btn", 3, "click"], [1, "fa-solid", "fa-external-link-alt"]], template: function MJEntityCardComponent_Template(rf, ctx) { if (rf & 1) {
|
|
515
|
+
static ɵfac = /*@__PURE__*/ (() => { let ɵMJEntityCardComponent_BaseFactory; return function MJEntityCardComponent_Factory(__ngFactoryType__) { return (ɵMJEntityCardComponent_BaseFactory || (ɵMJEntityCardComponent_BaseFactory = i0.ɵɵgetInheritedFactory(MJEntityCardComponent)))(__ngFactoryType__ || MJEntityCardComponent); }; })();
|
|
516
|
+
static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: MJEntityCardComponent, selectors: [["mj-entity-card"]], inputs: { EntityName: "EntityName", Entity: "Entity", Record: "Record", Variant: "Variant", Template: "Template", MaxDisplayFields: "MaxDisplayFields", ShowAvatar: "ShowAvatar", ShowOpenButton: "ShowOpenButton", ShowFieldLabels: "ShowFieldLabels", CssClass: "CssClass" }, outputs: { BeforeCardClick: "BeforeCardClick", CardClicked: "CardClicked", BeforeOpen: "BeforeOpen", OpenRequested: "OpenRequested" }, features: [i0.ɵɵInheritDefinitionFeature, i0.ɵɵNgOnChangesFeature], decls: 11, vars: 9, consts: [[1, "mj-entity-card", 3, "click"], [1, "mj-ec-avatar-area"], [1, "mj-ec-content"], [1, "mj-ec-header"], [1, "mj-ec-title"], [1, "mj-ec-entity"], [1, "mj-ec-subtitle"], [1, "mj-ec-description"], [1, "mj-ec-fields", 3, "mj-ec-fields--labeled"], ["title", "Open record", 1, "mj-ec-open-btn"], [1, "mj-ec-thumbnail"], [1, "mj-ec-avatar", 3, "background"], ["alt", "", 3, "src"], [1, "mj-ec-avatar"], [1, "mj-ec-fields"], [1, "mj-ec-field"], [1, "mj-ec-field-label"], [1, "mj-ec-field-value"], ["title", "Open record", 1, "mj-ec-open-btn", 3, "click"], [1, "fa-solid", "fa-external-link-alt"]], template: function MJEntityCardComponent_Template(rf, ctx) { if (rf & 1) {
|
|
517
517
|
i0.ɵɵdomElementStart(0, "div", 0);
|
|
518
518
|
i0.ɵɵdomListener("click", function MJEntityCardComponent_Template_div_click_0_listener() { return ctx.OnCardClick(); });
|
|
519
519
|
i0.ɵɵconditionalCreate(1, MJEntityCardComponent_Conditional_1_Template, 5, 1, "div", 1);
|
|
@@ -578,5 +578,5 @@ export class MJEntityCardComponent {
|
|
|
578
578
|
}], OpenRequested: [{
|
|
579
579
|
type: Output
|
|
580
580
|
}] }); })();
|
|
581
|
-
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(MJEntityCardComponent, { className: "MJEntityCardComponent", filePath: "lib/entity-card.component.ts", lineNumber:
|
|
581
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(MJEntityCardComponent, { className: "MJEntityCardComponent", filePath: "lib/entity-card.component.ts", lineNumber: 65 }); })();
|
|
582
582
|
//# sourceMappingURL=entity-card.component.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"entity-card.component.js","sourceRoot":"","sources":["../../src/lib/entity-card.component.ts","../../src/lib/entity-card.component.html"],"names":[],"mappings":"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","sourcesContent":["import {\n Component, Input, Output, EventEmitter,\n OnChanges, SimpleChanges, ChangeDetectionStrategy, ChangeDetectorRef, inject\n} from '@angular/core';\nimport { Metadata, EntityInfo } from '@memberjunction/core';\nimport {\n EntityCardVariant, CardTemplate, CardDisplayField,\n CancelableCardEvent, CardRecordEvent,\n GenerateCardTemplate, GenerateCardTemplateFromMetadata,\n CombineTitleFields, FormatKeyAsLabel,\n} from './entity-card.types';\n\n/**\n * `MJEntityCardComponent` — A generic, metadata-driven entity record card.\n *\n * Renders a single entity record as a visual card with automatically prioritized\n * fields based on MemberJunction entity metadata (`IsNameField`, `DefaultInView`,\n * `Sequence`). Designed to work with both full entity records and sparse vector\n * metadata from the Knowledge Hub.\n *\n * ## Variants\n * - `compact` — Tooltip-sized. Title + 2-3 key fields. No avatar.\n * - `card` — Grid card. Avatar + title + subtitle + display fields + badge.\n * - `detail` — Full panel. All available fields with labels.\n *\n * ## Field Resolution\n * 1. **Title**: All `IsNameField` fields combined in Sequence order\n * 2. **Primary fields**: `DefaultInView=true` sorted by Sequence\n * 3. **Fallback**: First 5 non-PK/FK fields when no metadata flags are set\n *\n * ## Events\n * All interactions follow the `BeforeXXX` / `AfterXXX` cancelable event pattern.\n * Set `event.Cancel = true` in a `BeforeXXX` handler to prevent the default action.\n *\n * @example\n * ```html\n * <!-- From entity name + vector metadata -->\n * <mj-entity-card\n * [EntityName]=\"'Members'\"\n * [Record]=\"vectorMetadata\"\n * Variant=\"card\"\n * (CardClicked)=\"onCardClick($event)\">\n * </mj-entity-card>\n *\n * <!-- From EntityInfo + full record -->\n * <mj-entity-card\n * [Entity]=\"entityInfo\"\n * [Record]=\"record.GetAll()\"\n * Variant=\"detail\"\n * [ShowOpenButton]=\"true\"\n * (OpenRequested)=\"onOpen($event)\">\n * </mj-entity-card>\n * ```\n *\n * @selector mj-entity-card\n */\n@Component({\n standalone: true,\n selector: 'mj-entity-card',\n templateUrl: './entity-card.component.html',\n styleUrls: ['./entity-card.component.css'],\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class MJEntityCardComponent implements OnChanges {\n private cdr = inject(ChangeDetectorRef);\n\n // ================================================================\n // Data Inputs\n // ================================================================\n\n /**\n * The entity name (e.g., \"Members\"). Used to look up `EntityInfo` from\n * the MJ Metadata cache. Mutually exclusive with `Entity` — if both are\n * provided, `Entity` takes precedence.\n */\n @Input() EntityName: string | null = null;\n\n /**\n * Pre-resolved EntityInfo object. Takes precedence over `EntityName`.\n * Use this when you already have the EntityInfo to avoid a redundant lookup.\n */\n @Input() Entity: EntityInfo | null = null;\n\n /**\n * The record data as a plain key-value object. Works with both full entity\n * records (`record.GetAll()`) and sparse vector metadata.\n *\n * Fields not present in the record are gracefully skipped in the display.\n */\n @Input() Record: Record<string, unknown> = {};\n\n /**\n * Visual variant controlling size, layout, and detail level.\n * @default 'card'\n */\n @Input() Variant: EntityCardVariant = 'card';\n\n // ================================================================\n // Customization Inputs\n // ================================================================\n\n /**\n * Custom card template. When provided, overrides the auto-generated template.\n * Use `GenerateCardTemplate()` from the types module to build one programmatically.\n */\n @Input() Template: CardTemplate | null = null;\n\n /**\n * Maximum number of display fields in the card body.\n * Only applies when using the auto-generated template.\n * @default 4 for 'card', 2 for 'compact', unlimited for 'detail'\n */\n @Input() MaxDisplayFields: number | null = null;\n\n /**\n * Whether to show the entity icon/avatar.\n * @default true for 'card', false for 'compact', true for 'detail'\n */\n @Input() ShowAvatar: boolean | null = null;\n\n /**\n * Whether to show the \"Open Record\" button.\n * @default false\n */\n @Input() ShowOpenButton = false;\n\n /**\n * Whether to show field labels alongside values.\n * @default true for 'detail', false for 'compact', true for 'card'\n */\n @Input() ShowFieldLabels: boolean | null = null;\n\n /**\n * CSS class to apply to the outer container for custom styling.\n */\n @Input() CssClass = '';\n\n // ================================================================\n // Events — BeforeXXX / AfterXXX Pattern\n // ================================================================\n\n /**\n * Fires before the card is clicked. Set `Cancel = true` to prevent\n * the `CardClicked` event from firing.\n */\n @Output() BeforeCardClick = new EventEmitter<CancelableCardEvent<CardRecordEvent>>();\n\n /** Fires after the card is clicked (if not canceled). */\n @Output() CardClicked = new EventEmitter<CardRecordEvent>();\n\n /**\n * Fires before the \"Open Record\" button is clicked. Set `Cancel = true`\n * to prevent the `OpenRequested` event from firing.\n */\n @Output() BeforeOpen = new EventEmitter<CancelableCardEvent<CardRecordEvent>>();\n\n /** Fires when the \"Open Record\" button is clicked (if not canceled). */\n @Output() OpenRequested = new EventEmitter<CardRecordEvent>();\n\n // ================================================================\n // Computed State\n // ================================================================\n\n /** The resolved EntityInfo (from Entity input or EntityName lookup) */\n public ResolvedEntity: EntityInfo | null = null;\n\n /** The effective card template (custom or auto-generated) */\n public EffectiveTemplate: CardTemplate | null = null;\n\n /** The combined title string */\n public Title = '';\n\n /** The entity icon class (from vector metadata or entity info) */\n public EntityIcon = '';\n\n /** Resolved entity display name */\n public EntityDisplayName = '';\n\n // ================================================================\n // Lifecycle\n // ================================================================\n\n ngOnChanges(changes: SimpleChanges): void {\n if (changes['Entity'] || changes['EntityName'] || changes['Record'] || changes['Template'] || changes['Variant']) {\n this.resolve();\n }\n }\n\n // ================================================================\n // Event Handlers\n // ================================================================\n\n /** @internal Handle card click with cancelable before/after pattern */\n public OnCardClick(): void {\n const payload = this.BuildEventPayload();\n const cancelable: CancelableCardEvent<CardRecordEvent> = { Data: payload, Cancel: false };\n this.BeforeCardClick.emit(cancelable);\n if (!cancelable.Cancel) {\n this.CardClicked.emit(payload);\n }\n }\n\n /** @internal Handle open button click with cancelable before/after pattern */\n public OnOpenClick(event: MouseEvent): void {\n event.stopPropagation();\n const payload = this.BuildEventPayload();\n const cancelable: CancelableCardEvent<CardRecordEvent> = { Data: payload, Cancel: false };\n this.BeforeOpen.emit(cancelable);\n if (!cancelable.Cancel) {\n this.OpenRequested.emit(payload);\n }\n }\n\n // ================================================================\n // Display Helpers (used in template)\n // ================================================================\n\n /** Get the resolved display fields, respecting maxFields for variant */\n public get DisplayFields(): CardDisplayField[] {\n if (!this.EffectiveTemplate) return [];\n const max = this.ResolvedMaxFields;\n return this.EffectiveTemplate.DisplayFields.slice(0, max);\n }\n\n /** Whether to show the avatar based on variant and input */\n public get ShouldShowAvatar(): boolean {\n if (this.ShowAvatar != null) return this.ShowAvatar;\n return this.Variant !== 'compact';\n }\n\n /** Whether to show field labels based on variant and input */\n public get ShouldShowLabels(): boolean {\n if (this.ShowFieldLabels != null) return this.ShowFieldLabels;\n return this.Variant !== 'compact';\n }\n\n /** Get formatted value for a display field */\n public GetFieldValue(field: CardDisplayField): string {\n const value = this.Record[field.Name];\n if (value == null || String(value).trim() === '') return '';\n return this.FormatValue(value, field);\n }\n\n /** Get the label for a field */\n public GetFieldLabel(fieldName: string): string {\n return this.EffectiveTemplate?.FieldLabels[fieldName] ?? FormatKeyAsLabel(fieldName);\n }\n\n /** Get initials from the title for avatar fallback */\n public get Initials(): string {\n if (!this.Title || this.Title === 'Unknown') return '?';\n const words = this.Title.split(/\\s+/).filter(w => w.length > 0);\n if (words.length === 1) return words[0].substring(0, 2).toUpperCase();\n return (words[0][0] + words[words.length - 1][0]).toUpperCase();\n }\n\n /** Get a deterministic color for the avatar based on the record */\n public get AvatarColor(): string {\n const colors = [\n 'var(--mj-brand-primary)', 'var(--mj-status-success)',\n 'var(--mj-status-warning)', 'var(--mj-status-info)',\n ];\n let hash = 0;\n const key = this.Title + (this.Record['RecordID'] ?? '');\n for (let i = 0; i < key.length; i++) {\n hash = key.charCodeAt(i) + ((hash << 5) - hash);\n }\n return colors[Math.abs(hash) % colors.length];\n }\n\n /** Get the subtitle value */\n public get Subtitle(): string {\n if (!this.EffectiveTemplate?.SubtitleField) return '';\n const v = this.Record[this.EffectiveTemplate.SubtitleField];\n return v != null ? String(v) : '';\n }\n\n /** Get the description value */\n public get Description(): string {\n if (!this.EffectiveTemplate?.DescriptionField) return '';\n const v = this.Record[this.EffectiveTemplate.DescriptionField];\n if (v == null) return '';\n const s = String(v);\n return s.length > 120 ? s.substring(0, 120) + '...' : s;\n }\n\n /** Check if a thumbnail is available */\n public get HasThumbnail(): boolean {\n if (!this.EffectiveTemplate?.ThumbnailFields) return false;\n return this.EffectiveTemplate.ThumbnailFields.some(f => {\n const v = this.Record[f];\n return v != null && String(v).trim() !== '';\n });\n }\n\n /** Get the thumbnail URL or icon class */\n public get ThumbnailValue(): string {\n if (!this.EffectiveTemplate?.ThumbnailFields) return '';\n for (const f of this.EffectiveTemplate.ThumbnailFields) {\n const v = this.Record[f];\n if (v != null && String(v).trim() !== '') return String(v);\n }\n return '';\n }\n\n /** Whether the thumbnail is an image URL */\n public get ThumbnailIsImage(): boolean {\n const v = this.ThumbnailValue;\n return v.startsWith('http://') || v.startsWith('https://') || v.startsWith('data:image/');\n }\n\n /** Whether the thumbnail is an icon class */\n public get ThumbnailIsIcon(): boolean {\n const v = this.ThumbnailValue.toLowerCase();\n return v.startsWith('fa-') || v.startsWith('fa ') || v.startsWith('fas ') || v.startsWith('far ');\n }\n\n // ================================================================\n // Private Methods\n // ================================================================\n\n private get ResolvedMaxFields(): number {\n if (this.MaxDisplayFields != null) return this.MaxDisplayFields;\n switch (this.Variant) {\n case 'compact': return 3;\n case 'card': return 4;\n case 'detail': return 999;\n }\n }\n\n private resolve(): void {\n this.ResolvedEntity = this.resolveEntity();\n this.EffectiveTemplate = this.resolveTemplate();\n this.Title = this.resolveTitle();\n this.EntityIcon = this.resolveEntityIcon();\n this.EntityDisplayName = this.resolveEntityDisplayName();\n this.cdr.markForCheck();\n }\n\n private resolveEntity(): EntityInfo | null {\n if (this.Entity) return this.Entity;\n if (!this.EntityName) return null;\n try {\n const md = new Metadata();\n return md.Entities.find(e => e.Name === this.EntityName) ?? null;\n } catch {\n return null;\n }\n }\n\n private resolveTemplate(): CardTemplate | null {\n if (this.Template) return this.Template;\n const entityName = this.Entity?.Name ?? this.EntityName;\n if (!entityName) return null;\n\n const metadataKeys = Object.keys(this.Record);\n if (this.ResolvedEntity) {\n const full = GenerateCardTemplate(this.ResolvedEntity, this.ResolvedMaxFields);\n // Filter to fields present in the record\n const keySet = new Set(metadataKeys);\n return {\n ...full,\n TitleFields: full.TitleFields.filter(f => keySet.has(f)),\n DisplayFields: full.DisplayFields.filter(f => keySet.has(f.Name)),\n SubtitleField: full.SubtitleField && keySet.has(full.SubtitleField) ? full.SubtitleField : null,\n DescriptionField: full.DescriptionField && keySet.has(full.DescriptionField) ? full.DescriptionField : null,\n ThumbnailFields: full.ThumbnailFields.filter(f => keySet.has(f)),\n BadgeField: full.BadgeField && keySet.has(full.BadgeField) ? full.BadgeField : null,\n };\n }\n\n return GenerateCardTemplateFromMetadata(entityName, metadataKeys, this.ResolvedMaxFields);\n }\n\n private resolveTitle(): string {\n if (!this.EffectiveTemplate || this.EffectiveTemplate.TitleFields.length === 0) {\n return String(this.Record['Name'] ?? this.Record['Title'] ?? 'Unknown');\n }\n return CombineTitleFields(this.Record, this.EffectiveTemplate.TitleFields);\n }\n\n private resolveEntityIcon(): string {\n // From vector metadata first\n const metaIcon = this.Record['EntityIcon'];\n if (metaIcon && typeof metaIcon === 'string') return metaIcon;\n // From entity info\n if (this.ResolvedEntity?.Icon) return this.ResolvedEntity.Icon;\n return 'fa-solid fa-table';\n }\n\n private resolveEntityDisplayName(): string {\n const metaEntity = this.Record['Entity'];\n if (metaEntity && typeof metaEntity === 'string') return metaEntity;\n return this.ResolvedEntity?.Name ?? this.EntityName ?? '';\n }\n\n private FormatValue(value: unknown, field: CardDisplayField): string {\n switch (field.Type) {\n case 'number': {\n const num = Number(value);\n if (isNaN(num)) return String(value);\n if (num >= 1000000) return `${(num / 1000000).toFixed(1)}M`;\n if (num >= 1000) return `${(num / 1000).toFixed(1)}K`;\n const nameLower = field.Name.toLowerCase();\n if (['amount', 'price', 'cost', 'value', 'revenue', 'total'].some(k => nameLower.includes(k))) {\n return `$${num.toLocaleString()}`;\n }\n return num.toLocaleString();\n }\n case 'boolean':\n return value ? 'Yes' : 'No';\n case 'date': {\n try {\n const d = value instanceof Date ? value : new Date(value as string | number);\n if (isNaN(d.getTime())) return String(value);\n return d.toLocaleDateString(undefined, { month: 'short', day: 'numeric', year: 'numeric' });\n } catch {\n return String(value);\n }\n }\n default: {\n const s = String(value);\n return s.length > 60 ? s.substring(0, 60) + '...' : s;\n }\n }\n }\n\n private BuildEventPayload(): CardRecordEvent {\n return {\n EntityName: this.EntityDisplayName,\n Record: this.Record,\n RecordID: String(this.Record['RecordID'] ?? this.Record['ID'] ?? ''),\n };\n }\n}\n","<div class=\"mj-entity-card\"\n [class]=\"'mj-entity-card--' + Variant + ' ' + CssClass\"\n (click)=\"OnCardClick()\">\n\n <!-- Avatar / Thumbnail -->\n @if (ShouldShowAvatar) {\n <div class=\"mj-ec-avatar-area\">\n @if (HasThumbnail && ThumbnailIsImage) {\n <div class=\"mj-ec-thumbnail\">\n <img [src]=\"ThumbnailValue\" alt=\"\" />\n </div>\n } @else if (HasThumbnail && ThumbnailIsIcon) {\n <div class=\"mj-ec-avatar\" [style.background]=\"AvatarColor\">\n <i [class]=\"ThumbnailValue\"></i>\n </div>\n } @else if (EntityIcon) {\n <div class=\"mj-ec-avatar\" [style.background]=\"AvatarColor\">\n <i [class]=\"EntityIcon\"></i>\n </div>\n } @else {\n <div class=\"mj-ec-avatar\" [style.background]=\"AvatarColor\">\n {{ Initials }}\n </div>\n }\n </div>\n }\n\n <!-- Content -->\n <div class=\"mj-ec-content\">\n <!-- Header: Title + Entity -->\n <div class=\"mj-ec-header\">\n <div class=\"mj-ec-title\">{{ Title }}</div>\n @if (EntityDisplayName && Variant !== 'compact') {\n <div class=\"mj-ec-entity\">{{ EntityDisplayName }}</div>\n }\n </div>\n\n <!-- Subtitle -->\n @if (Subtitle && Variant !== 'compact') {\n <div class=\"mj-ec-subtitle\">{{ Subtitle }}</div>\n }\n\n <!-- Description (detail variant only) -->\n @if (Description && Variant === 'detail') {\n <div class=\"mj-ec-description\">{{ Description }}</div>\n }\n\n <!-- Display Fields -->\n @if (DisplayFields.length > 0) {\n <div class=\"mj-ec-fields\" [class.mj-ec-fields--labeled]=\"ShouldShowLabels\">\n @for (field of DisplayFields; track field.Name) {\n @if (GetFieldValue(field); as value) {\n <div class=\"mj-ec-field\">\n @if (ShouldShowLabels) {\n <span class=\"mj-ec-field-label\">{{ field.Label }}</span>\n }\n <span class=\"mj-ec-field-value\">{{ value }}</span>\n </div>\n }\n }\n </div>\n }\n </div>\n\n <!-- Open Button -->\n @if (ShowOpenButton) {\n <button class=\"mj-ec-open-btn\" (click)=\"OnOpenClick($event)\" title=\"Open record\">\n <i class=\"fa-solid fa-external-link-alt\"></i>\n </button>\n }\n</div>\n"]}
|
|
1
|
+
{"version":3,"file":"entity-card.component.js","sourceRoot":"","sources":["../../src/lib/entity-card.component.ts","../../src/lib/entity-card.component.html"],"names":[],"mappings":"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","sourcesContent":["import {\n Component, Input, Output, EventEmitter,\n OnChanges, SimpleChanges, ChangeDetectionStrategy, ChangeDetectorRef, inject\n} from '@angular/core';\nimport { EntityInfo } from '@memberjunction/core';\nimport { BaseAngularComponent } from '@memberjunction/ng-base-types';\nimport {\n EntityCardVariant, CardTemplate, CardDisplayField,\n CancelableCardEvent, CardRecordEvent,\n GenerateCardTemplate, GenerateCardTemplateFromMetadata,\n CombineTitleFields, FormatKeyAsLabel,\n} from './entity-card.types';\n\n/**\n * `MJEntityCardComponent` — A generic, metadata-driven entity record card.\n *\n * Renders a single entity record as a visual card with automatically prioritized\n * fields based on MemberJunction entity metadata (`IsNameField`, `DefaultInView`,\n * `Sequence`). Designed to work with both full entity records and sparse vector\n * metadata from the Knowledge Hub.\n *\n * ## Variants\n * - `compact` — Tooltip-sized. Title + 2-3 key fields. No avatar.\n * - `card` — Grid card. Avatar + title + subtitle + display fields + badge.\n * - `detail` — Full panel. All available fields with labels.\n *\n * ## Field Resolution\n * 1. **Title**: All `IsNameField` fields combined in Sequence order\n * 2. **Primary fields**: `DefaultInView=true` sorted by Sequence\n * 3. **Fallback**: First 5 non-PK/FK fields when no metadata flags are set\n *\n * ## Events\n * All interactions follow the `BeforeXXX` / `AfterXXX` cancelable event pattern.\n * Set `event.Cancel = true` in a `BeforeXXX` handler to prevent the default action.\n *\n * @example\n * ```html\n * <!-- From entity name + vector metadata -->\n * <mj-entity-card\n * [EntityName]=\"'Members'\"\n * [Record]=\"vectorMetadata\"\n * Variant=\"card\"\n * (CardClicked)=\"onCardClick($event)\">\n * </mj-entity-card>\n *\n * <!-- From EntityInfo + full record -->\n * <mj-entity-card\n * [Entity]=\"entityInfo\"\n * [Record]=\"record.GetAll()\"\n * Variant=\"detail\"\n * [ShowOpenButton]=\"true\"\n * (OpenRequested)=\"onOpen($event)\">\n * </mj-entity-card>\n * ```\n *\n * @selector mj-entity-card\n */\n@Component({\n standalone: true,\n selector: 'mj-entity-card',\n templateUrl: './entity-card.component.html',\n styleUrls: ['./entity-card.component.css'],\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class MJEntityCardComponent extends BaseAngularComponent implements OnChanges {\n private cdr = inject(ChangeDetectorRef);\n\n // ================================================================\n // Data Inputs\n // ================================================================\n\n /**\n * The entity name (e.g., \"Members\"). Used to look up `EntityInfo` from\n * the MJ Metadata cache. Mutually exclusive with `Entity` — if both are\n * provided, `Entity` takes precedence.\n */\n @Input() EntityName: string | null = null;\n\n /**\n * Pre-resolved EntityInfo object. Takes precedence over `EntityName`.\n * Use this when you already have the EntityInfo to avoid a redundant lookup.\n */\n @Input() Entity: EntityInfo | null = null;\n\n /**\n * The record data as a plain key-value object. Works with both full entity\n * records (`record.GetAll()`) and sparse vector metadata.\n *\n * Fields not present in the record are gracefully skipped in the display.\n */\n @Input() Record: Record<string, unknown> = {};\n\n /**\n * Visual variant controlling size, layout, and detail level.\n * @default 'card'\n */\n @Input() Variant: EntityCardVariant = 'card';\n\n // ================================================================\n // Customization Inputs\n // ================================================================\n\n /**\n * Custom card template. When provided, overrides the auto-generated template.\n * Use `GenerateCardTemplate()` from the types module to build one programmatically.\n */\n @Input() Template: CardTemplate | null = null;\n\n /**\n * Maximum number of display fields in the card body.\n * Only applies when using the auto-generated template.\n * @default 4 for 'card', 2 for 'compact', unlimited for 'detail'\n */\n @Input() MaxDisplayFields: number | null = null;\n\n /**\n * Whether to show the entity icon/avatar.\n * @default true for 'card', false for 'compact', true for 'detail'\n */\n @Input() ShowAvatar: boolean | null = null;\n\n /**\n * Whether to show the \"Open Record\" button.\n * @default false\n */\n @Input() ShowOpenButton = false;\n\n /**\n * Whether to show field labels alongside values.\n * @default true for 'detail', false for 'compact', true for 'card'\n */\n @Input() ShowFieldLabels: boolean | null = null;\n\n /**\n * CSS class to apply to the outer container for custom styling.\n */\n @Input() CssClass = '';\n\n // ================================================================\n // Events — BeforeXXX / AfterXXX Pattern\n // ================================================================\n\n /**\n * Fires before the card is clicked. Set `Cancel = true` to prevent\n * the `CardClicked` event from firing.\n */\n @Output() BeforeCardClick = new EventEmitter<CancelableCardEvent<CardRecordEvent>>();\n\n /** Fires after the card is clicked (if not canceled). */\n @Output() CardClicked = new EventEmitter<CardRecordEvent>();\n\n /**\n * Fires before the \"Open Record\" button is clicked. Set `Cancel = true`\n * to prevent the `OpenRequested` event from firing.\n */\n @Output() BeforeOpen = new EventEmitter<CancelableCardEvent<CardRecordEvent>>();\n\n /** Fires when the \"Open Record\" button is clicked (if not canceled). */\n @Output() OpenRequested = new EventEmitter<CardRecordEvent>();\n\n // ================================================================\n // Computed State\n // ================================================================\n\n /** The resolved EntityInfo (from Entity input or EntityName lookup) */\n public ResolvedEntity: EntityInfo | null = null;\n\n /** The effective card template (custom or auto-generated) */\n public EffectiveTemplate: CardTemplate | null = null;\n\n /** The combined title string */\n public Title = '';\n\n /** The entity icon class (from vector metadata or entity info) */\n public EntityIcon = '';\n\n /** Resolved entity display name */\n public EntityDisplayName = '';\n\n // ================================================================\n // Lifecycle\n // ================================================================\n\n ngOnChanges(changes: SimpleChanges): void {\n if (changes['Entity'] || changes['EntityName'] || changes['Record'] || changes['Template'] || changes['Variant']) {\n this.resolve();\n }\n }\n\n // ================================================================\n // Event Handlers\n // ================================================================\n\n /** @internal Handle card click with cancelable before/after pattern */\n public OnCardClick(): void {\n const payload = this.BuildEventPayload();\n const cancelable: CancelableCardEvent<CardRecordEvent> = { Data: payload, Cancel: false };\n this.BeforeCardClick.emit(cancelable);\n if (!cancelable.Cancel) {\n this.CardClicked.emit(payload);\n }\n }\n\n /** @internal Handle open button click with cancelable before/after pattern */\n public OnOpenClick(event: MouseEvent): void {\n event.stopPropagation();\n const payload = this.BuildEventPayload();\n const cancelable: CancelableCardEvent<CardRecordEvent> = { Data: payload, Cancel: false };\n this.BeforeOpen.emit(cancelable);\n if (!cancelable.Cancel) {\n this.OpenRequested.emit(payload);\n }\n }\n\n // ================================================================\n // Display Helpers (used in template)\n // ================================================================\n\n /** Get the resolved display fields, respecting maxFields for variant */\n public get DisplayFields(): CardDisplayField[] {\n if (!this.EffectiveTemplate) return [];\n const max = this.ResolvedMaxFields;\n return this.EffectiveTemplate.DisplayFields.slice(0, max);\n }\n\n /** Whether to show the avatar based on variant and input */\n public get ShouldShowAvatar(): boolean {\n if (this.ShowAvatar != null) return this.ShowAvatar;\n return this.Variant !== 'compact';\n }\n\n /** Whether to show field labels based on variant and input */\n public get ShouldShowLabels(): boolean {\n if (this.ShowFieldLabels != null) return this.ShowFieldLabels;\n return this.Variant !== 'compact';\n }\n\n /** Get formatted value for a display field */\n public GetFieldValue(field: CardDisplayField): string {\n const value = this.Record[field.Name];\n if (value == null || String(value).trim() === '') return '';\n return this.FormatValue(value, field);\n }\n\n /** Get the label for a field */\n public GetFieldLabel(fieldName: string): string {\n return this.EffectiveTemplate?.FieldLabels[fieldName] ?? FormatKeyAsLabel(fieldName);\n }\n\n /** Get initials from the title for avatar fallback */\n public get Initials(): string {\n if (!this.Title || this.Title === 'Unknown') return '?';\n const words = this.Title.split(/\\s+/).filter(w => w.length > 0);\n if (words.length === 1) return words[0].substring(0, 2).toUpperCase();\n return (words[0][0] + words[words.length - 1][0]).toUpperCase();\n }\n\n /** Get a deterministic color for the avatar based on the record */\n public get AvatarColor(): string {\n const colors = [\n 'var(--mj-brand-primary)', 'var(--mj-status-success)',\n 'var(--mj-status-warning)', 'var(--mj-status-info)',\n ];\n let hash = 0;\n const key = this.Title + (this.Record['RecordID'] ?? '');\n for (let i = 0; i < key.length; i++) {\n hash = key.charCodeAt(i) + ((hash << 5) - hash);\n }\n return colors[Math.abs(hash) % colors.length];\n }\n\n /** Get the subtitle value */\n public get Subtitle(): string {\n if (!this.EffectiveTemplate?.SubtitleField) return '';\n const v = this.Record[this.EffectiveTemplate.SubtitleField];\n return v != null ? String(v) : '';\n }\n\n /** Get the description value */\n public get Description(): string {\n if (!this.EffectiveTemplate?.DescriptionField) return '';\n const v = this.Record[this.EffectiveTemplate.DescriptionField];\n if (v == null) return '';\n const s = String(v);\n return s.length > 120 ? s.substring(0, 120) + '...' : s;\n }\n\n /** Check if a thumbnail is available */\n public get HasThumbnail(): boolean {\n if (!this.EffectiveTemplate?.ThumbnailFields) return false;\n return this.EffectiveTemplate.ThumbnailFields.some(f => {\n const v = this.Record[f];\n return v != null && String(v).trim() !== '';\n });\n }\n\n /** Get the thumbnail URL or icon class */\n public get ThumbnailValue(): string {\n if (!this.EffectiveTemplate?.ThumbnailFields) return '';\n for (const f of this.EffectiveTemplate.ThumbnailFields) {\n const v = this.Record[f];\n if (v != null && String(v).trim() !== '') return String(v);\n }\n return '';\n }\n\n /** Whether the thumbnail is an image URL */\n public get ThumbnailIsImage(): boolean {\n const v = this.ThumbnailValue;\n return v.startsWith('http://') || v.startsWith('https://') || v.startsWith('data:image/');\n }\n\n /** Whether the thumbnail is an icon class */\n public get ThumbnailIsIcon(): boolean {\n const v = this.ThumbnailValue.toLowerCase();\n return v.startsWith('fa-') || v.startsWith('fa ') || v.startsWith('fas ') || v.startsWith('far ');\n }\n\n // ================================================================\n // Private Methods\n // ================================================================\n\n private get ResolvedMaxFields(): number {\n if (this.MaxDisplayFields != null) return this.MaxDisplayFields;\n switch (this.Variant) {\n case 'compact': return 3;\n case 'card': return 4;\n case 'detail': return 999;\n }\n }\n\n private resolve(): void {\n this.ResolvedEntity = this.resolveEntity();\n this.EffectiveTemplate = this.resolveTemplate();\n this.Title = this.resolveTitle();\n this.EntityIcon = this.resolveEntityIcon();\n this.EntityDisplayName = this.resolveEntityDisplayName();\n this.cdr.markForCheck();\n }\n\n private resolveEntity(): EntityInfo | null {\n if (this.Entity) return this.Entity;\n if (!this.EntityName) return null;\n try {\n const md = this.ProviderToUse;\n return md.Entities.find(e => e.Name === this.EntityName) ?? null;\n } catch {\n return null;\n }\n }\n\n private resolveTemplate(): CardTemplate | null {\n if (this.Template) return this.Template;\n const entityName = this.Entity?.Name ?? this.EntityName;\n if (!entityName) return null;\n\n const metadataKeys = Object.keys(this.Record);\n if (this.ResolvedEntity) {\n const full = GenerateCardTemplate(this.ResolvedEntity, this.ResolvedMaxFields);\n // Filter to fields present in the record\n const keySet = new Set(metadataKeys);\n return {\n ...full,\n TitleFields: full.TitleFields.filter(f => keySet.has(f)),\n DisplayFields: full.DisplayFields.filter(f => keySet.has(f.Name)),\n SubtitleField: full.SubtitleField && keySet.has(full.SubtitleField) ? full.SubtitleField : null,\n DescriptionField: full.DescriptionField && keySet.has(full.DescriptionField) ? full.DescriptionField : null,\n ThumbnailFields: full.ThumbnailFields.filter(f => keySet.has(f)),\n BadgeField: full.BadgeField && keySet.has(full.BadgeField) ? full.BadgeField : null,\n };\n }\n\n return GenerateCardTemplateFromMetadata(entityName, metadataKeys, this.ResolvedMaxFields, this.ProviderToUse);\n }\n\n private resolveTitle(): string {\n if (!this.EffectiveTemplate || this.EffectiveTemplate.TitleFields.length === 0) {\n return String(this.Record['Name'] ?? this.Record['Title'] ?? 'Unknown');\n }\n return CombineTitleFields(this.Record, this.EffectiveTemplate.TitleFields);\n }\n\n private resolveEntityIcon(): string {\n // From vector metadata first\n const metaIcon = this.Record['EntityIcon'];\n if (metaIcon && typeof metaIcon === 'string') return metaIcon;\n // From entity info\n if (this.ResolvedEntity?.Icon) return this.ResolvedEntity.Icon;\n return 'fa-solid fa-table';\n }\n\n private resolveEntityDisplayName(): string {\n const metaEntity = this.Record['Entity'];\n if (metaEntity && typeof metaEntity === 'string') return metaEntity;\n return this.ResolvedEntity?.Name ?? this.EntityName ?? '';\n }\n\n private FormatValue(value: unknown, field: CardDisplayField): string {\n switch (field.Type) {\n case 'number': {\n const num = Number(value);\n if (isNaN(num)) return String(value);\n if (num >= 1000000) return `${(num / 1000000).toFixed(1)}M`;\n if (num >= 1000) return `${(num / 1000).toFixed(1)}K`;\n const nameLower = field.Name.toLowerCase();\n if (['amount', 'price', 'cost', 'value', 'revenue', 'total'].some(k => nameLower.includes(k))) {\n return `$${num.toLocaleString()}`;\n }\n return num.toLocaleString();\n }\n case 'boolean':\n return value ? 'Yes' : 'No';\n case 'date': {\n try {\n const d = value instanceof Date ? value : new Date(value as string | number);\n if (isNaN(d.getTime())) return String(value);\n return d.toLocaleDateString(undefined, { month: 'short', day: 'numeric', year: 'numeric' });\n } catch {\n return String(value);\n }\n }\n default: {\n const s = String(value);\n return s.length > 60 ? s.substring(0, 60) + '...' : s;\n }\n }\n }\n\n private BuildEventPayload(): CardRecordEvent {\n return {\n EntityName: this.EntityDisplayName,\n Record: this.Record,\n RecordID: String(this.Record['RecordID'] ?? this.Record['ID'] ?? ''),\n };\n }\n}\n","<div class=\"mj-entity-card\"\n [class]=\"'mj-entity-card--' + Variant + ' ' + CssClass\"\n (click)=\"OnCardClick()\">\n\n <!-- Avatar / Thumbnail -->\n @if (ShouldShowAvatar) {\n <div class=\"mj-ec-avatar-area\">\n @if (HasThumbnail && ThumbnailIsImage) {\n <div class=\"mj-ec-thumbnail\">\n <img [src]=\"ThumbnailValue\" alt=\"\" />\n </div>\n } @else if (HasThumbnail && ThumbnailIsIcon) {\n <div class=\"mj-ec-avatar\" [style.background]=\"AvatarColor\">\n <i [class]=\"ThumbnailValue\"></i>\n </div>\n } @else if (EntityIcon) {\n <div class=\"mj-ec-avatar\" [style.background]=\"AvatarColor\">\n <i [class]=\"EntityIcon\"></i>\n </div>\n } @else {\n <div class=\"mj-ec-avatar\" [style.background]=\"AvatarColor\">\n {{ Initials }}\n </div>\n }\n </div>\n }\n\n <!-- Content -->\n <div class=\"mj-ec-content\">\n <!-- Header: Title + Entity -->\n <div class=\"mj-ec-header\">\n <div class=\"mj-ec-title\">{{ Title }}</div>\n @if (EntityDisplayName && Variant !== 'compact') {\n <div class=\"mj-ec-entity\">{{ EntityDisplayName }}</div>\n }\n </div>\n\n <!-- Subtitle -->\n @if (Subtitle && Variant !== 'compact') {\n <div class=\"mj-ec-subtitle\">{{ Subtitle }}</div>\n }\n\n <!-- Description (detail variant only) -->\n @if (Description && Variant === 'detail') {\n <div class=\"mj-ec-description\">{{ Description }}</div>\n }\n\n <!-- Display Fields -->\n @if (DisplayFields.length > 0) {\n <div class=\"mj-ec-fields\" [class.mj-ec-fields--labeled]=\"ShouldShowLabels\">\n @for (field of DisplayFields; track field.Name) {\n @if (GetFieldValue(field); as value) {\n <div class=\"mj-ec-field\">\n @if (ShouldShowLabels) {\n <span class=\"mj-ec-field-label\">{{ field.Label }}</span>\n }\n <span class=\"mj-ec-field-value\">{{ value }}</span>\n </div>\n }\n }\n </div>\n }\n </div>\n\n <!-- Open Button -->\n @if (ShowOpenButton) {\n <button class=\"mj-ec-open-btn\" (click)=\"OnOpenClick($event)\" title=\"Open record\">\n <i class=\"fa-solid fa-external-link-alt\"></i>\n </button>\n }\n</div>\n"]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { EntityInfo } from '@memberjunction/core';
|
|
1
|
+
import { EntityInfo, IMetadataProvider } from '@memberjunction/core';
|
|
2
2
|
/**
|
|
3
3
|
* Visual variant controlling the card's size, layout, and level of detail.
|
|
4
4
|
*
|
|
@@ -95,7 +95,7 @@ export declare function GenerateCardTemplate(entity: EntityInfo, maxDisplayField
|
|
|
95
95
|
* @param metadataKeys - The available field keys in the sparse metadata
|
|
96
96
|
* @param maxDisplayFields - Maximum display fields
|
|
97
97
|
*/
|
|
98
|
-
export declare function GenerateCardTemplateFromMetadata(entityName: string, metadataKeys: string[], maxDisplayFields?: number): CardTemplate;
|
|
98
|
+
export declare function GenerateCardTemplateFromMetadata(entityName: string, metadataKeys: string[], maxDisplayFields?: number, provider?: IMetadataProvider): CardTemplate;
|
|
99
99
|
/**
|
|
100
100
|
* Convert a PascalCase or camelCase key to a human-readable label.
|
|
101
101
|
* E.g., "FirstName" → "First Name", "contentTypeID" → "Content Type ID"
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"entity-card.types.d.ts","sourceRoot":"","sources":["../../src/lib/entity-card.types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAmB,UAAU,EAAE,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"entity-card.types.d.ts","sourceRoot":"","sources":["../../src/lib/entity-card.types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAmB,UAAU,EAAE,iBAAiB,EAAY,MAAM,sBAAsB,CAAC;AAMhG;;;;;;GAMG;AACH,MAAM,MAAM,iBAAiB,GAAG,SAAS,GAAG,MAAM,GAAG,QAAQ,CAAC;AAM9D,iEAAiE;AACjE,MAAM,MAAM,aAAa,GAAG,QAAQ,GAAG,SAAS,GAAG,MAAM,GAAG,MAAM,CAAC;AAEnE;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC7B,wCAAwC;IACxC,IAAI,EAAE,MAAM,CAAC;IACb,mDAAmD;IACnD,KAAK,EAAE,MAAM,CAAC;IACd,+CAA+C;IAC/C,IAAI,EAAE,aAAa,CAAC;IACpB,8DAA8D;IAC9D,eAAe,EAAE,OAAO,CAAC;IACzB,gDAAgD;IAChD,QAAQ,EAAE,MAAM,CAAC;CACpB;AAED;;;GAGG;AACH,MAAM,WAAW,YAAY;IACzB;;;OAGG;IACH,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,uDAAuD;IACvD,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,yCAAyC;IACzC,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,+CAA+C;IAC/C,aAAa,EAAE,gBAAgB,EAAE,CAAC;IAClC,wEAAwE;IACxE,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B,wDAAwD;IACxD,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,2CAA2C;IAC3C,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACvC;AAMD;;;GAGG;AACH,MAAM,WAAW,mBAAmB,CAAC,CAAC;IAClC,yCAAyC;IACzC,IAAI,EAAE,CAAC,CAAC;IACR,+CAA+C;IAC/C,MAAM,EAAE,OAAO,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC5B,sBAAsB;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,0BAA0B;IAC1B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAChC,wCAAwC;IACxC,QAAQ,EAAE,MAAM,CAAC;CACpB;AAmBD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,UAAU,EAAE,gBAAgB,GAAE,MAAU,GAAG,YAAY,CAoBnG;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,gCAAgC,CAC5C,UAAU,EAAE,MAAM,EAClB,YAAY,EAAE,MAAM,EAAE,EACtB,gBAAgB,GAAE,MAAU,EAC5B,QAAQ,CAAC,EAAE,iBAAiB,GAC7B,YAAY,CA0Bd;AAiLD;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAKpD;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAC9B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,WAAW,EAAE,MAAM,EAAE,GACtB,MAAM,CAMR"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { Metadata } from '@memberjunction/core';
|
|
1
2
|
// ================================================================
|
|
2
3
|
// Template Generation Utilities
|
|
3
4
|
// ================================================================
|
|
@@ -57,11 +58,10 @@ export function GenerateCardTemplate(entity, maxDisplayFields = 4) {
|
|
|
57
58
|
* @param metadataKeys - The available field keys in the sparse metadata
|
|
58
59
|
* @param maxDisplayFields - Maximum display fields
|
|
59
60
|
*/
|
|
60
|
-
export function GenerateCardTemplateFromMetadata(entityName, metadataKeys, maxDisplayFields = 4) {
|
|
61
|
+
export function GenerateCardTemplateFromMetadata(entityName, metadataKeys, maxDisplayFields = 4, provider) {
|
|
61
62
|
let entity = null;
|
|
62
63
|
try {
|
|
63
|
-
const
|
|
64
|
-
const md = new Metadata();
|
|
64
|
+
const md = (provider ?? Metadata.Provider);
|
|
65
65
|
entity = md.Entities.find((e) => e.Name === entityName) ?? null;
|
|
66
66
|
}
|
|
67
67
|
catch {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"entity-card.types.js","sourceRoot":"","sources":["../../src/lib/entity-card.types.ts"],"names":[],"mappings":"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","sourcesContent":["import { EntityFieldInfo, EntityInfo } from '@memberjunction/core';\n\n// ================================================================\n// Card Variant\n// ================================================================\n\n/**\n * Visual variant controlling the card's size, layout, and level of detail.\n *\n * - `compact` — Tooltip-sized: title + 2-3 primary fields, no avatar. ~80px.\n * - `card` — Grid card: avatar + title + subtitle + up to 4 display fields + badge. ~160px.\n * - `detail` — Full panel: all available fields with labels, grouped by priority.\n */\nexport type EntityCardVariant = 'compact' | 'card' | 'detail';\n\n// ================================================================\n// Card Template (auto-generated or user-provided)\n// ================================================================\n\n/** The detected type of a display field, used for formatting. */\nexport type CardFieldType = 'number' | 'boolean' | 'text' | 'date';\n\n/**\n * A single display field in the card body.\n */\nexport interface CardDisplayField {\n /** Entity field name (e.g., \"Email\") */\n Name: string;\n /** Human-readable label (e.g., \"Email Address\") */\n Label: string;\n /** Detected field type for value formatting */\n Type: CardFieldType;\n /** Whether this field has DefaultInView=true on the entity */\n IsDefaultInView: boolean;\n /** The field's Sequence from entity metadata */\n Sequence: number;\n}\n\n/**\n * Describes how a card should be laid out — which fields map to which\n * visual slots. Auto-generated from entity metadata when not provided.\n */\nexport interface CardTemplate {\n /**\n * Field names that form the card title, in display order.\n * Multiple IsNameField fields are combined (e.g., [\"FirstName\", \"LastName\"] → \"Elizabeth Rodriguez\").\n */\n TitleFields: string[];\n /** Optional subtitle field (status, type, category) */\n SubtitleField: string | null;\n /** Optional description/summary field */\n DescriptionField: string | null;\n /** Ordered display fields for the card body */\n DisplayFields: CardDisplayField[];\n /** Thumbnail field names in priority order (image URL or icon class) */\n ThumbnailFields: string[];\n /** Optional badge field (priority, severity, rating) */\n BadgeField: string | null;\n /** Map of field names to display labels */\n FieldLabels: Record<string, string>;\n}\n\n// ================================================================\n// Events\n// ================================================================\n\n/**\n * Cancelable event payload. Set `Cancel = true` in the handler to\n * prevent the default action from executing.\n */\nexport interface CancelableCardEvent<T> {\n /** The data associated with the event */\n Data: T;\n /** Set to true to cancel the default action */\n Cancel: boolean;\n}\n\n/**\n * Payload for record interaction events.\n */\nexport interface CardRecordEvent {\n /** The entity name */\n EntityName: string;\n /** The raw record data */\n Record: Record<string, unknown>;\n /** The record's primary key value(s) */\n RecordID: string;\n}\n\n// ================================================================\n// Template Generation Utilities\n// ================================================================\n\n/** Fields that should never appear in card display */\nconst INTERNAL_FIELD_PATTERNS = new Set([\n '__mj_createdat', '__mj_updatedat', 'createdat', 'updatedat',\n 'createdby', 'updatedby', 'password', 'secret', 'token', 'hash',\n]);\n\n/** Keyword patterns for auto-detecting field roles */\nconst SUBTITLE_KEYWORDS = ['status', 'type', 'category', 'state', 'stage'];\nconst DESCRIPTION_KEYWORDS = ['description', 'desc', 'summary', 'notes', 'comments', 'bio', 'about'];\nconst THUMBNAIL_KEYWORDS = ['image', 'photo', 'picture', 'thumbnail', 'avatar', 'logo', 'icon'];\nconst BADGE_KEYWORDS = ['priority', 'severity', 'importance', 'rating', 'rank', 'level'];\nconst METRIC_KEYWORDS = ['amount', 'total', 'count', 'value', 'price', 'cost', 'quantity', 'qty', 'balance', 'revenue', 'score'];\n\n/**\n * Auto-generate a CardTemplate from entity metadata.\n *\n * Priority logic:\n * 1. **Title**: All fields with `IsNameField=true`, sorted by Sequence. Falls back to\n * heuristic name detection, then first string field.\n * 2. **Display fields**: `DefaultInView=true` fields sorted by Sequence (up to maxFields).\n * Falls back to metric-keyword fields, then first N non-PK/FK fields.\n * 3. **Subtitle/Description/Thumbnail/Badge**: Detected via keyword matching on field names.\n *\n * @param entity - The entity metadata\n * @param maxDisplayFields - Maximum display fields for the card body (default 4)\n * @returns A fully populated CardTemplate\n */\nexport function GenerateCardTemplate(entity: EntityInfo, maxDisplayFields: number = 4): CardTemplate {\n const fields = entity.Fields;\n const titleFields = FindTitleFields(entity, fields);\n const titleFieldNames = new Set(titleFields.map(f => f.Name));\n\n // Build field labels map\n const fieldLabels: Record<string, string> = {};\n for (const f of fields) {\n fieldLabels[f.Name] = f.DisplayNameOrName;\n }\n\n return {\n TitleFields: titleFields.map(f => f.Name),\n SubtitleField: FindFieldByKeywords(fields, SUBTITLE_KEYWORDS, titleFieldNames),\n DescriptionField: FindFieldByKeywords(fields, DESCRIPTION_KEYWORDS, titleFieldNames),\n DisplayFields: FindDisplayFields(fields, titleFieldNames, maxDisplayFields),\n ThumbnailFields: FindThumbnailFields(fields),\n BadgeField: FindFieldByKeywords(fields, BADGE_KEYWORDS, titleFieldNames),\n FieldLabels: fieldLabels,\n };\n}\n\n/**\n * Generate a CardTemplate from an entity name string + sparse metadata keys.\n * This is the primary entry point for Knowledge Hub dashboards where we have\n * vector metadata (partial field set) rather than full entity records.\n *\n * Falls back gracefully when EntityInfo is not available (returns a template\n * based on the metadata keys alone).\n *\n * @param entityName - The entity name for metadata lookup\n * @param metadataKeys - The available field keys in the sparse metadata\n * @param maxDisplayFields - Maximum display fields\n */\nexport function GenerateCardTemplateFromMetadata(\n entityName: string,\n metadataKeys: string[],\n maxDisplayFields: number = 4\n): CardTemplate {\n let entity: EntityInfo | null = null;\n try {\n const { Metadata } = require('@memberjunction/core');\n const md = new Metadata();\n entity = md.Entities.find((e: EntityInfo) => e.Name === entityName) ?? null;\n } catch {\n // Metadata not available — use key-only fallback\n }\n\n if (entity) {\n const full = GenerateCardTemplate(entity, maxDisplayFields);\n // Filter to only fields present in the metadata\n const keySet = new Set(metadataKeys);\n return {\n ...full,\n TitleFields: full.TitleFields.filter(f => keySet.has(f)),\n DisplayFields: full.DisplayFields.filter(f => keySet.has(f.Name)),\n SubtitleField: full.SubtitleField && keySet.has(full.SubtitleField) ? full.SubtitleField : null,\n DescriptionField: full.DescriptionField && keySet.has(full.DescriptionField) ? full.DescriptionField : null,\n ThumbnailFields: full.ThumbnailFields.filter(f => keySet.has(f)),\n BadgeField: full.BadgeField && keySet.has(full.BadgeField) ? full.BadgeField : null,\n };\n }\n\n // Fallback: no entity metadata, use key heuristics\n return GenerateFallbackTemplate(metadataKeys, maxDisplayFields);\n}\n\n// ================================================================\n// Internal Helpers\n// ================================================================\n\nfunction FindTitleFields(entity: EntityInfo, fields: EntityFieldInfo[]): EntityFieldInfo[] {\n // 1. All IsNameField fields, sorted by Sequence\n const nameFields = fields\n .filter(f => f.IsNameField)\n .sort((a, b) => (a.Sequence ?? 9999) - (b.Sequence ?? 9999));\n if (nameFields.length > 0) return nameFields;\n\n // 2. entity.NameField (singular)\n if (entity.NameField) return [entity.NameField];\n\n // 3. Heuristic: field named \"Name\" or \"Title\"\n const heuristic = fields.find(f =>\n (f.Name.toLowerCase() === 'name' || f.Name.toLowerCase() === 'title') &&\n f.TSType === 'string' && !f.IsPrimaryKey\n );\n if (heuristic) return [heuristic];\n\n // 4. Field ending with \"Name\" that's a string\n const endsWithName = fields.find(f =>\n f.Name.toLowerCase().endsWith('name') && f.TSType === 'string' &&\n !f.IsPrimaryKey && !f.Name.toLowerCase().includes('file')\n );\n if (endsWithName) return [endsWithName];\n\n // 5. First string field\n const firstString = fields.find(f =>\n f.TSType === 'string' && !f.IsPrimaryKey && !f.Name.toLowerCase().includes('id')\n );\n if (firstString) return [firstString];\n\n return [];\n}\n\nfunction FindDisplayFields(\n fields: EntityFieldInfo[],\n excludeNames: Set<string>,\n maxFields: number\n): CardDisplayField[] {\n const result: CardDisplayField[] = [];\n const isExcluded = (f: EntityFieldInfo) =>\n f.IsPrimaryKey || excludeNames.has(f.Name) ||\n INTERNAL_FIELD_PATTERNS.has(f.Name.toLowerCase()) ||\n f.Name.toLowerCase().endsWith('id') && f.RelatedEntityID != null; // FK fields\n\n // 1. DefaultInView fields first\n const defaultInView = fields\n .filter(f => f.DefaultInView && !isExcluded(f))\n .sort((a, b) => (a.Sequence ?? 9999) - (b.Sequence ?? 9999));\n\n for (const f of defaultInView) {\n if (result.length >= maxFields) break;\n result.push(BuildCardField(f));\n }\n\n if (result.length >= 2) return result;\n\n // 2. Metric keyword fields\n const existing = new Set(result.map(r => r.Name));\n for (const f of fields) {\n if (result.length >= maxFields) break;\n if (existing.has(f.Name) || isExcluded(f)) continue;\n if (METRIC_KEYWORDS.some(kw => f.Name.toLowerCase().includes(kw))) {\n result.push(BuildCardField(f));\n existing.add(f.Name);\n }\n }\n\n if (result.length >= 2) return result;\n\n // 3. First N non-excluded fields by sequence\n for (const f of fields.sort((a, b) => (a.Sequence ?? 9999) - (b.Sequence ?? 9999))) {\n if (result.length >= maxFields) break;\n if (existing.has(f.Name) || isExcluded(f)) continue;\n result.push(BuildCardField(f));\n existing.add(f.Name);\n }\n\n return result;\n}\n\nfunction FindFieldByKeywords(\n fields: EntityFieldInfo[],\n keywords: string[],\n excludeNames: Set<string>\n): string | null {\n for (const keyword of keywords) {\n const field = fields.find(f =>\n f.Name.toLowerCase().includes(keyword) &&\n f.TSType === 'string' &&\n !f.IsPrimaryKey &&\n !excludeNames.has(f.Name)\n );\n if (field) return field.Name;\n }\n return null;\n}\n\nfunction FindThumbnailFields(fields: EntityFieldInfo[]): string[] {\n const result: string[] = [];\n for (const keyword of THUMBNAIL_KEYWORDS) {\n for (const f of fields) {\n if (f.Name.toLowerCase().includes(keyword) && f.TSType === 'string' && !result.includes(f.Name)) {\n result.push(f.Name);\n }\n }\n }\n return result;\n}\n\nfunction BuildCardField(field: EntityFieldInfo): CardDisplayField {\n return {\n Name: field.Name,\n Label: field.DisplayNameOrName,\n Type: DetectFieldType(field),\n IsDefaultInView: field.DefaultInView ?? false,\n Sequence: field.Sequence ?? 9999,\n };\n}\n\nfunction DetectFieldType(field: EntityFieldInfo): CardFieldType {\n if (field.TSType === 'boolean' || field.Type?.toLowerCase() === 'bit') return 'boolean';\n if (field.TSType === 'number') return 'number';\n if (field.TSType === 'Date' || field.Type?.toLowerCase().includes('date')) return 'date';\n return 'text';\n}\n\n/**\n * Fallback template generation when entity metadata is not available.\n * Uses field name heuristics on the raw metadata keys.\n */\nfunction GenerateFallbackTemplate(keys: string[], maxFields: number): CardTemplate {\n const internal = new Set([\n 'Entity', 'EntityIcon', 'RecordID', 'TemplateID',\n '__mj_UpdatedAt', '__mj_CreatedAt', 'ID',\n ]);\n const usable = keys.filter(k => !internal.has(k));\n\n // Title: 'Name' or first key\n const titleKey = usable.find(k => k.toLowerCase() === 'name')\n ?? usable.find(k => k.toLowerCase().endsWith('name'))\n ?? usable.find(k => k.toLowerCase() === 'title');\n const titleFields = titleKey ? [titleKey] : usable.length > 0 ? [usable[0]] : [];\n const titleSet = new Set(titleFields);\n\n const displayFields: CardDisplayField[] = usable\n .filter(k => !titleSet.has(k))\n .slice(0, maxFields)\n .map((k, i) => ({\n Name: k,\n Label: FormatKeyAsLabel(k),\n Type: 'text' as CardFieldType,\n IsDefaultInView: false,\n Sequence: i,\n }));\n\n const fieldLabels: Record<string, string> = {};\n for (const k of keys) {\n fieldLabels[k] = FormatKeyAsLabel(k);\n }\n\n return {\n TitleFields: titleFields,\n SubtitleField: null,\n DescriptionField: null,\n DisplayFields: displayFields,\n ThumbnailFields: [],\n BadgeField: null,\n FieldLabels: fieldLabels,\n };\n}\n\n/**\n * Convert a PascalCase or camelCase key to a human-readable label.\n * E.g., \"FirstName\" → \"First Name\", \"contentTypeID\" → \"Content Type ID\"\n */\nexport function FormatKeyAsLabel(key: string): string {\n return key\n .replace(/([a-z])([A-Z])/g, '$1 $2')\n .replace(/([A-Z]+)([A-Z][a-z])/g, '$1 $2')\n .replace(/^./, c => c.toUpperCase());\n}\n\n/**\n * Combine multiple title field values into a single display string.\n * E.g., [\"Elizabeth\", \"Rodriguez\"] → \"Elizabeth Rodriguez\"\n */\nexport function CombineTitleFields(\n record: Record<string, unknown>,\n titleFields: string[]\n): string {\n const parts = titleFields\n .map(f => record[f])\n .filter(v => v != null && String(v).trim() !== '')\n .map(v => String(v));\n return parts.length > 0 ? parts.join(' ') : 'Unknown';\n}\n"]}
|
|
1
|
+
{"version":3,"file":"entity-card.types.js","sourceRoot":"","sources":["../../src/lib/entity-card.types.ts"],"names":[],"mappings":"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","sourcesContent":["import { EntityFieldInfo, EntityInfo, IMetadataProvider, Metadata } from '@memberjunction/core';\n\n// ================================================================\n// Card Variant\n// ================================================================\n\n/**\n * Visual variant controlling the card's size, layout, and level of detail.\n *\n * - `compact` — Tooltip-sized: title + 2-3 primary fields, no avatar. ~80px.\n * - `card` — Grid card: avatar + title + subtitle + up to 4 display fields + badge. ~160px.\n * - `detail` — Full panel: all available fields with labels, grouped by priority.\n */\nexport type EntityCardVariant = 'compact' | 'card' | 'detail';\n\n// ================================================================\n// Card Template (auto-generated or user-provided)\n// ================================================================\n\n/** The detected type of a display field, used for formatting. */\nexport type CardFieldType = 'number' | 'boolean' | 'text' | 'date';\n\n/**\n * A single display field in the card body.\n */\nexport interface CardDisplayField {\n /** Entity field name (e.g., \"Email\") */\n Name: string;\n /** Human-readable label (e.g., \"Email Address\") */\n Label: string;\n /** Detected field type for value formatting */\n Type: CardFieldType;\n /** Whether this field has DefaultInView=true on the entity */\n IsDefaultInView: boolean;\n /** The field's Sequence from entity metadata */\n Sequence: number;\n}\n\n/**\n * Describes how a card should be laid out — which fields map to which\n * visual slots. Auto-generated from entity metadata when not provided.\n */\nexport interface CardTemplate {\n /**\n * Field names that form the card title, in display order.\n * Multiple IsNameField fields are combined (e.g., [\"FirstName\", \"LastName\"] → \"Elizabeth Rodriguez\").\n */\n TitleFields: string[];\n /** Optional subtitle field (status, type, category) */\n SubtitleField: string | null;\n /** Optional description/summary field */\n DescriptionField: string | null;\n /** Ordered display fields for the card body */\n DisplayFields: CardDisplayField[];\n /** Thumbnail field names in priority order (image URL or icon class) */\n ThumbnailFields: string[];\n /** Optional badge field (priority, severity, rating) */\n BadgeField: string | null;\n /** Map of field names to display labels */\n FieldLabels: Record<string, string>;\n}\n\n// ================================================================\n// Events\n// ================================================================\n\n/**\n * Cancelable event payload. Set `Cancel = true` in the handler to\n * prevent the default action from executing.\n */\nexport interface CancelableCardEvent<T> {\n /** The data associated with the event */\n Data: T;\n /** Set to true to cancel the default action */\n Cancel: boolean;\n}\n\n/**\n * Payload for record interaction events.\n */\nexport interface CardRecordEvent {\n /** The entity name */\n EntityName: string;\n /** The raw record data */\n Record: Record<string, unknown>;\n /** The record's primary key value(s) */\n RecordID: string;\n}\n\n// ================================================================\n// Template Generation Utilities\n// ================================================================\n\n/** Fields that should never appear in card display */\nconst INTERNAL_FIELD_PATTERNS = new Set([\n '__mj_createdat', '__mj_updatedat', 'createdat', 'updatedat',\n 'createdby', 'updatedby', 'password', 'secret', 'token', 'hash',\n]);\n\n/** Keyword patterns for auto-detecting field roles */\nconst SUBTITLE_KEYWORDS = ['status', 'type', 'category', 'state', 'stage'];\nconst DESCRIPTION_KEYWORDS = ['description', 'desc', 'summary', 'notes', 'comments', 'bio', 'about'];\nconst THUMBNAIL_KEYWORDS = ['image', 'photo', 'picture', 'thumbnail', 'avatar', 'logo', 'icon'];\nconst BADGE_KEYWORDS = ['priority', 'severity', 'importance', 'rating', 'rank', 'level'];\nconst METRIC_KEYWORDS = ['amount', 'total', 'count', 'value', 'price', 'cost', 'quantity', 'qty', 'balance', 'revenue', 'score'];\n\n/**\n * Auto-generate a CardTemplate from entity metadata.\n *\n * Priority logic:\n * 1. **Title**: All fields with `IsNameField=true`, sorted by Sequence. Falls back to\n * heuristic name detection, then first string field.\n * 2. **Display fields**: `DefaultInView=true` fields sorted by Sequence (up to maxFields).\n * Falls back to metric-keyword fields, then first N non-PK/FK fields.\n * 3. **Subtitle/Description/Thumbnail/Badge**: Detected via keyword matching on field names.\n *\n * @param entity - The entity metadata\n * @param maxDisplayFields - Maximum display fields for the card body (default 4)\n * @returns A fully populated CardTemplate\n */\nexport function GenerateCardTemplate(entity: EntityInfo, maxDisplayFields: number = 4): CardTemplate {\n const fields = entity.Fields;\n const titleFields = FindTitleFields(entity, fields);\n const titleFieldNames = new Set(titleFields.map(f => f.Name));\n\n // Build field labels map\n const fieldLabels: Record<string, string> = {};\n for (const f of fields) {\n fieldLabels[f.Name] = f.DisplayNameOrName;\n }\n\n return {\n TitleFields: titleFields.map(f => f.Name),\n SubtitleField: FindFieldByKeywords(fields, SUBTITLE_KEYWORDS, titleFieldNames),\n DescriptionField: FindFieldByKeywords(fields, DESCRIPTION_KEYWORDS, titleFieldNames),\n DisplayFields: FindDisplayFields(fields, titleFieldNames, maxDisplayFields),\n ThumbnailFields: FindThumbnailFields(fields),\n BadgeField: FindFieldByKeywords(fields, BADGE_KEYWORDS, titleFieldNames),\n FieldLabels: fieldLabels,\n };\n}\n\n/**\n * Generate a CardTemplate from an entity name string + sparse metadata keys.\n * This is the primary entry point for Knowledge Hub dashboards where we have\n * vector metadata (partial field set) rather than full entity records.\n *\n * Falls back gracefully when EntityInfo is not available (returns a template\n * based on the metadata keys alone).\n *\n * @param entityName - The entity name for metadata lookup\n * @param metadataKeys - The available field keys in the sparse metadata\n * @param maxDisplayFields - Maximum display fields\n */\nexport function GenerateCardTemplateFromMetadata(\n entityName: string,\n metadataKeys: string[],\n maxDisplayFields: number = 4,\n provider?: IMetadataProvider\n): CardTemplate {\n let entity: EntityInfo | null = null;\n try {\n const md = (provider ?? Metadata.Provider) as unknown as IMetadataProvider;\n entity = md.Entities.find((e: EntityInfo) => e.Name === entityName) ?? null;\n } catch {\n // Metadata not available — use key-only fallback\n }\n\n if (entity) {\n const full = GenerateCardTemplate(entity, maxDisplayFields);\n // Filter to only fields present in the metadata\n const keySet = new Set(metadataKeys);\n return {\n ...full,\n TitleFields: full.TitleFields.filter(f => keySet.has(f)),\n DisplayFields: full.DisplayFields.filter(f => keySet.has(f.Name)),\n SubtitleField: full.SubtitleField && keySet.has(full.SubtitleField) ? full.SubtitleField : null,\n DescriptionField: full.DescriptionField && keySet.has(full.DescriptionField) ? full.DescriptionField : null,\n ThumbnailFields: full.ThumbnailFields.filter(f => keySet.has(f)),\n BadgeField: full.BadgeField && keySet.has(full.BadgeField) ? full.BadgeField : null,\n };\n }\n\n // Fallback: no entity metadata, use key heuristics\n return GenerateFallbackTemplate(metadataKeys, maxDisplayFields);\n}\n\n// ================================================================\n// Internal Helpers\n// ================================================================\n\nfunction FindTitleFields(entity: EntityInfo, fields: EntityFieldInfo[]): EntityFieldInfo[] {\n // 1. All IsNameField fields, sorted by Sequence\n const nameFields = fields\n .filter(f => f.IsNameField)\n .sort((a, b) => (a.Sequence ?? 9999) - (b.Sequence ?? 9999));\n if (nameFields.length > 0) return nameFields;\n\n // 2. entity.NameField (singular)\n if (entity.NameField) return [entity.NameField];\n\n // 3. Heuristic: field named \"Name\" or \"Title\"\n const heuristic = fields.find(f =>\n (f.Name.toLowerCase() === 'name' || f.Name.toLowerCase() === 'title') &&\n f.TSType === 'string' && !f.IsPrimaryKey\n );\n if (heuristic) return [heuristic];\n\n // 4. Field ending with \"Name\" that's a string\n const endsWithName = fields.find(f =>\n f.Name.toLowerCase().endsWith('name') && f.TSType === 'string' &&\n !f.IsPrimaryKey && !f.Name.toLowerCase().includes('file')\n );\n if (endsWithName) return [endsWithName];\n\n // 5. First string field\n const firstString = fields.find(f =>\n f.TSType === 'string' && !f.IsPrimaryKey && !f.Name.toLowerCase().includes('id')\n );\n if (firstString) return [firstString];\n\n return [];\n}\n\nfunction FindDisplayFields(\n fields: EntityFieldInfo[],\n excludeNames: Set<string>,\n maxFields: number\n): CardDisplayField[] {\n const result: CardDisplayField[] = [];\n const isExcluded = (f: EntityFieldInfo) =>\n f.IsPrimaryKey || excludeNames.has(f.Name) ||\n INTERNAL_FIELD_PATTERNS.has(f.Name.toLowerCase()) ||\n f.Name.toLowerCase().endsWith('id') && f.RelatedEntityID != null; // FK fields\n\n // 1. DefaultInView fields first\n const defaultInView = fields\n .filter(f => f.DefaultInView && !isExcluded(f))\n .sort((a, b) => (a.Sequence ?? 9999) - (b.Sequence ?? 9999));\n\n for (const f of defaultInView) {\n if (result.length >= maxFields) break;\n result.push(BuildCardField(f));\n }\n\n if (result.length >= 2) return result;\n\n // 2. Metric keyword fields\n const existing = new Set(result.map(r => r.Name));\n for (const f of fields) {\n if (result.length >= maxFields) break;\n if (existing.has(f.Name) || isExcluded(f)) continue;\n if (METRIC_KEYWORDS.some(kw => f.Name.toLowerCase().includes(kw))) {\n result.push(BuildCardField(f));\n existing.add(f.Name);\n }\n }\n\n if (result.length >= 2) return result;\n\n // 3. First N non-excluded fields by sequence\n for (const f of fields.sort((a, b) => (a.Sequence ?? 9999) - (b.Sequence ?? 9999))) {\n if (result.length >= maxFields) break;\n if (existing.has(f.Name) || isExcluded(f)) continue;\n result.push(BuildCardField(f));\n existing.add(f.Name);\n }\n\n return result;\n}\n\nfunction FindFieldByKeywords(\n fields: EntityFieldInfo[],\n keywords: string[],\n excludeNames: Set<string>\n): string | null {\n for (const keyword of keywords) {\n const field = fields.find(f =>\n f.Name.toLowerCase().includes(keyword) &&\n f.TSType === 'string' &&\n !f.IsPrimaryKey &&\n !excludeNames.has(f.Name)\n );\n if (field) return field.Name;\n }\n return null;\n}\n\nfunction FindThumbnailFields(fields: EntityFieldInfo[]): string[] {\n const result: string[] = [];\n for (const keyword of THUMBNAIL_KEYWORDS) {\n for (const f of fields) {\n if (f.Name.toLowerCase().includes(keyword) && f.TSType === 'string' && !result.includes(f.Name)) {\n result.push(f.Name);\n }\n }\n }\n return result;\n}\n\nfunction BuildCardField(field: EntityFieldInfo): CardDisplayField {\n return {\n Name: field.Name,\n Label: field.DisplayNameOrName,\n Type: DetectFieldType(field),\n IsDefaultInView: field.DefaultInView ?? false,\n Sequence: field.Sequence ?? 9999,\n };\n}\n\nfunction DetectFieldType(field: EntityFieldInfo): CardFieldType {\n if (field.TSType === 'boolean' || field.Type?.toLowerCase() === 'bit') return 'boolean';\n if (field.TSType === 'number') return 'number';\n if (field.TSType === 'Date' || field.Type?.toLowerCase().includes('date')) return 'date';\n return 'text';\n}\n\n/**\n * Fallback template generation when entity metadata is not available.\n * Uses field name heuristics on the raw metadata keys.\n */\nfunction GenerateFallbackTemplate(keys: string[], maxFields: number): CardTemplate {\n const internal = new Set([\n 'Entity', 'EntityIcon', 'RecordID', 'TemplateID',\n '__mj_UpdatedAt', '__mj_CreatedAt', 'ID',\n ]);\n const usable = keys.filter(k => !internal.has(k));\n\n // Title: 'Name' or first key\n const titleKey = usable.find(k => k.toLowerCase() === 'name')\n ?? usable.find(k => k.toLowerCase().endsWith('name'))\n ?? usable.find(k => k.toLowerCase() === 'title');\n const titleFields = titleKey ? [titleKey] : usable.length > 0 ? [usable[0]] : [];\n const titleSet = new Set(titleFields);\n\n const displayFields: CardDisplayField[] = usable\n .filter(k => !titleSet.has(k))\n .slice(0, maxFields)\n .map((k, i) => ({\n Name: k,\n Label: FormatKeyAsLabel(k),\n Type: 'text' as CardFieldType,\n IsDefaultInView: false,\n Sequence: i,\n }));\n\n const fieldLabels: Record<string, string> = {};\n for (const k of keys) {\n fieldLabels[k] = FormatKeyAsLabel(k);\n }\n\n return {\n TitleFields: titleFields,\n SubtitleField: null,\n DescriptionField: null,\n DisplayFields: displayFields,\n ThumbnailFields: [],\n BadgeField: null,\n FieldLabels: fieldLabels,\n };\n}\n\n/**\n * Convert a PascalCase or camelCase key to a human-readable label.\n * E.g., \"FirstName\" → \"First Name\", \"contentTypeID\" → \"Content Type ID\"\n */\nexport function FormatKeyAsLabel(key: string): string {\n return key\n .replace(/([a-z])([A-Z])/g, '$1 $2')\n .replace(/([A-Z]+)([A-Z][a-z])/g, '$1 $2')\n .replace(/^./, c => c.toUpperCase());\n}\n\n/**\n * Combine multiple title field values into a single display string.\n * E.g., [\"Elizabeth\", \"Rodriguez\"] → \"Elizabeth Rodriguez\"\n */\nexport function CombineTitleFields(\n record: Record<string, unknown>,\n titleFields: string[]\n): string {\n const parts = titleFields\n .map(f => record[f])\n .filter(v => v != null && String(v).trim() !== '')\n .map(v => String(v));\n return parts.length > 0 ? parts.join(' ') : 'Unknown';\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@memberjunction/ng-entity-card",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.32.0",
|
|
4
4
|
"description": "MemberJunction: Generic entity record card component with metadata-driven field prioritization",
|
|
5
5
|
"main": "./dist/public-api.js",
|
|
6
6
|
"typings": "./dist/public-api.d.ts",
|
|
@@ -24,8 +24,9 @@
|
|
|
24
24
|
"@angular/core": "21.1.3"
|
|
25
25
|
},
|
|
26
26
|
"dependencies": {
|
|
27
|
-
"@memberjunction/core": "5.
|
|
28
|
-
"@memberjunction/
|
|
27
|
+
"@memberjunction/core": "5.32.0",
|
|
28
|
+
"@memberjunction/ng-base-types": "5.32.0",
|
|
29
|
+
"@memberjunction/global": "5.32.0",
|
|
29
30
|
"tslib": "^2.8.1"
|
|
30
31
|
},
|
|
31
32
|
"repository": {
|