@memberjunction/ng-dashboards 6.2.0-edge.2 → 6.2.0-edge.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. package/dist/ComponentStudio/components/form-override-dialog.component.d.ts +12 -1
  2. package/dist/ComponentStudio/components/form-override-dialog.component.d.ts.map +1 -1
  3. package/dist/ComponentStudio/components/form-override-dialog.component.js +128 -81
  4. package/dist/ComponentStudio/components/form-override-dialog.component.js.map +1 -1
  5. package/dist/DataExplorer/data-explorer-dashboard.component.d.ts +4 -0
  6. package/dist/DataExplorer/data-explorer-dashboard.component.d.ts.map +1 -1
  7. package/dist/DataExplorer/data-explorer-dashboard.component.js +47 -13
  8. package/dist/DataExplorer/data-explorer-dashboard.component.js.map +1 -1
  9. package/dist/TelephonyOperations/telephony-operations-agent-context.d.ts +107 -0
  10. package/dist/TelephonyOperations/telephony-operations-agent-context.d.ts.map +1 -0
  11. package/dist/TelephonyOperations/telephony-operations-agent-context.js +216 -0
  12. package/dist/TelephonyOperations/telephony-operations-agent-context.js.map +1 -0
  13. package/dist/TelephonyOperations/telephony-operations-dashboard.component.d.ts +57 -0
  14. package/dist/TelephonyOperations/telephony-operations-dashboard.component.d.ts.map +1 -0
  15. package/dist/TelephonyOperations/telephony-operations-dashboard.component.js +805 -0
  16. package/dist/TelephonyOperations/telephony-operations-dashboard.component.js.map +1 -0
  17. package/dist/WorkQueue/components/work-queue-bindings.component.d.ts +37 -0
  18. package/dist/WorkQueue/components/work-queue-bindings.component.d.ts.map +1 -0
  19. package/dist/WorkQueue/components/work-queue-bindings.component.js +210 -0
  20. package/dist/WorkQueue/components/work-queue-bindings.component.js.map +1 -0
  21. package/dist/WorkQueue/components/work-queue-dead-letters.component.d.ts +70 -0
  22. package/dist/WorkQueue/components/work-queue-dead-letters.component.d.ts.map +1 -0
  23. package/dist/WorkQueue/components/work-queue-dead-letters.component.js +496 -0
  24. package/dist/WorkQueue/components/work-queue-dead-letters.component.js.map +1 -0
  25. package/dist/WorkQueue/components/work-queue-discard-dialog.component.d.ts +22 -0
  26. package/dist/WorkQueue/components/work-queue-discard-dialog.component.d.ts.map +1 -0
  27. package/dist/WorkQueue/components/work-queue-discard-dialog.component.js +99 -0
  28. package/dist/WorkQueue/components/work-queue-discard-dialog.component.js.map +1 -0
  29. package/dist/WorkQueue/components/work-queue-overview.component.d.ts +29 -0
  30. package/dist/WorkQueue/components/work-queue-overview.component.d.ts.map +1 -0
  31. package/dist/WorkQueue/components/work-queue-overview.component.js +278 -0
  32. package/dist/WorkQueue/components/work-queue-overview.component.js.map +1 -0
  33. package/dist/WorkQueue/components/work-queue-partitions.component.d.ts +59 -0
  34. package/dist/WorkQueue/components/work-queue-partitions.component.d.ts.map +1 -0
  35. package/dist/WorkQueue/components/work-queue-partitions.component.js +359 -0
  36. package/dist/WorkQueue/components/work-queue-partitions.component.js.map +1 -0
  37. package/dist/WorkQueue/services/work-queue-operator.service.d.ts +22 -0
  38. package/dist/WorkQueue/services/work-queue-operator.service.d.ts.map +1 -0
  39. package/dist/WorkQueue/services/work-queue-operator.service.js +44 -0
  40. package/dist/WorkQueue/services/work-queue-operator.service.js.map +1 -0
  41. package/dist/WorkQueue/work-queue-agent-context.d.ts +67 -0
  42. package/dist/WorkQueue/work-queue-agent-context.d.ts.map +1 -0
  43. package/dist/WorkQueue/work-queue-agent-context.js +119 -0
  44. package/dist/WorkQueue/work-queue-agent-context.js.map +1 -0
  45. package/dist/WorkQueue/work-queue-dashboard.component.d.ts +94 -0
  46. package/dist/WorkQueue/work-queue-dashboard.component.d.ts.map +1 -0
  47. package/dist/WorkQueue/work-queue-dashboard.component.js +545 -0
  48. package/dist/WorkQueue/work-queue-dashboard.component.js.map +1 -0
  49. package/dist/WorkQueue/work-queue-types.d.ts +40 -0
  50. package/dist/WorkQueue/work-queue-types.d.ts.map +1 -0
  51. package/dist/WorkQueue/work-queue-types.js +2 -0
  52. package/dist/WorkQueue/work-queue-types.js.map +1 -0
  53. package/dist/core-dashboards.module.d.ts +29 -28
  54. package/dist/core-dashboards.module.d.ts.map +1 -1
  55. package/dist/core-dashboards.module.js +7 -0
  56. package/dist/core-dashboards.module.js.map +1 -1
  57. package/dist/module.d.ts +14 -13
  58. package/dist/module.d.ts.map +1 -1
  59. package/dist/module.js +7 -0
  60. package/dist/module.js.map +1 -1
  61. package/dist/public-api.d.ts +4 -0
  62. package/dist/public-api.d.ts.map +1 -1
  63. package/dist/public-api.js +6 -0
  64. package/dist/public-api.js.map +1 -1
  65. package/dist/work-queue-dashboards.module.d.ts +23 -0
  66. package/dist/work-queue-dashboards.module.d.ts.map +1 -0
  67. package/dist/work-queue-dashboards.module.js +92 -0
  68. package/dist/work-queue-dashboards.module.js.map +1 -0
  69. package/package.json +72 -66
