@memberjunction/ng-entity-card 0.0.1 → 5.24.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.
@@ -0,0 +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":"AAAA,OAAO,EACH,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,YAAY,EACZ,uBAAuB,EAAE,iBAAiB,EAAE,MAAM,EAC/E,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,QAAQ,EAAc,MAAM,sBAAsB,CAAC;AAC5D,OAAO,EAGH,oBAAoB,EAAE,gCAAgC,EACtD,kBAAkB,EAAE,gBAAgB,GACvC,MAAM,qBAAqB,CAAC;;;;ICFb,kCAA6B;IACzB,6BAAqC;IACzC,oBAAM;;;IADG,cAAsB;IAAtB,gEAAsB;;;IAG/B,kCAA2D;IACvD,uBAAgC;IACpC,oBAAM;;;IAFoB,gDAAgC;IACnD,cAAwB;IAAxB,oCAAwB;;;IAG/B,kCAA2D;IACvD,uBAA4B;IAChC,oBAAM;;;IAFoB,gDAAgC;IACnD,cAAoB;IAApB,gCAAoB;;;IAG3B,kCAA2D;IACvD,YACJ;IAAA,oBAAM;;;IAFoB,gDAAgC;IACtD,cACJ;IADI,gDACJ;;;IAhBR,iCAA+B;IAazB,AAJA,AAJA,AAJF,sGAAwC,gFAIM,gFAIrB,gFAIhB;IAKb,oBAAM;;;IAjBF,cAgBC;IAhBD,oJAgBC;;;IAUG,iCAA0B;IAAA,YAAuB;IAAA,oBAAM;;;IAA7B,cAAuB;IAAvB,8CAAuB;;;IAMrD,iCAA4B;IAAA,YAAc;IAAA,oBAAM;;;IAApB,cAAc;IAAd,qCAAc;;;IAK1C,iCAA+B;IAAA,YAAiB;IAAA,oBAAM;;;IAAvB,cAAiB;IAAjB,wCAAiB;;;IAU5B,mCAAgC;IAAA,YAAiB;IAAA,oBAAO;;;IAAxB,cAAiB;IAAjB,oCAAiB;;;IAFzD,kCAAyB;IACrB,2HAAwB;IAGxB,mCAAgC;IAAA,YAAW;IAC/C,AAD+C,oBAAO,EAChD;;;IAJF,cAEC;IAFD,kDAEC;IAC+B,eAAW;IAAX,yBAAW;;;IALnD,4GAAsC;;;;;IAAtC,gFAOC;;;IATT,kCAA2E;IACvE,wGASC;IACL,oBAAM;;;IAXoB,gEAAgD;IACtE,cASC;IATD,mCASC;;;;IAOT,qCAAiF;IAAlD,mMAAS,0BAAmB,KAAC;IACxD,2BAA6C;IACjD,oBAAS;;ADxDjB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AAQH,MAAM,OAAO,qBAAqB;IACtB,GAAG,GAAG,MAAM,CAAC,iBAAiB,CAAC,CAAC;IAExC,mEAAmE;IACnE,cAAc;IACd,mEAAmE;IAEnE;;;;OAIG;IACM,UAAU,GAAkB,IAAI,CAAC;IAE1C;;;OAGG;IACM,MAAM,GAAsB,IAAI,CAAC;IAE1C;;;;;OAKG;IACM,MAAM,GAA4B,EAAE,CAAC;IAE9C;;;OAGG;IACM,OAAO,GAAsB,MAAM,CAAC;IAE7C,mEAAmE;IACnE,uBAAuB;IACvB,mEAAmE;IAEnE;;;OAGG;IACM,QAAQ,GAAwB,IAAI,CAAC;IAE9C;;;;OAIG;IACM,gBAAgB,GAAkB,IAAI,CAAC;IAEhD;;;OAGG;IACM,UAAU,GAAmB,IAAI,CAAC;IAE3C;;;OAGG;IACM,cAAc,GAAG,KAAK,CAAC;IAEhC;;;OAGG;IACM,eAAe,GAAmB,IAAI,CAAC;IAEhD;;OAEG;IACM,QAAQ,GAAG,EAAE,CAAC;IAEvB,mEAAmE;IACnE,wCAAwC;IACxC,mEAAmE;IAEnE;;;OAGG;IACO,eAAe,GAAG,IAAI,YAAY,EAAwC,CAAC;IAErF,yDAAyD;IAC/C,WAAW,GAAG,IAAI,YAAY,EAAmB,CAAC;IAE5D;;;OAGG;IACO,UAAU,GAAG,IAAI,YAAY,EAAwC,CAAC;IAEhF,wEAAwE;IAC9D,aAAa,GAAG,IAAI,YAAY,EAAmB,CAAC;IAE9D,mEAAmE;IACnE,iBAAiB;IACjB,mEAAmE;IAEnE,uEAAuE;IAChE,cAAc,GAAsB,IAAI,CAAC;IAEhD,6DAA6D;IACtD,iBAAiB,GAAwB,IAAI,CAAC;IAErD,gCAAgC;IACzB,KAAK,GAAG,EAAE,CAAC;IAElB,kEAAkE;IAC3D,UAAU,GAAG,EAAE,CAAC;IAEvB,mCAAmC;IAC5B,iBAAiB,GAAG,EAAE,CAAC;IAE9B,mEAAmE;IACnE,YAAY;IACZ,mEAAmE;IAEnE,WAAW,CAAC,OAAsB;QAC9B,IAAI,OAAO,CAAC,QAAQ,CAAC,IAAI,OAAO,CAAC,YAAY,CAAC,IAAI,OAAO,CAAC,QAAQ,CAAC,IAAI,OAAO,CAAC,UAAU,CAAC,IAAI,OAAO,CAAC,SAAS,CAAC,EAAE,CAAC;YAC/G,IAAI,CAAC,OAAO,EAAE,CAAC;QACnB,CAAC;IACL,CAAC;IAED,mEAAmE;IACnE,iBAAiB;IACjB,mEAAmE;IAEnE,uEAAuE;IAChE,WAAW;QACd,MAAM,OAAO,GAAG,IAAI,CAAC,iBAAiB,EAAE,CAAC;QACzC,MAAM,UAAU,GAAyC,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC;QAC1F,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QACtC,IAAI,CAAC,UAAU,CAAC,MAAM,EAAE,CAAC;YACrB,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACnC,CAAC;IACL,CAAC;IAED,8EAA8E;IACvE,WAAW,CAAC,KAAiB;QAChC,KAAK,CAAC,eAAe,EAAE,CAAC;QACxB,MAAM,OAAO,GAAG,IAAI,CAAC,iBAAiB,EAAE,CAAC;QACzC,MAAM,UAAU,GAAyC,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC;QAC1F,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QACjC,IAAI,CAAC,UAAU,CAAC,MAAM,EAAE,CAAC;YACrB,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACrC,CAAC;IACL,CAAC;IAED,mEAAmE;IACnE,qCAAqC;IACrC,mEAAmE;IAEnE,wEAAwE;IACxE,IAAW,aAAa;QACpB,IAAI,CAAC,IAAI,CAAC,iBAAiB;YAAE,OAAO,EAAE,CAAC;QACvC,MAAM,GAAG,GAAG,IAAI,CAAC,iBAAiB,CAAC;QACnC,OAAO,IAAI,CAAC,iBAAiB,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IAC9D,CAAC;IAED,4DAA4D;IAC5D,IAAW,gBAAgB;QACvB,IAAI,IAAI,CAAC,UAAU,IAAI,IAAI;YAAE,OAAO,IAAI,CAAC,UAAU,CAAC;QACpD,OAAO,IAAI,CAAC,OAAO,KAAK,SAAS,CAAC;IACtC,CAAC;IAED,8DAA8D;IAC9D,IAAW,gBAAgB;QACvB,IAAI,IAAI,CAAC,eAAe,IAAI,IAAI;YAAE,OAAO,IAAI,CAAC,eAAe,CAAC;QAC9D,OAAO,IAAI,CAAC,OAAO,KAAK,SAAS,CAAC;IACtC,CAAC;IAED,8CAA8C;IACvC,aAAa,CAAC,KAAuB;QACxC,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QACtC,IAAI,KAAK,IAAI,IAAI,IAAI,MAAM,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE;YAAE,OAAO,EAAE,CAAC;QAC5D,OAAO,IAAI,CAAC,WAAW,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;IAC1C,CAAC;IAED,gCAAgC;IACzB,aAAa,CAAC,SAAiB;QAClC,OAAO,IAAI,CAAC,iBAAiB,EAAE,WAAW,CAAC,SAAS,CAAC,IAAI,gBAAgB,CAAC,SAAS,CAAC,CAAC;IACzF,CAAC;IAED,sDAAsD;IACtD,IAAW,QAAQ;QACf,IAAI,CAAC,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,KAAK,SAAS;YAAE,OAAO,GAAG,CAAC;QACxD,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;QAChE,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;QACtE,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;IACpE,CAAC;IAED,mEAAmE;IACnE,IAAW,WAAW;QAClB,MAAM,MAAM,GAAG;YACX,yBAAyB,EAAE,0BAA0B;YACrD,0BAA0B,EAAE,uBAAuB;SACtD,CAAC;QACF,IAAI,IAAI,GAAG,CAAC,CAAC;QACb,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,IAAI,EAAE,CAAC,CAAC;QACzD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YAClC,IAAI,GAAG,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;QACpD,CAAC;QACD,OAAO,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC;IAClD,CAAC;IAED,6BAA6B;IAC7B,IAAW,QAAQ;QACf,IAAI,CAAC,IAAI,CAAC,iBAAiB,EAAE,aAAa;YAAE,OAAO,EAAE,CAAC;QACtD,MAAM,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,iBAAiB,CAAC,aAAa,CAAC,CAAC;QAC5D,OAAO,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IACtC,CAAC;IAED,gCAAgC;IAChC,IAAW,WAAW;QAClB,IAAI,CAAC,IAAI,CAAC,iBAAiB,EAAE,gBAAgB;YAAE,OAAO,EAAE,CAAC;QACzD,MAAM,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,iBAAiB,CAAC,gBAAgB,CAAC,CAAC;QAC/D,IAAI,CAAC,IAAI,IAAI;YAAE,OAAO,EAAE,CAAC;QACzB,MAAM,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;QACpB,OAAO,CAAC,CAAC,MAAM,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,EAAE,GAAG,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IAC5D,CAAC;IAED,wCAAwC;IACxC,IAAW,YAAY;QACnB,IAAI,CAAC,IAAI,CAAC,iBAAiB,EAAE,eAAe;YAAE,OAAO,KAAK,CAAC;QAC3D,OAAO,IAAI,CAAC,iBAAiB,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE;YACnD,MAAM,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;YACzB,OAAO,CAAC,IAAI,IAAI,IAAI,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC;QAChD,CAAC,CAAC,CAAC;IACP,CAAC;IAED,0CAA0C;IAC1C,IAAW,cAAc;QACrB,IAAI,CAAC,IAAI,CAAC,iBAAiB,EAAE,eAAe;YAAE,OAAO,EAAE,CAAC;QACxD,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,iBAAiB,CAAC,eAAe,EAAE,CAAC;YACrD,MAAM,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;YACzB,IAAI,CAAC,IAAI,IAAI,IAAI,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE;gBAAE,OAAO,MAAM,CAAC,CAAC,CAAC,CAAC;QAC/D,CAAC;QACD,OAAO,EAAE,CAAC;IACd,CAAC;IAED,4CAA4C;IAC5C,IAAW,gBAAgB;QACvB,MAAM,CAAC,GAAG,IAAI,CAAC,cAAc,CAAC;QAC9B,OAAO,CAAC,CAAC,UAAU,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,UAAU,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,UAAU,CAAC,aAAa,CAAC,CAAC;IAC9F,CAAC;IAED,6CAA6C;IAC7C,IAAW,eAAe;QACtB,MAAM,CAAC,GAAG,IAAI,CAAC,cAAc,CAAC,WAAW,EAAE,CAAC;QAC5C,OAAO,CAAC,CAAC,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,UAAU,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;IACtG,CAAC;IAED,mEAAmE;IACnE,kBAAkB;IAClB,mEAAmE;IAEnE,IAAY,iBAAiB;QACzB,IAAI,IAAI,CAAC,gBAAgB,IAAI,IAAI;YAAE,OAAO,IAAI,CAAC,gBAAgB,CAAC;QAChE,QAAQ,IAAI,CAAC,OAAO,EAAE,CAAC;YACnB,KAAK,SAAS,CAAC,CAAC,OAAO,CAAC,CAAC;YACzB,KAAK,MAAM,CAAC,CAAC,OAAO,CAAC,CAAC;YACtB,KAAK,QAAQ,CAAC,CAAC,OAAO,GAAG,CAAC;QAC9B,CAAC;IACL,CAAC;IAEO,OAAO;QACX,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC,aAAa,EAAE,CAAC;QAC3C,IAAI,CAAC,iBAAiB,GAAG,IAAI,CAAC,eAAe,EAAE,CAAC;QAChD,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,YAAY,EAAE,CAAC;QACjC,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,iBAAiB,EAAE,CAAC;QAC3C,IAAI,CAAC,iBAAiB,GAAG,IAAI,CAAC,wBAAwB,EAAE,CAAC;QACzD,IAAI,CAAC,GAAG,CAAC,YAAY,EAAE,CAAC;IAC5B,CAAC;IAEO,aAAa;QACjB,IAAI,IAAI,CAAC,MAAM;YAAE,OAAO,IAAI,CAAC,MAAM,CAAC;QACpC,IAAI,CAAC,IAAI,CAAC,UAAU;YAAE,OAAO,IAAI,CAAC;QAClC,IAAI,CAAC;YACD,MAAM,EAAE,GAAG,IAAI,QAAQ,EAAE,CAAC;YAC1B,OAAO,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,UAAU,CAAC,IAAI,IAAI,CAAC;QACrE,CAAC;QAAC,MAAM,CAAC;YACL,OAAO,IAAI,CAAC;QAChB,CAAC;IACL,CAAC;IAEO,eAAe;QACnB,IAAI,IAAI,CAAC,QAAQ;YAAE,OAAO,IAAI,CAAC,QAAQ,CAAC;QACxC,MAAM,UAAU,GAAG,IAAI,CAAC,MAAM,EAAE,IAAI,IAAI,IAAI,CAAC,UAAU,CAAC;QACxD,IAAI,CAAC,UAAU;YAAE,OAAO,IAAI,CAAC;QAE7B,MAAM,YAAY,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAC9C,IAAI,IAAI,CAAC,cAAc,EAAE,CAAC;YACtB,MAAM,IAAI,GAAG,oBAAoB,CAAC,IAAI,CAAC,cAAc,EAAE,IAAI,CAAC,iBAAiB,CAAC,CAAC;YAC/E,yCAAyC;YACzC,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,YAAY,CAAC,CAAC;YACrC,OAAO;gBACH,GAAG,IAAI;gBACP,WAAW,EAAE,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;gBACxD,aAAa,EAAE,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;gBACjE,aAAa,EAAE,IAAI,CAAC,aAAa,IAAI,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI;gBAC/F,gBAAgB,EAAE,IAAI,CAAC,gBAAgB,IAAI,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC,CAAC,IAAI;gBAC3G,eAAe,EAAE,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;gBAChE,UAAU,EAAE,IAAI,CAAC,UAAU,IAAI,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI;aACtF,CAAC;QACN,CAAC;QAED,OAAO,gCAAgC,CAAC,UAAU,EAAE,YAAY,EAAE,IAAI,CAAC,iBAAiB,CAAC,CAAC;IAC9F,CAAC;IAEO,YAAY;QAChB,IAAI,CAAC,IAAI,CAAC,iBAAiB,IAAI,IAAI,CAAC,iBAAiB,CAAC,WAAW,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC7E,OAAO,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,SAAS,CAAC,CAAC;QAC5E,CAAC;QACD,OAAO,kBAAkB,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,iBAAiB,CAAC,WAAW,CAAC,CAAC;IAC/E,CAAC;IAEO,iBAAiB;QACrB,6BAA6B;QAC7B,MAAM,QAAQ,GAAG,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC;QAC3C,IAAI,QAAQ,IAAI,OAAO,QAAQ,KAAK,QAAQ;YAAE,OAAO,QAAQ,CAAC;QAC9D,mBAAmB;QACnB,IAAI,IAAI,CAAC,cAAc,EAAE,IAAI;YAAE,OAAO,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC;QAC/D,OAAO,mBAAmB,CAAC;IAC/B,CAAC;IAEO,wBAAwB;QAC5B,MAAM,UAAU,GAAG,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QACzC,IAAI,UAAU,IAAI,OAAO,UAAU,KAAK,QAAQ;YAAE,OAAO,UAAU,CAAC;QACpE,OAAO,IAAI,CAAC,cAAc,EAAE,IAAI,IAAI,IAAI,CAAC,UAAU,IAAI,EAAE,CAAC;IAC9D,CAAC;IAEO,WAAW,CAAC,KAAc,EAAE,KAAuB;QACvD,QAAQ,KAAK,CAAC,IAAI,EAAE,CAAC;YACjB,KAAK,QAAQ,CAAC,CAAC,CAAC;gBACZ,MAAM,GAAG,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;gBAC1B,IAAI,KAAK,CAAC,GAAG,CAAC;oBAAE,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC;gBACrC,IAAI,GAAG,IAAI,OAAO;oBAAE,OAAO,GAAG,CAAC,GAAG,GAAG,OAAO,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC;gBAC5D,IAAI,GAAG,IAAI,IAAI;oBAAE,OAAO,GAAG,CAAC,GAAG,GAAG,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC;gBACtD,MAAM,SAAS,GAAG,KAAK,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;gBAC3C,IAAI,CAAC,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;oBAC5F,OAAO,IAAI,GAAG,CAAC,cAAc,EAAE,EAAE,CAAC;gBACtC,CAAC;gBACD,OAAO,GAAG,CAAC,cAAc,EAAE,CAAC;YAChC,CAAC;YACD,KAAK,SAAS;gBACV,OAAO,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC;YAChC,KAAK,MAAM,CAAC,CAAC,CAAC;gBACV,IAAI,CAAC;oBACD,MAAM,CAAC,GAAG,KAAK,YAAY,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,KAAwB,CAAC,CAAC;oBAC7E,IAAI,KAAK,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC;wBAAE,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC;oBAC7C,OAAO,CAAC,CAAC,kBAAkB,CAAC,SAAS,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,SAAS,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC,CAAC;gBAChG,CAAC;gBAAC,MAAM,CAAC;oBACL,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC;gBACzB,CAAC;YACL,CAAC;YACD,OAAO,CAAC,CAAC,CAAC;gBACN,MAAM,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;gBACxB,OAAO,CAAC,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;YAC1D,CAAC;QACL,CAAC;IACL,CAAC;IAEO,iBAAiB;QACrB,OAAO;YACH,UAAU,EAAE,IAAI,CAAC,iBAAiB;YAClC,MAAM,EAAE,IAAI,CAAC,MAAM;YACnB,QAAQ,EAAE,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,IAAI,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;SACvE,CAAC;IACN,CAAC;+GAlXQ,qBAAqB;6DAArB,qBAAqB;YC/DlC,iCAE6B;YAAxB,kGAAS,iBAAa,IAAC;YAGxB,uFAAwB;YA0BhB,AADJ,AAFJ,iCAA2B,aAEG,aACG;YAAA,YAAW;YAAA,oBAAM;YAC1C,uFAAkD;YAGtD,oBAAM;YAGN,uFAAyC;YAKzC,uFAA2C;YAK3C,uFAAgC;YAcpC,oBAAM;YAGN,4FAAsB;YAK1B,oBAAM;;YArED,oEAAuD;YAIxD,cAoBC;YApBD,+CAoBC;YAMgC,eAAW;YAAX,+BAAW;YACpC,cAEC;YAFD,6EAEC;YAIL,cAEC;YAFD,oEAEC;YAGD,cAEC;YAFD,sEAEC;YAGD,cAaC;YAbD,uDAaC;YAIL,cAIC;YAJD,8CAIC;;;iFDNQ,qBAAqB;cAPjC,SAAS;6BACM,IAAI,YACN,gBAAgB,mBAGT,uBAAuB,CAAC,MAAM;;kBAc9C,KAAK;;kBAML,KAAK;;kBAQL,KAAK;;kBAML,KAAK;;kBAUL,KAAK;;kBAOL,KAAK;;kBAML,KAAK;;kBAML,KAAK;;kBAML,KAAK;;kBAKL,KAAK;;kBAUL,MAAM;;kBAGN,MAAM;;kBAMN,MAAM;;kBAGN,MAAM;;kFA9FE,qBAAqB","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"]}
@@ -0,0 +1,109 @@
1
+ import { EntityInfo } from '@memberjunction/core';
2
+ /**
3
+ * Visual variant controlling the card's size, layout, and level of detail.
4
+ *
5
+ * - `compact` — Tooltip-sized: title + 2-3 primary fields, no avatar. ~80px.
6
+ * - `card` — Grid card: avatar + title + subtitle + up to 4 display fields + badge. ~160px.
7
+ * - `detail` — Full panel: all available fields with labels, grouped by priority.
8
+ */
9
+ export type EntityCardVariant = 'compact' | 'card' | 'detail';
10
+ /** The detected type of a display field, used for formatting. */
11
+ export type CardFieldType = 'number' | 'boolean' | 'text' | 'date';
12
+ /**
13
+ * A single display field in the card body.
14
+ */
15
+ export interface CardDisplayField {
16
+ /** Entity field name (e.g., "Email") */
17
+ Name: string;
18
+ /** Human-readable label (e.g., "Email Address") */
19
+ Label: string;
20
+ /** Detected field type for value formatting */
21
+ Type: CardFieldType;
22
+ /** Whether this field has DefaultInView=true on the entity */
23
+ IsDefaultInView: boolean;
24
+ /** The field's Sequence from entity metadata */
25
+ Sequence: number;
26
+ }
27
+ /**
28
+ * Describes how a card should be laid out — which fields map to which
29
+ * visual slots. Auto-generated from entity metadata when not provided.
30
+ */
31
+ export interface CardTemplate {
32
+ /**
33
+ * Field names that form the card title, in display order.
34
+ * Multiple IsNameField fields are combined (e.g., ["FirstName", "LastName"] → "Elizabeth Rodriguez").
35
+ */
36
+ TitleFields: string[];
37
+ /** Optional subtitle field (status, type, category) */
38
+ SubtitleField: string | null;
39
+ /** Optional description/summary field */
40
+ DescriptionField: string | null;
41
+ /** Ordered display fields for the card body */
42
+ DisplayFields: CardDisplayField[];
43
+ /** Thumbnail field names in priority order (image URL or icon class) */
44
+ ThumbnailFields: string[];
45
+ /** Optional badge field (priority, severity, rating) */
46
+ BadgeField: string | null;
47
+ /** Map of field names to display labels */
48
+ FieldLabels: Record<string, string>;
49
+ }
50
+ /**
51
+ * Cancelable event payload. Set `Cancel = true` in the handler to
52
+ * prevent the default action from executing.
53
+ */
54
+ export interface CancelableCardEvent<T> {
55
+ /** The data associated with the event */
56
+ Data: T;
57
+ /** Set to true to cancel the default action */
58
+ Cancel: boolean;
59
+ }
60
+ /**
61
+ * Payload for record interaction events.
62
+ */
63
+ export interface CardRecordEvent {
64
+ /** The entity name */
65
+ EntityName: string;
66
+ /** The raw record data */
67
+ Record: Record<string, unknown>;
68
+ /** The record's primary key value(s) */
69
+ RecordID: string;
70
+ }
71
+ /**
72
+ * Auto-generate a CardTemplate from entity metadata.
73
+ *
74
+ * Priority logic:
75
+ * 1. **Title**: All fields with `IsNameField=true`, sorted by Sequence. Falls back to
76
+ * heuristic name detection, then first string field.
77
+ * 2. **Display fields**: `DefaultInView=true` fields sorted by Sequence (up to maxFields).
78
+ * Falls back to metric-keyword fields, then first N non-PK/FK fields.
79
+ * 3. **Subtitle/Description/Thumbnail/Badge**: Detected via keyword matching on field names.
80
+ *
81
+ * @param entity - The entity metadata
82
+ * @param maxDisplayFields - Maximum display fields for the card body (default 4)
83
+ * @returns A fully populated CardTemplate
84
+ */
85
+ export declare function GenerateCardTemplate(entity: EntityInfo, maxDisplayFields?: number): CardTemplate;
86
+ /**
87
+ * Generate a CardTemplate from an entity name string + sparse metadata keys.
88
+ * This is the primary entry point for Knowledge Hub dashboards where we have
89
+ * vector metadata (partial field set) rather than full entity records.
90
+ *
91
+ * Falls back gracefully when EntityInfo is not available (returns a template
92
+ * based on the metadata keys alone).
93
+ *
94
+ * @param entityName - The entity name for metadata lookup
95
+ * @param metadataKeys - The available field keys in the sparse metadata
96
+ * @param maxDisplayFields - Maximum display fields
97
+ */
98
+ export declare function GenerateCardTemplateFromMetadata(entityName: string, metadataKeys: string[], maxDisplayFields?: number): CardTemplate;
99
+ /**
100
+ * Convert a PascalCase or camelCase key to a human-readable label.
101
+ * E.g., "FirstName" → "First Name", "contentTypeID" → "Content Type ID"
102
+ */
103
+ export declare function FormatKeyAsLabel(key: string): string;
104
+ /**
105
+ * Combine multiple title field values into a single display string.
106
+ * E.g., ["Elizabeth", "Rodriguez"] → "Elizabeth Rodriguez"
107
+ */
108
+ export declare function CombineTitleFields(record: Record<string, unknown>, titleFields: string[]): string;
109
+ //# sourceMappingURL=entity-card.types.d.ts.map
@@ -0,0 +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;AAMnE;;;;;;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,GAC7B,YAAY,CA2Bd;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"}
@@ -0,0 +1,258 @@
1
+ // ================================================================
2
+ // Template Generation Utilities
3
+ // ================================================================
4
+ /** Fields that should never appear in card display */
5
+ const INTERNAL_FIELD_PATTERNS = new Set([
6
+ '__mj_createdat', '__mj_updatedat', 'createdat', 'updatedat',
7
+ 'createdby', 'updatedby', 'password', 'secret', 'token', 'hash',
8
+ ]);
9
+ /** Keyword patterns for auto-detecting field roles */
10
+ const SUBTITLE_KEYWORDS = ['status', 'type', 'category', 'state', 'stage'];
11
+ const DESCRIPTION_KEYWORDS = ['description', 'desc', 'summary', 'notes', 'comments', 'bio', 'about'];
12
+ const THUMBNAIL_KEYWORDS = ['image', 'photo', 'picture', 'thumbnail', 'avatar', 'logo', 'icon'];
13
+ const BADGE_KEYWORDS = ['priority', 'severity', 'importance', 'rating', 'rank', 'level'];
14
+ const METRIC_KEYWORDS = ['amount', 'total', 'count', 'value', 'price', 'cost', 'quantity', 'qty', 'balance', 'revenue', 'score'];
15
+ /**
16
+ * Auto-generate a CardTemplate from entity metadata.
17
+ *
18
+ * Priority logic:
19
+ * 1. **Title**: All fields with `IsNameField=true`, sorted by Sequence. Falls back to
20
+ * heuristic name detection, then first string field.
21
+ * 2. **Display fields**: `DefaultInView=true` fields sorted by Sequence (up to maxFields).
22
+ * Falls back to metric-keyword fields, then first N non-PK/FK fields.
23
+ * 3. **Subtitle/Description/Thumbnail/Badge**: Detected via keyword matching on field names.
24
+ *
25
+ * @param entity - The entity metadata
26
+ * @param maxDisplayFields - Maximum display fields for the card body (default 4)
27
+ * @returns A fully populated CardTemplate
28
+ */
29
+ export function GenerateCardTemplate(entity, maxDisplayFields = 4) {
30
+ const fields = entity.Fields;
31
+ const titleFields = FindTitleFields(entity, fields);
32
+ const titleFieldNames = new Set(titleFields.map(f => f.Name));
33
+ // Build field labels map
34
+ const fieldLabels = {};
35
+ for (const f of fields) {
36
+ fieldLabels[f.Name] = f.DisplayNameOrName;
37
+ }
38
+ return {
39
+ TitleFields: titleFields.map(f => f.Name),
40
+ SubtitleField: FindFieldByKeywords(fields, SUBTITLE_KEYWORDS, titleFieldNames),
41
+ DescriptionField: FindFieldByKeywords(fields, DESCRIPTION_KEYWORDS, titleFieldNames),
42
+ DisplayFields: FindDisplayFields(fields, titleFieldNames, maxDisplayFields),
43
+ ThumbnailFields: FindThumbnailFields(fields),
44
+ BadgeField: FindFieldByKeywords(fields, BADGE_KEYWORDS, titleFieldNames),
45
+ FieldLabels: fieldLabels,
46
+ };
47
+ }
48
+ /**
49
+ * Generate a CardTemplate from an entity name string + sparse metadata keys.
50
+ * This is the primary entry point for Knowledge Hub dashboards where we have
51
+ * vector metadata (partial field set) rather than full entity records.
52
+ *
53
+ * Falls back gracefully when EntityInfo is not available (returns a template
54
+ * based on the metadata keys alone).
55
+ *
56
+ * @param entityName - The entity name for metadata lookup
57
+ * @param metadataKeys - The available field keys in the sparse metadata
58
+ * @param maxDisplayFields - Maximum display fields
59
+ */
60
+ export function GenerateCardTemplateFromMetadata(entityName, metadataKeys, maxDisplayFields = 4) {
61
+ let entity = null;
62
+ try {
63
+ const { Metadata } = require('@memberjunction/core');
64
+ const md = new Metadata();
65
+ entity = md.Entities.find((e) => e.Name === entityName) ?? null;
66
+ }
67
+ catch {
68
+ // Metadata not available — use key-only fallback
69
+ }
70
+ if (entity) {
71
+ const full = GenerateCardTemplate(entity, maxDisplayFields);
72
+ // Filter to only fields present in the metadata
73
+ const keySet = new Set(metadataKeys);
74
+ return {
75
+ ...full,
76
+ TitleFields: full.TitleFields.filter(f => keySet.has(f)),
77
+ DisplayFields: full.DisplayFields.filter(f => keySet.has(f.Name)),
78
+ SubtitleField: full.SubtitleField && keySet.has(full.SubtitleField) ? full.SubtitleField : null,
79
+ DescriptionField: full.DescriptionField && keySet.has(full.DescriptionField) ? full.DescriptionField : null,
80
+ ThumbnailFields: full.ThumbnailFields.filter(f => keySet.has(f)),
81
+ BadgeField: full.BadgeField && keySet.has(full.BadgeField) ? full.BadgeField : null,
82
+ };
83
+ }
84
+ // Fallback: no entity metadata, use key heuristics
85
+ return GenerateFallbackTemplate(metadataKeys, maxDisplayFields);
86
+ }
87
+ // ================================================================
88
+ // Internal Helpers
89
+ // ================================================================
90
+ function FindTitleFields(entity, fields) {
91
+ // 1. All IsNameField fields, sorted by Sequence
92
+ const nameFields = fields
93
+ .filter(f => f.IsNameField)
94
+ .sort((a, b) => (a.Sequence ?? 9999) - (b.Sequence ?? 9999));
95
+ if (nameFields.length > 0)
96
+ return nameFields;
97
+ // 2. entity.NameField (singular)
98
+ if (entity.NameField)
99
+ return [entity.NameField];
100
+ // 3. Heuristic: field named "Name" or "Title"
101
+ const heuristic = fields.find(f => (f.Name.toLowerCase() === 'name' || f.Name.toLowerCase() === 'title') &&
102
+ f.TSType === 'string' && !f.IsPrimaryKey);
103
+ if (heuristic)
104
+ return [heuristic];
105
+ // 4. Field ending with "Name" that's a string
106
+ const endsWithName = fields.find(f => f.Name.toLowerCase().endsWith('name') && f.TSType === 'string' &&
107
+ !f.IsPrimaryKey && !f.Name.toLowerCase().includes('file'));
108
+ if (endsWithName)
109
+ return [endsWithName];
110
+ // 5. First string field
111
+ const firstString = fields.find(f => f.TSType === 'string' && !f.IsPrimaryKey && !f.Name.toLowerCase().includes('id'));
112
+ if (firstString)
113
+ return [firstString];
114
+ return [];
115
+ }
116
+ function FindDisplayFields(fields, excludeNames, maxFields) {
117
+ const result = [];
118
+ const isExcluded = (f) => f.IsPrimaryKey || excludeNames.has(f.Name) ||
119
+ INTERNAL_FIELD_PATTERNS.has(f.Name.toLowerCase()) ||
120
+ f.Name.toLowerCase().endsWith('id') && f.RelatedEntityID != null; // FK fields
121
+ // 1. DefaultInView fields first
122
+ const defaultInView = fields
123
+ .filter(f => f.DefaultInView && !isExcluded(f))
124
+ .sort((a, b) => (a.Sequence ?? 9999) - (b.Sequence ?? 9999));
125
+ for (const f of defaultInView) {
126
+ if (result.length >= maxFields)
127
+ break;
128
+ result.push(BuildCardField(f));
129
+ }
130
+ if (result.length >= 2)
131
+ return result;
132
+ // 2. Metric keyword fields
133
+ const existing = new Set(result.map(r => r.Name));
134
+ for (const f of fields) {
135
+ if (result.length >= maxFields)
136
+ break;
137
+ if (existing.has(f.Name) || isExcluded(f))
138
+ continue;
139
+ if (METRIC_KEYWORDS.some(kw => f.Name.toLowerCase().includes(kw))) {
140
+ result.push(BuildCardField(f));
141
+ existing.add(f.Name);
142
+ }
143
+ }
144
+ if (result.length >= 2)
145
+ return result;
146
+ // 3. First N non-excluded fields by sequence
147
+ for (const f of fields.sort((a, b) => (a.Sequence ?? 9999) - (b.Sequence ?? 9999))) {
148
+ if (result.length >= maxFields)
149
+ break;
150
+ if (existing.has(f.Name) || isExcluded(f))
151
+ continue;
152
+ result.push(BuildCardField(f));
153
+ existing.add(f.Name);
154
+ }
155
+ return result;
156
+ }
157
+ function FindFieldByKeywords(fields, keywords, excludeNames) {
158
+ for (const keyword of keywords) {
159
+ const field = fields.find(f => f.Name.toLowerCase().includes(keyword) &&
160
+ f.TSType === 'string' &&
161
+ !f.IsPrimaryKey &&
162
+ !excludeNames.has(f.Name));
163
+ if (field)
164
+ return field.Name;
165
+ }
166
+ return null;
167
+ }
168
+ function FindThumbnailFields(fields) {
169
+ const result = [];
170
+ for (const keyword of THUMBNAIL_KEYWORDS) {
171
+ for (const f of fields) {
172
+ if (f.Name.toLowerCase().includes(keyword) && f.TSType === 'string' && !result.includes(f.Name)) {
173
+ result.push(f.Name);
174
+ }
175
+ }
176
+ }
177
+ return result;
178
+ }
179
+ function BuildCardField(field) {
180
+ return {
181
+ Name: field.Name,
182
+ Label: field.DisplayNameOrName,
183
+ Type: DetectFieldType(field),
184
+ IsDefaultInView: field.DefaultInView ?? false,
185
+ Sequence: field.Sequence ?? 9999,
186
+ };
187
+ }
188
+ function DetectFieldType(field) {
189
+ if (field.TSType === 'boolean' || field.Type?.toLowerCase() === 'bit')
190
+ return 'boolean';
191
+ if (field.TSType === 'number')
192
+ return 'number';
193
+ if (field.TSType === 'Date' || field.Type?.toLowerCase().includes('date'))
194
+ return 'date';
195
+ return 'text';
196
+ }
197
+ /**
198
+ * Fallback template generation when entity metadata is not available.
199
+ * Uses field name heuristics on the raw metadata keys.
200
+ */
201
+ function GenerateFallbackTemplate(keys, maxFields) {
202
+ const internal = new Set([
203
+ 'Entity', 'EntityIcon', 'RecordID', 'TemplateID',
204
+ '__mj_UpdatedAt', '__mj_CreatedAt', 'ID',
205
+ ]);
206
+ const usable = keys.filter(k => !internal.has(k));
207
+ // Title: 'Name' or first key
208
+ const titleKey = usable.find(k => k.toLowerCase() === 'name')
209
+ ?? usable.find(k => k.toLowerCase().endsWith('name'))
210
+ ?? usable.find(k => k.toLowerCase() === 'title');
211
+ const titleFields = titleKey ? [titleKey] : usable.length > 0 ? [usable[0]] : [];
212
+ const titleSet = new Set(titleFields);
213
+ const displayFields = usable
214
+ .filter(k => !titleSet.has(k))
215
+ .slice(0, maxFields)
216
+ .map((k, i) => ({
217
+ Name: k,
218
+ Label: FormatKeyAsLabel(k),
219
+ Type: 'text',
220
+ IsDefaultInView: false,
221
+ Sequence: i,
222
+ }));
223
+ const fieldLabels = {};
224
+ for (const k of keys) {
225
+ fieldLabels[k] = FormatKeyAsLabel(k);
226
+ }
227
+ return {
228
+ TitleFields: titleFields,
229
+ SubtitleField: null,
230
+ DescriptionField: null,
231
+ DisplayFields: displayFields,
232
+ ThumbnailFields: [],
233
+ BadgeField: null,
234
+ FieldLabels: fieldLabels,
235
+ };
236
+ }
237
+ /**
238
+ * Convert a PascalCase or camelCase key to a human-readable label.
239
+ * E.g., "FirstName" → "First Name", "contentTypeID" → "Content Type ID"
240
+ */
241
+ export function FormatKeyAsLabel(key) {
242
+ return key
243
+ .replace(/([a-z])([A-Z])/g, '$1 $2')
244
+ .replace(/([A-Z]+)([A-Z][a-z])/g, '$1 $2')
245
+ .replace(/^./, c => c.toUpperCase());
246
+ }
247
+ /**
248
+ * Combine multiple title field values into a single display string.
249
+ * E.g., ["Elizabeth", "Rodriguez"] → "Elizabeth Rodriguez"
250
+ */
251
+ export function CombineTitleFields(record, titleFields) {
252
+ const parts = titleFields
253
+ .map(f => record[f])
254
+ .filter(v => v != null && String(v).trim() !== '')
255
+ .map(v => String(v));
256
+ return parts.length > 0 ? parts.join(' ') : 'Unknown';
257
+ }
258
+ //# sourceMappingURL=entity-card.types.js.map
@@ -0,0 +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"]}
@@ -0,0 +1,8 @@
1
+ export * from './lib/entity-card.component';
2
+ export * from './lib/entity-card.types';
3
+ /**
4
+ * Tree-shaking prevention function. Call this from consuming modules
5
+ * to ensure the component registration is not removed by bundlers.
6
+ */
7
+ export declare function LoadEntityCard(): void;
8
+ //# sourceMappingURL=public-api.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"public-api.d.ts","sourceRoot":"","sources":["../src/public-api.ts"],"names":[],"mappings":"AAIA,cAAc,6BAA6B,CAAC;AAC5C,cAAc,yBAAyB,CAAC;AAExC;;;GAGG;AACH,wBAAgB,cAAc,SAE7B"}
@@ -0,0 +1,13 @@
1
+ /*
2
+ * Public API Surface for @memberjunction/ng-entity-card
3
+ */
4
+ export * from './lib/entity-card.component';
5
+ export * from './lib/entity-card.types';
6
+ /**
7
+ * Tree-shaking prevention function. Call this from consuming modules
8
+ * to ensure the component registration is not removed by bundlers.
9
+ */
10
+ export function LoadEntityCard() {
11
+ /* prevent tree-shaking */
12
+ }
13
+ //# sourceMappingURL=public-api.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"public-api.js","sourceRoot":"","sources":["../src/public-api.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,cAAc,6BAA6B,CAAC;AAC5C,cAAc,yBAAyB,CAAC;AAExC;;;GAGG;AACH,MAAM,UAAU,cAAc;IAC1B,0BAA0B;AAC9B,CAAC","sourcesContent":["/*\n * Public API Surface for @memberjunction/ng-entity-card\n */\n\nexport * from './lib/entity-card.component';\nexport * from './lib/entity-card.types';\n\n/**\n * Tree-shaking prevention function. Call this from consuming modules\n * to ensure the component registration is not removed by bundlers.\n */\nexport function LoadEntityCard() {\n /* prevent tree-shaking */\n}\n"]}
package/package.json CHANGED
@@ -1,10 +1,35 @@
1
1
  {
2
2
  "name": "@memberjunction/ng-entity-card",
3
- "version": "0.0.1",
4
- "description": "OIDC trusted publishing setup package for @memberjunction/ng-entity-card",
5
- "keywords": [
6
- "oidc",
7
- "trusted-publishing",
8
- "setup"
9
- ]
3
+ "version": "5.24.0",
4
+ "description": "MemberJunction: Generic entity record card component with metadata-driven field prioritization",
5
+ "main": "./dist/public-api.js",
6
+ "typings": "./dist/public-api.d.ts",
7
+ "files": [
8
+ "/dist"
9
+ ],
10
+ "scripts": {
11
+ "build": "ngc",
12
+ "test": "vitest run",
13
+ "test:watch": "vitest"
14
+ },
15
+ "keywords": [],
16
+ "author": "MemberJunction.com",
17
+ "license": "ISC",
18
+ "devDependencies": {
19
+ "@angular/compiler": "21.1.3",
20
+ "@angular/compiler-cli": "21.1.3"
21
+ },
22
+ "peerDependencies": {
23
+ "@angular/common": "21.1.3",
24
+ "@angular/core": "21.1.3"
25
+ },
26
+ "dependencies": {
27
+ "@memberjunction/core": "5.24.0",
28
+ "@memberjunction/global": "5.24.0",
29
+ "tslib": "^2.8.1"
30
+ },
31
+ "repository": {
32
+ "type": "git",
33
+ "url": "https://github.com/MemberJunction/MJ"
34
+ }
10
35
  }
