@memberjunction/ng-dashboards 5.32.0 → 5.33.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.
Files changed (69) hide show
  1. package/dist/Admin/admin-data-schema.component.d.ts +16 -0
  2. package/dist/Admin/admin-data-schema.component.d.ts.map +1 -0
  3. package/dist/Admin/admin-data-schema.component.js +136 -0
  4. package/dist/Admin/admin-data-schema.component.js.map +1 -0
  5. package/dist/Admin/admin-dev-tools-resource.component.d.ts +14 -0
  6. package/dist/Admin/admin-dev-tools-resource.component.d.ts.map +1 -0
  7. package/dist/Admin/admin-dev-tools-resource.component.js +162 -0
  8. package/dist/Admin/admin-dev-tools-resource.component.js.map +1 -0
  9. package/dist/Admin/admin-identity-access.component.d.ts +15 -0
  10. package/dist/Admin/admin-identity-access.component.d.ts.map +1 -0
  11. package/dist/Admin/admin-identity-access.component.js +156 -0
  12. package/dist/Admin/admin-identity-access.component.js.map +1 -0
  13. package/dist/Admin/admin-monitoring.component.d.ts +15 -0
  14. package/dist/Admin/admin-monitoring.component.d.ts.map +1 -0
  15. package/dist/Admin/admin-monitoring.component.js +130 -0
  16. package/dist/Admin/admin-monitoring.component.js.map +1 -0
  17. package/dist/Admin/base-admin-container.component.d.ts +80 -0
  18. package/dist/Admin/base-admin-container.component.d.ts.map +1 -0
  19. package/dist/Admin/base-admin-container.component.js +198 -0
  20. package/dist/Admin/base-admin-container.component.js.map +1 -0
  21. package/dist/Admin/index.d.ts +6 -0
  22. package/dist/Admin/index.d.ts.map +1 -0
  23. package/dist/Admin/index.js +6 -0
  24. package/dist/Admin/index.js.map +1 -0
  25. package/dist/DevTools/app-state-inspector.component.d.ts +53 -0
  26. package/dist/DevTools/app-state-inspector.component.d.ts.map +1 -0
  27. package/dist/DevTools/app-state-inspector.component.js +301 -0
  28. package/dist/DevTools/app-state-inspector.component.js.map +1 -0
  29. package/dist/DevTools/class-registry.component.d.ts +64 -0
  30. package/dist/DevTools/class-registry.component.d.ts.map +1 -0
  31. package/dist/DevTools/class-registry.component.js +423 -0
  32. package/dist/DevTools/class-registry.component.js.map +1 -0
  33. package/dist/DevTools/dev-tools-prefs.d.ts +21 -0
  34. package/dist/DevTools/dev-tools-prefs.d.ts.map +1 -0
  35. package/dist/DevTools/dev-tools-prefs.js +48 -0
  36. package/dist/DevTools/dev-tools-prefs.js.map +1 -0
  37. package/dist/DevTools/event-monitor.component.d.ts +78 -0
  38. package/dist/DevTools/event-monitor.component.d.ts.map +1 -0
  39. package/dist/DevTools/event-monitor.component.js +659 -0
  40. package/dist/DevTools/event-monitor.component.js.map +1 -0
  41. package/dist/DevTools/graphql-console.component.d.ts +153 -0
  42. package/dist/DevTools/graphql-console.component.d.ts.map +1 -0
  43. package/dist/DevTools/graphql-console.component.js +1463 -0
  44. package/dist/DevTools/graphql-console.component.js.map +1 -0
  45. package/dist/DevTools/index.d.ts +8 -0
  46. package/dist/DevTools/index.d.ts.map +1 -0
  47. package/dist/DevTools/index.js +8 -0
  48. package/dist/DevTools/index.js.map +1 -0
  49. package/dist/DevTools/layout-inspector.component.d.ts +42 -0
  50. package/dist/DevTools/layout-inspector.component.d.ts.map +1 -0
  51. package/dist/DevTools/layout-inspector.component.js +208 -0
  52. package/dist/DevTools/layout-inspector.component.js.map +1 -0
  53. package/dist/DevTools/lazy-module-status.component.d.ts +65 -0
  54. package/dist/DevTools/lazy-module-status.component.d.ts.map +1 -0
  55. package/dist/DevTools/lazy-module-status.component.js +388 -0
  56. package/dist/DevTools/lazy-module-status.component.js.map +1 -0
  57. package/dist/DevTools/settings-explorer.component.d.ts +55 -0
  58. package/dist/DevTools/settings-explorer.component.d.ts.map +1 -0
  59. package/dist/DevTools/settings-explorer.component.js +394 -0
  60. package/dist/DevTools/settings-explorer.component.js.map +1 -0
  61. package/dist/core-dashboards.module.d.ts +45 -34
  62. package/dist/core-dashboards.module.d.ts.map +1 -1
  63. package/dist/core-dashboards.module.js +57 -0
  64. package/dist/core-dashboards.module.js.map +1 -1
  65. package/dist/public-api.d.ts +2 -0
  66. package/dist/public-api.d.ts.map +1 -1
  67. package/dist/public-api.js +2 -0
  68. package/dist/public-api.js.map +1 -1
  69. package/package.json +52 -52