@@ -0,0 +1,99 @@
1
+ import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core';
2
+ import * as i0 from "@angular/core";
3
+ import * as i1 from "@angular/forms";
4
+ import * as i2 from "@memberjunction/ng-ui-components";
5
+ function WorkQueueDiscardDialogComponent_Conditional_2_Template(rf, ctx) { if (rf & 1) {
6
+ i0.ɵɵelementStart(0, "p", 2);
7
+ i0.ɵɵtext(1);
8
+ i0.ɵɵelementEnd();
9
+ } if (rf & 2) {
10
+ const ctx_r0 = i0.ɵɵnextContext();
11
+ i0.ɵɵadvance();
12
+ i0.ɵɵtextInterpolate(ctx_r0.Message);
13
+ } }
14
+ function WorkQueueDiscardDialogComponent_Conditional_8_Template(rf, ctx) { if (rf & 1) {
15
+ i0.ɵɵelement(0, "i", 8);
16
+ i0.ɵɵtext(1, " Discarding\u2026 ");
17
+ } }
18
+ function WorkQueueDiscardDialogComponent_Conditional_9_Template(rf, ctx) { if (rf & 1) {
19
+ i0.ɵɵelement(0, "i", 9);
20
+ i0.ɵɵtext(1, " Discard ");
21
+ } }
22
+ /**
23
+ * Asks for the operator's reason before WorkQueue.DiscardDelivery runs (the operation requires one, and it is
24
+ * stored with the resolution). Shared by the Dead Letters and Partitions tabs.
25
+ */
26
+ export class WorkQueueDiscardDialogComponent {
27
+ Visible = false;
28
+ Title = 'Discard delivery';
29
+ /** What is about to happen, e.g. "3 dead letters will be discarded" or the blocked-key release warning. */
30
+ Message = '';
31
+ Busy = false;
32
+ VisibleChange = new EventEmitter();
33
+ Confirmed = new EventEmitter();
34
+ Reason = '';
35
+ get CanConfirm() {
36
+ return this.Reason.trim().length > 0 && !this.Busy;
37
+ }
38
+ Confirm() {
39
+ if (this.CanConfirm) {
40
+ this.Confirmed.emit(this.Reason.trim());
41
+ }
42
+ }
43
+ Cancel() {
44
+ this.Reason = '';
45
+ this.VisibleChange.emit(false);
46
+ }
47
+ static ɵfac = function WorkQueueDiscardDialogComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || WorkQueueDiscardDialogComponent)(); };
48
+ static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: WorkQueueDiscardDialogComponent, selectors: [["mj-work-queue-discard-dialog"]], inputs: { Visible: "Visible", Title: "Title", Message: "Message", Busy: "Busy" }, outputs: { VisibleChange: "VisibleChange", Confirmed: "Confirmed" }, standalone: false, decls: 12, vars: 8, consts: [["Size", "sm", 3, "Close", "Visible", "Title"], [1, "wq-discard-body"], [1, "wq-discard-message"], ["for", "wq-discard-reason", 1, "wq-discard-label"], ["id", "wq-discard-reason", "rows", "3", "maxlength", "1000", "placeholder", "Why this work is being dropped", 1, "wq-discard-reason", 3, "ngModelChange", "ngModel", "disabled"], [1, "wq-discard-actions"], ["mjButton", "", "variant", "primary", "size", "sm", 3, "click", "disabled"], ["mjButton", "", "variant", "secondary", "size", "sm", 3, "click", "disabled"], [1, "fa-solid", "fa-spinner", "fa-spin"], [1, "fa-solid", "fa-trash"]], template: function WorkQueueDiscardDialogComponent_Template(rf, ctx) { if (rf & 1) {
49
+ i0.ɵɵelementStart(0, "mj-dialog", 0);
50
+ i0.ɵɵlistener("Close", function WorkQueueDiscardDialogComponent_Template_mj_dialog_Close_0_listener() { return ctx.Cancel(); });
51
+ i0.ɵɵelementStart(1, "div", 1);
52
+ i0.ɵɵconditionalCreate(2, WorkQueueDiscardDialogComponent_Conditional_2_Template, 2, 1, "p", 2);
53
+ i0.ɵɵelementStart(3, "label", 3);
54
+ i0.ɵɵtext(4, "Reason (stored with the resolution)");
55
+ i0.ɵɵelementEnd();
56
+ i0.ɵɵelementStart(5, "textarea", 4);
57
+ i0.ɵɵtwoWayListener("ngModelChange", function WorkQueueDiscardDialogComponent_Template_textarea_ngModelChange_5_listener($event) { i0.ɵɵtwoWayBindingSet(ctx.Reason, $event) || (ctx.Reason = $event); return $event; });
58
+ i0.ɵɵelementEnd();
59
+ i0.ɵɵelementStart(6, "div", 5)(7, "button", 6);
60
+ i0.ɵɵlistener("click", function WorkQueueDiscardDialogComponent_Template_button_click_7_listener() { return ctx.Confirm(); });
61
+ i0.ɵɵconditionalCreate(8, WorkQueueDiscardDialogComponent_Conditional_8_Template, 2, 0)(9, WorkQueueDiscardDialogComponent_Conditional_9_Template, 2, 0);
62
+ i0.ɵɵelementEnd();
63
+ i0.ɵɵelementStart(10, "button", 7);
64
+ i0.ɵɵlistener("click", function WorkQueueDiscardDialogComponent_Template_button_click_10_listener() { return ctx.Cancel(); });
65
+ i0.ɵɵtext(11, "Cancel");
66
+ i0.ɵɵelementEnd()()()();
67
+ } if (rf & 2) {
68
+ i0.ɵɵproperty("Visible", ctx.Visible)("Title", ctx.Title);
69
+ i0.ɵɵadvance(2);
70
+ i0.ɵɵconditional(ctx.Message ? 2 : -1);
71
+ i0.ɵɵadvance(3);
72
+ i0.ɵɵtwoWayProperty("ngModel", ctx.Reason);
73
+ i0.ɵɵproperty("disabled", ctx.Busy);
74
+ i0.ɵɵadvance(2);
75
+ i0.ɵɵproperty("disabled", !ctx.CanConfirm);
76
+ i0.ɵɵadvance();
77
+ i0.ɵɵconditional(ctx.Busy ? 8 : 9);
78
+ i0.ɵɵadvance(2);
79
+ i0.ɵɵproperty("disabled", ctx.Busy);
80
+ } }, dependencies: [i1.DefaultValueAccessor, i1.NgControlStatus, i1.MaxLengthValidator, i1.NgModel, i2.MJButtonDirective, i2.MJDialogComponent], styles: [".wq-discard-body[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 12px;\n min-width: 320px;\n}\n\n.wq-discard-message[_ngcontent-%COMP%] {\n margin: 0;\n color: var(--mj-text-secondary);\n font-size: 0.9rem;\n}\n\n.wq-discard-label[_ngcontent-%COMP%] {\n font-size: 0.8rem;\n color: var(--mj-text-secondary);\n}\n\n.wq-discard-reason[_ngcontent-%COMP%] {\n width: 100%;\n box-sizing: border-box;\n padding: 8px 10px;\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n background: var(--mj-bg-surface);\n color: var(--mj-text-primary);\n font: inherit;\n resize: vertical;\n}\n\n.wq-discard-reason[_ngcontent-%COMP%]:focus {\n outline: none;\n border-color: var(--mj-border-focus);\n}\n\n.wq-discard-actions[_ngcontent-%COMP%] {\n display: flex;\n gap: 8px;\n justify-content: flex-start;\n}"], changeDetection: 0 });
81
+ }
82
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(WorkQueueDiscardDialogComponent, [{
83
+ type: Component,
84
+ args: [{ standalone: false, selector: 'mj-work-queue-discard-dialog', changeDetection: ChangeDetectionStrategy.OnPush, template: "<mj-dialog [Visible]=\"Visible\" [Title]=\"Title\" Size=\"sm\" (Close)=\"Cancel()\">\n <div class=\"wq-discard-body\">\n @if (Message) {\n <p class=\"wq-discard-message\">{{ Message }}</p>\n }\n <label class=\"wq-discard-label\" for=\"wq-discard-reason\">Reason (stored with the resolution)</label>\n <textarea id=\"wq-discard-reason\" class=\"wq-discard-reason\" rows=\"3\" maxlength=\"1000\"\n [(ngModel)]=\"Reason\" [disabled]=\"Busy\" placeholder=\"Why this work is being dropped\"></textarea>\n <div class=\"wq-discard-actions\">\n <button mjButton variant=\"primary\" size=\"sm\" [disabled]=\"!CanConfirm\" (click)=\"Confirm()\">\n @if (Busy) { <i class=\"fa-solid fa-spinner fa-spin\"></i> Discarding\u2026 } @else { <i class=\"fa-solid fa-trash\"></i> Discard }\n </button>\n <button mjButton variant=\"secondary\" size=\"sm\" [disabled]=\"Busy\" (click)=\"Cancel()\">Cancel</button>\n </div>\n </div>\n</mj-dialog>\n", styles: [".wq-discard-body {\n display: flex;\n flex-direction: column;\n gap: 12px;\n min-width: 320px;\n}\n\n.wq-discard-message {\n margin: 0;\n color: var(--mj-text-secondary);\n font-size: 0.9rem;\n}\n\n.wq-discard-label {\n font-size: 0.8rem;\n color: var(--mj-text-secondary);\n}\n\n.wq-discard-reason {\n width: 100%;\n box-sizing: border-box;\n padding: 8px 10px;\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n background: var(--mj-bg-surface);\n color: var(--mj-text-primary);\n font: inherit;\n resize: vertical;\n}\n\n.wq-discard-reason:focus {\n outline: none;\n border-color: var(--mj-border-focus);\n}\n\n.wq-discard-actions {\n display: flex;\n gap: 8px;\n justify-content: flex-start;\n}\n"] }]
85
+ }], null, { Visible: [{
86
+ type: Input
87
+ }], Title: [{
88
+ type: Input
89
+ }], Message: [{
90
+ type: Input
91
+ }], Busy: [{
92
+ type: Input
93
+ }], VisibleChange: [{
94
+ type: Output
95
+ }], Confirmed: [{
96
+ type: Output
97
+ }] }); })();
98
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(WorkQueueDiscardDialogComponent, { className: "WorkQueueDiscardDialogComponent", filePath: "src/WorkQueue/components/work-queue-discard-dialog.component.ts", lineNumber: 14 }); })();
99
+ //# sourceMappingURL=work-queue-discard-dialog.component.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"work-queue-discard-dialog.component.js","sourceRoot":"","sources":["../../../src/WorkQueue/components/work-queue-discard-dialog.component.ts","../../../src/WorkQueue/components/work-queue-discard-dialog.component.html"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,SAAS,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;;;;;ICG1F,4BAA8B;IAAA,YAAa;IAAA,iBAAI;;;IAAjB,cAAa;IAAb,oCAAa;;;IAO5B,uBAA2C;IAAC,kCAAY;;;IAAU,uBAAiC;IAAC,yBAAQ;;ADRjI;;;GAGG;AAQH,MAAM,OAAO,+BAA+B;IAC/B,OAAO,GAAG,KAAK,CAAC;IAChB,KAAK,GAAG,kBAAkB,CAAC;IACpC,2GAA2G;IAClG,OAAO,GAAG,EAAE,CAAC;IACb,IAAI,GAAG,KAAK,CAAC;IACZ,aAAa,GAAG,IAAI,YAAY,EAAW,CAAC;IAC5C,SAAS,GAAG,IAAI,YAAY,EAAU,CAAC;IAE1C,MAAM,GAAG,EAAE,CAAC;IAEnB,IAAW,UAAU;QACjB,OAAO,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC;IACvD,CAAC;IAEM,OAAO;QACV,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;YAClB,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;QAC5C,CAAC;IACL,CAAC;IAEM,MAAM;QACT,IAAI,CAAC,MAAM,GAAG,EAAE,CAAC;QACjB,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACnC,CAAC;yHAxBQ,+BAA+B;6DAA/B,+BAA+B;YCb5C,oCAA4E;YAAnB,+GAAS,YAAQ,IAAC;YACzE,8BAA6B;YAC3B,+FAAe;YAGf,gCAAwD;YAAA,mDAAmC;YAAA,iBAAQ;YACnG,mCACsF;YAApF,wNAAoB;YAAgE,iBAAW;YAE/F,AADF,8BAAgC,gBAC4D;YAApB,4GAAS,aAAS,IAAC;YAChB,AAAvE,uFAAY,iEAAkE;YAChF,iBAAS;YACT,kCAAoF;YAAnB,6GAAS,YAAQ,IAAC;YAAC,uBAAM;YAGhG,AADE,AADE,AAD4F,iBAAS,EAC/F,EACF,EACI;;YAfmB,AAApB,qCAAmB,oBAAgB;YAE1C,eAEC;YAFD,sCAEC;YAGC,eAAoB;YAApB,0CAAoB;YAAC,mCAAiB;YAEO,eAAwB;YAAxB,0CAAwB;YACnE,cAA0H;YAA1H,kCAA0H;YAE7E,eAAiB;YAAjB,mCAAiB;;;iFDCzD,+BAA+B;cAP3C,SAAS;6BACM,KAAK,YACP,8BAA8B,mBAGvB,uBAAuB,CAAC,MAAM;;kBAG9C,KAAK;;kBACL,KAAK;;kBAEL,KAAK;;kBACL,KAAK;;kBACL,MAAM;;kBACN,MAAM;;kFAPE,+BAA+B","sourcesContent":["import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core';\n\n/**\n * Asks for the operator's reason before WorkQueue.DiscardDelivery runs (the operation requires one, and it is\n * stored with the resolution). Shared by the Dead Letters and Partitions tabs.\n */\n@Component({\n standalone: false,\n selector: 'mj-work-queue-discard-dialog',\n templateUrl: './work-queue-discard-dialog.component.html',\n styleUrls: ['./work-queue-discard-dialog.component.css'],\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class WorkQueueDiscardDialogComponent {\n @Input() Visible = false;\n @Input() Title = 'Discard delivery';\n /** What is about to happen, e.g. \"3 dead letters will be discarded\" or the blocked-key release warning. */\n @Input() Message = '';\n @Input() Busy = false;\n @Output() VisibleChange = new EventEmitter<boolean>();\n @Output() Confirmed = new EventEmitter<string>();\n\n public Reason = '';\n\n public get CanConfirm(): boolean {\n return this.Reason.trim().length > 0 && !this.Busy;\n }\n\n public Confirm(): void {\n if (this.CanConfirm) {\n this.Confirmed.emit(this.Reason.trim());\n }\n }\n\n public Cancel(): void {\n this.Reason = '';\n this.VisibleChange.emit(false);\n }\n}\n","<mj-dialog [Visible]=\"Visible\" [Title]=\"Title\" Size=\"sm\" (Close)=\"Cancel()\">\n <div class=\"wq-discard-body\">\n @if (Message) {\n <p class=\"wq-discard-message\">{{ Message }}</p>\n }\n <label class=\"wq-discard-label\" for=\"wq-discard-reason\">Reason (stored with the resolution)</label>\n <textarea id=\"wq-discard-reason\" class=\"wq-discard-reason\" rows=\"3\" maxlength=\"1000\"\n [(ngModel)]=\"Reason\" [disabled]=\"Busy\" placeholder=\"Why this work is being dropped\"></textarea>\n <div class=\"wq-discard-actions\">\n <button mjButton variant=\"primary\" size=\"sm\" [disabled]=\"!CanConfirm\" (click)=\"Confirm()\">\n @if (Busy) { <i class=\"fa-solid fa-spinner fa-spin\"></i> Discarding… } @else { <i class=\"fa-solid fa-trash\"></i> Discard }\n </button>\n <button mjButton variant=\"secondary\" size=\"sm\" [disabled]=\"Busy\" (click)=\"Cancel()\">Cancel</button>\n </div>\n </div>\n</mj-dialog>\n"]}
@@ -0,0 +1,29 @@
1
+ import { EventEmitter } from '@angular/core';
2
+ import type { WorkQueueOverviewRow, WorkQueueRecordOpenRequest } from '../work-queue-types';
3
+ import * as i0 from "@angular/core";
4
+ interface OverviewGroup {
5
+ Transport: string;
6
+ DriverClass: string;
7
+ Rows: WorkQueueOverviewRow[];
8
+ }
9
+ /**
10
+ * Topology joined with live stats, grouped by transport. Pure display: the dashboard loads the rows and passes
11
+ * them in; this component only orders them (attention first) and raises selection / open-record events.
12
+ */
13
+ export declare class WorkQueueOverviewComponent {
14
+ Rows: WorkQueueOverviewRow[];
15
+ IsLoading: boolean;
16
+ LastRefreshedAt: Date | null;
17
+ SubscriptionSelected: EventEmitter<string>;
18
+ OpenRecord: EventEmitter<WorkQueueRecordOpenRequest>;
19
+ get Groups(): OverviewGroup[];
20
+ NeedsAttention(row: WorkQueueOverviewRow): boolean;
21
+ Age(seconds: number | null | undefined): string;
22
+ Count(value: number | null | undefined): string;
23
+ Select(row: WorkQueueOverviewRow): void;
24
+ Open(entityName: WorkQueueRecordOpenRequest['EntityName'], id: string, event: Event): void;
25
+ static ɵfac: i0.ɵɵFactoryDeclaration<WorkQueueOverviewComponent, never>;
26
+ static ɵcmp: i0.ɵɵComponentDeclaration<WorkQueueOverviewComponent, "mj-work-queue-overview", never, { "Rows": { "alias": "Rows"; "required": false; }; "IsLoading": { "alias": "IsLoading"; "required": false; }; "LastRefreshedAt": { "alias": "LastRefreshedAt"; "required": false; }; }, { "SubscriptionSelected": "SubscriptionSelected"; "OpenRecord": "OpenRecord"; }, never, never, false, never>;
27
+ }
28
+ export {};
29
+ //# sourceMappingURL=work-queue-overview.component.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"work-queue-overview.component.d.ts","sourceRoot":"","sources":["../../../src/WorkQueue/components/work-queue-overview.component.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsC,YAAY,EAAiB,MAAM,eAAe,CAAC;AAEhG,OAAO,KAAK,EAAE,oBAAoB,EAAE,0BAA0B,EAAE,MAAM,qBAAqB,CAAC;;AAE5F,UAAU,aAAa;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,oBAAoB,EAAE,CAAC;CAChC;AAED;;;GAGG;AACH,qBAOa,0BAA0B;IAC1B,IAAI,EAAE,oBAAoB,EAAE,CAAM;IAClC,SAAS,UAAS;IAClB,eAAe,EAAE,IAAI,GAAG,IAAI,CAAQ;IACnC,oBAAoB,uBAA8B;IAClD,UAAU,2CAAkD;IAEtE,IAAW,MAAM,IAAI,aAAa,EAAE,CAcnC;IAEM,cAAc,CAAC,GAAG,EAAE,oBAAoB,GAAG,OAAO;IAIlD,GAAG,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM;IAI/C,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM;IAI/C,MAAM,CAAC,GAAG,EAAE,oBAAoB,GAAG,IAAI;IAIvC,IAAI,CAAC,UAAU,EAAE,0BAA0B,CAAC,YAAY,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,GAAG,IAAI;yCAvCxF,0BAA0B;2CAA1B,0BAA0B;CA2CtC"}
@@ -0,0 +1,278 @@
1
+ import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core';
2
+ import { FormatAge, NeedsAttention } from '../work-queue-agent-context';
3
+ import * as i0 from "@angular/core";
4
+ import * as i1 from "@memberjunction/ng-ui-components";
5
+ import * as i2 from "@memberjunction/ng-shared-generic";
6
+ import * as i3 from "@angular/common";
7
+ const _forTrack0 = ($index, $item) => $item.Transport;
8
+ const _forTrack1 = ($index, $item) => $item.SubscriptionID;
9
+ function WorkQueueOverviewComponent_Conditional_1_Template(rf, ctx) { if (rf & 1) {
10
+ i0.ɵɵelementStart(0, "div", 1);
11
+ i0.ɵɵelement(1, "mj-loading", 3);
12
+ i0.ɵɵelementEnd();
13
+ } }
14
+ function WorkQueueOverviewComponent_Conditional_2_Template(rf, ctx) { if (rf & 1) {
15
+ i0.ɵɵelement(0, "mj-empty-state", 2);
16
+ } }
17
+ function WorkQueueOverviewComponent_Conditional_3_For_1_For_37_Conditional_4_Template(rf, ctx) { if (rf & 1) {
18
+ i0.ɵɵelementStart(0, "span", 9);
19
+ i0.ɵɵtext(1, "FIFO");
20
+ i0.ɵɵelementEnd();
21
+ } }
22
+ function WorkQueueOverviewComponent_Conditional_3_For_1_For_37_Conditional_13_Template(rf, ctx) { if (rf & 1) {
23
+ i0.ɵɵelementStart(0, "span", 18);
24
+ i0.ɵɵtext(1);
25
+ i0.ɵɵelementEnd();
26
+ } if (rf & 2) {
27
+ const row_r2 = i0.ɵɵnextContext().$implicit;
28
+ i0.ɵɵadvance();
29
+ i0.ɵɵtextInterpolate(row_r2.HandlerKey);
30
+ } }
31
+ function WorkQueueOverviewComponent_Conditional_3_For_1_For_37_Conditional_17_Template(rf, ctx) { if (rf & 1) {
32
+ i0.ɵɵelementStart(0, "td", 20);
33
+ i0.ɵɵelement(1, "i", 22);
34
+ i0.ɵɵtext(2);
35
+ i0.ɵɵelementEnd();
36
+ } if (rf & 2) {
37
+ const row_r2 = i0.ɵɵnextContext().$implicit;
38
+ i0.ɵɵadvance(2);
39
+ i0.ɵɵtextInterpolate1(" ", row_r2.Error);
40
+ } }
41
+ function WorkQueueOverviewComponent_Conditional_3_For_1_For_37_Conditional_18_Template(rf, ctx) { if (rf & 1) {
42
+ i0.ɵɵelementStart(0, "td", 12);
43
+ i0.ɵɵtext(1);
44
+ i0.ɵɵelementEnd();
45
+ i0.ɵɵelementStart(2, "td", 12);
46
+ i0.ɵɵtext(3);
47
+ i0.ɵɵelementEnd();
48
+ i0.ɵɵelementStart(4, "td", 12);
49
+ i0.ɵɵtext(5);
50
+ i0.ɵɵelementEnd();
51
+ i0.ɵɵelementStart(6, "td", 12);
52
+ i0.ɵɵtext(7);
53
+ i0.ɵɵelementEnd();
54
+ i0.ɵɵelementStart(8, "td", 12);
55
+ i0.ɵɵtext(9);
56
+ i0.ɵɵelementEnd();
57
+ i0.ɵɵelementStart(10, "td", 12);
58
+ i0.ɵɵtext(11);
59
+ i0.ɵɵelementEnd();
60
+ } if (rf & 2) {
61
+ const row_r2 = i0.ɵɵnextContext().$implicit;
62
+ const ctx_r2 = i0.ɵɵnextContext(3);
63
+ i0.ɵɵadvance();
64
+ i0.ɵɵtextInterpolate(ctx_r2.Count(row_r2.Stats == null ? null : row_r2.Stats.Pending));
65
+ i0.ɵɵadvance(2);
66
+ i0.ɵɵtextInterpolate(ctx_r2.Count(row_r2.Stats == null ? null : row_r2.Stats.InFlight));
67
+ i0.ɵɵadvance();
68
+ i0.ɵɵclassProp("wq-num-error", ((row_r2.Stats == null ? null : row_r2.Stats.DeadLettered) ?? 0) > 0);
69
+ i0.ɵɵadvance();
70
+ i0.ɵɵtextInterpolate(ctx_r2.Count(row_r2.Stats == null ? null : row_r2.Stats.DeadLettered));
71
+ i0.ɵɵadvance();
72
+ i0.ɵɵclassProp("wq-num-error", ((row_r2.Stats == null ? null : row_r2.Stats.BlockedKeys) ?? 0) > 0);
73
+ i0.ɵɵadvance();
74
+ i0.ɵɵtextInterpolate(ctx_r2.Count(row_r2.Stats == null ? null : row_r2.Stats.BlockedKeys));
75
+ i0.ɵɵadvance(2);
76
+ i0.ɵɵtextInterpolate(ctx_r2.Age(row_r2.Stats == null ? null : row_r2.Stats.OldestPendingAgeSeconds));
77
+ i0.ɵɵadvance(2);
78
+ i0.ɵɵtextInterpolate(ctx_r2.Count(row_r2.Stats == null ? null : row_r2.Stats.CompletedLastHour));
79
+ } }
80
+ function WorkQueueOverviewComponent_Conditional_3_For_1_For_37_Template(rf, ctx) { if (rf & 1) {
81
+ const _r1 = i0.ɵɵgetCurrentView();
82
+ i0.ɵɵelementStart(0, "tr", 14);
83
+ i0.ɵɵlistener("click", function WorkQueueOverviewComponent_Conditional_3_For_1_For_37_Template_tr_click_0_listener() { const row_r2 = i0.ɵɵrestoreView(_r1).$implicit; const ctx_r2 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r2.Select(row_r2)); });
84
+ i0.ɵɵelementStart(1, "td")(2, "a", 15);
85
+ i0.ɵɵlistener("click", function WorkQueueOverviewComponent_Conditional_3_For_1_For_37_Template_a_click_2_listener($event) { const row_r2 = i0.ɵɵrestoreView(_r1).$implicit; const ctx_r2 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r2.Open("MJ: Work Queue Topics", row_r2.TopicID, $event)); });
86
+ i0.ɵɵtext(3);
87
+ i0.ɵɵelementEnd();
88
+ i0.ɵɵconditionalCreate(4, WorkQueueOverviewComponent_Conditional_3_For_1_For_37_Conditional_4_Template, 2, 0, "span", 9);
89
+ i0.ɵɵelementEnd();
90
+ i0.ɵɵelementStart(5, "td")(6, "a", 16);
91
+ i0.ɵɵlistener("click", function WorkQueueOverviewComponent_Conditional_3_For_1_For_37_Template_a_click_6_listener($event) { const row_r2 = i0.ɵɵrestoreView(_r1).$implicit; const ctx_r2 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r2.Open("MJ: Work Queue Subscriptions", row_r2.SubscriptionID, $event)); });
92
+ i0.ɵɵtext(7);
93
+ i0.ɵɵelementEnd()();
94
+ i0.ɵɵelementStart(8, "td");
95
+ i0.ɵɵtext(9);
96
+ i0.ɵɵelementEnd();
97
+ i0.ɵɵelementStart(10, "td")(11, "span", 17);
98
+ i0.ɵɵtext(12);
99
+ i0.ɵɵelementEnd();
100
+ i0.ɵɵconditionalCreate(13, WorkQueueOverviewComponent_Conditional_3_For_1_For_37_Conditional_13_Template, 2, 1, "span", 18);
101
+ i0.ɵɵelementEnd();
102
+ i0.ɵɵelementStart(14, "td")(15, "span", 19);
103
+ i0.ɵɵtext(16);
104
+ i0.ɵɵelementEnd()();
105
+ i0.ɵɵconditionalCreate(17, WorkQueueOverviewComponent_Conditional_3_For_1_For_37_Conditional_17_Template, 3, 1, "td", 20)(18, WorkQueueOverviewComponent_Conditional_3_For_1_For_37_Conditional_18_Template, 12, 10);
106
+ i0.ɵɵelementStart(19, "td", 12);
107
+ i0.ɵɵelement(20, "i", 21);
108
+ i0.ɵɵelementEnd()();
109
+ } if (rf & 2) {
110
+ const row_r2 = ctx.$implicit;
111
+ const ctx_r2 = i0.ɵɵnextContext(3);
112
+ i0.ɵɵclassProp("wq-row-attention", ctx_r2.NeedsAttention(row_r2));
113
+ i0.ɵɵadvance(3);
114
+ i0.ɵɵtextInterpolate(row_r2.Topic);
115
+ i0.ɵɵadvance();
116
+ i0.ɵɵconditional(row_r2.IsFifo ? 4 : -1);
117
+ i0.ɵɵadvance(3);
118
+ i0.ɵɵtextInterpolate(row_r2.Subscription);
119
+ i0.ɵɵadvance(2);
120
+ i0.ɵɵtextInterpolate(row_r2.PartitionMode);
121
+ i0.ɵɵadvance(3);
122
+ i0.ɵɵtextInterpolate(row_r2.HostType);
123
+ i0.ɵɵadvance();
124
+ i0.ɵɵconditional(row_r2.HandlerKey ? 13 : -1);
125
+ i0.ɵɵadvance(2);
126
+ i0.ɵɵclassProp("wq-chip-success", row_r2.Status === "Active")("wq-chip-warning", row_r2.Status !== "Active");
127
+ i0.ɵɵadvance();
128
+ i0.ɵɵtextInterpolate(row_r2.Status);
129
+ i0.ɵɵadvance();
130
+ i0.ɵɵconditional(row_r2.Error ? 17 : 18);
131
+ } }
132
+ function WorkQueueOverviewComponent_Conditional_3_For_1_Template(rf, ctx) { if (rf & 1) {
133
+ i0.ɵɵelementStart(0, "section", 4)(1, "header", 6);
134
+ i0.ɵɵelement(2, "i", 7);
135
+ i0.ɵɵelementStart(3, "span", 8);
136
+ i0.ɵɵtext(4);
137
+ i0.ɵɵelementEnd();
138
+ i0.ɵɵelementStart(5, "span", 9);
139
+ i0.ɵɵtext(6);
140
+ i0.ɵɵelementEnd();
141
+ i0.ɵɵelementStart(7, "span", 10);
142
+ i0.ɵɵtext(8);
143
+ i0.ɵɵelementEnd()();
144
+ i0.ɵɵelementStart(9, "table", 11)(10, "thead")(11, "tr")(12, "th");
145
+ i0.ɵɵtext(13, "Topic");
146
+ i0.ɵɵelementEnd();
147
+ i0.ɵɵelementStart(14, "th");
148
+ i0.ɵɵtext(15, "Subscription");
149
+ i0.ɵɵelementEnd();
150
+ i0.ɵɵelementStart(16, "th");
151
+ i0.ɵɵtext(17, "Mode");
152
+ i0.ɵɵelementEnd();
153
+ i0.ɵɵelementStart(18, "th");
154
+ i0.ɵɵtext(19, "Host");
155
+ i0.ɵɵelementEnd();
156
+ i0.ɵɵelementStart(20, "th");
157
+ i0.ɵɵtext(21, "Status");
158
+ i0.ɵɵelementEnd();
159
+ i0.ɵɵelementStart(22, "th", 12);
160
+ i0.ɵɵtext(23, "Pending");
161
+ i0.ɵɵelementEnd();
162
+ i0.ɵɵelementStart(24, "th", 12);
163
+ i0.ɵɵtext(25, "In flight");
164
+ i0.ɵɵelementEnd();
165
+ i0.ɵɵelementStart(26, "th", 12);
166
+ i0.ɵɵtext(27, "Dead");
167
+ i0.ɵɵelementEnd();
168
+ i0.ɵɵelementStart(28, "th", 12);
169
+ i0.ɵɵtext(29, "Blocked keys");
170
+ i0.ɵɵelementEnd();
171
+ i0.ɵɵelementStart(30, "th", 12);
172
+ i0.ɵɵtext(31, "Oldest pending");
173
+ i0.ɵɵelementEnd();
174
+ i0.ɵɵelementStart(32, "th", 12);
175
+ i0.ɵɵtext(33, "Done / hour");
176
+ i0.ɵɵelementEnd();
177
+ i0.ɵɵelement(34, "th");
178
+ i0.ɵɵelementEnd()();
179
+ i0.ɵɵelementStart(35, "tbody");
180
+ i0.ɵɵrepeaterCreate(36, WorkQueueOverviewComponent_Conditional_3_For_1_For_37_Template, 21, 14, "tr", 13, _forTrack1);
181
+ i0.ɵɵelementEnd()()();
182
+ } if (rf & 2) {
183
+ const group_r4 = ctx.$implicit;
184
+ i0.ɵɵadvance(4);
185
+ i0.ɵɵtextInterpolate(group_r4.Transport);
186
+ i0.ɵɵadvance(2);
187
+ i0.ɵɵtextInterpolate(group_r4.DriverClass);
188
+ i0.ɵɵadvance(2);
189
+ i0.ɵɵtextInterpolate2("", group_r4.Rows.length, " subscription", group_r4.Rows.length === 1 ? "" : "s");
190
+ i0.ɵɵadvance(28);
191
+ i0.ɵɵrepeater(group_r4.Rows);
192
+ } }
193
+ function WorkQueueOverviewComponent_Conditional_3_Conditional_2_Template(rf, ctx) { if (rf & 1) {
194
+ i0.ɵɵelementStart(0, "p", 5);
195
+ i0.ɵɵtext(1);
196
+ i0.ɵɵpipe(2, "date");
197
+ i0.ɵɵelementEnd();
198
+ } if (rf & 2) {
199
+ const ctx_r2 = i0.ɵɵnextContext(2);
200
+ i0.ɵɵadvance();
201
+ i0.ɵɵtextInterpolate1("Stats as of ", i0.ɵɵpipeBind2(2, 1, ctx_r2.LastRefreshedAt, "mediumTime"), ". Rows with dead letters or blocked keys sort first.");
202
+ } }
203
+ function WorkQueueOverviewComponent_Conditional_3_Template(rf, ctx) { if (rf & 1) {
204
+ i0.ɵɵrepeaterCreate(0, WorkQueueOverviewComponent_Conditional_3_For_1_Template, 38, 4, "section", 4, _forTrack0);
205
+ i0.ɵɵconditionalCreate(2, WorkQueueOverviewComponent_Conditional_3_Conditional_2_Template, 3, 4, "p", 5);
206
+ } if (rf & 2) {
207
+ const ctx_r2 = i0.ɵɵnextContext();
208
+ i0.ɵɵrepeater(ctx_r2.Groups);
209
+ i0.ɵɵadvance(2);
210
+ i0.ɵɵconditional(ctx_r2.LastRefreshedAt ? 2 : -1);
211
+ } }
212
+ /**
213
+ * Topology joined with live stats, grouped by transport. Pure display: the dashboard loads the rows and passes
214
+ * them in; this component only orders them (attention first) and raises selection / open-record events.
215
+ */
216
+ export class WorkQueueOverviewComponent {
217
+ Rows = [];
218
+ IsLoading = false;
219
+ LastRefreshedAt = null;
220
+ SubscriptionSelected = new EventEmitter();
221
+ OpenRecord = new EventEmitter();
222
+ get Groups() {
223
+ const groups = new Map();
224
+ for (const row of this.Rows) {
225
+ const group = groups.get(row.TransportID) ?? { Transport: row.Transport, DriverClass: row.DriverClass, Rows: [] };
226
+ group.Rows.push(row);
227
+ groups.set(row.TransportID, group);
228
+ }
229
+ for (const group of groups.values()) {
230
+ group.Rows.sort((a, b) => {
231
+ const attention = Number(this.NeedsAttention(b)) - Number(this.NeedsAttention(a));
232
+ return attention !== 0 ? attention : `${a.Topic}/${a.Subscription}`.localeCompare(`${b.Topic}/${b.Subscription}`);
233
+ });
234
+ }
235
+ return [...groups.values()].sort((a, b) => a.Transport.localeCompare(b.Transport));
236
+ }
237
+ NeedsAttention(row) {
238
+ return row.Error !== null || (row.Stats !== null && NeedsAttention({ DeadLettered: row.Stats.DeadLettered, BlockedKeys: row.Stats.BlockedKeys }));
239
+ }
240
+ Age(seconds) {
241
+ return FormatAge(seconds);
242
+ }
243
+ Count(value) {
244
+ return value === null || value === undefined ? '—' : String(value);
245
+ }
246
+ Select(row) {
247
+ this.SubscriptionSelected.emit(row.Subscription);
248
+ }
249
+ Open(entityName, id, event) {
250
+ event.stopPropagation();
251
+ this.OpenRecord.emit({ EntityName: entityName, ID: id });
252
+ }
253
+ static ɵfac = function WorkQueueOverviewComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || WorkQueueOverviewComponent)(); };
254
+ static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: WorkQueueOverviewComponent, selectors: [["mj-work-queue-overview"]], inputs: { Rows: "Rows", IsLoading: "IsLoading", LastRefreshedAt: "LastRefreshedAt" }, outputs: { SubscriptionSelected: "SubscriptionSelected", OpenRecord: "OpenRecord" }, standalone: false, decls: 4, vars: 1, consts: [[1, "wq-overview"], [1, "wq-loading"], ["Icon", "fa-solid fa-layer-group", "Title", "No subscriptions", "Message", "Create a topic and a subscription (MJ: Work Queue Topics / Subscriptions) to see them here."], ["text", "Loading topology and stats...", "size", "medium"], [1, "wq-group"], [1, "wq-refreshed"], [1, "wq-group-header"], [1, "fa-solid", "fa-server"], [1, "wq-group-title"], [1, "wq-chip", "wq-chip-neutral"], [1, "wq-group-count"], [1, "wq-table"], [1, "wq-num"], ["title", "Open dead letters for this subscription", 1, "wq-row", 3, "wq-row-attention"], ["title", "Open dead letters for this subscription", 1, "wq-row", 3, "click"], ["title", "Open topic record", 1, "wq-link", 3, "click"], ["title", "Open subscription record", 1, "wq-link", 3, "click"], [1, "wq-host"], [1, "wq-handler"], [1, "wq-chip"], ["colspan", "6", 1, "wq-error"], [1, "fa-solid", "fa-chevron-right", "wq-row-arrow"], [1, "fa-solid", "fa-triangle-exclamation"]], template: function WorkQueueOverviewComponent_Template(rf, ctx) { if (rf & 1) {
255
+ i0.ɵɵelementStart(0, "div", 0);
256
+ i0.ɵɵconditionalCreate(1, WorkQueueOverviewComponent_Conditional_1_Template, 2, 0, "div", 1)(2, WorkQueueOverviewComponent_Conditional_2_Template, 1, 0, "mj-empty-state", 2)(3, WorkQueueOverviewComponent_Conditional_3_Template, 3, 1);
257
+ i0.ɵɵelementEnd();
258
+ } if (rf & 2) {
259
+ i0.ɵɵadvance();
260
+ i0.ɵɵconditional(ctx.IsLoading && ctx.Rows.length === 0 ? 1 : ctx.Rows.length === 0 ? 2 : 3);
261
+ } }, dependencies: [i1.MJEmptyStateComponent, i2.LoadingComponent, i3.DatePipe], styles: [".wq-overview[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 20px;\n}\n\n.wq-loading[_ngcontent-%COMP%] {\n padding: 40px 0;\n}\n\n.wq-group[_ngcontent-%COMP%] {\n background: var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-default);\n border-radius: 12px;\n overflow: hidden;\n}\n\n.wq-group-header[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 12px 16px;\n border-bottom: 1px solid var(--mj-border-subtle);\n color: var(--mj-text-primary);\n}\n\n.wq-group-header[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n color: var(--mj-text-muted);\n}\n\n.wq-group-title[_ngcontent-%COMP%] {\n font-weight: 600;\n}\n\n.wq-group-count[_ngcontent-%COMP%] {\n margin-left: auto;\n font-size: 0.8rem;\n color: var(--mj-text-muted);\n}\n\n.wq-table[_ngcontent-%COMP%] {\n width: 100%;\n border-collapse: collapse;\n font-size: 0.875rem;\n}\n\n.wq-table[_ngcontent-%COMP%] th[_ngcontent-%COMP%] {\n text-align: left;\n padding: 8px 12px;\n font-size: 0.75rem;\n text-transform: uppercase;\n letter-spacing: 0.03em;\n color: var(--mj-text-secondary);\n border-bottom: 1px solid var(--mj-border-subtle);\n}\n\n.wq-table[_ngcontent-%COMP%] td[_ngcontent-%COMP%] {\n padding: 10px 12px;\n border-bottom: 1px solid var(--mj-border-subtle);\n color: var(--mj-text-primary);\n vertical-align: middle;\n}\n\n.wq-table[_ngcontent-%COMP%] tbody[_ngcontent-%COMP%] tr[_ngcontent-%COMP%]:last-child td[_ngcontent-%COMP%] {\n border-bottom: none;\n}\n\n.wq-num[_ngcontent-%COMP%] {\n text-align: right;\n white-space: nowrap;\n}\n\n.wq-num-error[_ngcontent-%COMP%] {\n color: var(--mj-status-error-text);\n font-weight: 600;\n}\n\n.wq-row[_ngcontent-%COMP%] {\n cursor: pointer;\n}\n\n.wq-row[_ngcontent-%COMP%]:hover td[_ngcontent-%COMP%] {\n background: var(--mj-bg-surface-hover);\n}\n\n.wq-row-attention[_ngcontent-%COMP%] td[_ngcontent-%COMP%]:first-child {\n box-shadow: inset 3px 0 0 var(--mj-status-warning);\n}\n\n.wq-row-arrow[_ngcontent-%COMP%] {\n color: var(--mj-text-muted);\n font-size: 0.75rem;\n}\n\n.wq-link[_ngcontent-%COMP%] {\n color: var(--mj-text-link);\n cursor: pointer;\n}\n\n.wq-link[_ngcontent-%COMP%]:hover {\n text-decoration: underline;\n}\n\n.wq-host[_ngcontent-%COMP%] {\n color: var(--mj-text-secondary);\n}\n\n.wq-handler[_ngcontent-%COMP%] {\n display: block;\n font-size: 0.75rem;\n color: var(--mj-text-muted);\n font-family: var(--mj-font-family-mono);\n}\n\n.wq-chip[_ngcontent-%COMP%] {\n display: inline-block;\n margin-left: 6px;\n padding: 2px 8px;\n border-radius: 999px;\n font-size: 0.7rem;\n font-weight: 600;\n background: var(--mj-bg-surface-sunken);\n color: var(--mj-text-secondary);\n}\n\n.wq-chip-neutral[_ngcontent-%COMP%] {\n background: var(--mj-bg-surface-sunken);\n}\n\n.wq-chip-success[_ngcontent-%COMP%] {\n margin-left: 0;\n background: color-mix(in srgb, var(--mj-status-success) 14%, var(--mj-bg-surface));\n color: var(--mj-status-success);\n}\n\n.wq-chip-warning[_ngcontent-%COMP%] {\n margin-left: 0;\n background: color-mix(in srgb, var(--mj-status-warning) 14%, var(--mj-bg-surface));\n color: var(--mj-status-warning-text);\n}\n\n.wq-error[_ngcontent-%COMP%] {\n color: var(--mj-status-error-text);\n font-size: 0.8rem;\n}\n\n.wq-refreshed[_ngcontent-%COMP%] {\n margin: 0;\n font-size: 0.8rem;\n color: var(--mj-text-muted);\n}"], changeDetection: 0 });
262
+ }
263
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(WorkQueueOverviewComponent, [{
264
+ type: Component,
265
+ args: [{ standalone: false, selector: 'mj-work-queue-overview', changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"wq-overview\">\n @if (IsLoading && Rows.length === 0) {\n <div class=\"wq-loading\"><mj-loading text=\"Loading topology and stats...\" size=\"medium\"></mj-loading></div>\n } @else if (Rows.length === 0) {\n <mj-empty-state Icon=\"fa-solid fa-layer-group\" Title=\"No subscriptions\"\n Message=\"Create a topic and a subscription (MJ: Work Queue Topics / Subscriptions) to see them here.\" />\n } @else {\n @for (group of Groups; track group.Transport) {\n <section class=\"wq-group\">\n <header class=\"wq-group-header\">\n <i class=\"fa-solid fa-server\"></i>\n <span class=\"wq-group-title\">{{ group.Transport }}</span>\n <span class=\"wq-chip wq-chip-neutral\">{{ group.DriverClass }}</span>\n <span class=\"wq-group-count\">{{ group.Rows.length }} subscription{{ group.Rows.length === 1 ? '' : 's' }}</span>\n </header>\n <table class=\"wq-table\">\n <thead>\n <tr>\n <th>Topic</th>\n <th>Subscription</th>\n <th>Mode</th>\n <th>Host</th>\n <th>Status</th>\n <th class=\"wq-num\">Pending</th>\n <th class=\"wq-num\">In flight</th>\n <th class=\"wq-num\">Dead</th>\n <th class=\"wq-num\">Blocked keys</th>\n <th class=\"wq-num\">Oldest pending</th>\n <th class=\"wq-num\">Done / hour</th>\n <th></th>\n </tr>\n </thead>\n <tbody>\n @for (row of group.Rows; track row.SubscriptionID) {\n <tr class=\"wq-row\" [class.wq-row-attention]=\"NeedsAttention(row)\" (click)=\"Select(row)\" title=\"Open dead letters for this subscription\">\n <td>\n <a class=\"wq-link\" (click)=\"Open('MJ: Work Queue Topics', row.TopicID, $event)\" title=\"Open topic record\">{{ row.Topic }}</a>\n @if (row.IsFifo) { <span class=\"wq-chip wq-chip-neutral\">FIFO</span> }\n </td>\n <td>\n <a class=\"wq-link\" (click)=\"Open('MJ: Work Queue Subscriptions', row.SubscriptionID, $event)\" title=\"Open subscription record\">{{ row.Subscription }}</a>\n </td>\n <td>{{ row.PartitionMode }}</td>\n <td><span class=\"wq-host\">{{ row.HostType }}</span>@if (row.HandlerKey) { <span class=\"wq-handler\">{{ row.HandlerKey }}</span> }</td>\n <td><span class=\"wq-chip\" [class.wq-chip-success]=\"row.Status === 'Active'\" [class.wq-chip-warning]=\"row.Status !== 'Active'\">{{ row.Status }}</span></td>\n @if (row.Error) {\n <td colspan=\"6\" class=\"wq-error\"><i class=\"fa-solid fa-triangle-exclamation\"></i> {{ row.Error }}</td>\n } @else {\n <td class=\"wq-num\">{{ Count(row.Stats?.Pending) }}</td>\n <td class=\"wq-num\">{{ Count(row.Stats?.InFlight) }}</td>\n <td class=\"wq-num\" [class.wq-num-error]=\"(row.Stats?.DeadLettered ?? 0) > 0\">{{ Count(row.Stats?.DeadLettered) }}</td>\n <td class=\"wq-num\" [class.wq-num-error]=\"(row.Stats?.BlockedKeys ?? 0) > 0\">{{ Count(row.Stats?.BlockedKeys) }}</td>\n <td class=\"wq-num\">{{ Age(row.Stats?.OldestPendingAgeSeconds) }}</td>\n <td class=\"wq-num\">{{ Count(row.Stats?.CompletedLastHour) }}</td>\n }\n <td class=\"wq-num\"><i class=\"fa-solid fa-chevron-right wq-row-arrow\"></i></td>\n </tr>\n }\n </tbody>\n </table>\n </section>\n }\n @if (LastRefreshedAt) {\n <p class=\"wq-refreshed\">Stats as of {{ LastRefreshedAt | date:'mediumTime' }}. Rows with dead letters or blocked keys sort first.</p>\n }\n }\n</div>\n", styles: [".wq-overview {\n display: flex;\n flex-direction: column;\n gap: 20px;\n}\n\n.wq-loading {\n padding: 40px 0;\n}\n\n.wq-group {\n background: var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-default);\n border-radius: 12px;\n overflow: hidden;\n}\n\n.wq-group-header {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 12px 16px;\n border-bottom: 1px solid var(--mj-border-subtle);\n color: var(--mj-text-primary);\n}\n\n.wq-group-header i {\n color: var(--mj-text-muted);\n}\n\n.wq-group-title {\n font-weight: 600;\n}\n\n.wq-group-count {\n margin-left: auto;\n font-size: 0.8rem;\n color: var(--mj-text-muted);\n}\n\n.wq-table {\n width: 100%;\n border-collapse: collapse;\n font-size: 0.875rem;\n}\n\n.wq-table th {\n text-align: left;\n padding: 8px 12px;\n font-size: 0.75rem;\n text-transform: uppercase;\n letter-spacing: 0.03em;\n color: var(--mj-text-secondary);\n border-bottom: 1px solid var(--mj-border-subtle);\n}\n\n.wq-table td {\n padding: 10px 12px;\n border-bottom: 1px solid var(--mj-border-subtle);\n color: var(--mj-text-primary);\n vertical-align: middle;\n}\n\n.wq-table tbody tr:last-child td {\n border-bottom: none;\n}\n\n.wq-num {\n text-align: right;\n white-space: nowrap;\n}\n\n.wq-num-error {\n color: var(--mj-status-error-text);\n font-weight: 600;\n}\n\n.wq-row {\n cursor: pointer;\n}\n\n.wq-row:hover td {\n background: var(--mj-bg-surface-hover);\n}\n\n.wq-row-attention td:first-child {\n box-shadow: inset 3px 0 0 var(--mj-status-warning);\n}\n\n.wq-row-arrow {\n color: var(--mj-text-muted);\n font-size: 0.75rem;\n}\n\n.wq-link {\n color: var(--mj-text-link);\n cursor: pointer;\n}\n\n.wq-link:hover {\n text-decoration: underline;\n}\n\n.wq-host {\n color: var(--mj-text-secondary);\n}\n\n.wq-handler {\n display: block;\n font-size: 0.75rem;\n color: var(--mj-text-muted);\n font-family: var(--mj-font-family-mono);\n}\n\n.wq-chip {\n display: inline-block;\n margin-left: 6px;\n padding: 2px 8px;\n border-radius: 999px;\n font-size: 0.7rem;\n font-weight: 600;\n background: var(--mj-bg-surface-sunken);\n color: var(--mj-text-secondary);\n}\n\n.wq-chip-neutral {\n background: var(--mj-bg-surface-sunken);\n}\n\n.wq-chip-success {\n margin-left: 0;\n background: color-mix(in srgb, var(--mj-status-success) 14%, var(--mj-bg-surface));\n color: var(--mj-status-success);\n}\n\n.wq-chip-warning {\n margin-left: 0;\n background: color-mix(in srgb, var(--mj-status-warning) 14%, var(--mj-bg-surface));\n color: var(--mj-status-warning-text);\n}\n\n.wq-error {\n color: var(--mj-status-error-text);\n font-size: 0.8rem;\n}\n\n.wq-refreshed {\n margin: 0;\n font-size: 0.8rem;\n color: var(--mj-text-muted);\n}\n"] }]
266
+ }], null, { Rows: [{
267
+ type: Input
268
+ }], IsLoading: [{
269
+ type: Input
270
+ }], LastRefreshedAt: [{
271
+ type: Input
272
+ }], SubscriptionSelected: [{
273
+ type: Output
274
+ }], OpenRecord: [{
275
+ type: Output
276
+ }] }); })();
277
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(WorkQueueOverviewComponent, { className: "WorkQueueOverviewComponent", filePath: "src/WorkQueue/components/work-queue-overview.component.ts", lineNumber: 22 }); })();
278
+ //# sourceMappingURL=work-queue-overview.component.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"work-queue-overview.component.js","sourceRoot":"","sources":["../../../src/WorkQueue/components/work-queue-overview.component.ts","../../../src/WorkQueue/components/work-queue-overview.component.html"],"names":[],"mappings":"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","sourcesContent":["import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core';\nimport { FormatAge, NeedsAttention } from '../work-queue-agent-context';\nimport type { WorkQueueOverviewRow, WorkQueueRecordOpenRequest } from '../work-queue-types';\n\ninterface OverviewGroup {\n Transport: string;\n DriverClass: string;\n Rows: WorkQueueOverviewRow[];\n}\n\n/**\n * Topology joined with live stats, grouped by transport. Pure display: the dashboard loads the rows and passes\n * them in; this component only orders them (attention first) and raises selection / open-record events.\n */\n@Component({\n standalone: false,\n selector: 'mj-work-queue-overview',\n templateUrl: './work-queue-overview.component.html',\n styleUrls: ['./work-queue-overview.component.css'],\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class WorkQueueOverviewComponent {\n @Input() Rows: WorkQueueOverviewRow[] = [];\n @Input() IsLoading = false;\n @Input() LastRefreshedAt: Date | null = null;\n @Output() SubscriptionSelected = new EventEmitter<string>();\n @Output() OpenRecord = new EventEmitter<WorkQueueRecordOpenRequest>();\n\n public get Groups(): OverviewGroup[] {\n const groups = new Map<string, OverviewGroup>();\n for (const row of this.Rows) {\n const group = groups.get(row.TransportID) ?? { Transport: row.Transport, DriverClass: row.DriverClass, Rows: [] };\n group.Rows.push(row);\n groups.set(row.TransportID, group);\n }\n for (const group of groups.values()) {\n group.Rows.sort((a, b) => {\n const attention = Number(this.NeedsAttention(b)) - Number(this.NeedsAttention(a));\n return attention !== 0 ? attention : `${a.Topic}/${a.Subscription}`.localeCompare(`${b.Topic}/${b.Subscription}`);\n });\n }\n return [...groups.values()].sort((a, b) => a.Transport.localeCompare(b.Transport));\n }\n\n public NeedsAttention(row: WorkQueueOverviewRow): boolean {\n return row.Error !== null || (row.Stats !== null && NeedsAttention({ DeadLettered: row.Stats.DeadLettered, BlockedKeys: row.Stats.BlockedKeys }));\n }\n\n public Age(seconds: number | null | undefined): string {\n return FormatAge(seconds);\n }\n\n public Count(value: number | null | undefined): string {\n return value === null || value === undefined ? '—' : String(value);\n }\n\n public Select(row: WorkQueueOverviewRow): void {\n this.SubscriptionSelected.emit(row.Subscription);\n }\n\n public Open(entityName: WorkQueueRecordOpenRequest['EntityName'], id: string, event: Event): void {\n event.stopPropagation();\n this.OpenRecord.emit({ EntityName: entityName, ID: id });\n }\n}\n","<div class=\"wq-overview\">\n @if (IsLoading && Rows.length === 0) {\n <div class=\"wq-loading\"><mj-loading text=\"Loading topology and stats...\" size=\"medium\"></mj-loading></div>\n } @else if (Rows.length === 0) {\n <mj-empty-state Icon=\"fa-solid fa-layer-group\" Title=\"No subscriptions\"\n Message=\"Create a topic and a subscription (MJ: Work Queue Topics / Subscriptions) to see them here.\" />\n } @else {\n @for (group of Groups; track group.Transport) {\n <section class=\"wq-group\">\n <header class=\"wq-group-header\">\n <i class=\"fa-solid fa-server\"></i>\n <span class=\"wq-group-title\">{{ group.Transport }}</span>\n <span class=\"wq-chip wq-chip-neutral\">{{ group.DriverClass }}</span>\n <span class=\"wq-group-count\">{{ group.Rows.length }} subscription{{ group.Rows.length === 1 ? '' : 's' }}</span>\n </header>\n <table class=\"wq-table\">\n <thead>\n <tr>\n <th>Topic</th>\n <th>Subscription</th>\n <th>Mode</th>\n <th>Host</th>\n <th>Status</th>\n <th class=\"wq-num\">Pending</th>\n <th class=\"wq-num\">In flight</th>\n <th class=\"wq-num\">Dead</th>\n <th class=\"wq-num\">Blocked keys</th>\n <th class=\"wq-num\">Oldest pending</th>\n <th class=\"wq-num\">Done / hour</th>\n <th></th>\n </tr>\n </thead>\n <tbody>\n @for (row of group.Rows; track row.SubscriptionID) {\n <tr class=\"wq-row\" [class.wq-row-attention]=\"NeedsAttention(row)\" (click)=\"Select(row)\" title=\"Open dead letters for this subscription\">\n <td>\n <a class=\"wq-link\" (click)=\"Open('MJ: Work Queue Topics', row.TopicID, $event)\" title=\"Open topic record\">{{ row.Topic }}</a>\n @if (row.IsFifo) { <span class=\"wq-chip wq-chip-neutral\">FIFO</span> }\n </td>\n <td>\n <a class=\"wq-link\" (click)=\"Open('MJ: Work Queue Subscriptions', row.SubscriptionID, $event)\" title=\"Open subscription record\">{{ row.Subscription }}</a>\n </td>\n <td>{{ row.PartitionMode }}</td>\n <td><span class=\"wq-host\">{{ row.HostType }}</span>@if (row.HandlerKey) { <span class=\"wq-handler\">{{ row.HandlerKey }}</span> }</td>\n <td><span class=\"wq-chip\" [class.wq-chip-success]=\"row.Status === 'Active'\" [class.wq-chip-warning]=\"row.Status !== 'Active'\">{{ row.Status }}</span></td>\n @if (row.Error) {\n <td colspan=\"6\" class=\"wq-error\"><i class=\"fa-solid fa-triangle-exclamation\"></i> {{ row.Error }}</td>\n } @else {\n <td class=\"wq-num\">{{ Count(row.Stats?.Pending) }}</td>\n <td class=\"wq-num\">{{ Count(row.Stats?.InFlight) }}</td>\n <td class=\"wq-num\" [class.wq-num-error]=\"(row.Stats?.DeadLettered ?? 0) > 0\">{{ Count(row.Stats?.DeadLettered) }}</td>\n <td class=\"wq-num\" [class.wq-num-error]=\"(row.Stats?.BlockedKeys ?? 0) > 0\">{{ Count(row.Stats?.BlockedKeys) }}</td>\n <td class=\"wq-num\">{{ Age(row.Stats?.OldestPendingAgeSeconds) }}</td>\n <td class=\"wq-num\">{{ Count(row.Stats?.CompletedLastHour) }}</td>\n }\n <td class=\"wq-num\"><i class=\"fa-solid fa-chevron-right wq-row-arrow\"></i></td>\n </tr>\n }\n </tbody>\n </table>\n </section>\n }\n @if (LastRefreshedAt) {\n <p class=\"wq-refreshed\">Stats as of {{ LastRefreshedAt | date:'mediumTime' }}. Rows with dead letters or blocked keys sort first.</p>\n }\n }\n</div>\n"]}
@@ -0,0 +1,59 @@
1
+ import { ChangeDetectorRef, EventEmitter, OnChanges, SimpleChanges } from '@angular/core';
2
+ import type { IMetadataProvider } from '@memberjunction/core';
3
+ import type { WorkQueuePartitionStateRow } from '@memberjunction/core-entities';
4
+ import { WorkQueueOperatorService } from '../services/work-queue-operator.service';
5
+ import { WorkQueuePartitionCondition } from '../work-queue-agent-context';
6
+ import type { WorkQueueSubscriptionOption } from '../work-queue-types';
7
+ import * as i0 from "@angular/core";
8
+ /**
9
+ * Partition-key state for one Ordered subscription: which keys are Blocked (a dead-lettered head holds the key),
10
+ * InFlight or Idle, and how much waits behind each head. Replay / discard act on the blocking head delivery.
11
+ */
12
+ export declare class WorkQueuePartitionsComponent implements OnChanges {
13
+ private readonly operator;
14
+ private readonly cdr;
15
+ Provider: IMetadataProvider;
16
+ Subscriptions: WorkQueueSubscriptionOption[];
17
+ SelectedSubscription: string | null;
18
+ Condition: WorkQueuePartitionCondition | null;
19
+ CanOperate: boolean;
20
+ SelectedSubscriptionChange: EventEmitter<string | null>;
21
+ ConditionChange: EventEmitter<"Idle" | "Blocked" | "InFlight" | null>;
22
+ Changed: EventEmitter<void>;
23
+ Loaded: EventEmitter<{
24
+ Count: number;
25
+ Blocked: number;
26
+ }>;
27
+ readonly Conditions: readonly ["Blocked", "InFlight", "Idle"];
28
+ Rows: WorkQueuePartitionStateRow[];
29
+ Supported: boolean;
30
+ IsLoading: boolean;
31
+ LoadError: string | null;
32
+ PendingHead: WorkQueuePartitionStateRow | null;
33
+ ConfirmReplayVisible: boolean;
34
+ DiscardVisible: boolean;
35
+ ActionInProgress: boolean;
36
+ LastOutcome: {
37
+ Ok: boolean;
38
+ Message: string;
39
+ } | null;
40
+ constructor(operator: WorkQueueOperatorService, cdr: ChangeDetectorRef);
41
+ ngOnChanges(changes: SimpleChanges): void;
42
+ /** Only Ordered subscriptions have partitions; the picker lists just those. */
43
+ get OrderedSubscriptions(): WorkQueueSubscriptionOption[];
44
+ get BlockedCount(): number;
45
+ OnSubscriptionChange(name: string): void;
46
+ ToggleCondition(condition: WorkQueuePartitionCondition): void;
47
+ Refresh(): Promise<void>;
48
+ ConditionClass(condition: WorkQueuePartitionStateRow['Condition']): string;
49
+ RequestReplay(row: WorkQueuePartitionStateRow, event: Event): void;
50
+ RequestDiscard(row: WorkQueuePartitionStateRow, event: Event): void;
51
+ get DiscardMessage(): string;
52
+ ReplayHead(): Promise<void>;
53
+ DiscardHead(reason: string): Promise<void>;
54
+ private runAction;
55
+ private emitLoaded;
56
+ static ɵfac: i0.ɵɵFactoryDeclaration<WorkQueuePartitionsComponent, never>;
57
+ static ɵcmp: i0.ɵɵComponentDeclaration<WorkQueuePartitionsComponent, "mj-work-queue-partitions", never, { "Provider": { "alias": "Provider"; "required": false; }; "Subscriptions": { "alias": "Subscriptions"; "required": false; }; "SelectedSubscription": { "alias": "SelectedSubscription"; "required": false; }; "Condition": { "alias": "Condition"; "required": false; }; "CanOperate": { "alias": "CanOperate"; "required": false; }; }, { "SelectedSubscriptionChange": "SelectedSubscriptionChange"; "ConditionChange": "ConditionChange"; "Changed": "Changed"; "Loaded": "Loaded"; }, never, never, false, never>;
58
+ }
59
+ //# sourceMappingURL=work-queue-partitions.component.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"work-queue-partitions.component.d.ts","sourceRoot":"","sources":["../../../src/WorkQueue/components/work-queue-partitions.component.ts"],"names":[],"mappings":"AAAA,OAAO,EAA2B,iBAAiB,EAAa,YAAY,EAAS,SAAS,EAAU,aAAa,EAAE,MAAM,eAAe,CAAC;AAC7I,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,+BAA+B,CAAC;AAChF,OAAO,EAAE,wBAAwB,EAAE,MAAM,yCAAyC,CAAC;AACnF,OAAO,EAAmC,2BAA2B,EAAE,MAAM,6BAA6B,CAAC;AAC3G,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,qBAAqB,CAAC;;AAEvE;;;GAGG;AACH,qBAOa,4BAA6B,YAAW,SAAS;IAsB9C,OAAO,CAAC,QAAQ,CAAC,QAAQ;IAA4B,OAAO,CAAC,QAAQ,CAAC,GAAG;IArB5E,QAAQ,EAAG,iBAAiB,CAAC;IAC7B,aAAa,EAAE,2BAA2B,EAAE,CAAM;IAClD,oBAAoB,EAAE,MAAM,GAAG,IAAI,CAAQ;IAC3C,SAAS,EAAE,2BAA2B,GAAG,IAAI,CAAQ;IACrD,UAAU,UAAS;IAClB,0BAA0B,8BAAqC;IAC/D,eAAe,uDAA0D;IACzE,OAAO,qBAA4B;IACnC,MAAM;eAA6B,MAAM;iBAAW,MAAM;OAAM;IAE1E,SAAgB,UAAU,2CAAmC;IACtD,IAAI,EAAE,0BAA0B,EAAE,CAAM;IACxC,SAAS,UAAQ;IACjB,SAAS,UAAS;IAClB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAQ;IAChC,WAAW,EAAE,0BAA0B,GAAG,IAAI,CAAQ;IACtD,oBAAoB,UAAS;IAC7B,cAAc,UAAS;IACvB,gBAAgB,UAAS;IACzB,WAAW,EAAE;QAAE,EAAE,EAAE,OAAO,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAQ;gBAEtC,QAAQ,EAAE,wBAAwB,EAAmB,GAAG,EAAE,iBAAiB;IAEjG,WAAW,CAAC,OAAO,EAAE,aAAa,GAAG,IAAI;IAMhD,+EAA+E;IAC/E,IAAW,oBAAoB,IAAI,2BAA2B,EAAE,CAE/D;IAED,IAAW,YAAY,IAAI,MAAM,CAEhC;IAEM,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAIxC,eAAe,CAAC,SAAS,EAAE,2BAA2B,GAAG,IAAI;IAIvD,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAyB9B,cAAc,CAAC,SAAS,EAAE,0BAA0B,CAAC,WAAW,CAAC,GAAG,MAAM;IAQ1E,aAAa,CAAC,GAAG,EAAE,0BAA0B,EAAE,KAAK,EAAE,KAAK,GAAG,IAAI;IAQlE,cAAc,CAAC,GAAG,EAAE,0BAA0B,EAAE,KAAK,EAAE,KAAK,GAAG,IAAI;IAQ1E,IAAW,cAAc,IAAI,MAAM,CAIlC;IAEY,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAQ3B,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;YASzC,SAAS;IAiBvB,OAAO,CAAC,UAAU;yCAxIT,4BAA4B;2CAA5B,4BAA4B;CA2IxC"}