package/README.md DELETED
@@ -1,45 +0,0 @@
1
- # @memberjunction/ng-entity-card
2
-
3
- ## ⚠️ IMPORTANT NOTICE ⚠️
4
-
5
- **This package is created solely for the purpose of setting up OIDC (OpenID Connect) trusted publishing with npm.**
6
-
7
- This is **NOT** a functional package and contains **NO** code or functionality beyond the OIDC setup configuration.
8
-
9
- ## Purpose
10
-
11
- This package exists to:
12
- 1. Configure OIDC trusted publishing for the package name `@memberjunction/ng-entity-card`
13
- 2. Enable secure, token-less publishing from CI/CD workflows
14
- 3. Establish provenance for packages published under this name
15
-
16
- ## What is OIDC Trusted Publishing?
17
-
18
- OIDC trusted publishing allows package maintainers to publish packages directly from their CI/CD workflows without needing to manage npm access tokens. Instead, it uses OpenID Connect to establish trust between the CI/CD provider (like GitHub Actions) and npm.
19
-
20
- ## Setup Instructions
21
-
22
- To properly configure OIDC trusted publishing for this package:
23
-
24
- 1. Go to [npmjs.com](https://www.npmjs.com/) and navigate to your package settings
25
- 2. Configure the trusted publisher (e.g., GitHub Actions)
26
- 3. Specify the repository and workflow that should be allowed to publish
27
- 4. Use the configured workflow to publish your actual package
28
-
29
- ## DO NOT USE THIS PACKAGE
30
-
31
- This package is a placeholder for OIDC configuration only. It:
32
- - Contains no executable code
33
- - Provides no functionality
34
- - Should not be installed as a dependency
35
- - Exists only for administrative purposes
36
-
37
- ## More Information
38
-
39
- For more details about npm's trusted publishing feature, see:
40
- - [npm Trusted Publishing Documentation](https://docs.npmjs.com/generating-provenance-statements)
41
- - [GitHub Actions OIDC Documentation](https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/about-security-hardening-with-openid-connect)
42
-
43
- ---
44
-
45
- **Maintained for OIDC setup purposes only**