@memberjunction/ng-record-process-studio 5.51.0 → 6.1.0-edge.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/LICENSE +7 -0
- package/dist/lib/record-process-history/record-process-history.component.js +2 -2
- package/dist/lib/record-process-history/record-process-history.component.js.map +1 -1
- package/dist/lib/record-process-studio/record-process-studio.component.js +1 -1
- package/dist/lib/record-process-studio/record-process-studio.component.js.map +1 -1
- package/package.json +19 -16
package/LICENSE
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
ISC License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2023 MemberJunction
|
|
4
|
+
|
|
5
|
+
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.
|
|
6
|
+
|
|
7
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
@@ -315,7 +315,7 @@ export class RecordProcessHistoryComponent extends BaseAngularComponent {
|
|
|
315
315
|
async reload() {
|
|
316
316
|
this.Loading = true;
|
|
317
317
|
this.cdr.detectChanges();
|
|
318
|
-
const rv =
|
|
318
|
+
const rv = RunView.FromMetadataProvider(this.ProviderToUse);
|
|
319
319
|
const filter = this.RecordProcessID ? `RecordProcessID='${this.RecordProcessID}'` : '';
|
|
320
320
|
const [runs, procs] = await rv.RunViews([
|
|
321
321
|
{
|
|
@@ -345,7 +345,7 @@ export class RecordProcessHistoryComponent extends BaseAngularComponent {
|
|
|
345
345
|
this.DetailLoading = true;
|
|
346
346
|
this.Details = [];
|
|
347
347
|
this.cdr.detectChanges();
|
|
348
|
-
const rv =
|
|
348
|
+
const rv = RunView.FromMetadataProvider(this.ProviderToUse);
|
|
349
349
|
const result = await rv.RunView({
|
|
350
350
|
EntityName: 'MJ: Process Run Details',
|
|
351
351
|
Fields: ['RecordID', 'Status', 'ResultPayload', 'ErrorMessage'],
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"record-process-history.component.js","sourceRoot":"","sources":["../../../src/lib/record-process-history/record-process-history.component.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAE,uBAAuB,EAAE,iBAAiB,EAAE,SAAS,EAAE,KAAK,EAAqB,MAAM,EAAE,MAAM,eAAe,CAAC;AAExH,OAAO,EAAE,oBAAoB,EAAE,MAAM,+BAA+B,CAAC;AACrE,OAAO,EAAE,UAAU,EAAmB,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAC5E,OAAO,EAAW,WAAW,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AACvF,OAAO,EAAE,iBAAiB,EAAE,MAAM,kCAAkC,CAAC;AACrE,OAAO,EAAE,4BAA4B,EAAE,eAAe,EAAwB,MAAM,eAAe,CAAC;;;;;;IAwBpF,8BAAqB;IAAA,uBAAgD;IAAC,8BAAQ;IAAA,iBAAM;;;IAEpF,8BAAqB;IAAA,4BAAY;IAAA,iBAAM;;;IAUiI,gCAA4E;IAAA,uBAAO;IAAA,iBAAO;;;IAG7N,gCAAyB;IAAA,YAAsB;IAAA,iBAAO;;;IAA7B,cAAsB;IAAtB,kDAAsB;;;IAC7C,gCAAqB;IAAA,YAAyB;IAAA,iBAAO;;;IAAhC,cAAyB;IAAzB,qDAAyB;;;;IAT7E,+BAAyC;IAArB,2OAAS,oBAAU,KAAC;IACpC,+BAAqB;IAAA,YAAsB;IAAA,iBAAM;IACjD,+BAAoB;IAAA,YAAmB;IAAA,iBAAM;IAC7C,2BAAK;IAAA,YAAkB;IAAA,iBAAM;IAC7B,+BAAqB;IAAA,YAA0B;IAAA,iBAAM;IAChD,AAAL,2BAAK,gBAAuF;IAAA,4BAAyB;IAAC,aAAc;IAAA,iBAAO;IAAA,sIAAgB;IAA6F,iBAAM;IAE1P,AADJ,gCAAoB,gBACQ;IAAA,aAA4B;IAAA,iBAAO;IAC3D,sIAAoB;IACpB,sIAAsB;IAE9B,AADI,iBAAM,EACJ;;;;IAVmB,eAAsB;IAAtB,gDAAsB;IACvB,eAAmB;IAAnB,sCAAmB;IAClC,eAAkB;IAAlB,qCAAkB;IACF,eAA0B;IAA1B,kDAA0B;IACzB,eAAmC;IAAC,AAApC,iDAAmC,iCAAkC;IAA2B,eAAc;IAAd,uCAAc;IAAO,cAA6G;IAA7G,uCAA6G;IAE5N,eAA4B;IAA5B,wDAA4B;IACpD,cAA6E;IAA7E,2CAA6E;IAC7E,cAA8E;IAA9E,6CAA8E;;;IAX7D,AAA7B,AADJ,8BAAuB,aACU,UAAK;IAAA,oBAAI;IAAA,iBAAM;IAAA,2BAAK;IAAA,yBAAS;IAAA,iBAAM;IAAA,2BAAK;IAAA,sBAAM;IAAA,iBAAM;IAAA,2BAAK;IAAA,kBAAE;IAAA,iBAAM;IAAA,4BAAK;IAAA,uBAAM;IAAA,iBAAM;IAAA,4BAAK;IAAA,uBAAM;IAAM,AAAN,iBAAM,EAAM;IACtI,gIAaC;IACL,iBAAM;;;IAdF,gBAaC;IAbD,0BAaC;;;;IAvBL,AADJ,8BAAqB,YACK;IAAA,2BAAW;IAAA,iBAAK;IAAA,0BAAgC;IACtE,iCAAmD;IAAnB,iMAAS,eAAQ,KAAC;IAAC,uBAAyC;IAAC,wBAAO;IACxG,AADwG,iBAAS,EAC3G;IAKJ,AAFA,AAFF,6GAAe,uFAEiB,wFAEvB;;;IAJT,eAsBC;IAtBD,uEAsBC;;;IAK8B,gCAA4E;IAAA,uBAAO;IAAA,iBAAO;;;IAGrH,8BAAqB;IAAA,uBAAgD;IAAC,sCAAgB;IAAA,iBAAM;;;IAE5F,8BAAqB;IAAA,4DAA4C;IAAA,iBAAM;;;IAW9B,AAArB,+BAAqB,eAAgB;IAAA,YAAa;IAAA,iBAAO;IACrD,gCAAgB;IAAA,YAAsB;IAAA,iBAAO;IAAA,wBAA+C;IAAA,gCAAiB;IAAA,YAAsB;IAAO,AAAP,iBAAO,EAAM;;;;IAD/G,eAAa;IAAb,gCAAa;IAC9B,eAAsB;IAAtB,gDAAsB;IAAuE,eAAsB;IAAtB,gDAAsB;;;IAEpH,+BAA6B;IAAA,YAAoB;IAAA,iBAAM;;;IAA1B,cAAoB;IAApB,uCAAoB;;;IACtB,gCAAsB;IAAA,0BAAU;IAAA,iBAAO;;;IATzF,AADJ,AADJ,+BAAwB,cACI,eAC2G;IAAA,2BAAyB;IAAC,YAAc;IAAA,iBAAO;IAC9K,+BAAoB;IAAA,YAAgB;IACxC,AADwC,iBAAM,EACxC;IACN,+BAAyB;IACrB,mIAGC;IACD,oIAAsB;IACtB,qIAAiD;IAEzD,AADI,iBAAM,EACJ;;;IAXsB,eAAmC;IAAmC,AAAlC,AAApC,iDAAmC,iCAAkC,oCAAqC;IAA2B,eAAc;IAAd,uCAAc;IACnJ,eAAgB;IAAhB,mCAAgB;IAGpC,eAGC;IAHD,2BAGC;IACD,eAAgF;IAAhF,6CAAgF;IAChF,cAA2F;IAA3F,2EAA2F;;;IAb3G,+BAAyB;IACrB,+HAeC;IACL,iBAAM;;;IAhBF,cAeC;IAfD,6BAeC;;;;IAzBL,AADJ,8BAAqB,gBACsC;IAAvB,iMAAS,mBAAY,KAAC;IAAC,wBAAsC;IAAC,qBAAI;IAAA,iBAAS;IAC3G,6BAAsB;IAAA,YAAgE;IAAA,iBAAK;IAC3F,+GAA0B;IAC9B,iBAAM;IAKJ,AAFA,AAFF,6GAAqB,uFAEc,wFAE1B;;;IAPiB,eAAgE;IAAhE,mLAAgE;IACtF,cAAuH;IAAvH,wFAAuH;IAE3H,cAuBC;IAvBD,gFAuBC;;AAyCb,MAAM,OAAO,6BAA8B,SAAQ,oBAAoB;IAC3D,GAAG,GAAG,MAAM,CAAC,iBAAiB,CAAC,CAAC;IAExC,uDAAuD;IAC9C,eAAe,GAAkB,IAAI,CAAC;IAExC,IAAI,GAAsB,MAAM,CAAC;IACjC,OAAO,GAAG,IAAI,CAAC;IACf,aAAa,GAAG,KAAK,CAAC;IACtB,IAAI,GAAa,EAAE,CAAC;IACpB,OAAO,GAAgB,EAAE,CAAC;IAC1B,UAAU,GAAkB,IAAI,CAAC;IAExC;;;;;OAKG;IACK,QAAQ,CAAgB;IACxB,aAAa,GAAG,KAAK,CAAC;IACtB,WAAW,GAAyC,IAAI,CAAC;IAEjE,KAAK,CAAC,QAAQ;QACV,MAAM,IAAI,CAAC,MAAM,EAAE,CAAC;QACpB,IAAI,CAAC,qBAAqB,EAAE,CAAC;IACjC,CAAC;IAED,WAAW;QACP,IAAI,CAAC,QAAQ,EAAE,WAAW,EAAE,CAAC;QAC7B,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;YACnB,YAAY,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QACnC,CAAC;IACL,CAAC;IAED,0GAA0G;IAClG,qBAAqB;QACzB,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC,QAAQ,CAAC,gBAAgB,CAAC,KAAK,CAAC,CAAC,SAAS,CAAC,CAAC,KAAc,EAAE,EAAE;YACnF,IAAI,KAAK,CAAC,KAAK,KAAK,WAAW,CAAC,cAAc,IAAI,KAAK,CAAC,SAAS,KAAK,UAAU,CAAC,aAAa,EAAE,CAAC;gBAC7F,OAAO;YACX,CAAC;YACD,MAAM,CAAC,GAAG,KAAK,CAAC,IAAuB,CAAC;YACxC,IAAI,CAAC,CAAC,IAAI,KAAK,MAAM,IAAI,CAAC,CAAC,IAAI,KAAK,QAAQ,IAAI,CAAC,CAAC,IAAI,KAAK,mBAAmB,EAAE,CAAC;gBAC7E,OAAO;YACX,CAAC;YACD,MAAM,IAAI,GAAG,CAAC,CAAC,UAAU,EAAE,UAAU,EAAE,IAAI,IAAI,CAAC,CAAC,UAAU,CAAC;YAC5D,IAAI,IAAI,KAAK,kBAAkB,IAAI,IAAI,KAAK,yBAAyB,EAAE,CAAC;gBACpE,IAAI,CAAC,cAAc,EAAE,CAAC;YAC1B,CAAC;QACL,CAAC,CAAC,CAAC;IACP,CAAC;IAED,wHAAwH;IAChH,cAAc;QAClB,IAAI,IAAI,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;YACvB,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC,CAAC,2EAA2E;YACtG,OAAO;QACX,CAAC;QACD,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;YACnB,YAAY,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QACnC,CAAC;QACD,IAAI,CAAC,WAAW,GAAG,UAAU,CAAC,GAAG,EAAE;YAC/B,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC;YACxB,KAAK,IAAI,CAAC,MAAM,EAAE,CAAC;QACvB,CAAC,EAAE,GAAG,CAAC,CAAC;IACZ,CAAC;IAED,KAAK,CAAC,MAAM;QACR,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;QAAC,IAAI,CAAC,GAAG,CAAC,aAAa,EAAE,CAAC;QAC9C,MAAM,EAAE,GAAG,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,oBAAoB,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,OAAO,EAAE,CAAC;QACvF,MAAM,MAAM,GAAG,IAAI,CAAC,eAAe,CAAC,CAAC,CAAC,oBAAoB,IAAI,CAAC,eAAe,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;QACvF,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC;YACpC;gBACI,UAAU,EAAE,kBAAkB;gBAC9B,MAAM,EAAE,CAAC,IAAI,EAAE,iBAAiB,EAAE,UAAU,EAAE,QAAQ,EAAE,WAAW,EAAE,cAAc,EAAE,YAAY,EAAE,cAAc,EAAE,gBAAgB,EAAE,aAAa,EAAE,YAAY,EAAE,QAAQ,CAAC;gBAC3K,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,gBAAgB,EAAE,OAAO,EAAE,GAAG,EAAE,UAAU,EAAE,QAAQ;aACrF;YACD,EAAE,UAAU,EAAE,sBAAsB,EAAE,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,CAAC,EAAE,UAAU,EAAE,QAAQ,EAAE;SACvF,CAAC,CAAC;QACH,yFAAyF;QACzF,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAkB,CAAC;QAC3C,IAAI,KAAK,CAAC,OAAO;YAAE,KAAK,MAAM,CAAC,IAAI,KAAK,CAAC,OAA8C;gBAAE,QAAQ,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC;QACnI,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,CAAC,CAAE,IAAI,CAAC,OAAoB,CAAC,CAAC,CAAC,EAAE,CAAC;QAC/D,IAAI,CAAC,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YAC5B,GAAG,CAAC;YACJ,WAAW,EAAE,QAAQ,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,IAAI,WAAW;YAC1E,UAAU,EAAE,IAAI,CAAC,aAAa,CAAC,UAAU,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,IAAI,IAAI,CAAC,CAAC,QAAQ;SAC5E,CAAC,CAAC,CAAC;QACJ,IAAI,CAAC,OAAO,GAAG,KAAK,CAAC;QACrB,IAAI,CAAC,GAAG,CAAC,aAAa,EAAE,CAAC;IAC7B,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,GAAW;QACrB,IAAI,CAAC,UAAU,GAAG,GAAG,CAAC;QAAC,IAAI,CAAC,IAAI,GAAG,QAAQ,CAAC;QAAC,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC;QAAC,IAAI,CAAC,OAAO,GAAG,EAAE,CAAC;QAAC,IAAI,CAAC,GAAG,CAAC,aAAa,EAAE,CAAC;QACpH,MAAM,EAAE,GAAG,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,oBAAoB,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,OAAO,EAAE,CAAC;QACvF,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC,OAAO,CAAY;YACvC,UAAU,EAAE,yBAAyB;YACrC,MAAM,EAAE,CAAC,UAAU,EAAE,QAAQ,EAAE,eAAe,EAAE,cAAc,CAAC;YAC/D,WAAW,EAAE,iBAAiB,GAAG,CAAC,EAAE,GAAG,EAAE,OAAO,EAAE,kBAAkB,EAAE,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,QAAQ;SAC5G,CAAC,CAAC;QACH,IAAI,CAAC,OAAO,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YACtE,QAAQ,EAAE,CAAC,CAAC,QAAQ,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,OAAO,EAAE,4BAA4B,CAAC,CAAC,CAAC,aAAa,CAAC,EAAE,YAAY,EAAE,CAAC,CAAC,YAAY;SAC/H,CAAC,CAAC,CAAC;QACJ,IAAI,CAAC,aAAa,GAAG,KAAK,CAAC;QAC3B,IAAI,CAAC,GAAG,CAAC,aAAa,EAAE,CAAC;IAC7B,CAAC;IAED,UAAU;QACN,IAAI,CAAC,IAAI,GAAG,MAAM,CAAC;QACnB,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC;QACvB,IAAI,CAAC,GAAG,CAAC,aAAa,EAAE,CAAC;QACzB,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC;YACrB,IAAI,CAAC,aAAa,GAAG,KAAK,CAAC;YAC3B,KAAK,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,kEAAkE;QAC1F,CAAC;IACL,CAAC;IAED,GAAG,CAAC,KAAqB;QACrB,IAAI,CAAC,KAAK;YAAE,OAAO,GAAG,CAAC;QACvB,MAAM,CAAC,GAAG,KAAK,YAAY,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,CAAC;QAC1D,OAAO,KAAK,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,cAAc,EAAE,CAAC;IACnE,CAAC;IACD,IAAI,CAAC,KAAc,IAAY,OAAO,eAAe,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;iSA1HtD,6BAA6B,yBAA7B,6BAA6B;6DAA7B,6BAA6B;YAtEhC,AA5BF,sFAAuB,gEA4Bd;;YA5BT,6CA0DC;4BA5DK,iBAAiB;;iFAoGlB,6BAA6B;cAxGzC,SAAS;2BACI,2BAA2B,cACzB,IAAI,mBACC,uBAAuB,CAAC,MAAM,WACtC,CAAC,iBAAiB,CAAC,YAClB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KA4DT;;kBA2CA,KAAK;;kFAJG,6BAA6B","sourcesContent":["/**\n * @fileoverview Run history for Bulk Operations: every Record Process run (who/when/scope/status/counts),\n * drilling into the per-record outcome (the field diff persisted on each Process Run Detail). Read-only\n * audit surface. Generic + reusable.\n * @module @memberjunction/ng-record-process-studio\n */\nimport { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnDestroy, OnInit, inject } from '@angular/core';\nimport { Subscription } from 'rxjs';\nimport { BaseAngularComponent } from '@memberjunction/ng-base-types';\nimport { BaseEntity, BaseEntityEvent, RunView } from '@memberjunction/core';\nimport { MJEvent, MJEventType, MJGlobal, NormalizeUUID } from '@memberjunction/global';\nimport { MJButtonDirective } from '@memberjunction/ng-ui-components';\nimport { parseAppliedRunDetailChanges, displayRunValue, type RunDetailChange } from '../run-detail';\n\ninterface RawRun {\n ID: string; RecordProcessID: string; EntityID: string; Status: string;\n StartTime?: string | Date; SuccessCount?: number; ErrorCount?: number; SkippedCount?: number;\n TotalItemCount?: number; TriggeredBy?: string; SourceType?: string; DryRun?: boolean;\n}\ninterface RunRow extends RawRun { ProcessName: string; EntityName: string; }\n\ninterface RawDetail { RecordID: string; Status: string; ResultPayload?: string; ErrorMessage?: string; }\ninterface DetailRow { RecordID: string; Status: string; Changes: RunDetailChange[]; ErrorMessage?: string; }\n\n@Component({\n selector: 'mj-record-process-history',\n standalone: true,\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [MJButtonDirective],\n template: `\n @if (Mode === 'list') {\n <div class=\"rph-bar\">\n <h3 class=\"rph-title\">Run history</h3><span class=\"rph-spacer\"></span>\n <button mjButton variant=\"flat\" (click)=\"reload()\"><i class=\"fa-solid fa-arrows-rotate\"></i> Refresh</button>\n </div>\n @if (Loading) {\n <div class=\"rph-msg\"><i class=\"fa-solid fa-circle-notch fa-spin\"></i> Loading…</div>\n } @else if (Runs.length === 0) {\n <div class=\"rph-msg\">No runs yet.</div>\n } @else {\n <div class=\"rph-table\">\n <div class=\"rph-tr rph-head\"><div>When</div><div>Operation</div><div>Entity</div><div>By</div><div>Status</div><div>Counts</div></div>\n @for (r of Runs; track r.ID) {\n <div class=\"rph-tr\" (click)=\"openRun(r)\">\n <div class=\"rph-sub\">{{ fmt(r.StartTime) }}</div>\n <div class=\"rph-nm\">{{ r.ProcessName }}</div>\n <div>{{ r.EntityName }}</div>\n <div class=\"rph-sub\">{{ r.TriggeredBy || '—' }}</div>\n <div><span class=\"st\" [class.ok]=\"r.Status==='Completed'\" [class.err]=\"r.Status==='Failed'\"><span class=\"dot\"></span> {{ r.Status }}</span>@if (r.DryRun) { <span class=\"dryrun\" title=\"Compute-only preview — no changes were written\">Dry Run</span> }</div>\n <div class=\"counts\">\n <span class=\"cbadge ok\">{{ r.SuccessCount || 0 }} ok</span>\n @if (r.ErrorCount) { <span class=\"cbadge err\">{{ r.ErrorCount }} err</span> }\n @if (r.SkippedCount) { <span class=\"cbadge\">{{ r.SkippedCount }} skip</span> }\n </div>\n </div>\n }\n </div>\n }\n } @else {\n <div class=\"rph-bar\">\n <button mjButton variant=\"flat\" (click)=\"backToList()\"><i class=\"fa-solid fa-arrow-left\"></i> Back</button>\n <h3 class=\"rph-title\">{{ OpenRunRow?.ProcessName }} · {{ fmt(OpenRunRow?.StartTime) }}</h3>\n @if (OpenRunRow?.DryRun) { <span class=\"dryrun\" title=\"Compute-only preview — no changes were written\">Dry Run</span> }\n </div>\n @if (DetailLoading) {\n <div class=\"rph-msg\"><i class=\"fa-solid fa-circle-notch fa-spin\"></i> Loading details…</div>\n } @else if (Details.length === 0) {\n <div class=\"rph-msg\">No per-record details recorded for this run.</div>\n } @else {\n <div class=\"rph-details\">\n @for (d of Details; track d.RecordID) {\n <div class=\"rph-detrec\">\n <div class=\"rph-detkey\">\n <span class=\"st sm\" [class.ok]=\"d.Status==='Succeeded'\" [class.err]=\"d.Status==='Failed'\" [class.muted]=\"d.Status==='Skipped'\"><span class=\"dot\"></span> {{ d.Status }}</span>\n <div class=\"rph-rk\">{{ d.RecordID }}</div>\n </div>\n <div class=\"rph-changes\">\n @for (c of d.Changes; track c.Field) {\n <div class=\"rph-chg\"><span class=\"f\">{{ c.Field }}</span>\n <span class=\"o\">{{ disp(c.OldValue) }}</span><i class=\"fa-solid fa-arrow-right-long ar\"></i><span class=\"nw\">{{ disp(c.NewValue) }}</span></div>\n }\n @if (d.ErrorMessage) { <div class=\"rph-chg err-msg\">{{ d.ErrorMessage }}</div> }\n @if (d.Changes.length === 0 && !d.ErrorMessage) { <span class=\"rph-sub\">no changes</span> }\n </div>\n </div>\n }\n </div>\n }\n }\n `,\n styles: [`\n :host{display:block;color:var(--mj-text-primary)}\n .rph-bar{display:flex;align-items:center;gap:14px;margin-bottom:16px}\n .rph-title{margin:0;font-size:16px;font-weight:700}\n .rph-spacer{flex:1}\n .rph-bar button i{margin-right:6px}\n .rph-msg{padding:36px;text-align:center;color:var(--mj-text-muted)}\n .rph-table{border:1px solid var(--mj-border-subtle);border-radius:var(--mj-radius-md,10px);overflow:hidden}\n .rph-tr{display:grid;grid-template-columns:1.3fr 1.6fr 1.2fr .9fr 1fr 1.4fr;gap:14px;align-items:center;padding:12px 16px;border-bottom:1px solid var(--mj-border-subtle);font-size:13px;cursor:pointer}\n .rph-tr:last-child{border-bottom:none}\n .rph-tr:not(.rph-head):hover{background:var(--mj-bg-surface-hover)}\n .rph-head{background:var(--mj-bg-surface-sunken);font-size:11px;text-transform:uppercase;letter-spacing:.05em;color:var(--mj-text-muted);font-weight:700;cursor:default}\n .rph-nm{font-weight:600}\n .rph-sub{color:var(--mj-text-muted);font-size:12px}\n .st{display:inline-flex;align-items:center;gap:7px;font-weight:600;font-size:12.5px}\n .st .dot{width:8px;height:8px;border-radius:50%;background:var(--mj-text-disabled)}\n .st.ok .dot{background:var(--mj-status-success)} .st.ok{color:var(--mj-status-success-text)}\n .st.err .dot{background:var(--mj-status-error)} .st.err{color:var(--mj-status-error-text)}\n .st.muted{color:var(--mj-text-muted)}\n .st.sm{font-size:11.5px}\n .counts{display:flex;gap:6px;flex-wrap:wrap}\n .cbadge{font-size:11px;font-weight:700;padding:2px 8px;border-radius:6px;background:var(--mj-bg-surface-sunken);color:var(--mj-text-secondary)}\n .dryrun{font-size:10px;font-weight:700;text-transform:uppercase;letter-spacing:.04em;padding:2px 7px;border-radius:6px;margin-left:6px;background:var(--mj-status-info-bg);color:var(--mj-status-info-text);border:1px solid var(--mj-status-info-border)}\n .cbadge.ok{background:var(--mj-status-success-bg);color:var(--mj-status-success-text)}\n .cbadge.err{background:var(--mj-status-error-bg);color:var(--mj-status-error-text)}\n .rph-details{border:1px solid var(--mj-border-subtle);border-radius:var(--mj-radius-md,10px);overflow:hidden;max-height:560px;overflow-y:auto}\n .rph-detrec{display:grid;grid-template-columns:200px 1fr;gap:14px;padding:11px 16px;border-bottom:1px solid var(--mj-border-subtle)}\n .rph-detrec:last-child{border-bottom:none}\n .rph-detkey{display:flex;flex-direction:column;gap:5px}\n .rph-rk{font-family:var(--mj-font-mono,monospace);font-size:11px;color:var(--mj-text-muted);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}\n .rph-chg{display:flex;align-items:center;gap:8px;flex-wrap:wrap;font-size:13px;padding:2px 0}\n .rph-chg .f{font-weight:700;min-width:120px}\n .rph-chg .o{color:var(--mj-text-muted);text-decoration:line-through}\n .rph-chg .nw{color:var(--mj-status-success-text);font-weight:600}\n .rph-chg .ar{color:var(--mj-text-disabled);font-size:11px}\n .rph-chg.err-msg{color:var(--mj-status-error-text)}\n `],\n})\nexport class RecordProcessHistoryComponent extends BaseAngularComponent implements OnInit, OnDestroy {\n private cdr = inject(ChangeDetectorRef);\n\n /** Optional: show runs for one Record Process only. */\n @Input() RecordProcessID: string | null = null;\n\n public Mode: 'list' | 'detail' = 'list';\n public Loading = true;\n public DetailLoading = false;\n public Runs: RunRow[] = [];\n public Details: DetailRow[] = [];\n public OpenRunRow: RunRow | null = null;\n\n /**\n * There is no RecordProcess BaseEngine to observe — Process Runs is an unbounded log table, so we don't\n * bulk-cache it. Instead we re-query on demand AND keep the list live by listening for BaseEntity save/\n * delete events on the run tables: a run started/finished anywhere (this view, the editor preview, a grid\n * runner, the scheduler) fires an event, and we debounce-reload so history never goes stale behind a run.\n */\n private eventSub?: Subscription;\n private pendingReload = false;\n private reloadTimer: ReturnType<typeof setTimeout> | null = null;\n\n async ngOnInit(): Promise<void> {\n await this.reload();\n this.subscribeToRunChanges();\n }\n\n ngOnDestroy(): void {\n this.eventSub?.unsubscribe();\n if (this.reloadTimer) {\n clearTimeout(this.reloadTimer);\n }\n }\n\n /** Subscribe to BaseEntity events and debounce-reload whenever a Process Run (or its details) changes. */\n private subscribeToRunChanges(): void {\n this.eventSub = MJGlobal.Instance.GetEventListener(false).subscribe((event: MJEvent) => {\n if (event.event !== MJEventType.ComponentEvent || event.eventCode !== BaseEntity.BaseEventCode) {\n return;\n }\n const e = event.args as BaseEntityEvent;\n if (e.type !== 'save' && e.type !== 'delete' && e.type !== 'remote-invalidate') {\n return;\n }\n const name = e.baseEntity?.EntityInfo?.Name ?? e.entityName;\n if (name === 'MJ: Process Runs' || name === 'MJ: Process Run Details') {\n this.scheduleReload();\n }\n });\n }\n\n /** Debounce a burst of run/detail/checkpoint saves into a single reload; defer if the user is drilled into a detail. */\n private scheduleReload(): void {\n if (this.Mode !== 'list') {\n this.pendingReload = true; // refresh when they return to the list — don't yank them out of a drill-in\n return;\n }\n if (this.reloadTimer) {\n clearTimeout(this.reloadTimer);\n }\n this.reloadTimer = setTimeout(() => {\n this.reloadTimer = null;\n void this.reload();\n }, 400);\n }\n\n async reload(): Promise<void> {\n this.Loading = true; this.cdr.detectChanges();\n const rv = this.Provider ? RunView.FromMetadataProvider(this.Provider) : new RunView();\n const filter = this.RecordProcessID ? `RecordProcessID='${this.RecordProcessID}'` : '';\n const [runs, procs] = await rv.RunViews([\n {\n EntityName: 'MJ: Process Runs',\n Fields: ['ID', 'RecordProcessID', 'EntityID', 'Status', 'StartTime', 'SuccessCount', 'ErrorCount', 'SkippedCount', 'TotalItemCount', 'TriggeredBy', 'SourceType', 'DryRun'],\n ExtraFilter: filter, OrderBy: 'StartTime DESC', MaxRows: 200, ResultType: 'simple',\n },\n { EntityName: 'MJ: Record Processes', Fields: ['ID', 'Name'], ResultType: 'simple' },\n ]);\n // Normalized-UUID keys → O(1), case-safe across SQL Server (upper) / PostgreSQL (lower).\n const nameByID = new Map<string, string>();\n if (procs.Success) for (const p of procs.Results as Array<{ ID: string; Name: string }>) nameByID.set(NormalizeUUID(p.ID), p.Name);\n const rawRuns = runs.Success ? (runs.Results as RawRun[]) : [];\n this.Runs = rawRuns.map((r) => ({\n ...r,\n ProcessName: nameByID.get(NormalizeUUID(r.RecordProcessID)) ?? '(deleted)',\n EntityName: this.ProviderToUse.EntityByID(r.EntityID)?.Name ?? r.EntityID,\n }));\n this.Loading = false;\n this.cdr.detectChanges();\n }\n\n async openRun(run: RunRow): Promise<void> {\n this.OpenRunRow = run; this.Mode = 'detail'; this.DetailLoading = true; this.Details = []; this.cdr.detectChanges();\n const rv = this.Provider ? RunView.FromMetadataProvider(this.Provider) : new RunView();\n const result = await rv.RunView<RawDetail>({\n EntityName: 'MJ: Process Run Details',\n Fields: ['RecordID', 'Status', 'ResultPayload', 'ErrorMessage'],\n ExtraFilter: `ProcessRunID='${run.ID}'`, OrderBy: 'Status, RecordID', MaxRows: 1000, ResultType: 'simple',\n });\n this.Details = (result.Success ? (result.Results ?? []) : []).map((d) => ({\n RecordID: d.RecordID, Status: d.Status, Changes: parseAppliedRunDetailChanges(d.ResultPayload), ErrorMessage: d.ErrorMessage,\n }));\n this.DetailLoading = false;\n this.cdr.detectChanges();\n }\n\n backToList(): void {\n this.Mode = 'list';\n this.OpenRunRow = null;\n this.cdr.detectChanges();\n if (this.pendingReload) {\n this.pendingReload = false;\n void this.reload(); // a run changed while we were drilled into a detail — refresh now\n }\n }\n\n fmt(value?: string | Date): string {\n if (!value) return '—';\n const d = value instanceof Date ? value : new Date(value);\n return isNaN(d.getTime()) ? String(value) : d.toLocaleString();\n }\n disp(value: unknown): string { return displayRunValue(value); }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"record-process-history.component.js","sourceRoot":"","sources":["../../../src/lib/record-process-history/record-process-history.component.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * @fileoverview Run history for Bulk Operations: every Record Process run (who/when/scope/status/counts),\n * drilling into the per-record outcome (the field diff persisted on each Process Run Detail). Read-only\n * audit surface. Generic + reusable.\n * @module @memberjunction/ng-record-process-studio\n */\nimport { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnDestroy, OnInit, inject } from '@angular/core';\nimport { Subscription } from 'rxjs';\nimport { BaseAngularComponent } from '@memberjunction/ng-base-types';\nimport { BaseEntity, BaseEntityEvent, RunView } from '@memberjunction/core';\nimport { MJEvent, MJEventType, MJGlobal, NormalizeUUID } from '@memberjunction/global';\nimport { MJButtonDirective } from '@memberjunction/ng-ui-components';\nimport { parseAppliedRunDetailChanges, displayRunValue, type RunDetailChange } from '../run-detail';\n\ninterface RawRun {\n ID: string; RecordProcessID: string; EntityID: string; Status: string;\n StartTime?: string | Date; SuccessCount?: number; ErrorCount?: number; SkippedCount?: number;\n TotalItemCount?: number; TriggeredBy?: string; SourceType?: string; DryRun?: boolean;\n}\ninterface RunRow extends RawRun { ProcessName: string; EntityName: string; }\n\ninterface RawDetail { RecordID: string; Status: string; ResultPayload?: string; ErrorMessage?: string; }\ninterface DetailRow { RecordID: string; Status: string; Changes: RunDetailChange[]; ErrorMessage?: string; }\n\n@Component({\n selector: 'mj-record-process-history',\n standalone: true,\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [MJButtonDirective],\n template: `\n @if (Mode === 'list') {\n <div class=\"rph-bar\">\n <h3 class=\"rph-title\">Run history</h3><span class=\"rph-spacer\"></span>\n <button mjButton variant=\"flat\" (click)=\"reload()\"><i class=\"fa-solid fa-arrows-rotate\"></i> Refresh</button>\n </div>\n @if (Loading) {\n <div class=\"rph-msg\"><i class=\"fa-solid fa-circle-notch fa-spin\"></i> Loading…</div>\n } @else if (Runs.length === 0) {\n <div class=\"rph-msg\">No runs yet.</div>\n } @else {\n <div class=\"rph-table\">\n <div class=\"rph-tr rph-head\"><div>When</div><div>Operation</div><div>Entity</div><div>By</div><div>Status</div><div>Counts</div></div>\n @for (r of Runs; track r.ID) {\n <div class=\"rph-tr\" (click)=\"openRun(r)\">\n <div class=\"rph-sub\">{{ fmt(r.StartTime) }}</div>\n <div class=\"rph-nm\">{{ r.ProcessName }}</div>\n <div>{{ r.EntityName }}</div>\n <div class=\"rph-sub\">{{ r.TriggeredBy || '—' }}</div>\n <div><span class=\"st\" [class.ok]=\"r.Status==='Completed'\" [class.err]=\"r.Status==='Failed'\"><span class=\"dot\"></span> {{ r.Status }}</span>@if (r.DryRun) { <span class=\"dryrun\" title=\"Compute-only preview — no changes were written\">Dry Run</span> }</div>\n <div class=\"counts\">\n <span class=\"cbadge ok\">{{ r.SuccessCount || 0 }} ok</span>\n @if (r.ErrorCount) { <span class=\"cbadge err\">{{ r.ErrorCount }} err</span> }\n @if (r.SkippedCount) { <span class=\"cbadge\">{{ r.SkippedCount }} skip</span> }\n </div>\n </div>\n }\n </div>\n }\n } @else {\n <div class=\"rph-bar\">\n <button mjButton variant=\"flat\" (click)=\"backToList()\"><i class=\"fa-solid fa-arrow-left\"></i> Back</button>\n <h3 class=\"rph-title\">{{ OpenRunRow?.ProcessName }} · {{ fmt(OpenRunRow?.StartTime) }}</h3>\n @if (OpenRunRow?.DryRun) { <span class=\"dryrun\" title=\"Compute-only preview — no changes were written\">Dry Run</span> }\n </div>\n @if (DetailLoading) {\n <div class=\"rph-msg\"><i class=\"fa-solid fa-circle-notch fa-spin\"></i> Loading details…</div>\n } @else if (Details.length === 0) {\n <div class=\"rph-msg\">No per-record details recorded for this run.</div>\n } @else {\n <div class=\"rph-details\">\n @for (d of Details; track d.RecordID) {\n <div class=\"rph-detrec\">\n <div class=\"rph-detkey\">\n <span class=\"st sm\" [class.ok]=\"d.Status==='Succeeded'\" [class.err]=\"d.Status==='Failed'\" [class.muted]=\"d.Status==='Skipped'\"><span class=\"dot\"></span> {{ d.Status }}</span>\n <div class=\"rph-rk\">{{ d.RecordID }}</div>\n </div>\n <div class=\"rph-changes\">\n @for (c of d.Changes; track c.Field) {\n <div class=\"rph-chg\"><span class=\"f\">{{ c.Field }}</span>\n <span class=\"o\">{{ disp(c.OldValue) }}</span><i class=\"fa-solid fa-arrow-right-long ar\"></i><span class=\"nw\">{{ disp(c.NewValue) }}</span></div>\n }\n @if (d.ErrorMessage) { <div class=\"rph-chg err-msg\">{{ d.ErrorMessage }}</div> }\n @if (d.Changes.length === 0 && !d.ErrorMessage) { <span class=\"rph-sub\">no changes</span> }\n </div>\n </div>\n }\n </div>\n }\n }\n `,\n styles: [`\n :host{display:block;color:var(--mj-text-primary)}\n .rph-bar{display:flex;align-items:center;gap:14px;margin-bottom:16px}\n .rph-title{margin:0;font-size:16px;font-weight:700}\n .rph-spacer{flex:1}\n .rph-bar button i{margin-right:6px}\n .rph-msg{padding:36px;text-align:center;color:var(--mj-text-muted)}\n .rph-table{border:1px solid var(--mj-border-subtle);border-radius:var(--mj-radius-md,10px);overflow:hidden}\n .rph-tr{display:grid;grid-template-columns:1.3fr 1.6fr 1.2fr .9fr 1fr 1.4fr;gap:14px;align-items:center;padding:12px 16px;border-bottom:1px solid var(--mj-border-subtle);font-size:13px;cursor:pointer}\n .rph-tr:last-child{border-bottom:none}\n .rph-tr:not(.rph-head):hover{background:var(--mj-bg-surface-hover)}\n .rph-head{background:var(--mj-bg-surface-sunken);font-size:11px;text-transform:uppercase;letter-spacing:.05em;color:var(--mj-text-muted);font-weight:700;cursor:default}\n .rph-nm{font-weight:600}\n .rph-sub{color:var(--mj-text-muted);font-size:12px}\n .st{display:inline-flex;align-items:center;gap:7px;font-weight:600;font-size:12.5px}\n .st .dot{width:8px;height:8px;border-radius:50%;background:var(--mj-text-disabled)}\n .st.ok .dot{background:var(--mj-status-success)} .st.ok{color:var(--mj-status-success-text)}\n .st.err .dot{background:var(--mj-status-error)} .st.err{color:var(--mj-status-error-text)}\n .st.muted{color:var(--mj-text-muted)}\n .st.sm{font-size:11.5px}\n .counts{display:flex;gap:6px;flex-wrap:wrap}\n .cbadge{font-size:11px;font-weight:700;padding:2px 8px;border-radius:6px;background:var(--mj-bg-surface-sunken);color:var(--mj-text-secondary)}\n .dryrun{font-size:10px;font-weight:700;text-transform:uppercase;letter-spacing:.04em;padding:2px 7px;border-radius:6px;margin-left:6px;background:var(--mj-status-info-bg);color:var(--mj-status-info-text);border:1px solid var(--mj-status-info-border)}\n .cbadge.ok{background:var(--mj-status-success-bg);color:var(--mj-status-success-text)}\n .cbadge.err{background:var(--mj-status-error-bg);color:var(--mj-status-error-text)}\n .rph-details{border:1px solid var(--mj-border-subtle);border-radius:var(--mj-radius-md,10px);overflow:hidden;max-height:560px;overflow-y:auto}\n .rph-detrec{display:grid;grid-template-columns:200px 1fr;gap:14px;padding:11px 16px;border-bottom:1px solid var(--mj-border-subtle)}\n .rph-detrec:last-child{border-bottom:none}\n .rph-detkey{display:flex;flex-direction:column;gap:5px}\n .rph-rk{font-family:var(--mj-font-mono,monospace);font-size:11px;color:var(--mj-text-muted);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}\n .rph-chg{display:flex;align-items:center;gap:8px;flex-wrap:wrap;font-size:13px;padding:2px 0}\n .rph-chg .f{font-weight:700;min-width:120px}\n .rph-chg .o{color:var(--mj-text-muted);text-decoration:line-through}\n .rph-chg .nw{color:var(--mj-status-success-text);font-weight:600}\n .rph-chg .ar{color:var(--mj-text-disabled);font-size:11px}\n .rph-chg.err-msg{color:var(--mj-status-error-text)}\n `],\n})\nexport class RecordProcessHistoryComponent extends BaseAngularComponent implements OnInit, OnDestroy {\n private cdr = inject(ChangeDetectorRef);\n\n /** Optional: show runs for one Record Process only. */\n @Input() RecordProcessID: string | null = null;\n\n public Mode: 'list' | 'detail' = 'list';\n public Loading = true;\n public DetailLoading = false;\n public Runs: RunRow[] = [];\n public Details: DetailRow[] = [];\n public OpenRunRow: RunRow | null = null;\n\n /**\n * There is no RecordProcess BaseEngine to observe — Process Runs is an unbounded log table, so we don't\n * bulk-cache it. Instead we re-query on demand AND keep the list live by listening for BaseEntity save/\n * delete events on the run tables: a run started/finished anywhere (this view, the editor preview, a grid\n * runner, the scheduler) fires an event, and we debounce-reload so history never goes stale behind a run.\n */\n private eventSub?: Subscription;\n private pendingReload = false;\n private reloadTimer: ReturnType<typeof setTimeout> | null = null;\n\n async ngOnInit(): Promise<void> {\n await this.reload();\n this.subscribeToRunChanges();\n }\n\n ngOnDestroy(): void {\n this.eventSub?.unsubscribe();\n if (this.reloadTimer) {\n clearTimeout(this.reloadTimer);\n }\n }\n\n /** Subscribe to BaseEntity events and debounce-reload whenever a Process Run (or its details) changes. */\n private subscribeToRunChanges(): void {\n this.eventSub = MJGlobal.Instance.GetEventListener(false).subscribe((event: MJEvent) => {\n if (event.event !== MJEventType.ComponentEvent || event.eventCode !== BaseEntity.BaseEventCode) {\n return;\n }\n const e = event.args as BaseEntityEvent;\n if (e.type !== 'save' && e.type !== 'delete' && e.type !== 'remote-invalidate') {\n return;\n }\n const name = e.baseEntity?.EntityInfo?.Name ?? e.entityName;\n if (name === 'MJ: Process Runs' || name === 'MJ: Process Run Details') {\n this.scheduleReload();\n }\n });\n }\n\n /** Debounce a burst of run/detail/checkpoint saves into a single reload; defer if the user is drilled into a detail. */\n private scheduleReload(): void {\n if (this.Mode !== 'list') {\n this.pendingReload = true; // refresh when they return to the list — don't yank them out of a drill-in\n return;\n }\n if (this.reloadTimer) {\n clearTimeout(this.reloadTimer);\n }\n this.reloadTimer = setTimeout(() => {\n this.reloadTimer = null;\n void this.reload();\n }, 400);\n }\n\n async reload(): Promise<void> {\n this.Loading = true; this.cdr.detectChanges();\n const rv = RunView.FromMetadataProvider(this.ProviderToUse);\n const filter = this.RecordProcessID ? `RecordProcessID='${this.RecordProcessID}'` : '';\n const [runs, procs] = await rv.RunViews([\n {\n EntityName: 'MJ: Process Runs',\n Fields: ['ID', 'RecordProcessID', 'EntityID', 'Status', 'StartTime', 'SuccessCount', 'ErrorCount', 'SkippedCount', 'TotalItemCount', 'TriggeredBy', 'SourceType', 'DryRun'],\n ExtraFilter: filter, OrderBy: 'StartTime DESC', MaxRows: 200, ResultType: 'simple',\n },\n { EntityName: 'MJ: Record Processes', Fields: ['ID', 'Name'], ResultType: 'simple' },\n ]);\n // Normalized-UUID keys → O(1), case-safe across SQL Server (upper) / PostgreSQL (lower).\n const nameByID = new Map<string, string>();\n if (procs.Success) for (const p of procs.Results as Array<{ ID: string; Name: string }>) nameByID.set(NormalizeUUID(p.ID), p.Name);\n const rawRuns = runs.Success ? (runs.Results as RawRun[]) : [];\n this.Runs = rawRuns.map((r) => ({\n ...r,\n ProcessName: nameByID.get(NormalizeUUID(r.RecordProcessID)) ?? '(deleted)',\n EntityName: this.ProviderToUse.EntityByID(r.EntityID)?.Name ?? r.EntityID,\n }));\n this.Loading = false;\n this.cdr.detectChanges();\n }\n\n async openRun(run: RunRow): Promise<void> {\n this.OpenRunRow = run; this.Mode = 'detail'; this.DetailLoading = true; this.Details = []; this.cdr.detectChanges();\n const rv = RunView.FromMetadataProvider(this.ProviderToUse);\n const result = await rv.RunView<RawDetail>({\n EntityName: 'MJ: Process Run Details',\n Fields: ['RecordID', 'Status', 'ResultPayload', 'ErrorMessage'],\n ExtraFilter: `ProcessRunID='${run.ID}'`, OrderBy: 'Status, RecordID', MaxRows: 1000, ResultType: 'simple',\n });\n this.Details = (result.Success ? (result.Results ?? []) : []).map((d) => ({\n RecordID: d.RecordID, Status: d.Status, Changes: parseAppliedRunDetailChanges(d.ResultPayload), ErrorMessage: d.ErrorMessage,\n }));\n this.DetailLoading = false;\n this.cdr.detectChanges();\n }\n\n backToList(): void {\n this.Mode = 'list';\n this.OpenRunRow = null;\n this.cdr.detectChanges();\n if (this.pendingReload) {\n this.pendingReload = false;\n void this.reload(); // a run changed while we were drilled into a detail — refresh now\n }\n }\n\n fmt(value?: string | Date): string {\n if (!value) return '—';\n const d = value instanceof Date ? value : new Date(value);\n return isNaN(d.getTime()) ? String(value) : d.toLocaleString();\n }\n disp(value: unknown): string { return displayRunValue(value); }\n}\n"]}
|
|
@@ -178,7 +178,7 @@ export class RecordProcessStudioComponent extends BaseAngularComponent {
|
|
|
178
178
|
async reload() {
|
|
179
179
|
this.Loading = true;
|
|
180
180
|
this.cdr.detectChanges();
|
|
181
|
-
const rv =
|
|
181
|
+
const rv = RunView.FromMetadataProvider(this.ProviderToUse);
|
|
182
182
|
const filter = this.EntityID ? `EntityID='${this.EntityID}'` : '';
|
|
183
183
|
const result = await rv.RunView({
|
|
184
184
|
EntityName: 'MJ: Record Processes',
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"record-process-studio.component.js","sourceRoot":"","sources":["../../../src/lib/record-process-studio/record-process-studio.component.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * @fileoverview The Bulk Operations hub: lists the org's Record Processes, lets a user create / edit one\n * (via the embedded {@link RecordProcessEditorComponent}), and run any of them on demand (mounting the\n * same dry-run → confirm runner used from a grid). Generic + reusable — Explorer's Bulk Operations app\n * hosts it, but any MJ app can drop it in.\n * @module @memberjunction/ng-record-process-studio\n */\nimport { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnInit, inject } from '@angular/core';\nimport { BaseAngularComponent } from '@memberjunction/ng-base-types';\nimport { RunView } from '@memberjunction/core';\nimport { EntityActionUXHostComponent, type EntityActionUXContext, type EntityActionUXResult } from '@memberjunction/ng-entity-action-ux';\nimport { MJButtonDirective, MJEmptyStateComponent } from '@memberjunction/ng-ui-components';\nimport { RecordProcessEditorComponent } from '../record-process-editor/record-process-editor.component';\n\n/** A row of the process list (read-only projection). */\ninterface ProcessRow {\n ID: string;\n Name: string;\n Description?: string;\n Entity: string;\n EntityID: string;\n WorkType: string;\n Status: string;\n ScopeType: string;\n ScopeFilter?: string;\n ScopeViewID?: string;\n ScopeListID?: string;\n}\n\n@Component({\n selector: 'mj-record-process-studio',\n standalone: true,\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [RecordProcessEditorComponent, EntityActionUXHostComponent, MJButtonDirective, MJEmptyStateComponent],\n template: `\n @if (Mode === 'list') {\n <div class=\"rps-bar\">\n <div class=\"rps-search\"><i class=\"fa-solid fa-magnifying-glass\"></i>\n <input class=\"mj-input\" placeholder=\"Search bulk operations…\" [value]=\"Search\" (input)=\"onSearch($event)\"></div>\n <span class=\"rps-spacer\"></span>\n <button mjButton variant=\"flat\" (click)=\"reload()\"><i class=\"fa-solid fa-arrows-rotate\"></i> Refresh</button>\n <button mjButton variant=\"primary\" (click)=\"New()\"><i class=\"fa-solid fa-plus\"></i> New operation</button>\n </div>\n\n @if (Loading) {\n <div class=\"rps-msg\"><i class=\"fa-solid fa-circle-notch fa-spin\"></i> Loading…</div>\n } @else if (Filtered.length === 0) {\n <mj-empty-state class=\"rps-empty\"\n Icon=\"fa-solid fa-list-check\"\n Title=\"No bulk operations yet\"\n ActionText=\"Create one\"\n ActionIcon=\"fa-solid fa-plus\"\n (Action)=\"New()\" />\n } @else {\n <div class=\"rps-table\">\n <div class=\"rps-tr rps-head\">\n <div>Operation</div><div>Entity</div><div>Type</div><div>Status</div><div class=\"ta-r\">Actions</div>\n </div>\n @for (p of Filtered; track p.ID) {\n <div class=\"rps-tr\" (click)=\"Edit(p)\">\n <div><div class=\"rps-nm\">{{ p.Name }}</div>@if (p.Description) {<div class=\"rps-sub\">{{ p.Description }}</div>}</div>\n <div>{{ p.Entity }}</div>\n <div><span class=\"pill purple\">{{ workLabel(p.WorkType) }}</span></div>\n <div><span class=\"pill\" [class.ok]=\"p.Status==='Active'\" [class.warn]=\"p.Status==='Draft'\" [class.muted]=\"p.Status==='Disabled'\">{{ p.Status }}</span></div>\n <div class=\"ta-r rps-acts\" (click)=\"$event.stopPropagation()\">\n <button class=\"iconbtn run\" title=\"Run\" (click)=\"Run(p)\"><i class=\"fa-solid fa-play\"></i></button>\n <button class=\"iconbtn\" title=\"Edit\" (click)=\"Edit(p)\"><i class=\"fa-solid fa-pen\"></i></button>\n </div>\n </div>\n }\n </div>\n }\n } @else {\n <div class=\"rps-editbar\">\n <button mjButton variant=\"flat\" (click)=\"backToList()\"><i class=\"fa-solid fa-arrow-left\"></i> Back</button>\n <h2>{{ EditingID ? 'Edit operation' : 'New operation' }}</h2>\n </div>\n <mj-record-process-editor\n [RecordProcessID]=\"EditingID\"\n [DefaultEntityID]=\"EntityID\"\n [Provider]=\"Provider\"\n (Saved)=\"onSaved()\"\n (Cancelled)=\"backToList()\">\n </mj-record-process-editor>\n }\n\n @if (RunDriver) {\n <mj-entity-action-ux-host\n [DriverClass]=\"RunDriver.DriverClass\" [Context]=\"RunDriver.Context\"\n (Completed)=\"onRunDone($event)\" (Cancelled)=\"RunDriver = null\">\n </mj-entity-action-ux-host>\n }\n `,\n styles: [`\n :host{display:block;color:var(--mj-text-primary)}\n .rps-bar{display:flex;align-items:center;gap:12px;margin-bottom:16px}\n .rps-search{position:relative;flex:0 0 360px;max-width:360px}\n .rps-search i{position:absolute;left:12px;top:50%;transform:translateY(-50%);color:var(--mj-text-muted);font-size:13px}\n .rps-search input{padding-left:34px}\n .rps-spacer{flex:1}\n .rps-bar button i,.rps-editbar button i{margin-right:6px}\n .rps-msg,.rps-empty{padding:40px;text-align:center;color:var(--mj-text-muted)}\n .rps-empty i{font-size:30px;display:block;margin-bottom:12px;color:var(--mj-text-disabled)}\n .rps-empty p{margin:0 0 16px}\n .rps-table{border:1px solid var(--mj-border-subtle);border-radius:var(--mj-radius-md,10px);overflow:hidden}\n .rps-tr{display:grid;grid-template-columns:2.2fr 1.2fr 1fr .8fr 1fr;gap:14px;align-items:center;padding:13px 16px;border-bottom:1px solid var(--mj-border-subtle);cursor:pointer;font-size:13.5px}\n .rps-tr:last-child{border-bottom:none}\n .rps-tr:not(.rps-head):hover{background:var(--mj-bg-surface-hover)}\n .rps-head{background:var(--mj-bg-surface-sunken);font-size:11.5px;text-transform:uppercase;letter-spacing:.05em;color:var(--mj-text-muted);font-weight:700;cursor:default}\n .rps-nm{font-weight:600}\n .rps-sub{color:var(--mj-text-muted);font-size:12px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:420px}\n .ta-r{text-align:right;justify-self:end}\n .rps-acts{display:flex;gap:6px}\n .iconbtn{width:30px;height:30px;border-radius:7px;border:1px solid var(--mj-border-default);background:var(--mj-bg-surface-card);color:var(--mj-text-secondary);cursor:pointer;font-size:12.5px}\n .iconbtn:hover{background:var(--mj-bg-surface-hover);color:var(--mj-text-primary)}\n .iconbtn.run:hover{color:var(--mj-status-success-text);border-color:var(--mj-status-success)}\n .pill{display:inline-flex;align-items:center;padding:3px 10px;border-radius:999px;font-size:11.5px;font-weight:700;background:var(--mj-bg-surface-sunken);color:var(--mj-text-secondary)}\n .pill.purple{background:color-mix(in srgb,var(--mj-brand-primary) 14%,transparent);color:var(--mj-brand-primary)}\n .pill.ok{background:var(--mj-status-success-bg);color:var(--mj-status-success-text)}\n .pill.warn{background:var(--mj-status-warning-bg);color:var(--mj-status-warning-text)}\n .pill.muted{background:var(--mj-bg-surface-sunken);color:var(--mj-text-muted)}\n .rps-editbar{display:flex;align-items:center;gap:14px;margin-bottom:18px}\n .rps-editbar h2{margin:0;font-size:18px;font-weight:700}\n `],\n})\nexport class RecordProcessStudioComponent extends BaseAngularComponent implements OnInit {\n private cdr = inject(ChangeDetectorRef);\n\n /** Optional: restrict the hub to processes for one entity (e.g. when embedded on an entity page). */\n @Input() EntityID: string | null = null;\n\n public Mode: 'list' | 'edit' = 'list';\n public EditingID: string | null = null;\n public Loading = true;\n public Search = '';\n public Processes: ProcessRow[] = [];\n public RunDriver: { DriverClass: string; Context: EntityActionUXContext } | null = null;\n\n async ngOnInit(): Promise<void> {\n await this.reload();\n }\n\n get Filtered(): ProcessRow[] {\n const q = this.Search.trim().toLowerCase();\n if (!q) return this.Processes;\n return this.Processes.filter((p) => p.Name.toLowerCase().includes(q) || (p.Entity ?? '').toLowerCase().includes(q));\n }\n\n async reload(): Promise<void> {\n this.Loading = true; this.cdr.detectChanges();\n const rv = this.Provider ? RunView.FromMetadataProvider(this.Provider) : new RunView();\n const filter = this.EntityID ? `EntityID='${this.EntityID}'` : '';\n const result = await rv.RunView<ProcessRow>({\n EntityName: 'MJ: Record Processes',\n Fields: ['ID', 'Name', 'Description', 'Entity', 'EntityID', 'WorkType', 'Status', 'ScopeType', 'ScopeFilter', 'ScopeViewID', 'ScopeListID'],\n ExtraFilter: filter,\n OrderBy: 'Name',\n ResultType: 'simple',\n });\n this.Processes = result.Success ? (result.Results ?? []) : [];\n this.Loading = false;\n this.cdr.detectChanges();\n }\n\n onSearch(event: Event): void { this.Search = (event.target as HTMLInputElement).value; this.cdr.detectChanges(); }\n\n New(): void { this.EditingID = null; this.Mode = 'edit'; this.cdr.detectChanges(); }\n Edit(p: ProcessRow): void { this.EditingID = p.ID; this.Mode = 'edit'; this.cdr.detectChanges(); }\n backToList(): void { this.Mode = 'list'; this.EditingID = null; this.cdr.detectChanges(); }\n async onSaved(): Promise<void> { this.backToList(); await this.reload(); }\n\n /** Runs a process on demand against its default scope, via the dry-run → confirm runner. */\n Run(p: ProcessRow): void {\n const entity = this.ProviderToUse.EntityByID(p.EntityID);\n if (!entity) return;\n this.RunDriver = {\n DriverClass: 'RecordProcessRunnerUX',\n Context: {\n EntityInfo: entity,\n ScopeKind: (p.ScopeType?.toLowerCase() as EntityActionUXContext['ScopeKind']) || 'filter',\n Filter: p.ScopeFilter ?? undefined,\n ViewID: p.ScopeViewID ?? undefined,\n ListID: p.ScopeListID ?? undefined,\n Config: { RecordProcessID: p.ID },\n Provider: this.ProviderToUse,\n ActionLabel: p.Name,\n },\n };\n this.cdr.detectChanges();\n }\n\n onRunDone(_r: EntityActionUXResult): void { this.RunDriver = null; this.cdr.detectChanges(); }\n\n workLabel(workType: string): string {\n return workType === 'FieldRules' ? 'Field Rules' : workType;\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"record-process-studio.component.js","sourceRoot":"","sources":["../../../src/lib/record-process-studio/record-process-studio.component.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * @fileoverview The Bulk Operations hub: lists the org's Record Processes, lets a user create / edit one\n * (via the embedded {@link RecordProcessEditorComponent}), and run any of them on demand (mounting the\n * same dry-run → confirm runner used from a grid). Generic + reusable — Explorer's Bulk Operations app\n * hosts it, but any MJ app can drop it in.\n * @module @memberjunction/ng-record-process-studio\n */\nimport { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnInit, inject } from '@angular/core';\nimport { BaseAngularComponent } from '@memberjunction/ng-base-types';\nimport { RunView } from '@memberjunction/core';\nimport { EntityActionUXHostComponent, type EntityActionUXContext, type EntityActionUXResult } from '@memberjunction/ng-entity-action-ux';\nimport { MJButtonDirective, MJEmptyStateComponent } from '@memberjunction/ng-ui-components';\nimport { RecordProcessEditorComponent } from '../record-process-editor/record-process-editor.component';\n\n/** A row of the process list (read-only projection). */\ninterface ProcessRow {\n ID: string;\n Name: string;\n Description?: string;\n Entity: string;\n EntityID: string;\n WorkType: string;\n Status: string;\n ScopeType: string;\n ScopeFilter?: string;\n ScopeViewID?: string;\n ScopeListID?: string;\n}\n\n@Component({\n selector: 'mj-record-process-studio',\n standalone: true,\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [RecordProcessEditorComponent, EntityActionUXHostComponent, MJButtonDirective, MJEmptyStateComponent],\n template: `\n @if (Mode === 'list') {\n <div class=\"rps-bar\">\n <div class=\"rps-search\"><i class=\"fa-solid fa-magnifying-glass\"></i>\n <input class=\"mj-input\" placeholder=\"Search bulk operations…\" [value]=\"Search\" (input)=\"onSearch($event)\"></div>\n <span class=\"rps-spacer\"></span>\n <button mjButton variant=\"flat\" (click)=\"reload()\"><i class=\"fa-solid fa-arrows-rotate\"></i> Refresh</button>\n <button mjButton variant=\"primary\" (click)=\"New()\"><i class=\"fa-solid fa-plus\"></i> New operation</button>\n </div>\n\n @if (Loading) {\n <div class=\"rps-msg\"><i class=\"fa-solid fa-circle-notch fa-spin\"></i> Loading…</div>\n } @else if (Filtered.length === 0) {\n <mj-empty-state class=\"rps-empty\"\n Icon=\"fa-solid fa-list-check\"\n Title=\"No bulk operations yet\"\n ActionText=\"Create one\"\n ActionIcon=\"fa-solid fa-plus\"\n (Action)=\"New()\" />\n } @else {\n <div class=\"rps-table\">\n <div class=\"rps-tr rps-head\">\n <div>Operation</div><div>Entity</div><div>Type</div><div>Status</div><div class=\"ta-r\">Actions</div>\n </div>\n @for (p of Filtered; track p.ID) {\n <div class=\"rps-tr\" (click)=\"Edit(p)\">\n <div><div class=\"rps-nm\">{{ p.Name }}</div>@if (p.Description) {<div class=\"rps-sub\">{{ p.Description }}</div>}</div>\n <div>{{ p.Entity }}</div>\n <div><span class=\"pill purple\">{{ workLabel(p.WorkType) }}</span></div>\n <div><span class=\"pill\" [class.ok]=\"p.Status==='Active'\" [class.warn]=\"p.Status==='Draft'\" [class.muted]=\"p.Status==='Disabled'\">{{ p.Status }}</span></div>\n <div class=\"ta-r rps-acts\" (click)=\"$event.stopPropagation()\">\n <button class=\"iconbtn run\" title=\"Run\" (click)=\"Run(p)\"><i class=\"fa-solid fa-play\"></i></button>\n <button class=\"iconbtn\" title=\"Edit\" (click)=\"Edit(p)\"><i class=\"fa-solid fa-pen\"></i></button>\n </div>\n </div>\n }\n </div>\n }\n } @else {\n <div class=\"rps-editbar\">\n <button mjButton variant=\"flat\" (click)=\"backToList()\"><i class=\"fa-solid fa-arrow-left\"></i> Back</button>\n <h2>{{ EditingID ? 'Edit operation' : 'New operation' }}</h2>\n </div>\n <mj-record-process-editor\n [RecordProcessID]=\"EditingID\"\n [DefaultEntityID]=\"EntityID\"\n [Provider]=\"Provider\"\n (Saved)=\"onSaved()\"\n (Cancelled)=\"backToList()\">\n </mj-record-process-editor>\n }\n\n @if (RunDriver) {\n <mj-entity-action-ux-host\n [DriverClass]=\"RunDriver.DriverClass\" [Context]=\"RunDriver.Context\"\n (Completed)=\"onRunDone($event)\" (Cancelled)=\"RunDriver = null\">\n </mj-entity-action-ux-host>\n }\n `,\n styles: [`\n :host{display:block;color:var(--mj-text-primary)}\n .rps-bar{display:flex;align-items:center;gap:12px;margin-bottom:16px}\n .rps-search{position:relative;flex:0 0 360px;max-width:360px}\n .rps-search i{position:absolute;left:12px;top:50%;transform:translateY(-50%);color:var(--mj-text-muted);font-size:13px}\n .rps-search input{padding-left:34px}\n .rps-spacer{flex:1}\n .rps-bar button i,.rps-editbar button i{margin-right:6px}\n .rps-msg,.rps-empty{padding:40px;text-align:center;color:var(--mj-text-muted)}\n .rps-empty i{font-size:30px;display:block;margin-bottom:12px;color:var(--mj-text-disabled)}\n .rps-empty p{margin:0 0 16px}\n .rps-table{border:1px solid var(--mj-border-subtle);border-radius:var(--mj-radius-md,10px);overflow:hidden}\n .rps-tr{display:grid;grid-template-columns:2.2fr 1.2fr 1fr .8fr 1fr;gap:14px;align-items:center;padding:13px 16px;border-bottom:1px solid var(--mj-border-subtle);cursor:pointer;font-size:13.5px}\n .rps-tr:last-child{border-bottom:none}\n .rps-tr:not(.rps-head):hover{background:var(--mj-bg-surface-hover)}\n .rps-head{background:var(--mj-bg-surface-sunken);font-size:11.5px;text-transform:uppercase;letter-spacing:.05em;color:var(--mj-text-muted);font-weight:700;cursor:default}\n .rps-nm{font-weight:600}\n .rps-sub{color:var(--mj-text-muted);font-size:12px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:420px}\n .ta-r{text-align:right;justify-self:end}\n .rps-acts{display:flex;gap:6px}\n .iconbtn{width:30px;height:30px;border-radius:7px;border:1px solid var(--mj-border-default);background:var(--mj-bg-surface-card);color:var(--mj-text-secondary);cursor:pointer;font-size:12.5px}\n .iconbtn:hover{background:var(--mj-bg-surface-hover);color:var(--mj-text-primary)}\n .iconbtn.run:hover{color:var(--mj-status-success-text);border-color:var(--mj-status-success)}\n .pill{display:inline-flex;align-items:center;padding:3px 10px;border-radius:999px;font-size:11.5px;font-weight:700;background:var(--mj-bg-surface-sunken);color:var(--mj-text-secondary)}\n .pill.purple{background:color-mix(in srgb,var(--mj-brand-primary) 14%,transparent);color:var(--mj-brand-primary)}\n .pill.ok{background:var(--mj-status-success-bg);color:var(--mj-status-success-text)}\n .pill.warn{background:var(--mj-status-warning-bg);color:var(--mj-status-warning-text)}\n .pill.muted{background:var(--mj-bg-surface-sunken);color:var(--mj-text-muted)}\n .rps-editbar{display:flex;align-items:center;gap:14px;margin-bottom:18px}\n .rps-editbar h2{margin:0;font-size:18px;font-weight:700}\n `],\n})\nexport class RecordProcessStudioComponent extends BaseAngularComponent implements OnInit {\n private cdr = inject(ChangeDetectorRef);\n\n /** Optional: restrict the hub to processes for one entity (e.g. when embedded on an entity page). */\n @Input() EntityID: string | null = null;\n\n public Mode: 'list' | 'edit' = 'list';\n public EditingID: string | null = null;\n public Loading = true;\n public Search = '';\n public Processes: ProcessRow[] = [];\n public RunDriver: { DriverClass: string; Context: EntityActionUXContext } | null = null;\n\n async ngOnInit(): Promise<void> {\n await this.reload();\n }\n\n get Filtered(): ProcessRow[] {\n const q = this.Search.trim().toLowerCase();\n if (!q) return this.Processes;\n return this.Processes.filter((p) => p.Name.toLowerCase().includes(q) || (p.Entity ?? '').toLowerCase().includes(q));\n }\n\n async reload(): Promise<void> {\n this.Loading = true; this.cdr.detectChanges();\n const rv = RunView.FromMetadataProvider(this.ProviderToUse);\n const filter = this.EntityID ? `EntityID='${this.EntityID}'` : '';\n const result = await rv.RunView<ProcessRow>({\n EntityName: 'MJ: Record Processes',\n Fields: ['ID', 'Name', 'Description', 'Entity', 'EntityID', 'WorkType', 'Status', 'ScopeType', 'ScopeFilter', 'ScopeViewID', 'ScopeListID'],\n ExtraFilter: filter,\n OrderBy: 'Name',\n ResultType: 'simple',\n });\n this.Processes = result.Success ? (result.Results ?? []) : [];\n this.Loading = false;\n this.cdr.detectChanges();\n }\n\n onSearch(event: Event): void { this.Search = (event.target as HTMLInputElement).value; this.cdr.detectChanges(); }\n\n New(): void { this.EditingID = null; this.Mode = 'edit'; this.cdr.detectChanges(); }\n Edit(p: ProcessRow): void { this.EditingID = p.ID; this.Mode = 'edit'; this.cdr.detectChanges(); }\n backToList(): void { this.Mode = 'list'; this.EditingID = null; this.cdr.detectChanges(); }\n async onSaved(): Promise<void> { this.backToList(); await this.reload(); }\n\n /** Runs a process on demand against its default scope, via the dry-run → confirm runner. */\n Run(p: ProcessRow): void {\n const entity = this.ProviderToUse.EntityByID(p.EntityID);\n if (!entity) return;\n this.RunDriver = {\n DriverClass: 'RecordProcessRunnerUX',\n Context: {\n EntityInfo: entity,\n ScopeKind: (p.ScopeType?.toLowerCase() as EntityActionUXContext['ScopeKind']) || 'filter',\n Filter: p.ScopeFilter ?? undefined,\n ViewID: p.ScopeViewID ?? undefined,\n ListID: p.ScopeListID ?? undefined,\n Config: { RecordProcessID: p.ID },\n Provider: this.ProviderToUse,\n ActionLabel: p.Name,\n },\n };\n this.cdr.detectChanges();\n }\n\n onRunDone(_r: EntityActionUXResult): void { this.RunDriver = null; this.cdr.detectChanges(); }\n\n workLabel(workType: string): string {\n return workType === 'FieldRules' ? 'Field Rules' : workType;\n }\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,18 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@memberjunction/ng-record-process-studio",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "6.1.0-edge.0",
|
|
4
4
|
"description": "MemberJunction: Bulk Operations studio — generic, reusable Angular UI for authoring, running, and auditing Record Processes (rules-driven bulk updates). Consumed by MJExplorer's Bulk Operations app or any MJ app.",
|
|
5
5
|
"main": "./dist/public-api.js",
|
|
6
6
|
"typings": "./dist/public-api.d.ts",
|
|
7
7
|
"files": [
|
|
8
8
|
"/dist"
|
|
9
9
|
],
|
|
10
|
-
"scripts": {
|
|
11
|
-
"build": "npm run clean && ngc",
|
|
12
|
-
"clean": "rimraf dist",
|
|
13
|
-
"test": "vitest run",
|
|
14
|
-
"test:watch": "vitest"
|
|
15
|
-
},
|
|
16
10
|
"keywords": [
|
|
17
11
|
"MemberJunction",
|
|
18
12
|
"Angular",
|
|
@@ -26,7 +20,8 @@
|
|
|
26
20
|
"@angular/compiler": "21.1.3",
|
|
27
21
|
"@angular/compiler-cli": "21.1.3",
|
|
28
22
|
"rimraf": "^5.0.0",
|
|
29
|
-
"vitest": "^4.0.18"
|
|
23
|
+
"vitest": "^4.0.18",
|
|
24
|
+
"@memberjunction/ng-test-utils": "6.1.0-edge.0"
|
|
30
25
|
},
|
|
31
26
|
"peerDependencies": {
|
|
32
27
|
"@angular/common": "21.1.3",
|
|
@@ -35,18 +30,26 @@
|
|
|
35
30
|
"rxjs": "^7.8.2"
|
|
36
31
|
},
|
|
37
32
|
"dependencies": {
|
|
38
|
-
"@memberjunction/core": "
|
|
39
|
-
"@memberjunction/core-entities": "
|
|
40
|
-
"@memberjunction/global": "
|
|
41
|
-
"@memberjunction/graphql-dataprovider": "
|
|
42
|
-
"@memberjunction/ng-base-types": "
|
|
43
|
-
"@memberjunction/ng-entity-action-ux": "
|
|
44
|
-
"@memberjunction/ng-ui-components": "
|
|
33
|
+
"@memberjunction/core": "6.1.0-edge.0",
|
|
34
|
+
"@memberjunction/core-entities": "6.1.0-edge.0",
|
|
35
|
+
"@memberjunction/global": "6.1.0-edge.0",
|
|
36
|
+
"@memberjunction/graphql-dataprovider": "6.1.0-edge.0",
|
|
37
|
+
"@memberjunction/ng-base-types": "6.1.0-edge.0",
|
|
38
|
+
"@memberjunction/ng-entity-action-ux": "6.1.0-edge.0",
|
|
39
|
+
"@memberjunction/ng-ui-components": "6.1.0-edge.0",
|
|
45
40
|
"tslib": "^2.8.1"
|
|
46
41
|
},
|
|
47
42
|
"sideEffects": false,
|
|
48
43
|
"repository": {
|
|
49
44
|
"type": "git",
|
|
50
45
|
"url": "https://github.com/MemberJunction/MJ"
|
|
46
|
+
},
|
|
47
|
+
"mjUILayer": "widgets",
|
|
48
|
+
"scripts": {
|
|
49
|
+
"build": "npm run clean && ngc",
|
|
50
|
+
"clean": "rimraf dist",
|
|
51
|
+
"test": "vitest run",
|
|
52
|
+
"test:types": "tsc --noEmit -p tsconfig.spec.json",
|
|
53
|
+
"test:watch": "vitest"
|
|
51
54
|
}
|
|
52
|
-
}
|
|
55
|
+
}
|