@@ -0,0 +1,198 @@
1
+ import { Directive, ChangeDetectorRef, ViewChild, ViewContainerRef, inject } from '@angular/core';
2
+ import { BaseResourceComponent, BaseDashboard } from '@memberjunction/ng-shared';
3
+ import { MJGlobal } from '@memberjunction/global';
4
+ import { DashboardEngine } from '@memberjunction/core-entities';
5
+ import * as i0 from "@angular/core";
6
+ const _c0 = ["contentHost"];
7
+ /**
8
+ * Shared base for all Admin app container resources. Subclasses declare:
9
+ * - ContainerTitle / ContainerIcon / ContainerSubtitle
10
+ * - Sections[]
11
+ * - @RegisterClass(BaseResourceComponent, '...')
12
+ *
13
+ * The base handles: left-nav state, URL deep-linking via NavigationService,
14
+ * dynamic component instantiation for both resource and dashboard sections,
15
+ * teardown on switch.
16
+ */
17
+ export class BaseAdminContainerComponent extends BaseResourceComponent {
18
+ contentHost;
19
+ ActiveSection = '';
20
+ LoadError = null;
21
+ IsLoading = false;
22
+ /**
23
+ * Cache of section.id → ComponentRef. Once a sub-section is rendered we
24
+ * keep its component alive across switches by detaching + reattaching the
25
+ * view (instead of destroying + recreating). This preserves state — Event
26
+ * Monitor's captured events, GraphQL Console's history, query inputs,
27
+ * scroll position, etc. — and avoids expensive re-init.
28
+ */
29
+ cache = new Map();
30
+ currentSectionId = null;
31
+ cdr = inject(ChangeDetectorRef);
32
+ async ngOnInit() {
33
+ super.ngOnInit();
34
+ const params = this.GetQueryParams();
35
+ const requested = params['section'];
36
+ const initial = this.findSection(requested)?.id ?? this.Sections[0]?.id ?? '';
37
+ const target = this.findSection(initial);
38
+ if (target) {
39
+ await this.selectSection(target, /*syncUrl*/ false);
40
+ }
41
+ if (!requested && this.ActiveSection) {
42
+ this.UpdateQueryParams({ section: this.ActiveSection });
43
+ }
44
+ this.NotifyLoadComplete();
45
+ }
46
+ ngOnDestroy() {
47
+ for (const ref of this.cache.values()) {
48
+ try {
49
+ ref.destroy();
50
+ }
51
+ catch { /* ignore */ }
52
+ }
53
+ this.cache.clear();
54
+ this.currentSectionId = null;
55
+ super.ngOnDestroy();
56
+ }
57
+ async GetResourceDisplayName() { return this.ContainerTitle; }
58
+ async GetResourceIconClass() { return this.ContainerIcon; }
59
+ /** Called by the framework on browser back/forward + deep-link entry. */
60
+ OnQueryParamsChanged(params, _source) {
61
+ const section = params['section'];
62
+ if (!section || section === this.ActiveSection)
63
+ return;
64
+ const target = this.findSection(section);
65
+ if (target) {
66
+ void this.selectSection(target, /*syncUrl*/ false);
67
+ }
68
+ }
69
+ async OnSectionClick(section) {
70
+ if (section.id === this.ActiveSection)
71
+ return;
72
+ await this.selectSection(section, /*syncUrl*/ true);
73
+ }
74
+ // ---------- protected ----------
75
+ async selectSection(section, syncUrl) {
76
+ this.ActiveSection = section.id;
77
+ this.IsLoading = true;
78
+ this.LoadError = null;
79
+ this.cdr.markForCheck();
80
+ if (syncUrl) {
81
+ this.UpdateQueryParams({ section: section.id });
82
+ }
83
+ try {
84
+ await this.renderSection(section);
85
+ }
86
+ catch (err) {
87
+ this.LoadError = err instanceof Error ? err.message : String(err);
88
+ }
89
+ finally {
90
+ this.IsLoading = false;
91
+ this.cdr.markForCheck();
92
+ }
93
+ }
94
+ async renderSection(section) {
95
+ // Detach (don't destroy) the currently visible sub-component so it
96
+ // stays alive in memory with its state intact.
97
+ this.detachCurrent();
98
+ // Reattach if cached
99
+ const cached = this.cache.get(section.id);
100
+ if (cached) {
101
+ this.contentHost.insert(cached.hostView);
102
+ this.currentSectionId = section.id;
103
+ return;
104
+ }
105
+ // Otherwise, create fresh
106
+ const ref = section.source.kind === 'resource'
107
+ ? await this.createResourceRef(section.source.driverClass)
108
+ : await this.createDashboardRef(section.source.dashboardName);
109
+ if (ref) {
110
+ this.applyHostSizing(ref);
111
+ this.cache.set(section.id, ref);
112
+ this.currentSectionId = section.id;
113
+ }
114
+ }
115
+ detachCurrent() {
116
+ if (!this.currentSectionId)
117
+ return;
118
+ const cur = this.cache.get(this.currentSectionId);
119
+ if (!cur)
120
+ return;
121
+ const idx = this.contentHost.indexOf(cur.hostView);
122
+ if (idx >= 0)
123
+ this.contentHost.detach(idx);
124
+ }
125
+ async createResourceRef(driverClass) {
126
+ const reg = await MJGlobal.Instance.ClassFactory.GetRegistrationAsync(BaseResourceComponent, driverClass);
127
+ if (!reg) {
128
+ this.LoadError = `Component "${driverClass}" is not registered. Make sure its module is imported.`;
129
+ return null;
130
+ }
131
+ const ref = this.contentHost.createComponent(reg.SubClass);
132
+ const instance = ref.instance;
133
+ if (this.Data)
134
+ instance.Data = this.Data;
135
+ return ref;
136
+ }
137
+ async createDashboardRef(dashboardName) {
138
+ await DashboardEngine.Instance.Config(false);
139
+ const dashboard = DashboardEngine.Instance.Dashboards.find(d => d.Name === dashboardName);
140
+ if (!dashboard) {
141
+ this.LoadError = `Dashboard "${dashboardName}" was not found in metadata.`;
142
+ return null;
143
+ }
144
+ if (dashboard.Type !== 'Code') {
145
+ this.LoadError = `Dashboard "${dashboardName}" has type "${dashboard.Type}" — only Code dashboards can be embedded here.`;
146
+ return null;
147
+ }
148
+ if (!dashboard.DriverClass) {
149
+ this.LoadError = `Dashboard "${dashboardName}" has no DriverClass set.`;
150
+ return null;
151
+ }
152
+ const reg = await MJGlobal.Instance.ClassFactory.GetRegistrationAsync(BaseDashboard, dashboard.DriverClass);
153
+ if (!reg) {
154
+ this.LoadError = `Driver class "${dashboard.DriverClass}" is not registered against BaseDashboard.`;
155
+ return null;
156
+ }
157
+ const ref = this.contentHost.createComponent(reg.SubClass);
158
+ const instance = ref.instance;
159
+ instance.Config = { dashboard, userState: undefined };
160
+ instance.Refresh();
161
+ return ref;
162
+ }
163
+ /**
164
+ * Force the rendered sub-component's host element to fill the container
165
+ * cell. Mirrors what `tab-container` does for top-level resources — many
166
+ * dashboards rely on inline `height: 100%` to bound their internal layout
167
+ * (so their own `overflow-y: auto` regions actually scroll).
168
+ */
169
+ applyHostSizing(ref) {
170
+ const hostEl = ref.hostView.rootNodes[0];
171
+ if (!hostEl || !(hostEl instanceof HTMLElement))
172
+ return;
173
+ hostEl.style.height = '100%';
174
+ hostEl.style.minHeight = '0';
175
+ hostEl.style.display = 'flex';
176
+ hostEl.style.flexDirection = 'column';
177
+ hostEl.style.flex = '1 1 auto';
178
+ }
179
+ findSection(id) {
180
+ if (!id)
181
+ return undefined;
182
+ return this.Sections.find(s => s.id === id);
183
+ }
184
+ static ɵfac = /*@__PURE__*/ (() => { let ɵBaseAdminContainerComponent_BaseFactory; return function BaseAdminContainerComponent_Factory(__ngFactoryType__) { return (ɵBaseAdminContainerComponent_BaseFactory || (ɵBaseAdminContainerComponent_BaseFactory = i0.ɵɵgetInheritedFactory(BaseAdminContainerComponent)))(__ngFactoryType__ || BaseAdminContainerComponent); }; })();
185
+ static ɵdir = /*@__PURE__*/ i0.ɵɵdefineDirective({ type: BaseAdminContainerComponent, viewQuery: function BaseAdminContainerComponent_Query(rf, ctx) { if (rf & 1) {
186
+ i0.ɵɵviewQuery(_c0, 7, ViewContainerRef);
187
+ } if (rf & 2) {
188
+ let _t;
189
+ i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.contentHost = _t.first);
190
+ } }, features: [i0.ɵɵInheritDefinitionFeature] });
191
+ }
192
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(BaseAdminContainerComponent, [{
193
+ type: Directive
194
+ }], null, { contentHost: [{
195
+ type: ViewChild,
196
+ args: ['contentHost', { read: ViewContainerRef, static: true }]
197
+ }] }); })();
198
+ //# sourceMappingURL=base-admin-container.component.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"base-admin-container.component.js","sourceRoot":"","sources":["../../src/Admin/base-admin-container.component.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n Directive,\n OnInit,\n OnDestroy,\n ChangeDetectorRef,\n ViewChild,\n ViewContainerRef,\n ComponentRef,\n Type,\n inject\n} from '@angular/core';\nimport { BaseResourceComponent, BaseDashboard } from '@memberjunction/ng-shared';\nimport { MJGlobal } from '@memberjunction/global';\nimport { DashboardEngine } from '@memberjunction/core-entities';\n\n/** A single sub-section inside an admin container's left-nav. */\nexport interface AdminSection {\n /** Stable identifier — used in the URL `?section=` deep-link. */\n id: string;\n label: string;\n icon: string;\n description: string;\n source: AdminSectionSource;\n}\n\n/**\n * How to resolve and instantiate the component for this sub-section.\n *\n * - `resource`: looks up `@RegisterClass(BaseResourceComponent, driverClass)` and renders it directly.\n * - `dashboard`: looks up the MJ Dashboard record by name, then renders its `DriverClass` via the\n * BaseDashboard registry with a synthetic `{ dashboard }` config (mirrors the shell's flow for\n * `ResourceType: \"Dashboards\"` nav items).\n */\nexport type AdminSectionSource =\n | { kind: 'resource'; driverClass: string }\n | { kind: 'dashboard'; dashboardName: string };\n\n/**\n * Shared base for all Admin app container resources. Subclasses declare:\n * - ContainerTitle / ContainerIcon / ContainerSubtitle\n * - Sections[]\n * - @RegisterClass(BaseResourceComponent, '...')\n *\n * The base handles: left-nav state, URL deep-linking via NavigationService,\n * dynamic component instantiation for both resource and dashboard sections,\n * teardown on switch.\n */\n@Directive()\nexport abstract class BaseAdminContainerComponent extends BaseResourceComponent implements OnInit, OnDestroy {\n public abstract readonly ContainerTitle: string;\n public abstract readonly ContainerIcon: string;\n public abstract readonly ContainerSubtitle: string;\n public abstract readonly Sections: AdminSection[];\n\n @ViewChild('contentHost', { read: ViewContainerRef, static: true })\n protected contentHost!: ViewContainerRef;\n\n public ActiveSection = '';\n public LoadError: string | null = null;\n public IsLoading = false;\n\n /**\n * Cache of section.id → ComponentRef. Once a sub-section is rendered we\n * keep its component alive across switches by detaching + reattaching the\n * view (instead of destroying + recreating). This preserves state — Event\n * Monitor's captured events, GraphQL Console's history, query inputs,\n * scroll position, etc. — and avoids expensive re-init.\n */\n protected cache = new Map<string, ComponentRef<unknown>>();\n protected currentSectionId: string | null = null;\n\n protected readonly cdr = inject(ChangeDetectorRef);\n\n public override async ngOnInit(): Promise<void> {\n super.ngOnInit();\n\n const params = this.GetQueryParams();\n const requested = params['section'];\n const initial = this.findSection(requested)?.id ?? this.Sections[0]?.id ?? '';\n const target = this.findSection(initial);\n if (target) {\n await this.selectSection(target, /*syncUrl*/ false);\n }\n\n if (!requested && this.ActiveSection) {\n this.UpdateQueryParams({ section: this.ActiveSection });\n }\n\n this.NotifyLoadComplete();\n }\n\n public override ngOnDestroy(): void {\n for (const ref of this.cache.values()) {\n try { ref.destroy(); } catch { /* ignore */ }\n }\n this.cache.clear();\n this.currentSectionId = null;\n super.ngOnDestroy();\n }\n\n public override async GetResourceDisplayName(): Promise<string> { return this.ContainerTitle; }\n public override async GetResourceIconClass(): Promise<string> { return this.ContainerIcon; }\n\n /** Called by the framework on browser back/forward + deep-link entry. */\n protected override OnQueryParamsChanged(params: Record<string, string>, _source: 'popstate' | 'deeplink'): void {\n const section = params['section'];\n if (!section || section === this.ActiveSection) return;\n const target = this.findSection(section);\n if (target) {\n void this.selectSection(target, /*syncUrl*/ false);\n }\n }\n\n public async OnSectionClick(section: AdminSection): Promise<void> {\n if (section.id === this.ActiveSection) return;\n await this.selectSection(section, /*syncUrl*/ true);\n }\n\n // ---------- protected ----------\n\n private async selectSection(section: AdminSection, syncUrl: boolean): Promise<void> {\n this.ActiveSection = section.id;\n this.IsLoading = true;\n this.LoadError = null;\n this.cdr.markForCheck();\n\n if (syncUrl) {\n this.UpdateQueryParams({ section: section.id });\n }\n\n try {\n await this.renderSection(section);\n } catch (err) {\n this.LoadError = err instanceof Error ? err.message : String(err);\n } finally {\n this.IsLoading = false;\n this.cdr.markForCheck();\n }\n }\n\n private async renderSection(section: AdminSection): Promise<void> {\n // Detach (don't destroy) the currently visible sub-component so it\n // stays alive in memory with its state intact.\n this.detachCurrent();\n\n // Reattach if cached\n const cached = this.cache.get(section.id);\n if (cached) {\n this.contentHost.insert(cached.hostView);\n this.currentSectionId = section.id;\n return;\n }\n\n // Otherwise, create fresh\n const ref = section.source.kind === 'resource'\n ? await this.createResourceRef(section.source.driverClass)\n : await this.createDashboardRef(section.source.dashboardName);\n\n if (ref) {\n this.applyHostSizing(ref);\n this.cache.set(section.id, ref);\n this.currentSectionId = section.id;\n }\n }\n\n private detachCurrent(): void {\n if (!this.currentSectionId) return;\n const cur = this.cache.get(this.currentSectionId);\n if (!cur) return;\n const idx = this.contentHost.indexOf(cur.hostView);\n if (idx >= 0) this.contentHost.detach(idx);\n }\n\n private async createResourceRef(driverClass: string): Promise<ComponentRef<unknown> | null> {\n const reg = await MJGlobal.Instance.ClassFactory.GetRegistrationAsync(BaseResourceComponent, driverClass);\n if (!reg) {\n this.LoadError = `Component \"${driverClass}\" is not registered. Make sure its module is imported.`;\n return null;\n }\n const ref = this.contentHost.createComponent(reg.SubClass as Type<BaseResourceComponent>);\n const instance = ref.instance as BaseResourceComponent;\n if (this.Data) instance.Data = this.Data;\n return ref;\n }\n\n private async createDashboardRef(dashboardName: string): Promise<ComponentRef<unknown> | null> {\n await DashboardEngine.Instance.Config(false);\n const dashboard = DashboardEngine.Instance.Dashboards.find(d => d.Name === dashboardName);\n if (!dashboard) {\n this.LoadError = `Dashboard \"${dashboardName}\" was not found in metadata.`;\n return null;\n }\n if (dashboard.Type !== 'Code') {\n this.LoadError = `Dashboard \"${dashboardName}\" has type \"${dashboard.Type}\" — only Code dashboards can be embedded here.`;\n return null;\n }\n if (!dashboard.DriverClass) {\n this.LoadError = `Dashboard \"${dashboardName}\" has no DriverClass set.`;\n return null;\n }\n const reg = await MJGlobal.Instance.ClassFactory.GetRegistrationAsync(BaseDashboard, dashboard.DriverClass);\n if (!reg) {\n this.LoadError = `Driver class \"${dashboard.DriverClass}\" is not registered against BaseDashboard.`;\n return null;\n }\n const ref = this.contentHost.createComponent(reg.SubClass as Type<BaseDashboard>);\n const instance = ref.instance as BaseDashboard;\n instance.Config = { dashboard, userState: undefined };\n instance.Refresh();\n return ref;\n }\n\n /**\n * Force the rendered sub-component's host element to fill the container\n * cell. Mirrors what `tab-container` does for top-level resources — many\n * dashboards rely on inline `height: 100%` to bound their internal layout\n * (so their own `overflow-y: auto` regions actually scroll).\n */\n private applyHostSizing(ref: ComponentRef<unknown>): void {\n const hostEl = (ref.hostView as unknown as { rootNodes: HTMLElement[] }).rootNodes[0];\n if (!hostEl || !(hostEl instanceof HTMLElement)) return;\n hostEl.style.height = '100%';\n hostEl.style.minHeight = '0';\n hostEl.style.display = 'flex';\n hostEl.style.flexDirection = 'column';\n hostEl.style.flex = '1 1 auto';\n }\n\n private findSection(id: string | undefined | null): AdminSection | undefined {\n if (!id) return undefined;\n return this.Sections.find(s => s.id === id);\n }\n}\n"]}
@@ -0,0 +1,6 @@
1
+ export * from './base-admin-container.component';
2
+ export * from './admin-dev-tools-resource.component';
3
+ export * from './admin-identity-access.component';
4
+ export * from './admin-data-schema.component';
5
+ export * from './admin-monitoring.component';
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/Admin/index.ts"],"names":[],"mappings":"AAAA,cAAc,kCAAkC,CAAC;AACjD,cAAc,sCAAsC,CAAC;AACrD,cAAc,mCAAmC,CAAC;AAClD,cAAc,+BAA+B,CAAC;AAC9C,cAAc,8BAA8B,CAAC"}
@@ -0,0 +1,6 @@
1
+ export * from './base-admin-container.component';
2
+ export * from './admin-dev-tools-resource.component';
3
+ export * from './admin-identity-access.component';
4
+ export * from './admin-data-schema.component';
5
+ export * from './admin-monitoring.component';
6
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/Admin/index.ts"],"names":[],"mappings":"AAAA,cAAc,kCAAkC,CAAC;AACjD,cAAc,sCAAsC,CAAC;AACrD,cAAc,mCAAmC,CAAC;AAClD,cAAc,+BAA+B,CAAC;AAC9C,cAAc,8BAA8B,CAAC","sourcesContent":["export * from './base-admin-container.component';\nexport * from './admin-dev-tools-resource.component';\nexport * from './admin-identity-access.component';\nexport * from './admin-data-schema.component';\nexport * from './admin-monitoring.component';\n"]}
@@ -0,0 +1,53 @@
1
+ import { OnInit, OnDestroy, ChangeDetectorRef } from '@angular/core';
2
+ import { BaseResourceComponent } from '@memberjunction/ng-shared';
3
+ import { WorkspaceStateManager } from '@memberjunction/ng-base-application';
4
+ import { DeveloperModeService } from '@memberjunction/ng-shared';
5
+ import * as i0 from "@angular/core";
6
+ interface InspectorSection {
7
+ id: string;
8
+ label: string;
9
+ icon: string;
10
+ enabled: boolean;
11
+ description?: string;
12
+ }
13
+ /**
14
+ * App State Inspector — read-only structured view of the Explorer's runtime
15
+ * state. Replaces the legacy "Inspect App State" user-menu item which only
16
+ * dumped to console. Lives in the Admin app's dev tools area.
17
+ */
18
+ export declare class AppStateInspectorComponent extends BaseResourceComponent implements OnInit, OnDestroy {
19
+ private cdr;
20
+ private workspace;
21
+ private devMode;
22
+ Sections: InspectorSection[];
23
+ ActiveSection: string;
24
+ StateJson: string;
25
+ SearchQuery: string;
26
+ CopyConfirmed: boolean;
27
+ LastRefreshed: Date;
28
+ constructor(cdr: ChangeDetectorRef, workspace: WorkspaceStateManager, devMode: DeveloperModeService);
29
+ ngOnInit(): void;
30
+ ngOnDestroy(): void;
31
+ GetResourceDisplayName(): Promise<string>;
32
+ GetResourceIconClass(): Promise<string>;
33
+ OnSectionClick(section: InspectorSection): void;
34
+ refresh(): void;
35
+ OnCopy(): Promise<void>;
36
+ OnDownload(): void;
37
+ get LastRefreshedLabel(): string;
38
+ get SectionLabel(): string;
39
+ private computeSectionData;
40
+ private userData;
41
+ private providerData;
42
+ private workspaceData;
43
+ private appData;
44
+ private devData;
45
+ private browserData;
46
+ private jsonReplacer;
47
+ private readMemory;
48
+ private timestampSlug;
49
+ static ɵfac: i0.ɵɵFactoryDeclaration<AppStateInspectorComponent, never>;
50
+ static ɵcmp: i0.ɵɵComponentDeclaration<AppStateInspectorComponent, "mj-app-state-inspector", never, {}, {}, never, never, false, never>;
51
+ }
52
+ export {};
53
+ //# sourceMappingURL=app-state-inspector.component.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"app-state-inspector.component.d.ts","sourceRoot":"","sources":["../../src/DevTools/app-state-inspector.component.ts"],"names":[],"mappings":"AAAA,OAAO,EAAa,MAAM,EAAE,SAAS,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAChF,OAAO,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AAIlE,OAAO,EAAE,qBAAqB,EAAE,MAAM,qCAAqC,CAAC;AAC5E,OAAO,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;;AAEjE,UAAU,gBAAgB;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,OAAO,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;;;GAIG;AACH,qBAOa,0BAA2B,SAAQ,qBAAsB,YAAW,MAAM,EAAE,SAAS;IAkB1F,OAAO,CAAC,GAAG;IACX,OAAO,CAAC,SAAS;IACjB,OAAO,CAAC,OAAO;IAlBZ,QAAQ,EAAE,gBAAgB,EAAE,CAOjC;IAEK,aAAa,SAAU;IACvB,SAAS,SAAQ;IACjB,WAAW,SAAM;IACjB,aAAa,UAAS;IACtB,aAAa,OAAc;gBAGtB,GAAG,EAAE,iBAAiB,EACtB,SAAS,EAAE,qBAAqB,EAChC,OAAO,EAAE,oBAAoB;IAKlC,QAAQ,IAAI,IAAI;IASP,WAAW,IAAI,IAAI;IAKb,sBAAsB,IAAI,OAAO,CAAC,MAAM,CAAC;IACzC,oBAAoB,IAAI,OAAO,CAAC,MAAM,CAAC;IAEtD,cAAc,CAAC,OAAO,EAAE,gBAAgB,GAAG,IAAI;IAO/C,OAAO,IAAI,IAAI;IAMT,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;IAc7B,UAAU,IAAI,IAAI;IAUzB,IAAW,kBAAkB,IAAI,MAAM,CAEtC;IAED,IAAW,YAAY,IAAI,MAAM,CAEhC;IAID,OAAO,CAAC,kBAAkB;IAY1B,OAAO,CAAC,QAAQ;IAiBhB,OAAO,CAAC,YAAY;IAiBpB,OAAO,CAAC,aAAa;IAKrB,OAAO,CAAC,OAAO;IAWf,OAAO,CAAC,OAAO;IAQf,OAAO,CAAC,WAAW;IAenB,OAAO,CAAC,YAAY;IAQpB,OAAO,CAAC,UAAU;IAQlB,OAAO,CAAC,aAAa;yCA9LZ,0BAA0B;2CAA1B,0BAA0B;CAoMtC"}
@@ -0,0 +1,301 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ import { Component } from '@angular/core';
8
+ import { BaseResourceComponent } from '@memberjunction/ng-shared';
9
+ import { RegisterClass } from '@memberjunction/global';
10
+ import { DevToolsPrefs } from './dev-tools-prefs';
11
+ import { GraphQLDataProvider } from '@memberjunction/graphql-dataprovider';
12
+ import * as i0 from "@angular/core";
13
+ import * as i1 from "@memberjunction/ng-base-application";
14
+ import * as i2 from "@memberjunction/ng-shared";
15
+ import * as i3 from "@angular/forms";
16
+ import * as i4 from "@memberjunction/ng-code-editor";
17
+ const _forTrack0 = ($index, $item) => $item.id;
18
+ function AppStateInspectorComponent_For_22_Conditional_5_Template(rf, ctx) { if (rf & 1) {
19
+ i0.ɵɵelementStart(0, "div", 27);
20
+ i0.ɵɵtext(1);
21
+ i0.ɵɵelementEnd();
22
+ } if (rf & 2) {
23
+ const section_r2 = i0.ɵɵnextContext().$implicit;
24
+ i0.ɵɵadvance();
25
+ i0.ɵɵtextInterpolate(section_r2.description);
26
+ } }
27
+ function AppStateInspectorComponent_For_22_Template(rf, ctx) { if (rf & 1) {
28
+ const _r1 = i0.ɵɵgetCurrentView();
29
+ i0.ɵɵelementStart(0, "button", 23);
30
+ i0.ɵɵlistener("click", function AppStateInspectorComponent_For_22_Template_button_click_0_listener() { const section_r2 = i0.ɵɵrestoreView(_r1).$implicit; const ctx_r2 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r2.OnSectionClick(section_r2)); });
31
+ i0.ɵɵelement(1, "i", 24);
32
+ i0.ɵɵelementStart(2, "div", 25)(3, "div", 26);
33
+ i0.ɵɵtext(4);
34
+ i0.ɵɵelementEnd();
35
+ i0.ɵɵconditionalCreate(5, AppStateInspectorComponent_For_22_Conditional_5_Template, 2, 1, "div", 27);
36
+ i0.ɵɵelementEnd()();
37
+ } if (rf & 2) {
38
+ const section_r2 = ctx.$implicit;
39
+ const ctx_r2 = i0.ɵɵnextContext();
40
+ i0.ɵɵclassProp("mj-inspector__nav-item--active", ctx_r2.ActiveSection === section_r2.id);
41
+ i0.ɵɵadvance();
42
+ i0.ɵɵclassMap(section_r2.icon);
43
+ i0.ɵɵadvance(3);
44
+ i0.ɵɵtextInterpolate(section_r2.label);
45
+ i0.ɵɵadvance();
46
+ i0.ɵɵconditional(section_r2.description ? 5 : -1);
47
+ } }
48
+ /**
49
+ * App State Inspector — read-only structured view of the Explorer's runtime
50
+ * state. Replaces the legacy "Inspect App State" user-menu item which only
51
+ * dumped to console. Lives in the Admin app's dev tools area.
52
+ */
53
+ let AppStateInspectorComponent = class AppStateInspectorComponent extends BaseResourceComponent {
54
+ cdr;
55
+ workspace;
56
+ devMode;
57
+ Sections = [
58
+ { id: 'user', label: 'Current User', icon: 'fa-solid fa-user', enabled: true, description: 'Identity, roles, email' },
59
+ { id: 'provider', label: 'Provider', icon: 'fa-solid fa-plug', enabled: true, description: 'API URL, entities loaded' },
60
+ { id: 'workspace', label: 'Workspace', icon: 'fa-solid fa-table-columns', enabled: true, description: 'Tabs, layout, active state' },
61
+ { id: 'app', label: 'Active Application', icon: 'fa-solid fa-th-large', enabled: true, description: 'Current app + nav items' },
62
+ { id: 'dev', label: 'Developer Mode', icon: 'fa-solid fa-code', enabled: true, description: 'Dev mode flag, eligibility' },
63
+ { id: 'browser', label: 'Browser & Session', icon: 'fa-solid fa-window-maximize', enabled: true, description: 'User agent, viewport, URL' }
64
+ ];
65
+ ActiveSection = 'user';
66
+ StateJson = '{}';
67
+ SearchQuery = '';
68
+ CopyConfirmed = false;
69
+ LastRefreshed = new Date();
70
+ constructor(cdr, workspace, devMode) {
71
+ super();
72
+ this.cdr = cdr;
73
+ this.workspace = workspace;
74
+ this.devMode = devMode;
75
+ }
76
+ ngOnInit() {
77
+ const p = DevToolsPrefs.Get('appStateInspector');
78
+ if (p?.activeSection && this.Sections.some(s => s.id === p.activeSection)) {
79
+ this.ActiveSection = p.activeSection;
80
+ }
81
+ this.refresh();
82
+ this.NotifyLoadComplete();
83
+ }
84
+ ngOnDestroy() {
85
+ DevToolsPrefs.Save('appStateInspector', { activeSection: this.ActiveSection });
86
+ super.ngOnDestroy();
87
+ }
88
+ async GetResourceDisplayName() { return 'App State Inspector'; }
89
+ async GetResourceIconClass() { return 'fa-solid fa-magnifying-glass-chart'; }
90
+ OnSectionClick(section) {
91
+ if (this.ActiveSection === section.id)
92
+ return;
93
+ this.ActiveSection = section.id;
94
+ DevToolsPrefs.Save('appStateInspector', { activeSection: this.ActiveSection });
95
+ this.refresh();
96
+ }
97
+ refresh() {
98
+ this.StateJson = JSON.stringify(this.computeSectionData(this.ActiveSection), this.jsonReplacer, 2);
99
+ this.LastRefreshed = new Date();
100
+ this.cdr.markForCheck();
101
+ }
102
+ async OnCopy() {
103
+ try {
104
+ await navigator.clipboard.writeText(this.StateJson);
105
+ this.CopyConfirmed = true;
106
+ this.cdr.markForCheck();
107
+ setTimeout(() => {
108
+ this.CopyConfirmed = false;
109
+ this.cdr.markForCheck();
110
+ }, 1800);
111
+ }
112
+ catch {
113
+ // clipboard unavailable
114
+ }
115
+ }
116
+ OnDownload() {
117
+ const blob = new Blob([this.StateJson], { type: 'application/json' });
118
+ const url = URL.createObjectURL(blob);
119
+ const a = document.createElement('a');
120
+ a.href = url;
121
+ a.download = `mj-app-state-${this.ActiveSection}-${this.timestampSlug()}.json`;
122
+ a.click();
123
+ URL.revokeObjectURL(url);
124
+ }
125
+ get LastRefreshedLabel() {
126
+ return this.LastRefreshed.toLocaleTimeString();
127
+ }
128
+ get SectionLabel() {
129
+ return this.Sections.find(s => s.id === this.ActiveSection)?.label ?? '';
130
+ }
131
+ // ---------- private ----------
132
+ computeSectionData(section) {
133
+ switch (section) {
134
+ case 'user': return this.userData();
135
+ case 'provider': return this.providerData();
136
+ case 'workspace': return this.workspaceData();
137
+ case 'app': return this.appData();
138
+ case 'dev': return this.devData();
139
+ case 'browser': return this.browserData();
140
+ default: return {};
141
+ }
142
+ }
143
+ userData() {
144
+ const user = this.ProviderToUse?.CurrentUser;
145
+ if (!user)
146
+ return { error: 'No current user' };
147
+ return {
148
+ ID: user.ID,
149
+ Name: user.Name,
150
+ FirstName: user.FirstName,
151
+ LastName: user.LastName,
152
+ Email: user.Email,
153
+ Type: user.Type,
154
+ IsActive: user.IsActive,
155
+ LinkedRecordType: user.LinkedRecordType,
156
+ CreatedAt: user.__mj_CreatedAt,
157
+ Roles: (user.UserRoles ?? []).map(r => ({ Role: r.Role, RoleID: r.RoleID }))
158
+ };
159
+ }
160
+ providerData() {
161
+ const provider = this.ProviderToUse;
162
+ if (!provider)
163
+ return { error: 'No provider' };
164
+ const data = {
165
+ type: provider.constructor.name,
166
+ EntitiesCount: provider.Entities?.length ?? 0,
167
+ ApplicationsCount: provider.Applications?.length ?? 0,
168
+ QueriesCount: provider.Queries?.length ?? 0,
169
+ RolesCount: provider.Roles?.length ?? 0
170
+ };
171
+ if (provider instanceof GraphQLDataProvider) {
172
+ data.URL = provider.ConfigData?.URL;
173
+ data.WSURL = provider.ConfigData?.WSURL;
174
+ }
175
+ return data;
176
+ }
177
+ workspaceData() {
178
+ const config = this.workspace.GetConfiguration();
179
+ return config;
180
+ }
181
+ appData() {
182
+ const config = this.workspace.GetConfiguration();
183
+ if (!config)
184
+ return { error: 'No workspace configuration' };
185
+ const activeTab = config.tabs?.find(t => t.id === config.activeTabId);
186
+ return {
187
+ ActiveTabId: config.activeTabId,
188
+ ActiveTab: activeTab,
189
+ TabCount: config.tabs?.length ?? 0
190
+ };
191
+ }
192
+ devData() {
193
+ return {
194
+ IsEnabled: this.devMode.IsEnabled,
195
+ IsDeveloper: this.devMode.IsDeveloper,
196
+ EligibleRoles: ['Developer', 'Admin', 'System Administrator', 'Integration']
197
+ };
198
+ }
199
+ browserData() {
200
+ return {
201
+ UserAgent: navigator.userAgent,
202
+ Language: navigator.language,
203
+ Languages: navigator.languages,
204
+ Platform: navigator.platform,
205
+ Online: navigator.onLine,
206
+ Viewport: { width: window.innerWidth, height: window.innerHeight, devicePixelRatio: window.devicePixelRatio },
207
+ URL: window.location.href,
208
+ Origin: window.location.origin,
209
+ Path: window.location.pathname + window.location.search,
210
+ Memory: this.readMemory()
211
+ };
212
+ }
213
+ jsonReplacer(_key, value) {
214
+ if (value instanceof Date)
215
+ return value.toISOString();
216
+ if (value instanceof Map)
217
+ return Object.fromEntries(value.entries());
218
+ if (value instanceof Set)
219
+ return Array.from(value);
220
+ if (typeof value === 'function')
221
+ return `[Function ${value.name || 'anonymous'}]`;
222
+ return value;
223
+ }
224
+ readMemory() {
225
+ const memory = performance.memory;
226
+ if (!memory)
227
+ return undefined;
228
+ return { usedJSHeapSize: memory.usedJSHeapSize, totalJSHeapSize: memory.totalJSHeapSize };
229
+ }
230
+ timestampSlug() {
231
+ const d = new Date();
232
+ const pad = (n) => n.toString().padStart(2, '0');
233
+ return `${d.getFullYear()}${pad(d.getMonth() + 1)}${pad(d.getDate())}-${pad(d.getHours())}${pad(d.getMinutes())}${pad(d.getSeconds())}`;
234
+ }
235
+ static ɵfac = function AppStateInspectorComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || AppStateInspectorComponent)(i0.ɵɵdirectiveInject(i0.ChangeDetectorRef), i0.ɵɵdirectiveInject(i1.WorkspaceStateManager), i0.ɵɵdirectiveInject(i2.DeveloperModeService)); };
236
+ static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: AppStateInspectorComponent, selectors: [["mj-app-state-inspector"]], standalone: false, features: [i0.ɵɵInheritDefinitionFeature], decls: 31, vars: 14, consts: [[1, "mj-inspector"], [1, "mj-inspector__header"], [1, "mj-inspector__header-icon"], [1, "fa-solid", "fa-magnifying-glass-chart"], [1, "mj-inspector__header-title"], [1, "mj-inspector__header-sub"], [1, "mj-inspector__header-spacer"], [1, "mj-inspector__header-actions"], ["type", "button", "title", "Refresh", 1, "mj-inspector__btn", 3, "click"], [1, "fa-solid", "fa-rotate"], ["type", "button", 1, "mj-inspector__btn", 3, "click", "title"], [1, "fa-solid"], ["type", "button", "title", "Download as JSON", 1, "mj-inspector__btn", 3, "click"], [1, "fa-solid", "fa-download"], [1, "mj-inspector__body"], [1, "mj-inspector__sidebar"], ["type", "button", 1, "mj-inspector__nav-item", 3, "mj-inspector__nav-item--active"], [1, "mj-inspector__content"], [1, "mj-inspector__content-head"], [1, "mj-inspector__content-title"], [1, "mj-inspector__content-meta"], [1, "mj-inspector__editor"], [3, "ngModelChange", "ngModel", "language", "readonly", "lineWrapping"], ["type", "button", 1, "mj-inspector__nav-item", 3, "click"], [1, "mj-inspector__nav-icon"], [1, "mj-inspector__nav-text"], [1, "mj-inspector__nav-label"], [1, "mj-inspector__nav-desc"]], template: function AppStateInspectorComponent_Template(rf, ctx) { if (rf & 1) {
237
+ i0.ɵɵelementStart(0, "div", 0)(1, "div", 1)(2, "div", 2);
238
+ i0.ɵɵelement(3, "i", 3);
239
+ i0.ɵɵelementEnd();
240
+ i0.ɵɵelementStart(4, "div")(5, "h3", 4);
241
+ i0.ɵɵtext(6, "App State Inspector");
242
+ i0.ɵɵelementEnd();
243
+ i0.ɵɵelementStart(7, "div", 5);
244
+ i0.ɵɵtext(8, "Read-only snapshot of Explorer runtime state");
245
+ i0.ɵɵelementEnd()();
246
+ i0.ɵɵelement(9, "span", 6);
247
+ i0.ɵɵelementStart(10, "div", 7)(11, "button", 8);
248
+ i0.ɵɵlistener("click", function AppStateInspectorComponent_Template_button_click_11_listener() { return ctx.refresh(); });
249
+ i0.ɵɵelement(12, "i", 9);
250
+ i0.ɵɵtext(13, " Refresh ");
251
+ i0.ɵɵelementEnd();
252
+ i0.ɵɵelementStart(14, "button", 10);
253
+ i0.ɵɵlistener("click", function AppStateInspectorComponent_Template_button_click_14_listener() { return ctx.OnCopy(); });
254
+ i0.ɵɵelement(15, "i", 11);
255
+ i0.ɵɵtext(16);
256
+ i0.ɵɵelementEnd();
257
+ i0.ɵɵelementStart(17, "button", 12);
258
+ i0.ɵɵlistener("click", function AppStateInspectorComponent_Template_button_click_17_listener() { return ctx.OnDownload(); });
259
+ i0.ɵɵelement(18, "i", 13);
260
+ i0.ɵɵelementEnd()()();
261
+ i0.ɵɵelementStart(19, "div", 14)(20, "aside", 15);
262
+ i0.ɵɵrepeaterCreate(21, AppStateInspectorComponent_For_22_Template, 6, 6, "button", 16, _forTrack0);
263
+ i0.ɵɵelementEnd();
264
+ i0.ɵɵelementStart(23, "div", 17)(24, "div", 18)(25, "h4", 19);
265
+ i0.ɵɵtext(26);
266
+ i0.ɵɵelementEnd();
267
+ i0.ɵɵelementStart(27, "span", 20);
268
+ i0.ɵɵtext(28);
269
+ i0.ɵɵelementEnd()();
270
+ i0.ɵɵelementStart(29, "div", 21)(30, "mj-code-editor", 22);
271
+ i0.ɵɵtwoWayListener("ngModelChange", function AppStateInspectorComponent_Template_mj_code_editor_ngModelChange_30_listener($event) { i0.ɵɵtwoWayBindingSet(ctx.StateJson, $event) || (ctx.StateJson = $event); return $event; });
272
+ i0.ɵɵelementEnd()()()()();
273
+ } if (rf & 2) {
274
+ i0.ɵɵadvance(14);
275
+ i0.ɵɵclassProp("mj-inspector__btn--success", ctx.CopyConfirmed);
276
+ i0.ɵɵproperty("title", ctx.CopyConfirmed ? "Copied" : "Copy JSON");
277
+ i0.ɵɵadvance();
278
+ i0.ɵɵclassProp("fa-clipboard", !ctx.CopyConfirmed)("fa-check", ctx.CopyConfirmed);
279
+ i0.ɵɵadvance();
280
+ i0.ɵɵtextInterpolate1(" ", ctx.CopyConfirmed ? "Copied" : "Copy", " ");
281
+ i0.ɵɵadvance(5);
282
+ i0.ɵɵrepeater(ctx.Sections);
283
+ i0.ɵɵadvance(5);
284
+ i0.ɵɵtextInterpolate(ctx.SectionLabel);
285
+ i0.ɵɵadvance(2);
286
+ i0.ɵɵtextInterpolate1("Refreshed ", ctx.LastRefreshedLabel);
287
+ i0.ɵɵadvance(2);
288
+ i0.ɵɵtwoWayProperty("ngModel", ctx.StateJson);
289
+ i0.ɵɵproperty("language", "json")("readonly", true)("lineWrapping", true);
290
+ } }, dependencies: [i3.NgControlStatus, i3.NgModel, i4.CodeEditorComponent], styles: ["[_nghost-%COMP%] { display: block; height: 100%; }\n\n.mj-inspector[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n height: 100%;\n min-height: 0;\n background: var(--mj-bg-page);\n color: var(--mj-text-primary);\n font-family: inherit;\n}\n\n\n\n.mj-inspector__header[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 14px;\n padding: 14px 20px;\n border-bottom: 1px solid var(--mj-border-default);\n background: var(--mj-bg-surface);\n flex-shrink: 0;\n}\n.mj-inspector__header-icon[_ngcontent-%COMP%] {\n width: 36px; height: 36px;\n border-radius: 9px;\n background: linear-gradient(135deg, #264FAF 0%, #0076b6 100%);\n color: white;\n display: flex; align-items: center; justify-content: center;\n font-size: 15px;\n flex-shrink: 0;\n}\n.mj-inspector__header-title[_ngcontent-%COMP%] {\n margin: 0;\n font-size: 15px;\n font-weight: 600;\n color: var(--mj-text-primary);\n letter-spacing: -0.2px;\n}\n.mj-inspector__header-sub[_ngcontent-%COMP%] {\n font-size: 11.5px;\n color: var(--mj-text-muted);\n margin-top: 2px;\n}\n.mj-inspector__header-spacer[_ngcontent-%COMP%] { flex: 1; }\n.mj-inspector__header-actions[_ngcontent-%COMP%] {\n display: flex; gap: 6px;\n}\n.mj-inspector__btn[_ngcontent-%COMP%] {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 7px 12px;\n border-radius: 7px;\n border: 1px solid var(--mj-border-default);\n background: var(--mj-bg-surface);\n color: var(--mj-text-secondary);\n font-size: 12px;\n font-weight: 500;\n cursor: pointer;\n font-family: inherit;\n transition: all 0.15s;\n}\n.mj-inspector__btn[_ngcontent-%COMP%]:hover {\n background: var(--mj-bg-surface-hover);\n border-color: var(--mj-brand-primary);\n color: var(--mj-brand-primary);\n}\n.mj-inspector__btn--success[_ngcontent-%COMP%] {\n color: var(--mj-status-success);\n border-color: color-mix(in srgb, var(--mj-status-success) 30%, var(--mj-border-default));\n}\n\n\n\n.mj-inspector__body[_ngcontent-%COMP%] {\n display: flex;\n flex: 1;\n min-height: 0;\n}\n.mj-inspector__sidebar[_ngcontent-%COMP%] {\n width: 220px;\n border-right: 1px solid var(--mj-border-default);\n background: var(--mj-bg-surface);\n overflow-y: auto;\n flex-shrink: 0;\n padding: 8px;\n}\n.mj-inspector__nav-item[_ngcontent-%COMP%] {\n display: flex;\n align-items: flex-start;\n gap: 10px;\n padding: 10px 12px;\n border-radius: 8px;\n cursor: pointer;\n transition: all 0.12s;\n margin-bottom: 2px;\n font-family: inherit;\n border: none;\n background: transparent;\n color: var(--mj-text-secondary);\n text-align: left;\n width: 100%;\n}\n.mj-inspector__nav-item[_ngcontent-%COMP%]:hover {\n background: var(--mj-bg-surface-hover);\n color: var(--mj-text-primary);\n}\n.mj-inspector__nav-item--active[_ngcontent-%COMP%] {\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, transparent);\n color: var(--mj-brand-primary);\n}\n.mj-inspector__nav-item--active[_ngcontent-%COMP%]:hover {\n background: color-mix(in srgb, var(--mj-brand-primary) 14%, transparent);\n}\n.mj-inspector__nav-icon[_ngcontent-%COMP%] {\n width: 18px;\n text-align: center;\n font-size: 13px;\n margin-top: 1px;\n flex-shrink: 0;\n color: inherit;\n}\n.mj-inspector__nav-text[_ngcontent-%COMP%] { flex: 1; min-width: 0; }\n.mj-inspector__nav-label[_ngcontent-%COMP%] {\n font-size: 13px;\n font-weight: 500;\n line-height: 1.2;\n}\n.mj-inspector__nav-desc[_ngcontent-%COMP%] {\n font-size: 11px;\n color: var(--mj-text-muted);\n margin-top: 3px;\n line-height: 1.3;\n}\n.mj-inspector__nav-item--active[_ngcontent-%COMP%] .mj-inspector__nav-desc[_ngcontent-%COMP%] {\n color: color-mix(in srgb, var(--mj-brand-primary) 70%, var(--mj-text-muted));\n}\n\n\n\n.mj-inspector__content[_ngcontent-%COMP%] {\n flex: 1;\n min-width: 0;\n display: flex;\n flex-direction: column;\n background: var(--mj-bg-page);\n}\n.mj-inspector__content-head[_ngcontent-%COMP%] {\n padding: 12px 20px;\n border-bottom: 1px solid var(--mj-border-subtle);\n display: flex;\n align-items: center;\n justify-content: space-between;\n background: var(--mj-bg-surface-card);\n}\n.mj-inspector__content-title[_ngcontent-%COMP%] {\n margin: 0;\n font-size: 13px;\n font-weight: 600;\n color: var(--mj-text-primary);\n}\n.mj-inspector__content-meta[_ngcontent-%COMP%] {\n font-size: 11px;\n color: var(--mj-text-muted);\n}\n.mj-inspector__editor[_ngcontent-%COMP%] {\n flex: 1;\n min-height: 0;\n display: flex;\n flex-direction: column;\n overflow: hidden;\n}\n.mj-inspector__editor[_ngcontent-%COMP%] mj-code-editor[_ngcontent-%COMP%] {\n flex: 1;\n min-height: 0;\n display: block;\n}\n\n\n\n.mj-inspector__empty[_ngcontent-%COMP%] {\n flex: 1;\n display: flex;\n align-items: center;\n justify-content: center;\n color: var(--mj-text-muted);\n font-size: 13px;\n}\n\n@media (max-width: 700px) {\n .mj-inspector__body[_ngcontent-%COMP%] { flex-direction: column; }\n .mj-inspector__sidebar[_ngcontent-%COMP%] {\n width: 100%;\n height: auto;\n max-height: 200px;\n border-right: none;\n border-bottom: 1px solid var(--mj-border-default);\n display: flex;\n flex-wrap: wrap;\n gap: 4px;\n }\n .mj-inspector__nav-item[_ngcontent-%COMP%] { flex: 1 1 calc(50% - 8px); margin-bottom: 0; }\n .mj-inspector__nav-desc[_ngcontent-%COMP%] { display: none; }\n .mj-inspector__header[_ngcontent-%COMP%] { padding: 12px 14px; gap: 10px; }\n}"] });
291
+ };
292
+ AppStateInspectorComponent = __decorate([
293
+ RegisterClass(BaseResourceComponent, 'AppStateInspector')
294
+ ], AppStateInspectorComponent);
295
+ export { AppStateInspectorComponent };
296
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(AppStateInspectorComponent, [{
297
+ type: Component,
298
+ args: [{ standalone: false, selector: 'mj-app-state-inspector', template: "<div class=\"mj-inspector\">\n <div class=\"mj-inspector__header\">\n <div class=\"mj-inspector__header-icon\"><i class=\"fa-solid fa-magnifying-glass-chart\"></i></div>\n <div>\n <h3 class=\"mj-inspector__header-title\">App State Inspector</h3>\n <div class=\"mj-inspector__header-sub\">Read-only snapshot of Explorer runtime state</div>\n </div>\n <span class=\"mj-inspector__header-spacer\"></span>\n <div class=\"mj-inspector__header-actions\">\n <button class=\"mj-inspector__btn\" type=\"button\" (click)=\"refresh()\" title=\"Refresh\">\n <i class=\"fa-solid fa-rotate\"></i> Refresh\n </button>\n <button class=\"mj-inspector__btn\"\n [class.mj-inspector__btn--success]=\"CopyConfirmed\"\n type=\"button\" (click)=\"OnCopy()\"\n [title]=\"CopyConfirmed ? 'Copied' : 'Copy JSON'\">\n <i class=\"fa-solid\" [class.fa-clipboard]=\"!CopyConfirmed\" [class.fa-check]=\"CopyConfirmed\"></i>\n {{ CopyConfirmed ? 'Copied' : 'Copy' }}\n </button>\n <button class=\"mj-inspector__btn\" type=\"button\" (click)=\"OnDownload()\" title=\"Download as JSON\">\n <i class=\"fa-solid fa-download\"></i>\n </button>\n </div>\n </div>\n\n <div class=\"mj-inspector__body\">\n <aside class=\"mj-inspector__sidebar\">\n @for (section of Sections; track section.id) {\n <button type=\"button\"\n class=\"mj-inspector__nav-item\"\n [class.mj-inspector__nav-item--active]=\"ActiveSection === section.id\"\n (click)=\"OnSectionClick(section)\">\n <i class=\"mj-inspector__nav-icon\" [class]=\"section.icon\"></i>\n <div class=\"mj-inspector__nav-text\">\n <div class=\"mj-inspector__nav-label\">{{ section.label }}</div>\n @if (section.description) {\n <div class=\"mj-inspector__nav-desc\">{{ section.description }}</div>\n }\n </div>\n </button>\n }\n </aside>\n\n <div class=\"mj-inspector__content\">\n <div class=\"mj-inspector__content-head\">\n <h4 class=\"mj-inspector__content-title\">{{ SectionLabel }}</h4>\n <span class=\"mj-inspector__content-meta\">Refreshed {{ LastRefreshedLabel }}</span>\n </div>\n <div class=\"mj-inspector__editor\">\n <mj-code-editor\n [(ngModel)]=\"StateJson\"\n [language]=\"'json'\"\n [readonly]=\"true\"\n [lineWrapping]=\"true\">\n </mj-code-editor>\n </div>\n </div>\n </div>\n</div>\n", styles: [":host { display: block; height: 100%; }\n\n.mj-inspector {\n display: flex;\n flex-direction: column;\n height: 100%;\n min-height: 0;\n background: var(--mj-bg-page);\n color: var(--mj-text-primary);\n font-family: inherit;\n}\n\n/* Header */\n.mj-inspector__header {\n display: flex;\n align-items: center;\n gap: 14px;\n padding: 14px 20px;\n border-bottom: 1px solid var(--mj-border-default);\n background: var(--mj-bg-surface);\n flex-shrink: 0;\n}\n.mj-inspector__header-icon {\n width: 36px; height: 36px;\n border-radius: 9px;\n background: linear-gradient(135deg, #264FAF 0%, #0076b6 100%);\n color: white;\n display: flex; align-items: center; justify-content: center;\n font-size: 15px;\n flex-shrink: 0;\n}\n.mj-inspector__header-title {\n margin: 0;\n font-size: 15px;\n font-weight: 600;\n color: var(--mj-text-primary);\n letter-spacing: -0.2px;\n}\n.mj-inspector__header-sub {\n font-size: 11.5px;\n color: var(--mj-text-muted);\n margin-top: 2px;\n}\n.mj-inspector__header-spacer { flex: 1; }\n.mj-inspector__header-actions {\n display: flex; gap: 6px;\n}\n.mj-inspector__btn {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 7px 12px;\n border-radius: 7px;\n border: 1px solid var(--mj-border-default);\n background: var(--mj-bg-surface);\n color: var(--mj-text-secondary);\n font-size: 12px;\n font-weight: 500;\n cursor: pointer;\n font-family: inherit;\n transition: all 0.15s;\n}\n.mj-inspector__btn:hover {\n background: var(--mj-bg-surface-hover);\n border-color: var(--mj-brand-primary);\n color: var(--mj-brand-primary);\n}\n.mj-inspector__btn--success {\n color: var(--mj-status-success);\n border-color: color-mix(in srgb, var(--mj-status-success) 30%, var(--mj-border-default));\n}\n\n/* Layout: sidebar + content */\n.mj-inspector__body {\n display: flex;\n flex: 1;\n min-height: 0;\n}\n.mj-inspector__sidebar {\n width: 220px;\n border-right: 1px solid var(--mj-border-default);\n background: var(--mj-bg-surface);\n overflow-y: auto;\n flex-shrink: 0;\n padding: 8px;\n}\n.mj-inspector__nav-item {\n display: flex;\n align-items: flex-start;\n gap: 10px;\n padding: 10px 12px;\n border-radius: 8px;\n cursor: pointer;\n transition: all 0.12s;\n margin-bottom: 2px;\n font-family: inherit;\n border: none;\n background: transparent;\n color: var(--mj-text-secondary);\n text-align: left;\n width: 100%;\n}\n.mj-inspector__nav-item:hover {\n background: var(--mj-bg-surface-hover);\n color: var(--mj-text-primary);\n}\n.mj-inspector__nav-item--active {\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, transparent);\n color: var(--mj-brand-primary);\n}\n.mj-inspector__nav-item--active:hover {\n background: color-mix(in srgb, var(--mj-brand-primary) 14%, transparent);\n}\n.mj-inspector__nav-icon {\n width: 18px;\n text-align: center;\n font-size: 13px;\n margin-top: 1px;\n flex-shrink: 0;\n color: inherit;\n}\n.mj-inspector__nav-text { flex: 1; min-width: 0; }\n.mj-inspector__nav-label {\n font-size: 13px;\n font-weight: 500;\n line-height: 1.2;\n}\n.mj-inspector__nav-desc {\n font-size: 11px;\n color: var(--mj-text-muted);\n margin-top: 3px;\n line-height: 1.3;\n}\n.mj-inspector__nav-item--active .mj-inspector__nav-desc {\n color: color-mix(in srgb, var(--mj-brand-primary) 70%, var(--mj-text-muted));\n}\n\n/* Content area */\n.mj-inspector__content {\n flex: 1;\n min-width: 0;\n display: flex;\n flex-direction: column;\n background: var(--mj-bg-page);\n}\n.mj-inspector__content-head {\n padding: 12px 20px;\n border-bottom: 1px solid var(--mj-border-subtle);\n display: flex;\n align-items: center;\n justify-content: space-between;\n background: var(--mj-bg-surface-card);\n}\n.mj-inspector__content-title {\n margin: 0;\n font-size: 13px;\n font-weight: 600;\n color: var(--mj-text-primary);\n}\n.mj-inspector__content-meta {\n font-size: 11px;\n color: var(--mj-text-muted);\n}\n.mj-inspector__editor {\n flex: 1;\n min-height: 0;\n display: flex;\n flex-direction: column;\n overflow: hidden;\n}\n.mj-inspector__editor mj-code-editor {\n flex: 1;\n min-height: 0;\n display: block;\n}\n\n/* Empty state */\n.mj-inspector__empty {\n flex: 1;\n display: flex;\n align-items: center;\n justify-content: center;\n color: var(--mj-text-muted);\n font-size: 13px;\n}\n\n@media (max-width: 700px) {\n .mj-inspector__body { flex-direction: column; }\n .mj-inspector__sidebar {\n width: 100%;\n height: auto;\n max-height: 200px;\n border-right: none;\n border-bottom: 1px solid var(--mj-border-default);\n display: flex;\n flex-wrap: wrap;\n gap: 4px;\n }\n .mj-inspector__nav-item { flex: 1 1 calc(50% - 8px); margin-bottom: 0; }\n .mj-inspector__nav-desc { display: none; }\n .mj-inspector__header { padding: 12px 14px; gap: 10px; }\n}\n"] }]
299
+ }], () => [{ type: i0.ChangeDetectorRef }, { type: i1.WorkspaceStateManager }, { type: i2.DeveloperModeService }], null); })();
300
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(AppStateInspectorComponent, { className: "AppStateInspectorComponent", filePath: "src/DevTools/app-state-inspector.component.ts", lineNumber: 29 }); })();
301
+ //# sourceMappingURL=app-state-inspector.component.js.map