@memberjunction/ng-dashboards 2.121.0 → 2.122.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/AI/components/agents/agent-configuration.component.d.ts +23 -11
- package/dist/AI/components/agents/agent-configuration.component.d.ts.map +1 -1
- package/dist/AI/components/agents/agent-configuration.component.js +122 -95
- package/dist/AI/components/agents/agent-configuration.component.js.map +1 -1
- package/dist/AI/components/agents/agent-editor.component.js +88 -90
- package/dist/AI/components/agents/agent-editor.component.js.map +1 -1
- package/dist/AI/components/agents/agent-filter-panel.component.js +2 -2
- package/dist/AI/components/execution-monitoring.component.d.ts +23 -10
- package/dist/AI/components/execution-monitoring.component.d.ts.map +1 -1
- package/dist/AI/components/execution-monitoring.component.js +143 -124
- package/dist/AI/components/execution-monitoring.component.js.map +1 -1
- package/dist/AI/components/models/model-management-v2.component.d.ts +17 -13
- package/dist/AI/components/models/model-management-v2.component.d.ts.map +1 -1
- package/dist/AI/components/models/model-management-v2.component.js +248 -266
- package/dist/AI/components/models/model-management-v2.component.js.map +1 -1
- package/dist/AI/components/prompts/model-prompt-priority-matrix.component.js +76 -78
- package/dist/AI/components/prompts/model-prompt-priority-matrix.component.js.map +1 -1
- package/dist/AI/components/prompts/prompt-filter-panel.component.js +2 -2
- package/dist/AI/components/prompts/prompt-management-v2.component.d.ts +17 -15
- package/dist/AI/components/prompts/prompt-management-v2.component.d.ts.map +1 -1
- package/dist/AI/components/prompts/prompt-management-v2.component.js +372 -397
- package/dist/AI/components/prompts/prompt-management-v2.component.js.map +1 -1
- package/dist/AI/components/prompts/prompt-version-control.component.js +100 -102
- package/dist/AI/components/prompts/prompt-version-control.component.js.map +1 -1
- package/dist/AI/components/system/system-config-filter-panel.component.js +2 -2
- package/dist/AI/components/system/system-configuration.component.d.ts +17 -10
- package/dist/AI/components/system/system-configuration.component.d.ts.map +1 -1
- package/dist/AI/components/system/system-configuration.component.js +82 -61
- package/dist/AI/components/system/system-configuration.component.js.map +1 -1
- package/dist/AI/components/widgets/kpi-card.component.d.ts.map +1 -1
- package/dist/AI/components/widgets/kpi-card.component.js +11 -7
- package/dist/AI/components/widgets/kpi-card.component.js.map +1 -1
- package/dist/AI/index.d.ts +4 -0
- package/dist/AI/index.d.ts.map +1 -1
- package/dist/AI/index.js +6 -1
- package/dist/AI/index.js.map +1 -1
- package/dist/Actions/components/actions-list-view.component.js +9 -9
- package/dist/Actions/components/actions-list-view.component.js.map +1 -1
- package/dist/Actions/components/actions-overview.component.d.ts +16 -13
- package/dist/Actions/components/actions-overview.component.d.ts.map +1 -1
- package/dist/Actions/components/actions-overview.component.js +62 -48
- package/dist/Actions/components/actions-overview.component.js.map +1 -1
- package/dist/Actions/components/categories-list-view.component.js +9 -9
- package/dist/Actions/components/categories-list-view.component.js.map +1 -1
- package/dist/Actions/components/code-management.component.d.ts +17 -7
- package/dist/Actions/components/code-management.component.d.ts.map +1 -1
- package/dist/Actions/components/code-management.component.js +45 -12
- package/dist/Actions/components/code-management.component.js.map +1 -1
- package/dist/Actions/components/entity-integration.component.d.ts +17 -7
- package/dist/Actions/components/entity-integration.component.d.ts.map +1 -1
- package/dist/Actions/components/entity-integration.component.js +45 -12
- package/dist/Actions/components/entity-integration.component.js.map +1 -1
- package/dist/Actions/components/execution-monitoring.component.d.ts +16 -10
- package/dist/Actions/components/execution-monitoring.component.d.ts.map +1 -1
- package/dist/Actions/components/execution-monitoring.component.js +56 -30
- package/dist/Actions/components/execution-monitoring.component.js.map +1 -1
- package/dist/Actions/components/scheduled-actions.component.d.ts +17 -7
- package/dist/Actions/components/scheduled-actions.component.d.ts.map +1 -1
- package/dist/Actions/components/scheduled-actions.component.js +45 -12
- package/dist/Actions/components/scheduled-actions.component.js.map +1 -1
- package/dist/Actions/components/security-permissions.component.d.ts +17 -7
- package/dist/Actions/components/security-permissions.component.d.ts.map +1 -1
- package/dist/Actions/components/security-permissions.component.js +45 -12
- package/dist/Actions/components/security-permissions.component.js.map +1 -1
- package/dist/Actions/index.d.ts +6 -1
- package/dist/Actions/index.d.ts.map +1 -1
- package/dist/Actions/index.js +9 -1
- package/dist/Actions/index.js.map +1 -1
- package/dist/ComponentStudio/component-studio-dashboard.component.d.ts +1 -1
- package/dist/ComponentStudio/component-studio-dashboard.component.js +8 -8
- package/dist/ComponentStudio/component-studio-dashboard.component.js.map +1 -1
- package/dist/ComponentStudio/components/artifact-load-dialog.component.js +52 -57
- package/dist/ComponentStudio/components/artifact-load-dialog.component.js.map +1 -1
- package/dist/ComponentStudio/components/artifact-selection-dialog.component.js +8 -9
- package/dist/ComponentStudio/components/artifact-selection-dialog.component.js.map +1 -1
- package/dist/DataExplorer/components/navigation-panel/navigation-panel.component.d.ts +107 -0
- package/dist/DataExplorer/components/navigation-panel/navigation-panel.component.d.ts.map +1 -0
- package/dist/DataExplorer/components/navigation-panel/navigation-panel.component.js +553 -0
- package/dist/DataExplorer/components/navigation-panel/navigation-panel.component.js.map +1 -0
- package/dist/DataExplorer/components/view-config-panel/view-config-panel.component.d.ts +179 -0
- package/dist/DataExplorer/components/view-config-panel/view-config-panel.component.d.ts.map +1 -0
- package/dist/DataExplorer/components/view-config-panel/view-config-panel.component.js +814 -0
- package/dist/DataExplorer/components/view-config-panel/view-config-panel.component.js.map +1 -0
- package/dist/DataExplorer/components/view-selector/view-selector.component.d.ts +151 -0
- package/dist/DataExplorer/components/view-selector/view-selector.component.d.ts.map +1 -0
- package/dist/DataExplorer/components/view-selector/view-selector.component.js +480 -0
- package/dist/DataExplorer/components/view-selector/view-selector.component.js.map +1 -0
- package/dist/DataExplorer/data-explorer-dashboard.component.d.ts +439 -0
- package/dist/DataExplorer/data-explorer-dashboard.component.d.ts.map +1 -0
- package/dist/DataExplorer/data-explorer-dashboard.component.js +2129 -0
- package/dist/DataExplorer/data-explorer-dashboard.component.js.map +1 -0
- package/dist/DataExplorer/index.d.ts +5 -0
- package/dist/DataExplorer/index.d.ts.map +1 -0
- package/dist/DataExplorer/index.js +10 -0
- package/dist/DataExplorer/index.js.map +1 -0
- package/dist/DataExplorer/models/explorer-state.interface.d.ts +183 -0
- package/dist/DataExplorer/models/explorer-state.interface.d.ts.map +1 -0
- package/dist/DataExplorer/models/explorer-state.interface.js +31 -0
- package/dist/DataExplorer/models/explorer-state.interface.js.map +1 -0
- package/dist/DataExplorer/services/explorer-state.service.d.ts +232 -0
- package/dist/DataExplorer/services/explorer-state.service.d.ts.map +1 -0
- package/dist/DataExplorer/services/explorer-state.service.js +912 -0
- package/dist/DataExplorer/services/explorer-state.service.js.map +1 -0
- package/dist/EntityAdmin/components/entity-details.component.d.ts.map +1 -1
- package/dist/EntityAdmin/components/entity-details.component.js +11 -13
- package/dist/EntityAdmin/components/entity-details.component.js.map +1 -1
- package/dist/EntityAdmin/components/entity-filter-panel.component.js +2 -2
- package/dist/EntityAdmin/components/erd-composite.component.js +2 -2
- package/dist/EntityAdmin/components/erd-diagram.component.js +2 -2
- package/dist/EntityAdmin/entity-admin-dashboard.component.d.ts +1 -1
- package/dist/EntityAdmin/entity-admin-dashboard.component.d.ts.map +1 -1
- package/dist/EntityAdmin/entity-admin-dashboard.component.js +14 -15
- package/dist/EntityAdmin/entity-admin-dashboard.component.js.map +1 -1
- package/dist/Home/home-dashboard.component.d.ts +122 -0
- package/dist/Home/home-dashboard.component.d.ts.map +1 -0
- package/dist/Home/home-dashboard.component.js +698 -0
- package/dist/Home/home-dashboard.component.js.map +1 -0
- package/dist/Scheduling/components/index.d.ts +11 -0
- package/dist/Scheduling/components/index.d.ts.map +1 -0
- package/dist/Scheduling/components/index.js +13 -0
- package/dist/Scheduling/components/index.js.map +1 -0
- package/dist/Scheduling/components/scheduling-health-resource.component.d.ts +20 -0
- package/dist/Scheduling/components/scheduling-health-resource.component.d.ts.map +1 -0
- package/dist/Scheduling/components/scheduling-health-resource.component.js +55 -0
- package/dist/Scheduling/components/scheduling-health-resource.component.js.map +1 -0
- package/dist/Scheduling/components/scheduling-health.component.js +7 -8
- package/dist/Scheduling/components/scheduling-health.component.js.map +1 -1
- package/dist/Scheduling/components/scheduling-history-resource.component.d.ts +20 -0
- package/dist/Scheduling/components/scheduling-history-resource.component.d.ts.map +1 -0
- package/dist/Scheduling/components/scheduling-history-resource.component.js +55 -0
- package/dist/Scheduling/components/scheduling-history-resource.component.js.map +1 -0
- package/dist/Scheduling/components/scheduling-history.component.js +7 -8
- package/dist/Scheduling/components/scheduling-history.component.js.map +1 -1
- package/dist/Scheduling/components/scheduling-jobs-resource.component.d.ts +20 -0
- package/dist/Scheduling/components/scheduling-jobs-resource.component.d.ts.map +1 -0
- package/dist/Scheduling/components/scheduling-jobs-resource.component.js +55 -0
- package/dist/Scheduling/components/scheduling-jobs-resource.component.js.map +1 -0
- package/dist/Scheduling/components/scheduling-jobs.component.js +7 -8
- package/dist/Scheduling/components/scheduling-jobs.component.js.map +1 -1
- package/dist/Scheduling/components/scheduling-monitor-resource.component.d.ts +20 -0
- package/dist/Scheduling/components/scheduling-monitor-resource.component.d.ts.map +1 -0
- package/dist/Scheduling/components/scheduling-monitor-resource.component.js +55 -0
- package/dist/Scheduling/components/scheduling-monitor-resource.component.js.map +1 -0
- package/dist/Scheduling/components/scheduling-monitoring.component.js +7 -8
- package/dist/Scheduling/components/scheduling-monitoring.component.js.map +1 -1
- package/dist/Scheduling/components/scheduling-types-resource.component.d.ts +20 -0
- package/dist/Scheduling/components/scheduling-types-resource.component.d.ts.map +1 -0
- package/dist/Scheduling/components/scheduling-types-resource.component.js +55 -0
- package/dist/Scheduling/components/scheduling-types-resource.component.js.map +1 -0
- package/dist/Scheduling/components/scheduling-types.component.js +7 -8
- package/dist/Scheduling/components/scheduling-types.component.js.map +1 -1
- package/dist/Scheduling/scheduling-dashboard.component.d.ts +1 -1
- package/dist/Scheduling/scheduling-dashboard.component.js +3 -3
- package/dist/Testing/components/index.d.ts +11 -0
- package/dist/Testing/components/index.d.ts.map +1 -0
- package/dist/Testing/components/index.js +13 -0
- package/dist/Testing/components/index.js.map +1 -0
- package/dist/Testing/components/testing-analytics-resource.component.d.ts +20 -0
- package/dist/Testing/components/testing-analytics-resource.component.d.ts.map +1 -0
- package/dist/Testing/components/testing-analytics-resource.component.js +55 -0
- package/dist/Testing/components/testing-analytics-resource.component.js.map +1 -0
- package/dist/Testing/components/testing-execution-resource.component.d.ts +20 -0
- package/dist/Testing/components/testing-execution-resource.component.d.ts.map +1 -0
- package/dist/Testing/components/testing-execution-resource.component.js +55 -0
- package/dist/Testing/components/testing-execution-resource.component.js.map +1 -0
- package/dist/Testing/components/testing-execution.component.js +3 -3
- package/dist/Testing/components/testing-execution.component.js.map +1 -1
- package/dist/Testing/components/testing-feedback-resource.component.d.ts +20 -0
- package/dist/Testing/components/testing-feedback-resource.component.d.ts.map +1 -0
- package/dist/Testing/components/testing-feedback-resource.component.js +55 -0
- package/dist/Testing/components/testing-feedback-resource.component.js.map +1 -0
- package/dist/Testing/components/testing-overview-resource.component.d.ts +20 -0
- package/dist/Testing/components/testing-overview-resource.component.d.ts.map +1 -0
- package/dist/Testing/components/testing-overview-resource.component.js +55 -0
- package/dist/Testing/components/testing-overview-resource.component.js.map +1 -0
- package/dist/Testing/components/testing-version-resource.component.d.ts +20 -0
- package/dist/Testing/components/testing-version-resource.component.d.ts.map +1 -0
- package/dist/Testing/components/testing-version-resource.component.js +55 -0
- package/dist/Testing/components/testing-version-resource.component.js.map +1 -0
- package/dist/Testing/testing-dashboard.component.d.ts +1 -1
- package/dist/Testing/testing-dashboard.component.js +23 -25
- package/dist/Testing/testing-dashboard.component.js.map +1 -1
- package/dist/module.d.ts +83 -66
- package/dist/module.d.ts.map +1 -1
- package/dist/module.js +137 -19
- package/dist/module.js.map +1 -1
- package/dist/public-api.d.ts +6 -4
- package/dist/public-api.d.ts.map +1 -1
- package/dist/public-api.js +41 -13
- package/dist/public-api.js.map +1 -1
- package/package.json +26 -14
- package/dist/AI/ai-dashboard.component.d.ts +0 -62
- package/dist/AI/ai-dashboard.component.d.ts.map +0 -1
- package/dist/AI/ai-dashboard.component.js +0 -338
- package/dist/AI/ai-dashboard.component.js.map +0 -1
- package/dist/Actions/actions-management-dashboard.component.d.ts +0 -52
- package/dist/Actions/actions-management-dashboard.component.d.ts.map +0 -1
- package/dist/Actions/actions-management-dashboard.component.js +0 -308
- package/dist/Actions/actions-management-dashboard.component.js.map +0 -1
- package/dist/generic/base-dashboard.d.ts +0 -65
- package/dist/generic/base-dashboard.d.ts.map +0 -1
- package/dist/generic/base-dashboard.js +0 -74
- package/dist/generic/base-dashboard.js.map +0 -1
|
@@ -0,0 +1,2129 @@
|
|
|
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, Input, HostListener, ViewChild } from '@angular/core';
|
|
8
|
+
import { NavigationEnd } from '@angular/router';
|
|
9
|
+
import { trigger, transition, style, animate } from '@angular/animations';
|
|
10
|
+
import { Subject } from 'rxjs';
|
|
11
|
+
import { takeUntil, debounceTime, distinctUntilChanged, filter } from 'rxjs/operators';
|
|
12
|
+
import { BaseDashboard } from '@memberjunction/ng-shared';
|
|
13
|
+
import { RegisterClass } from '@memberjunction/global';
|
|
14
|
+
import { Metadata, RunView, CompositeKey } from '@memberjunction/core';
|
|
15
|
+
import { EntityViewerComponent } from '@memberjunction/ng-entity-viewer';
|
|
16
|
+
import { ViewSelectorComponent } from './components/view-selector/view-selector.component';
|
|
17
|
+
import * as i0 from "@angular/core";
|
|
18
|
+
import * as i1 from "./services/explorer-state.service";
|
|
19
|
+
import * as i2 from "@angular/router";
|
|
20
|
+
import * as i3 from "@memberjunction/ng-shared-generic";
|
|
21
|
+
import * as i4 from "@angular/forms";
|
|
22
|
+
import * as i5 from "@memberjunction/ng-entity-viewer";
|
|
23
|
+
import * as i6 from "./components/navigation-panel/navigation-panel.component";
|
|
24
|
+
import * as i7 from "./components/view-selector/view-selector.component";
|
|
25
|
+
import * as i8 from "./components/view-config-panel/view-config-panel.component";
|
|
26
|
+
import * as i9 from "@angular/common";
|
|
27
|
+
const _c0 = ["filterInput"];
|
|
28
|
+
const _forTrack0 = ($index, $item) => $item.label;
|
|
29
|
+
const _forTrack1 = ($index, $item) => $item.ID;
|
|
30
|
+
const _forTrack2 = ($index, $item) => $item.entityId + "|" + $item.recordId;
|
|
31
|
+
const _forTrack3 = ($index, $item) => $item.entityId;
|
|
32
|
+
const _forTrack4 = ($index, $item) => $item.userFavoriteId;
|
|
33
|
+
function DataExplorerDashboardComponent_Conditional_1_Template(rf, ctx) { if (rf & 1) {
|
|
34
|
+
const _r1 = i0.ɵɵgetCurrentView();
|
|
35
|
+
i0.ɵɵelementStart(0, "div", 16)(1, "mj-explorer-navigation-panel", 17);
|
|
36
|
+
i0.ɵɵlistener("entitySelected", function DataExplorerDashboardComponent_Conditional_1_Template_mj_explorer_navigation_panel_entitySelected_1_listener($event) { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.onEntitySelected($event)); })("toggleCollapse", function DataExplorerDashboardComponent_Conditional_1_Template_mj_explorer_navigation_panel_toggleCollapse_1_listener() { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.toggleNavigationPanel()); })("sectionToggled", function DataExplorerDashboardComponent_Conditional_1_Template_mj_explorer_navigation_panel_sectionToggled_1_listener($event) { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.stateService.toggleSection($event)); })("openRecord", function DataExplorerDashboardComponent_Conditional_1_Template_mj_explorer_navigation_panel_openRecord_1_listener($event) { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.onOpenRecordFromNav($event)); })("selectRecord", function DataExplorerDashboardComponent_Conditional_1_Template_mj_explorer_navigation_panel_selectRecord_1_listener($event) { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.onSelectRecordFromNav($event)); })("expandAndFocus", function DataExplorerDashboardComponent_Conditional_1_Template_mj_explorer_navigation_panel_expandAndFocus_1_listener($event) { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.onExpandAndFocus($event)); });
|
|
37
|
+
i0.ɵɵelementEnd()();
|
|
38
|
+
} if (rf & 2) {
|
|
39
|
+
const ctx_r1 = i0.ɵɵnextContext();
|
|
40
|
+
i0.ɵɵstyleProp("width", ctx_r1.state.navigationPanelCollapsed ? 48 : ctx_r1.state.navigationPanelWidth, "px");
|
|
41
|
+
i0.ɵɵclassProp("collapsed", ctx_r1.state.navigationPanelCollapsed);
|
|
42
|
+
i0.ɵɵproperty("@slideInLeft", undefined);
|
|
43
|
+
i0.ɵɵadvance();
|
|
44
|
+
i0.ɵɵproperty("entities", ctx_r1.entities)("selectedEntityName", ctx_r1.state.selectedEntityName)("favorites", ctx_r1.state.favorites)("recentItems", ctx_r1.state.recentItems)("collapsed", ctx_r1.state.navigationPanelCollapsed)("allowedEntityNames", ctx_r1.allowedEntityNames)("favoritesSectionExpanded", ctx_r1.state.favoritesSectionExpanded)("recentSectionExpanded", ctx_r1.state.recentSectionExpanded)("entitiesSectionExpanded", ctx_r1.state.entitiesSectionExpanded)("viewsSectionExpanded", ctx_r1.state.viewsSectionExpanded);
|
|
45
|
+
} }
|
|
46
|
+
function DataExplorerDashboardComponent_Conditional_3_For_2_Conditional_1_Template(rf, ctx) { if (rf & 1) {
|
|
47
|
+
i0.ɵɵelement(0, "i", 22);
|
|
48
|
+
} if (rf & 2) {
|
|
49
|
+
const crumb_r5 = i0.ɵɵnextContext().$implicit;
|
|
50
|
+
i0.ɵɵclassMap(crumb_r5.icon);
|
|
51
|
+
} }
|
|
52
|
+
function DataExplorerDashboardComponent_Conditional_3_For_2_Conditional_4_Template(rf, ctx) { if (rf & 1) {
|
|
53
|
+
i0.ɵɵelement(0, "i", 21);
|
|
54
|
+
} }
|
|
55
|
+
function DataExplorerDashboardComponent_Conditional_3_For_2_Template(rf, ctx) { if (rf & 1) {
|
|
56
|
+
const _r3 = i0.ɵɵgetCurrentView();
|
|
57
|
+
i0.ɵɵelementStart(0, "span", 18);
|
|
58
|
+
i0.ɵɵlistener("click", function DataExplorerDashboardComponent_Conditional_3_For_2_Template_span_click_0_listener() { const ctx_r3 = i0.ɵɵrestoreView(_r3); const crumb_r5 = ctx_r3.$implicit; const ɵ$index_13_r6 = ctx_r3.$index; const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.onBreadcrumbClick(crumb_r5, ɵ$index_13_r6)); });
|
|
59
|
+
i0.ɵɵtemplate(1, DataExplorerDashboardComponent_Conditional_3_For_2_Conditional_1_Template, 1, 2, "i", 19);
|
|
60
|
+
i0.ɵɵelementStart(2, "span", 20);
|
|
61
|
+
i0.ɵɵtext(3);
|
|
62
|
+
i0.ɵɵelementEnd()();
|
|
63
|
+
i0.ɵɵtemplate(4, DataExplorerDashboardComponent_Conditional_3_For_2_Conditional_4_Template, 1, 0, "i", 21);
|
|
64
|
+
} if (rf & 2) {
|
|
65
|
+
const crumb_r5 = ctx.$implicit;
|
|
66
|
+
const ɵ$index_13_r6 = ctx.$index;
|
|
67
|
+
const ɵ$count_13_r7 = ctx.$count;
|
|
68
|
+
i0.ɵɵclassProp("clickable", !(ɵ$index_13_r6 === ɵ$count_13_r7 - 1))("current", ɵ$index_13_r6 === ɵ$count_13_r7 - 1);
|
|
69
|
+
i0.ɵɵproperty("title", crumb_r5.label);
|
|
70
|
+
i0.ɵɵadvance();
|
|
71
|
+
i0.ɵɵconditional(crumb_r5.icon ? 1 : -1);
|
|
72
|
+
i0.ɵɵadvance(2);
|
|
73
|
+
i0.ɵɵtextInterpolate(crumb_r5.label);
|
|
74
|
+
i0.ɵɵadvance();
|
|
75
|
+
i0.ɵɵconditional(!(ɵ$index_13_r6 === ɵ$count_13_r7 - 1) ? 4 : -1);
|
|
76
|
+
} }
|
|
77
|
+
function DataExplorerDashboardComponent_Conditional_3_Template(rf, ctx) { if (rf & 1) {
|
|
78
|
+
i0.ɵɵelementStart(0, "div", 4);
|
|
79
|
+
i0.ɵɵrepeaterCreate(1, DataExplorerDashboardComponent_Conditional_3_For_2_Template, 5, 8, null, null, _forTrack0);
|
|
80
|
+
i0.ɵɵelementEnd();
|
|
81
|
+
} if (rf & 2) {
|
|
82
|
+
const ctx_r1 = i0.ɵɵnextContext();
|
|
83
|
+
i0.ɵɵadvance();
|
|
84
|
+
i0.ɵɵrepeater(ctx_r1.breadcrumbs);
|
|
85
|
+
} }
|
|
86
|
+
function DataExplorerDashboardComponent_Conditional_6_Conditional_3_Template(rf, ctx) { if (rf & 1) {
|
|
87
|
+
i0.ɵɵelementStart(0, "span", 25);
|
|
88
|
+
i0.ɵɵtext(1);
|
|
89
|
+
i0.ɵɵpipe(2, "number");
|
|
90
|
+
i0.ɵɵpipe(3, "number");
|
|
91
|
+
i0.ɵɵelementEnd();
|
|
92
|
+
} if (rf & 2) {
|
|
93
|
+
const ctx_r1 = i0.ɵɵnextContext(2);
|
|
94
|
+
i0.ɵɵadvance();
|
|
95
|
+
i0.ɵɵtextInterpolate2("", i0.ɵɵpipeBind1(2, 2, ctx_r1.filteredRecordCount), " of ", i0.ɵɵpipeBind1(3, 4, ctx_r1.totalRecordCount), " records");
|
|
96
|
+
} }
|
|
97
|
+
function DataExplorerDashboardComponent_Conditional_6_Conditional_4_Template(rf, ctx) { if (rf & 1) {
|
|
98
|
+
i0.ɵɵelementStart(0, "span", 25);
|
|
99
|
+
i0.ɵɵtext(1);
|
|
100
|
+
i0.ɵɵpipe(2, "number");
|
|
101
|
+
i0.ɵɵelementEnd();
|
|
102
|
+
} if (rf & 2) {
|
|
103
|
+
const ctx_r1 = i0.ɵɵnextContext(2);
|
|
104
|
+
i0.ɵɵadvance();
|
|
105
|
+
i0.ɵɵtextInterpolate1("", i0.ɵɵpipeBind1(2, 1, ctx_r1.totalRecordCount), " records");
|
|
106
|
+
} }
|
|
107
|
+
function DataExplorerDashboardComponent_Conditional_6_Template(rf, ctx) { if (rf & 1) {
|
|
108
|
+
const _r8 = i0.ɵɵgetCurrentView();
|
|
109
|
+
i0.ɵɵelement(0, "i", 23);
|
|
110
|
+
i0.ɵɵelementStart(1, "h2", 24);
|
|
111
|
+
i0.ɵɵtext(2);
|
|
112
|
+
i0.ɵɵelementEnd();
|
|
113
|
+
i0.ɵɵtemplate(3, DataExplorerDashboardComponent_Conditional_6_Conditional_3_Template, 4, 6, "span", 25)(4, DataExplorerDashboardComponent_Conditional_6_Conditional_4_Template, 3, 3, "span", 25);
|
|
114
|
+
i0.ɵɵelementStart(5, "mj-view-selector", 26);
|
|
115
|
+
i0.ɵɵlistener("viewSelected", function DataExplorerDashboardComponent_Conditional_6_Template_mj_view_selector_viewSelected_5_listener($event) { i0.ɵɵrestoreView(_r8); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.onViewSelected($event)); })("saveViewRequested", function DataExplorerDashboardComponent_Conditional_6_Template_mj_view_selector_saveViewRequested_5_listener($event) { i0.ɵɵrestoreView(_r8); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.onSaveViewRequested($event)); })("manageViewsRequested", function DataExplorerDashboardComponent_Conditional_6_Template_mj_view_selector_manageViewsRequested_5_listener() { i0.ɵɵrestoreView(_r8); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.onManageViewsRequested()); })("openInTabRequested", function DataExplorerDashboardComponent_Conditional_6_Template_mj_view_selector_openInTabRequested_5_listener($event) { i0.ɵɵrestoreView(_r8); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.onOpenInTabRequested($event)); })("configureViewRequested", function DataExplorerDashboardComponent_Conditional_6_Template_mj_view_selector_configureViewRequested_5_listener() { i0.ɵɵrestoreView(_r8); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.onConfigureViewRequested()); });
|
|
116
|
+
i0.ɵɵelementEnd();
|
|
117
|
+
} if (rf & 2) {
|
|
118
|
+
const ctx_r1 = i0.ɵɵnextContext();
|
|
119
|
+
i0.ɵɵclassMap(ctx_r1.getEntityIcon(ctx_r1.selectedEntity));
|
|
120
|
+
i0.ɵɵadvance(2);
|
|
121
|
+
i0.ɵɵtextInterpolate(ctx_r1.selectedEntity.Name);
|
|
122
|
+
i0.ɵɵadvance();
|
|
123
|
+
i0.ɵɵconditional(ctx_r1.debouncedFilterText && ctx_r1.filteredRecordCount !== ctx_r1.totalRecordCount ? 3 : 4);
|
|
124
|
+
i0.ɵɵadvance(2);
|
|
125
|
+
i0.ɵɵproperty("entity", ctx_r1.selectedEntity)("selectedViewId", ctx_r1.state.selectedViewId)("viewModified", ctx_r1.state.viewModified);
|
|
126
|
+
} }
|
|
127
|
+
function DataExplorerDashboardComponent_Conditional_7_Conditional_0_Template(rf, ctx) { if (rf & 1) {
|
|
128
|
+
i0.ɵɵelement(0, "i", 23);
|
|
129
|
+
} if (rf & 2) {
|
|
130
|
+
const ctx_r1 = i0.ɵɵnextContext(2);
|
|
131
|
+
i0.ɵɵclassMap(ctx_r1.displayIcon);
|
|
132
|
+
} }
|
|
133
|
+
function DataExplorerDashboardComponent_Conditional_7_Template(rf, ctx) { if (rf & 1) {
|
|
134
|
+
i0.ɵɵtemplate(0, DataExplorerDashboardComponent_Conditional_7_Conditional_0_Template, 1, 2, "i", 27);
|
|
135
|
+
i0.ɵɵelementStart(1, "h2", 24);
|
|
136
|
+
i0.ɵɵtext(2);
|
|
137
|
+
i0.ɵɵelementEnd();
|
|
138
|
+
i0.ɵɵelementStart(3, "span", 25);
|
|
139
|
+
i0.ɵɵtext(4);
|
|
140
|
+
i0.ɵɵelementEnd();
|
|
141
|
+
} if (rf & 2) {
|
|
142
|
+
const ctx_r1 = i0.ɵɵnextContext();
|
|
143
|
+
i0.ɵɵconditional(ctx_r1.displayIcon ? 0 : -1);
|
|
144
|
+
i0.ɵɵadvance(2);
|
|
145
|
+
i0.ɵɵtextInterpolate(ctx_r1.displayTitle);
|
|
146
|
+
i0.ɵɵadvance(2);
|
|
147
|
+
i0.ɵɵtextInterpolate1("", ctx_r1.entities.length, " entities available");
|
|
148
|
+
} }
|
|
149
|
+
function DataExplorerDashboardComponent_Conditional_9_Conditional_4_Template(rf, ctx) { if (rf & 1) {
|
|
150
|
+
const _r10 = i0.ɵɵgetCurrentView();
|
|
151
|
+
i0.ɵɵelementStart(0, "button", 31);
|
|
152
|
+
i0.ɵɵlistener("click", function DataExplorerDashboardComponent_Conditional_9_Conditional_4_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r10); const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.onSmartFilterChanged("")); });
|
|
153
|
+
i0.ɵɵelement(1, "i", 32);
|
|
154
|
+
i0.ɵɵelementEnd();
|
|
155
|
+
} }
|
|
156
|
+
function DataExplorerDashboardComponent_Conditional_9_Template(rf, ctx) { if (rf & 1) {
|
|
157
|
+
const _r9 = i0.ɵɵgetCurrentView();
|
|
158
|
+
i0.ɵɵelementStart(0, "div", 8);
|
|
159
|
+
i0.ɵɵelement(1, "i", 28);
|
|
160
|
+
i0.ɵɵelementStart(2, "input", 29, 0);
|
|
161
|
+
i0.ɵɵlistener("ngModelChange", function DataExplorerDashboardComponent_Conditional_9_Template_input_ngModelChange_2_listener($event) { i0.ɵɵrestoreView(_r9); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.onSmartFilterChanged($event)); });
|
|
162
|
+
i0.ɵɵelementEnd();
|
|
163
|
+
i0.ɵɵtemplate(4, DataExplorerDashboardComponent_Conditional_9_Conditional_4_Template, 2, 0, "button", 30);
|
|
164
|
+
i0.ɵɵelementEnd();
|
|
165
|
+
} if (rf & 2) {
|
|
166
|
+
const ctx_r1 = i0.ɵɵnextContext();
|
|
167
|
+
i0.ɵɵadvance(2);
|
|
168
|
+
i0.ɵɵproperty("ngModel", ctx_r1.state.smartFilterPrompt);
|
|
169
|
+
i0.ɵɵadvance(2);
|
|
170
|
+
i0.ɵɵconditional(ctx_r1.state.smartFilterPrompt ? 4 : -1);
|
|
171
|
+
} }
|
|
172
|
+
function DataExplorerDashboardComponent_Conditional_10_Conditional_4_Template(rf, ctx) { if (rf & 1) {
|
|
173
|
+
const _r12 = i0.ɵɵgetCurrentView();
|
|
174
|
+
i0.ɵɵelementStart(0, "button", 31);
|
|
175
|
+
i0.ɵɵlistener("click", function DataExplorerDashboardComponent_Conditional_10_Conditional_4_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r12); const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.entityFilterText = ""); });
|
|
176
|
+
i0.ɵɵelement(1, "i", 32);
|
|
177
|
+
i0.ɵɵelementEnd();
|
|
178
|
+
} }
|
|
179
|
+
function DataExplorerDashboardComponent_Conditional_10_Template(rf, ctx) { if (rf & 1) {
|
|
180
|
+
const _r11 = i0.ɵɵgetCurrentView();
|
|
181
|
+
i0.ɵɵelementStart(0, "div", 8);
|
|
182
|
+
i0.ɵɵelement(1, "i", 28);
|
|
183
|
+
i0.ɵɵelementStart(2, "input", 33, 0);
|
|
184
|
+
i0.ɵɵtwoWayListener("ngModelChange", function DataExplorerDashboardComponent_Conditional_10_Template_input_ngModelChange_2_listener($event) { i0.ɵɵrestoreView(_r11); const ctx_r1 = i0.ɵɵnextContext(); i0.ɵɵtwoWayBindingSet(ctx_r1.entityFilterText, $event) || (ctx_r1.entityFilterText = $event); return i0.ɵɵresetView($event); });
|
|
185
|
+
i0.ɵɵelementEnd();
|
|
186
|
+
i0.ɵɵtemplate(4, DataExplorerDashboardComponent_Conditional_10_Conditional_4_Template, 2, 0, "button", 30);
|
|
187
|
+
i0.ɵɵelementEnd();
|
|
188
|
+
} if (rf & 2) {
|
|
189
|
+
const ctx_r1 = i0.ɵɵnextContext();
|
|
190
|
+
i0.ɵɵadvance(2);
|
|
191
|
+
i0.ɵɵtwoWayProperty("ngModel", ctx_r1.entityFilterText);
|
|
192
|
+
i0.ɵɵadvance(2);
|
|
193
|
+
i0.ɵɵconditional(ctx_r1.entityFilterText ? 4 : -1);
|
|
194
|
+
} }
|
|
195
|
+
function DataExplorerDashboardComponent_Conditional_12_Template(rf, ctx) { if (rf & 1) {
|
|
196
|
+
const _r13 = i0.ɵɵgetCurrentView();
|
|
197
|
+
i0.ɵɵelementStart(0, "div", 10)(1, "button", 34);
|
|
198
|
+
i0.ɵɵlistener("click", function DataExplorerDashboardComponent_Conditional_12_Template_button_click_1_listener() { i0.ɵɵrestoreView(_r13); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.onViewModeChanged("grid")); });
|
|
199
|
+
i0.ɵɵelement(2, "i", 35);
|
|
200
|
+
i0.ɵɵelementEnd();
|
|
201
|
+
i0.ɵɵelementStart(3, "button", 36);
|
|
202
|
+
i0.ɵɵlistener("click", function DataExplorerDashboardComponent_Conditional_12_Template_button_click_3_listener() { i0.ɵɵrestoreView(_r13); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.onViewModeChanged("cards")); });
|
|
203
|
+
i0.ɵɵelement(4, "i", 37);
|
|
204
|
+
i0.ɵɵelementEnd()();
|
|
205
|
+
} if (rf & 2) {
|
|
206
|
+
const ctx_r1 = i0.ɵɵnextContext();
|
|
207
|
+
i0.ɵɵadvance();
|
|
208
|
+
i0.ɵɵclassProp("active", ctx_r1.state.viewMode === "grid");
|
|
209
|
+
i0.ɵɵadvance(2);
|
|
210
|
+
i0.ɵɵclassProp("active", ctx_r1.state.viewMode === "cards");
|
|
211
|
+
} }
|
|
212
|
+
function DataExplorerDashboardComponent_Conditional_14_Conditional_1_Template(rf, ctx) { if (rf & 1) {
|
|
213
|
+
i0.ɵɵelementStart(0, "div", 38);
|
|
214
|
+
i0.ɵɵelement(1, "mj-loading", 40);
|
|
215
|
+
i0.ɵɵelementEnd();
|
|
216
|
+
} }
|
|
217
|
+
function DataExplorerDashboardComponent_Conditional_14_Conditional_2_Conditional_0_Conditional_2_Conditional_5_For_4_Template(rf, ctx) { if (rf & 1) {
|
|
218
|
+
const _r15 = i0.ɵɵgetCurrentView();
|
|
219
|
+
i0.ɵɵelementStart(0, "button", 61);
|
|
220
|
+
i0.ɵɵlistener("click", function DataExplorerDashboardComponent_Conditional_14_Conditional_2_Conditional_0_Conditional_2_Conditional_5_For_4_Template_button_click_0_listener() { const entity_r16 = i0.ɵɵrestoreView(_r15).$implicit; const ctx_r1 = i0.ɵɵnextContext(6); return i0.ɵɵresetView(ctx_r1.setRecentRecordsEntityFilter(entity_r16.entityId)); });
|
|
221
|
+
i0.ɵɵelement(1, "i");
|
|
222
|
+
i0.ɵɵelementEnd();
|
|
223
|
+
} if (rf & 2) {
|
|
224
|
+
const entity_r16 = ctx.$implicit;
|
|
225
|
+
const ctx_r1 = i0.ɵɵnextContext(6);
|
|
226
|
+
i0.ɵɵclassProp("active", ctx_r1.recentRecordsEntityFilter === entity_r16.entityId);
|
|
227
|
+
i0.ɵɵproperty("title", entity_r16.entityName);
|
|
228
|
+
i0.ɵɵadvance();
|
|
229
|
+
i0.ɵɵclassMap(entity_r16.icon);
|
|
230
|
+
} }
|
|
231
|
+
function DataExplorerDashboardComponent_Conditional_14_Conditional_2_Conditional_0_Conditional_2_Conditional_5_Template(rf, ctx) { if (rf & 1) {
|
|
232
|
+
const _r14 = i0.ɵɵgetCurrentView();
|
|
233
|
+
i0.ɵɵelementStart(0, "div", 55)(1, "button", 58);
|
|
234
|
+
i0.ɵɵlistener("click", function DataExplorerDashboardComponent_Conditional_14_Conditional_2_Conditional_0_Conditional_2_Conditional_5_Template_button_click_1_listener() { i0.ɵɵrestoreView(_r14); const ctx_r1 = i0.ɵɵnextContext(5); return i0.ɵɵresetView(ctx_r1.setRecentRecordsEntityFilter(null)); });
|
|
235
|
+
i0.ɵɵelement(2, "i", 59);
|
|
236
|
+
i0.ɵɵelementEnd();
|
|
237
|
+
i0.ɵɵrepeaterCreate(3, DataExplorerDashboardComponent_Conditional_14_Conditional_2_Conditional_0_Conditional_2_Conditional_5_For_4_Template, 2, 5, "button", 60, _forTrack3);
|
|
238
|
+
i0.ɵɵelementEnd();
|
|
239
|
+
} if (rf & 2) {
|
|
240
|
+
const ctx_r1 = i0.ɵɵnextContext(5);
|
|
241
|
+
i0.ɵɵadvance();
|
|
242
|
+
i0.ɵɵclassProp("active", ctx_r1.recentRecordsEntityFilter === null);
|
|
243
|
+
i0.ɵɵadvance(2);
|
|
244
|
+
i0.ɵɵrepeater(ctx_r1.uniqueRecentRecordEntities);
|
|
245
|
+
} }
|
|
246
|
+
function DataExplorerDashboardComponent_Conditional_14_Conditional_2_Conditional_0_Conditional_2_For_8_Template(rf, ctx) { if (rf & 1) {
|
|
247
|
+
const _r17 = i0.ɵɵgetCurrentView();
|
|
248
|
+
i0.ɵɵelementStart(0, "div", 62);
|
|
249
|
+
i0.ɵɵlistener("click", function DataExplorerDashboardComponent_Conditional_14_Conditional_2_Conditional_0_Conditional_2_For_8_Template_div_click_0_listener() { const record_r18 = i0.ɵɵrestoreView(_r17).$implicit; const ctx_r1 = i0.ɵɵnextContext(5); return i0.ɵɵresetView(ctx_r1.onRecentRecordClick(record_r18)); });
|
|
250
|
+
i0.ɵɵelementStart(1, "div", 63);
|
|
251
|
+
i0.ɵɵelement(2, "i");
|
|
252
|
+
i0.ɵɵelementEnd();
|
|
253
|
+
i0.ɵɵelementStart(3, "div", 64)(4, "span", 65);
|
|
254
|
+
i0.ɵɵtext(5);
|
|
255
|
+
i0.ɵɵelementEnd();
|
|
256
|
+
i0.ɵɵelementStart(6, "span", 66);
|
|
257
|
+
i0.ɵɵtext(7);
|
|
258
|
+
i0.ɵɵelementEnd()();
|
|
259
|
+
i0.ɵɵelementStart(8, "span", 67);
|
|
260
|
+
i0.ɵɵtext(9);
|
|
261
|
+
i0.ɵɵelementEnd()();
|
|
262
|
+
} if (rf & 2) {
|
|
263
|
+
const record_r18 = ctx.$implicit;
|
|
264
|
+
const ctx_r1 = i0.ɵɵnextContext(5);
|
|
265
|
+
i0.ɵɵproperty("title", record_r18.entityName + " - " + record_r18.recordId);
|
|
266
|
+
i0.ɵɵadvance(2);
|
|
267
|
+
i0.ɵɵclassMap(ctx_r1.getEntityIconById(record_r18.entityId));
|
|
268
|
+
i0.ɵɵadvance(3);
|
|
269
|
+
i0.ɵɵtextInterpolate(record_r18.recordName || record_r18.recordId);
|
|
270
|
+
i0.ɵɵadvance(2);
|
|
271
|
+
i0.ɵɵtextInterpolate(record_r18.entityName);
|
|
272
|
+
i0.ɵɵadvance(2);
|
|
273
|
+
i0.ɵɵtextInterpolate(ctx_r1.formatRelativeTime(record_r18.latestAt));
|
|
274
|
+
} }
|
|
275
|
+
function DataExplorerDashboardComponent_Conditional_14_Conditional_2_Conditional_0_Conditional_2_Template(rf, ctx) { if (rf & 1) {
|
|
276
|
+
i0.ɵɵelementStart(0, "div", 39)(1, "div", 53);
|
|
277
|
+
i0.ɵɵelement(2, "i", 54);
|
|
278
|
+
i0.ɵɵelementStart(3, "h3", 45);
|
|
279
|
+
i0.ɵɵtext(4, "Recent Records");
|
|
280
|
+
i0.ɵɵelementEnd();
|
|
281
|
+
i0.ɵɵtemplate(5, DataExplorerDashboardComponent_Conditional_14_Conditional_2_Conditional_0_Conditional_2_Conditional_5_Template, 5, 2, "div", 55);
|
|
282
|
+
i0.ɵɵelementEnd();
|
|
283
|
+
i0.ɵɵelementStart(6, "div", 56);
|
|
284
|
+
i0.ɵɵrepeaterCreate(7, DataExplorerDashboardComponent_Conditional_14_Conditional_2_Conditional_0_Conditional_2_For_8_Template, 10, 6, "div", 57, _forTrack2);
|
|
285
|
+
i0.ɵɵelementEnd()();
|
|
286
|
+
} if (rf & 2) {
|
|
287
|
+
const ctx_r1 = i0.ɵɵnextContext(4);
|
|
288
|
+
i0.ɵɵadvance(5);
|
|
289
|
+
i0.ɵɵconditional(ctx_r1.showRecentRecordsEntityFilter ? 5 : -1);
|
|
290
|
+
i0.ɵɵadvance(2);
|
|
291
|
+
i0.ɵɵrepeater(ctx_r1.filteredRecentRecords);
|
|
292
|
+
} }
|
|
293
|
+
function DataExplorerDashboardComponent_Conditional_14_Conditional_2_Conditional_0_Conditional_3_For_7_Template(rf, ctx) { if (rf & 1) {
|
|
294
|
+
const _r19 = i0.ɵɵgetCurrentView();
|
|
295
|
+
i0.ɵɵelementStart(0, "div", 62);
|
|
296
|
+
i0.ɵɵlistener("click", function DataExplorerDashboardComponent_Conditional_14_Conditional_2_Conditional_0_Conditional_3_For_7_Template_div_click_0_listener() { const record_r20 = i0.ɵɵrestoreView(_r19).$implicit; const ctx_r1 = i0.ɵɵnextContext(5); return i0.ɵɵresetView(ctx_r1.onFavoriteRecordClick(record_r20)); });
|
|
297
|
+
i0.ɵɵelementStart(1, "div", 63);
|
|
298
|
+
i0.ɵɵelement(2, "i");
|
|
299
|
+
i0.ɵɵelementEnd();
|
|
300
|
+
i0.ɵɵelementStart(3, "div", 64)(4, "span", 65);
|
|
301
|
+
i0.ɵɵtext(5);
|
|
302
|
+
i0.ɵɵelementEnd();
|
|
303
|
+
i0.ɵɵelementStart(6, "span", 66);
|
|
304
|
+
i0.ɵɵtext(7);
|
|
305
|
+
i0.ɵɵelementEnd()()();
|
|
306
|
+
} if (rf & 2) {
|
|
307
|
+
const record_r20 = ctx.$implicit;
|
|
308
|
+
const ctx_r1 = i0.ɵɵnextContext(5);
|
|
309
|
+
i0.ɵɵproperty("title", record_r20.entityName + " - " + record_r20.recordId);
|
|
310
|
+
i0.ɵɵadvance(2);
|
|
311
|
+
i0.ɵɵclassMap(ctx_r1.getEntityIconById(record_r20.entityId));
|
|
312
|
+
i0.ɵɵadvance(3);
|
|
313
|
+
i0.ɵɵtextInterpolate(record_r20.recordName || record_r20.recordId);
|
|
314
|
+
i0.ɵɵadvance(2);
|
|
315
|
+
i0.ɵɵtextInterpolate(record_r20.entityName);
|
|
316
|
+
} }
|
|
317
|
+
function DataExplorerDashboardComponent_Conditional_14_Conditional_2_Conditional_0_Conditional_3_Template(rf, ctx) { if (rf & 1) {
|
|
318
|
+
i0.ɵɵelementStart(0, "div", 39)(1, "div", 53);
|
|
319
|
+
i0.ɵɵelement(2, "i", 68);
|
|
320
|
+
i0.ɵɵelementStart(3, "h3", 45);
|
|
321
|
+
i0.ɵɵtext(4, "Favorite Records");
|
|
322
|
+
i0.ɵɵelementEnd()();
|
|
323
|
+
i0.ɵɵelementStart(5, "div", 56);
|
|
324
|
+
i0.ɵɵrepeaterCreate(6, DataExplorerDashboardComponent_Conditional_14_Conditional_2_Conditional_0_Conditional_3_For_7_Template, 8, 5, "div", 57, _forTrack4);
|
|
325
|
+
i0.ɵɵelementEnd()();
|
|
326
|
+
} if (rf & 2) {
|
|
327
|
+
const ctx_r1 = i0.ɵɵnextContext(4);
|
|
328
|
+
i0.ɵɵadvance(6);
|
|
329
|
+
i0.ɵɵrepeater(ctx_r1.favoriteRecords);
|
|
330
|
+
} }
|
|
331
|
+
function DataExplorerDashboardComponent_Conditional_14_Conditional_2_Conditional_0_Conditional_5_For_7_Template(rf, ctx) { if (rf & 1) {
|
|
332
|
+
const _r21 = i0.ɵɵgetCurrentView();
|
|
333
|
+
i0.ɵɵelementStart(0, "div", 72);
|
|
334
|
+
i0.ɵɵlistener("click", function DataExplorerDashboardComponent_Conditional_14_Conditional_2_Conditional_0_Conditional_5_For_7_Template_div_click_0_listener() { const entity_r22 = i0.ɵɵrestoreView(_r21).$implicit; const ctx_r1 = i0.ɵɵnextContext(5); return i0.ɵɵresetView(ctx_r1.onEntitySelected(entity_r22)); });
|
|
335
|
+
i0.ɵɵelementStart(1, "div", 73);
|
|
336
|
+
i0.ɵɵelement(2, "i");
|
|
337
|
+
i0.ɵɵelementEnd();
|
|
338
|
+
i0.ɵɵelementStart(3, "div", 74)(4, "span", 75);
|
|
339
|
+
i0.ɵɵtext(5);
|
|
340
|
+
i0.ɵɵelementEnd()();
|
|
341
|
+
i0.ɵɵelementStart(6, "button", 76);
|
|
342
|
+
i0.ɵɵlistener("click", function DataExplorerDashboardComponent_Conditional_14_Conditional_2_Conditional_0_Conditional_5_For_7_Template_button_click_6_listener($event) { const entity_r22 = i0.ɵɵrestoreView(_r21).$implicit; const ctx_r1 = i0.ɵɵnextContext(5); return i0.ɵɵresetView(ctx_r1.toggleEntityFavorite(entity_r22, $event)); });
|
|
343
|
+
i0.ɵɵelement(7, "i");
|
|
344
|
+
i0.ɵɵelementEnd()();
|
|
345
|
+
} if (rf & 2) {
|
|
346
|
+
const entity_r22 = ctx.$implicit;
|
|
347
|
+
const ctx_r1 = i0.ɵɵnextContext(5);
|
|
348
|
+
i0.ɵɵproperty("title", entity_r22.Description || entity_r22.Name);
|
|
349
|
+
i0.ɵɵadvance(2);
|
|
350
|
+
i0.ɵɵclassMap(ctx_r1.getEntityIcon(entity_r22));
|
|
351
|
+
i0.ɵɵadvance(3);
|
|
352
|
+
i0.ɵɵtextInterpolate(entity_r22.Name);
|
|
353
|
+
i0.ɵɵadvance();
|
|
354
|
+
i0.ɵɵclassProp("favorited", ctx_r1.isEntityFavorited(entity_r22));
|
|
355
|
+
i0.ɵɵproperty("title", ctx_r1.isEntityFavorited(entity_r22) ? "Remove from favorites" : "Add to favorites");
|
|
356
|
+
i0.ɵɵadvance();
|
|
357
|
+
i0.ɵɵclassMap(ctx_r1.isEntityFavorited(entity_r22) ? "fa-solid fa-star" : "fa-regular fa-star");
|
|
358
|
+
} }
|
|
359
|
+
function DataExplorerDashboardComponent_Conditional_14_Conditional_2_Conditional_0_Conditional_5_Template(rf, ctx) { if (rf & 1) {
|
|
360
|
+
i0.ɵɵelementStart(0, "div", 39)(1, "div", 53);
|
|
361
|
+
i0.ɵɵelement(2, "i", 69);
|
|
362
|
+
i0.ɵɵelementStart(3, "h3", 45);
|
|
363
|
+
i0.ɵɵtext(4, "Recent Entities");
|
|
364
|
+
i0.ɵɵelementEnd()();
|
|
365
|
+
i0.ɵɵelementStart(5, "div", 70);
|
|
366
|
+
i0.ɵɵrepeaterCreate(6, DataExplorerDashboardComponent_Conditional_14_Conditional_2_Conditional_0_Conditional_5_For_7_Template, 8, 9, "div", 71, _forTrack1);
|
|
367
|
+
i0.ɵɵelementEnd()();
|
|
368
|
+
} if (rf & 2) {
|
|
369
|
+
const ctx_r1 = i0.ɵɵnextContext(4);
|
|
370
|
+
i0.ɵɵadvance(6);
|
|
371
|
+
i0.ɵɵrepeater(ctx_r1.recentEntities);
|
|
372
|
+
} }
|
|
373
|
+
function DataExplorerDashboardComponent_Conditional_14_Conditional_2_Conditional_0_Conditional_6_For_7_Template(rf, ctx) { if (rf & 1) {
|
|
374
|
+
const _r23 = i0.ɵɵgetCurrentView();
|
|
375
|
+
i0.ɵɵelementStart(0, "div", 72);
|
|
376
|
+
i0.ɵɵlistener("click", function DataExplorerDashboardComponent_Conditional_14_Conditional_2_Conditional_0_Conditional_6_For_7_Template_div_click_0_listener() { const entity_r24 = i0.ɵɵrestoreView(_r23).$implicit; const ctx_r1 = i0.ɵɵnextContext(5); return i0.ɵɵresetView(ctx_r1.onEntitySelected(entity_r24)); });
|
|
377
|
+
i0.ɵɵelementStart(1, "div", 73);
|
|
378
|
+
i0.ɵɵelement(2, "i");
|
|
379
|
+
i0.ɵɵelementEnd();
|
|
380
|
+
i0.ɵɵelementStart(3, "div", 74)(4, "span", 75);
|
|
381
|
+
i0.ɵɵtext(5);
|
|
382
|
+
i0.ɵɵelementEnd()();
|
|
383
|
+
i0.ɵɵelementStart(6, "button", 77);
|
|
384
|
+
i0.ɵɵlistener("click", function DataExplorerDashboardComponent_Conditional_14_Conditional_2_Conditional_0_Conditional_6_For_7_Template_button_click_6_listener($event) { const entity_r24 = i0.ɵɵrestoreView(_r23).$implicit; const ctx_r1 = i0.ɵɵnextContext(5); return i0.ɵɵresetView(ctx_r1.toggleEntityFavorite(entity_r24, $event)); });
|
|
385
|
+
i0.ɵɵelement(7, "i", 78);
|
|
386
|
+
i0.ɵɵelementEnd()();
|
|
387
|
+
} if (rf & 2) {
|
|
388
|
+
const entity_r24 = ctx.$implicit;
|
|
389
|
+
const ctx_r1 = i0.ɵɵnextContext(5);
|
|
390
|
+
i0.ɵɵproperty("title", entity_r24.Description || entity_r24.Name);
|
|
391
|
+
i0.ɵɵadvance(2);
|
|
392
|
+
i0.ɵɵclassMap(ctx_r1.getEntityIcon(entity_r24));
|
|
393
|
+
i0.ɵɵadvance(3);
|
|
394
|
+
i0.ɵɵtextInterpolate(entity_r24.Name);
|
|
395
|
+
} }
|
|
396
|
+
function DataExplorerDashboardComponent_Conditional_14_Conditional_2_Conditional_0_Conditional_6_Template(rf, ctx) { if (rf & 1) {
|
|
397
|
+
i0.ɵɵelementStart(0, "div", 39)(1, "div", 53);
|
|
398
|
+
i0.ɵɵelement(2, "i", 68);
|
|
399
|
+
i0.ɵɵelementStart(3, "h3", 45);
|
|
400
|
+
i0.ɵɵtext(4, "Favorite Entities");
|
|
401
|
+
i0.ɵɵelementEnd()();
|
|
402
|
+
i0.ɵɵelementStart(5, "div", 70);
|
|
403
|
+
i0.ɵɵrepeaterCreate(6, DataExplorerDashboardComponent_Conditional_14_Conditional_2_Conditional_0_Conditional_6_For_7_Template, 8, 4, "div", 71, _forTrack1);
|
|
404
|
+
i0.ɵɵelementEnd()();
|
|
405
|
+
} if (rf & 2) {
|
|
406
|
+
const ctx_r1 = i0.ɵɵnextContext(4);
|
|
407
|
+
i0.ɵɵadvance(6);
|
|
408
|
+
i0.ɵɵrepeater(ctx_r1.favoriteEntities);
|
|
409
|
+
} }
|
|
410
|
+
function DataExplorerDashboardComponent_Conditional_14_Conditional_2_Conditional_0_Template(rf, ctx) { if (rf & 1) {
|
|
411
|
+
i0.ɵɵelementStart(0, "div", 41)(1, "div", 52);
|
|
412
|
+
i0.ɵɵtemplate(2, DataExplorerDashboardComponent_Conditional_14_Conditional_2_Conditional_0_Conditional_2_Template, 9, 1, "div", 39)(3, DataExplorerDashboardComponent_Conditional_14_Conditional_2_Conditional_0_Conditional_3_Template, 8, 0, "div", 39);
|
|
413
|
+
i0.ɵɵelementEnd();
|
|
414
|
+
i0.ɵɵelementStart(4, "div", 52);
|
|
415
|
+
i0.ɵɵtemplate(5, DataExplorerDashboardComponent_Conditional_14_Conditional_2_Conditional_0_Conditional_5_Template, 8, 0, "div", 39)(6, DataExplorerDashboardComponent_Conditional_14_Conditional_2_Conditional_0_Conditional_6_Template, 8, 0, "div", 39);
|
|
416
|
+
i0.ɵɵelementEnd()();
|
|
417
|
+
} if (rf & 2) {
|
|
418
|
+
const ctx_r1 = i0.ɵɵnextContext(3);
|
|
419
|
+
i0.ɵɵadvance(2);
|
|
420
|
+
i0.ɵɵconditional(ctx_r1.recentRecords.length > 0 ? 2 : -1);
|
|
421
|
+
i0.ɵɵadvance();
|
|
422
|
+
i0.ɵɵconditional(ctx_r1.favoriteRecords.length > 0 ? 3 : -1);
|
|
423
|
+
i0.ɵɵadvance(2);
|
|
424
|
+
i0.ɵɵconditional(ctx_r1.recentEntities.length > 0 ? 5 : -1);
|
|
425
|
+
i0.ɵɵadvance();
|
|
426
|
+
i0.ɵɵconditional(ctx_r1.favoriteEntities.length > 0 ? 6 : -1);
|
|
427
|
+
} }
|
|
428
|
+
function DataExplorerDashboardComponent_Conditional_14_Conditional_2_Conditional_6_Template(rf, ctx) { if (rf & 1) {
|
|
429
|
+
i0.ɵɵtext(0, " All Entities ");
|
|
430
|
+
} }
|
|
431
|
+
function DataExplorerDashboardComponent_Conditional_14_Conditional_2_Conditional_7_Template(rf, ctx) { if (rf & 1) {
|
|
432
|
+
i0.ɵɵtext(0, " Common Entities ");
|
|
433
|
+
} }
|
|
434
|
+
function DataExplorerDashboardComponent_Conditional_14_Conditional_2_Conditional_8_Template(rf, ctx) { if (rf & 1) {
|
|
435
|
+
i0.ɵɵelementStart(0, "span", 46);
|
|
436
|
+
i0.ɵɵtext(1);
|
|
437
|
+
i0.ɵɵelementEnd();
|
|
438
|
+
} if (rf & 2) {
|
|
439
|
+
const ctx_r1 = i0.ɵɵnextContext(3);
|
|
440
|
+
i0.ɵɵadvance();
|
|
441
|
+
i0.ɵɵtextInterpolate1("", ctx_r1.filteredEntities.length, " matching");
|
|
442
|
+
} }
|
|
443
|
+
function DataExplorerDashboardComponent_Conditional_14_Conditional_2_Conditional_9_Template(rf, ctx) { if (rf & 1) {
|
|
444
|
+
const _r25 = i0.ɵɵgetCurrentView();
|
|
445
|
+
i0.ɵɵelementStart(0, "div", 47)(1, "button", 79);
|
|
446
|
+
i0.ɵɵlistener("click", function DataExplorerDashboardComponent_Conditional_14_Conditional_2_Conditional_9_Template_button_click_1_listener() { i0.ɵɵrestoreView(_r25); const ctx_r1 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r1.toggleShowAllEntities()); });
|
|
447
|
+
i0.ɵɵtext(2, " Common ");
|
|
448
|
+
i0.ɵɵelementEnd();
|
|
449
|
+
i0.ɵɵelementStart(3, "button", 79);
|
|
450
|
+
i0.ɵɵlistener("click", function DataExplorerDashboardComponent_Conditional_14_Conditional_2_Conditional_9_Template_button_click_3_listener() { i0.ɵɵrestoreView(_r25); const ctx_r1 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r1.toggleShowAllEntities()); });
|
|
451
|
+
i0.ɵɵtext(4, " All ");
|
|
452
|
+
i0.ɵɵelementEnd()();
|
|
453
|
+
} if (rf & 2) {
|
|
454
|
+
const ctx_r1 = i0.ɵɵnextContext(3);
|
|
455
|
+
i0.ɵɵadvance();
|
|
456
|
+
i0.ɵɵclassProp("active", !ctx_r1.state.showAllEntities);
|
|
457
|
+
i0.ɵɵpropertyInterpolate1("title", "Show common entities (", ctx_r1.commonEntitiesCount, ")");
|
|
458
|
+
i0.ɵɵadvance(2);
|
|
459
|
+
i0.ɵɵclassProp("active", ctx_r1.state.showAllEntities);
|
|
460
|
+
i0.ɵɵpropertyInterpolate1("title", "Show all entities (", ctx_r1.allEntitiesCount, ")");
|
|
461
|
+
} }
|
|
462
|
+
function DataExplorerDashboardComponent_Conditional_14_Conditional_2_For_12_Conditional_6_Template(rf, ctx) { if (rf & 1) {
|
|
463
|
+
i0.ɵɵelementStart(0, "p", 84);
|
|
464
|
+
i0.ɵɵtext(1);
|
|
465
|
+
i0.ɵɵelementEnd();
|
|
466
|
+
} if (rf & 2) {
|
|
467
|
+
const entity_r27 = i0.ɵɵnextContext().$implicit;
|
|
468
|
+
i0.ɵɵadvance();
|
|
469
|
+
i0.ɵɵtextInterpolate(entity_r27.Description);
|
|
470
|
+
} }
|
|
471
|
+
function DataExplorerDashboardComponent_Conditional_14_Conditional_2_For_12_Template(rf, ctx) { if (rf & 1) {
|
|
472
|
+
const _r26 = i0.ɵɵgetCurrentView();
|
|
473
|
+
i0.ɵɵelementStart(0, "div", 80);
|
|
474
|
+
i0.ɵɵlistener("click", function DataExplorerDashboardComponent_Conditional_14_Conditional_2_For_12_Template_div_click_0_listener() { const entity_r27 = i0.ɵɵrestoreView(_r26).$implicit; const ctx_r1 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r1.onEntitySelected(entity_r27)); });
|
|
475
|
+
i0.ɵɵelementStart(1, "div", 81);
|
|
476
|
+
i0.ɵɵelement(2, "i");
|
|
477
|
+
i0.ɵɵelementEnd();
|
|
478
|
+
i0.ɵɵelementStart(3, "div", 82)(4, "h4", 83);
|
|
479
|
+
i0.ɵɵtext(5);
|
|
480
|
+
i0.ɵɵelementEnd();
|
|
481
|
+
i0.ɵɵtemplate(6, DataExplorerDashboardComponent_Conditional_14_Conditional_2_For_12_Conditional_6_Template, 2, 1, "p", 84);
|
|
482
|
+
i0.ɵɵelementEnd();
|
|
483
|
+
i0.ɵɵelementStart(7, "button", 76);
|
|
484
|
+
i0.ɵɵlistener("click", function DataExplorerDashboardComponent_Conditional_14_Conditional_2_For_12_Template_button_click_7_listener($event) { const entity_r27 = i0.ɵɵrestoreView(_r26).$implicit; const ctx_r1 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r1.toggleEntityFavorite(entity_r27, $event)); });
|
|
485
|
+
i0.ɵɵelement(8, "i");
|
|
486
|
+
i0.ɵɵelementEnd()();
|
|
487
|
+
} if (rf & 2) {
|
|
488
|
+
const entity_r27 = ctx.$implicit;
|
|
489
|
+
const ctx_r1 = i0.ɵɵnextContext(3);
|
|
490
|
+
i0.ɵɵproperty("title", entity_r27.Description || entity_r27.Name);
|
|
491
|
+
i0.ɵɵadvance(2);
|
|
492
|
+
i0.ɵɵclassMap(ctx_r1.getEntityIcon(entity_r27));
|
|
493
|
+
i0.ɵɵadvance(3);
|
|
494
|
+
i0.ɵɵtextInterpolate(entity_r27.Name);
|
|
495
|
+
i0.ɵɵadvance();
|
|
496
|
+
i0.ɵɵconditional(entity_r27.Description ? 6 : -1);
|
|
497
|
+
i0.ɵɵadvance();
|
|
498
|
+
i0.ɵɵclassProp("favorited", ctx_r1.isEntityFavorited(entity_r27));
|
|
499
|
+
i0.ɵɵproperty("title", ctx_r1.isEntityFavorited(entity_r27) ? "Remove from favorites" : "Add to favorites");
|
|
500
|
+
i0.ɵɵadvance();
|
|
501
|
+
i0.ɵɵclassMap(ctx_r1.isEntityFavorited(entity_r27) ? "fa-solid fa-star" : "fa-regular fa-star");
|
|
502
|
+
} }
|
|
503
|
+
function DataExplorerDashboardComponent_Conditional_14_Conditional_2_Conditional_13_Template(rf, ctx) { if (rf & 1) {
|
|
504
|
+
i0.ɵɵelementStart(0, "div", 50);
|
|
505
|
+
i0.ɵɵelement(1, "i", 85);
|
|
506
|
+
i0.ɵɵelementStart(2, "h3");
|
|
507
|
+
i0.ɵɵtext(3, "No Matching Entities");
|
|
508
|
+
i0.ɵɵelementEnd();
|
|
509
|
+
i0.ɵɵelementStart(4, "p");
|
|
510
|
+
i0.ɵɵtext(5);
|
|
511
|
+
i0.ɵɵelementEnd()();
|
|
512
|
+
} if (rf & 2) {
|
|
513
|
+
const ctx_r1 = i0.ɵɵnextContext(3);
|
|
514
|
+
i0.ɵɵadvance(5);
|
|
515
|
+
i0.ɵɵtextInterpolate1("No entities match \"", ctx_r1.entityFilterText, "\"");
|
|
516
|
+
} }
|
|
517
|
+
function DataExplorerDashboardComponent_Conditional_14_Conditional_2_Conditional_14_Template(rf, ctx) { if (rf & 1) {
|
|
518
|
+
i0.ɵɵelementStart(0, "div", 51);
|
|
519
|
+
i0.ɵɵelement(1, "i", 86);
|
|
520
|
+
i0.ɵɵelementStart(2, "h3");
|
|
521
|
+
i0.ɵɵtext(3, "No Entities Available");
|
|
522
|
+
i0.ɵɵelementEnd();
|
|
523
|
+
i0.ɵɵelementStart(4, "p");
|
|
524
|
+
i0.ɵɵtext(5, "There are no entities configured for this application.");
|
|
525
|
+
i0.ɵɵelementEnd()();
|
|
526
|
+
} }
|
|
527
|
+
function DataExplorerDashboardComponent_Conditional_14_Conditional_2_Template(rf, ctx) { if (rf & 1) {
|
|
528
|
+
i0.ɵɵtemplate(0, DataExplorerDashboardComponent_Conditional_14_Conditional_2_Conditional_0_Template, 7, 4, "div", 41);
|
|
529
|
+
i0.ɵɵelementStart(1, "div", 39)(2, "div", 42)(3, "div", 43);
|
|
530
|
+
i0.ɵɵelement(4, "i", 44);
|
|
531
|
+
i0.ɵɵelementStart(5, "h3", 45);
|
|
532
|
+
i0.ɵɵtemplate(6, DataExplorerDashboardComponent_Conditional_14_Conditional_2_Conditional_6_Template, 1, 0)(7, DataExplorerDashboardComponent_Conditional_14_Conditional_2_Conditional_7_Template, 1, 0);
|
|
533
|
+
i0.ɵɵelementEnd();
|
|
534
|
+
i0.ɵɵtemplate(8, DataExplorerDashboardComponent_Conditional_14_Conditional_2_Conditional_8_Template, 2, 1, "span", 46);
|
|
535
|
+
i0.ɵɵelementEnd();
|
|
536
|
+
i0.ɵɵtemplate(9, DataExplorerDashboardComponent_Conditional_14_Conditional_2_Conditional_9_Template, 5, 8, "div", 47);
|
|
537
|
+
i0.ɵɵelementEnd();
|
|
538
|
+
i0.ɵɵelementStart(10, "div", 48);
|
|
539
|
+
i0.ɵɵrepeaterCreate(11, DataExplorerDashboardComponent_Conditional_14_Conditional_2_For_12_Template, 9, 10, "div", 49, _forTrack1);
|
|
540
|
+
i0.ɵɵelementEnd();
|
|
541
|
+
i0.ɵɵtemplate(13, DataExplorerDashboardComponent_Conditional_14_Conditional_2_Conditional_13_Template, 6, 1, "div", 50)(14, DataExplorerDashboardComponent_Conditional_14_Conditional_2_Conditional_14_Template, 6, 0, "div", 51);
|
|
542
|
+
i0.ɵɵelementEnd();
|
|
543
|
+
} if (rf & 2) {
|
|
544
|
+
const ctx_r1 = i0.ɵɵnextContext(2);
|
|
545
|
+
i0.ɵɵconditional(ctx_r1.hasTopSectionContent ? 0 : -1);
|
|
546
|
+
i0.ɵɵadvance(6);
|
|
547
|
+
i0.ɵɵconditional(ctx_r1.state.showAllEntities || !ctx_r1.showCommonAllToggle ? 6 : 7);
|
|
548
|
+
i0.ɵɵadvance(2);
|
|
549
|
+
i0.ɵɵconditional(ctx_r1.entityFilterText && ctx_r1.filteredEntities.length !== ctx_r1.entities.length ? 8 : -1);
|
|
550
|
+
i0.ɵɵadvance();
|
|
551
|
+
i0.ɵɵconditional(ctx_r1.showCommonAllToggle ? 9 : -1);
|
|
552
|
+
i0.ɵɵadvance(2);
|
|
553
|
+
i0.ɵɵrepeater(ctx_r1.filteredEntities);
|
|
554
|
+
i0.ɵɵadvance(2);
|
|
555
|
+
i0.ɵɵconditional(ctx_r1.filteredEntities.length === 0 && ctx_r1.entities.length > 0 ? 13 : -1);
|
|
556
|
+
i0.ɵɵadvance();
|
|
557
|
+
i0.ɵɵconditional(ctx_r1.entities.length === 0 ? 14 : -1);
|
|
558
|
+
} }
|
|
559
|
+
function DataExplorerDashboardComponent_Conditional_14_Template(rf, ctx) { if (rf & 1) {
|
|
560
|
+
i0.ɵɵelementStart(0, "div", 12);
|
|
561
|
+
i0.ɵɵtemplate(1, DataExplorerDashboardComponent_Conditional_14_Conditional_1_Template, 2, 0, "div", 38)(2, DataExplorerDashboardComponent_Conditional_14_Conditional_2_Template, 15, 6, "div", 39);
|
|
562
|
+
i0.ɵɵelementEnd();
|
|
563
|
+
} if (rf & 2) {
|
|
564
|
+
const ctx_r1 = i0.ɵɵnextContext();
|
|
565
|
+
i0.ɵɵadvance();
|
|
566
|
+
i0.ɵɵconditional(ctx_r1.isLoadingEntities ? 1 : 2);
|
|
567
|
+
} }
|
|
568
|
+
function DataExplorerDashboardComponent_Conditional_15_Template(rf, ctx) { if (rf & 1) {
|
|
569
|
+
const _r28 = i0.ɵɵgetCurrentView();
|
|
570
|
+
i0.ɵɵelementStart(0, "mj-entity-viewer", 87);
|
|
571
|
+
i0.ɵɵlistener("viewModeChange", function DataExplorerDashboardComponent_Conditional_15_Template_mj_entity_viewer_viewModeChange_0_listener($event) { i0.ɵɵrestoreView(_r28); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.onViewModeChanged($event)); })("filterTextChange", function DataExplorerDashboardComponent_Conditional_15_Template_mj_entity_viewer_filterTextChange_0_listener($event) { i0.ɵɵrestoreView(_r28); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.onFilterTextChanged($event)); })("recordSelected", function DataExplorerDashboardComponent_Conditional_15_Template_mj_entity_viewer_recordSelected_0_listener($event) { i0.ɵɵrestoreView(_r28); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.onViewerRecordSelected($event)); })("recordOpened", function DataExplorerDashboardComponent_Conditional_15_Template_mj_entity_viewer_recordOpened_0_listener($event) { i0.ɵɵrestoreView(_r28); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.onViewerRecordOpened($event)); })("dataLoaded", function DataExplorerDashboardComponent_Conditional_15_Template_mj_entity_viewer_dataLoaded_0_listener($event) { i0.ɵɵrestoreView(_r28); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.onDataLoaded($event)); })("filteredCountChanged", function DataExplorerDashboardComponent_Conditional_15_Template_mj_entity_viewer_filteredCountChanged_0_listener($event) { i0.ɵɵrestoreView(_r28); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.onFilteredCountChanged($event)); })("gridStateChanged", function DataExplorerDashboardComponent_Conditional_15_Template_mj_entity_viewer_gridStateChanged_0_listener($event) { i0.ɵɵrestoreView(_r28); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.onGridStateChanged($event)); });
|
|
572
|
+
i0.ɵɵelementEnd();
|
|
573
|
+
} if (rf & 2) {
|
|
574
|
+
const ctx_r1 = i0.ɵɵnextContext();
|
|
575
|
+
i0.ɵɵproperty("entity", ctx_r1.selectedEntity)("viewEntity", ctx_r1.selectedViewEntity)("viewMode", ctx_r1.state.viewMode)("filterText", ctx_r1.debouncedFilterText)("selectedRecordId", ctx_r1.state.selectedRecordId)("config", ctx_r1.viewerConfig)("gridState", ctx_r1.currentGridState);
|
|
576
|
+
} }
|
|
577
|
+
function DataExplorerDashboardComponent_Conditional_16_Template(rf, ctx) { if (rf & 1) {
|
|
578
|
+
const _r29 = i0.ɵɵgetCurrentView();
|
|
579
|
+
i0.ɵɵelementStart(0, "div", 88)(1, "mj-entity-record-detail-panel", 89);
|
|
580
|
+
i0.ɵɵlistener("close", function DataExplorerDashboardComponent_Conditional_16_Template_mj_entity_record_detail_panel_close_1_listener() { i0.ɵɵrestoreView(_r29); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.onDetailPanelClosed()); })("openRecord", function DataExplorerDashboardComponent_Conditional_16_Template_mj_entity_record_detail_panel_openRecord_1_listener($event) { i0.ɵɵrestoreView(_r29); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.onOpenRecord($event)); })("navigateToRelated", function DataExplorerDashboardComponent_Conditional_16_Template_mj_entity_record_detail_panel_navigateToRelated_1_listener($event) { i0.ɵɵrestoreView(_r29); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.onNavigateToRelated($event)); })("openRelatedRecord", function DataExplorerDashboardComponent_Conditional_16_Template_mj_entity_record_detail_panel_openRelatedRecord_1_listener($event) { i0.ɵɵrestoreView(_r29); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.onOpenRelatedRecord($event)); })("openForeignKeyRecord", function DataExplorerDashboardComponent_Conditional_16_Template_mj_entity_record_detail_panel_openForeignKeyRecord_1_listener($event) { i0.ɵɵrestoreView(_r29); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.onOpenForeignKeyRecord($event)); });
|
|
581
|
+
i0.ɵɵelementEnd()();
|
|
582
|
+
} if (rf & 2) {
|
|
583
|
+
const ctx_r1 = i0.ɵɵnextContext();
|
|
584
|
+
i0.ɵɵstyleProp("width", ctx_r1.state.detailPanelWidth, "px");
|
|
585
|
+
i0.ɵɵadvance();
|
|
586
|
+
i0.ɵɵproperty("entity", ctx_r1.detailPanelEntity)("record", ctx_r1.selectedRecord);
|
|
587
|
+
} }
|
|
588
|
+
/**
|
|
589
|
+
* Data Explorer Dashboard - Power user interface for exploring data across entities
|
|
590
|
+
* Combines card-based browsing with grid views and relationship visualization
|
|
591
|
+
*
|
|
592
|
+
* Uses mj-entity-viewer composite component for the main content area,
|
|
593
|
+
* which handles data loading, filtering, and view mode switching.
|
|
594
|
+
*/
|
|
595
|
+
let DataExplorerDashboardComponent = class DataExplorerDashboardComponent extends BaseDashboard {
|
|
596
|
+
stateService;
|
|
597
|
+
cdr;
|
|
598
|
+
router;
|
|
599
|
+
recentAccessService;
|
|
600
|
+
destroy$ = new Subject();
|
|
601
|
+
metadata = new Metadata();
|
|
602
|
+
/** Reference to the filter input for keyboard shortcuts */
|
|
603
|
+
filterInputRef;
|
|
604
|
+
/** Reference to the view selector for refreshing after save */
|
|
605
|
+
viewSelectorRef;
|
|
606
|
+
/** Reference to the entity viewer for refreshing after view save */
|
|
607
|
+
entityViewerRef;
|
|
608
|
+
/**
|
|
609
|
+
* Optional filter to constrain which entities are shown in the explorer.
|
|
610
|
+
* Can filter by applicationId, schemaNames, or explicit entityNames.
|
|
611
|
+
*/
|
|
612
|
+
entityFilter = null;
|
|
613
|
+
/**
|
|
614
|
+
* Optional deep link to navigate to a specific entity/record on load.
|
|
615
|
+
* Parsed from URL query parameters (e.g., ?entity=Users&record=123)
|
|
616
|
+
*/
|
|
617
|
+
deepLink = null;
|
|
618
|
+
/**
|
|
619
|
+
* Optional context name to display in the header instead of "Data Explorer".
|
|
620
|
+
* Use this to customize the explorer for specific applications (e.g., "CRM", "Association Demo").
|
|
621
|
+
*/
|
|
622
|
+
contextName = null;
|
|
623
|
+
/**
|
|
624
|
+
* Optional context icon (Font Awesome class) to display in the header.
|
|
625
|
+
* Use this alongside contextName for a fully customized header (e.g., "fa-solid fa-users" for CRM).
|
|
626
|
+
*/
|
|
627
|
+
contextIcon = null;
|
|
628
|
+
// State
|
|
629
|
+
state;
|
|
630
|
+
// Entity data - all entities available to the user
|
|
631
|
+
allEntities = [];
|
|
632
|
+
// Filtered entities based on entityFilter
|
|
633
|
+
entities = [];
|
|
634
|
+
// Entity IDs for the current application (loaded when applicationId filter is set)
|
|
635
|
+
applicationEntityIds = new Set();
|
|
636
|
+
selectedEntity = null;
|
|
637
|
+
// Record counts (updated by mj-entity-viewer)
|
|
638
|
+
totalRecordCount = 0;
|
|
639
|
+
filteredRecordCount = 0;
|
|
640
|
+
// Selected record for detail panel
|
|
641
|
+
selectedRecord = null;
|
|
642
|
+
// Entity info for the detail panel (may differ from selectedEntity when viewing FK/related records)
|
|
643
|
+
detailPanelEntity = null;
|
|
644
|
+
// Currently loaded records from mj-entity-viewer (for back/forward navigation lookup)
|
|
645
|
+
loadedRecords = [];
|
|
646
|
+
// Currently selected view entity (for view data loading)
|
|
647
|
+
selectedViewEntity = null;
|
|
648
|
+
// Debounced filter text (synced with mj-entity-viewer)
|
|
649
|
+
debouncedFilterText = '';
|
|
650
|
+
filterInput$ = new Subject();
|
|
651
|
+
// Entity filter text for home screen
|
|
652
|
+
entityFilterText = '';
|
|
653
|
+
// Breadcrumbs for navigation display
|
|
654
|
+
breadcrumbs = [];
|
|
655
|
+
// Loading state for entities
|
|
656
|
+
isLoadingEntities = true;
|
|
657
|
+
// Flag to skip URL updates during initialization (when applying deep link)
|
|
658
|
+
skipUrlUpdates = true;
|
|
659
|
+
// Track the last URL we navigated to, to avoid reacting to our own navigation
|
|
660
|
+
lastNavigatedUrl = '';
|
|
661
|
+
// Recent records from User Record Logs
|
|
662
|
+
recentRecords = [];
|
|
663
|
+
// Favorite records from User Favorites (non-entity favorites)
|
|
664
|
+
favoriteRecords = [];
|
|
665
|
+
// Loading state for home screen sections
|
|
666
|
+
isLoadingRecentRecords = true;
|
|
667
|
+
// Entity filter for recent records (null = show all, string = filter by entityId)
|
|
668
|
+
recentRecordsEntityFilter = null;
|
|
669
|
+
/**
|
|
670
|
+
* Filtered entities based on entityFilterText (for home screen)
|
|
671
|
+
* Excludes entities shown in recent or favorites sections
|
|
672
|
+
* Applies Common/All toggle filtering
|
|
673
|
+
*/
|
|
674
|
+
get filteredEntities() {
|
|
675
|
+
// Get IDs of entities in recent and favorites to exclude
|
|
676
|
+
const recentEntityIds = new Set(this.state.recentEntityAccesses.map(r => r.entityId));
|
|
677
|
+
const favoriteEntityIds = new Set(this.state.favoriteEntities.map(f => f.entityId));
|
|
678
|
+
let result = this.entities.filter(e => {
|
|
679
|
+
// Exclude entities shown in recent or favorites sections
|
|
680
|
+
if (recentEntityIds.has(e.ID) || favoriteEntityIds.has(e.ID)) {
|
|
681
|
+
return false;
|
|
682
|
+
}
|
|
683
|
+
// Apply Common/All toggle filter (only if we have DefaultForNewUser info)
|
|
684
|
+
if (!this.state.showAllEntities && this.stateService.DefaultEntityIds.size > 0) {
|
|
685
|
+
if (!this.stateService.DefaultEntityIds.has(e.ID)) {
|
|
686
|
+
return false;
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
return true;
|
|
690
|
+
});
|
|
691
|
+
// Apply text filter
|
|
692
|
+
if (this.entityFilterText && this.entityFilterText.trim() !== '') {
|
|
693
|
+
const filter = this.entityFilterText.toLowerCase().trim();
|
|
694
|
+
result = result.filter(e => e.Name.toLowerCase().includes(filter) ||
|
|
695
|
+
(e.Description && e.Description.toLowerCase().includes(filter)));
|
|
696
|
+
}
|
|
697
|
+
return result;
|
|
698
|
+
}
|
|
699
|
+
/**
|
|
700
|
+
* Get recent entities for home screen display (max 5)
|
|
701
|
+
*/
|
|
702
|
+
get recentEntities() {
|
|
703
|
+
return this.state.recentEntityAccesses
|
|
704
|
+
.slice(0, 5)
|
|
705
|
+
.map(r => this.entities.find(e => e.ID === r.entityId))
|
|
706
|
+
.filter((e) => e !== undefined);
|
|
707
|
+
}
|
|
708
|
+
/**
|
|
709
|
+
* Get favorite entities for home screen display
|
|
710
|
+
*/
|
|
711
|
+
get favoriteEntities() {
|
|
712
|
+
return this.state.favoriteEntities
|
|
713
|
+
.map(f => this.entities.find(e => e.ID === f.entityId))
|
|
714
|
+
.filter((e) => e !== undefined);
|
|
715
|
+
}
|
|
716
|
+
/**
|
|
717
|
+
* Check if we should show the Common/All toggle
|
|
718
|
+
* Only show if we have DefaultForNewUser information
|
|
719
|
+
*/
|
|
720
|
+
get showCommonAllToggle() {
|
|
721
|
+
return this.stateService.DefaultEntityIds.size > 0;
|
|
722
|
+
}
|
|
723
|
+
/**
|
|
724
|
+
* Total count of all entities (for display)
|
|
725
|
+
*/
|
|
726
|
+
get allEntitiesCount() {
|
|
727
|
+
return this.entities.length;
|
|
728
|
+
}
|
|
729
|
+
/**
|
|
730
|
+
* Count of common (DefaultForNewUser) entities
|
|
731
|
+
*/
|
|
732
|
+
get commonEntitiesCount() {
|
|
733
|
+
return this.entities.filter(e => this.stateService.DefaultEntityIds.has(e.ID)).length;
|
|
734
|
+
}
|
|
735
|
+
/**
|
|
736
|
+
* Check if we have any content for the top two-column section
|
|
737
|
+
* (recent/favorite entities OR recent/favorite records)
|
|
738
|
+
*/
|
|
739
|
+
get hasTopSectionContent() {
|
|
740
|
+
return this.recentEntities.length > 0 ||
|
|
741
|
+
this.favoriteEntities.length > 0 ||
|
|
742
|
+
this.recentRecords.length > 0 ||
|
|
743
|
+
this.favoriteRecords.length > 0;
|
|
744
|
+
}
|
|
745
|
+
/**
|
|
746
|
+
* Get unique entities from recent records for the filter strip.
|
|
747
|
+
* Returns up to 5 entities, sorted by frequency in the recent records.
|
|
748
|
+
*/
|
|
749
|
+
get uniqueRecentRecordEntities() {
|
|
750
|
+
const entityCounts = new Map();
|
|
751
|
+
for (const record of this.recentRecords) {
|
|
752
|
+
const existing = entityCounts.get(record.entityId);
|
|
753
|
+
if (existing) {
|
|
754
|
+
existing.count++;
|
|
755
|
+
}
|
|
756
|
+
else {
|
|
757
|
+
entityCounts.set(record.entityId, {
|
|
758
|
+
entityId: record.entityId,
|
|
759
|
+
entityName: record.entityName,
|
|
760
|
+
count: 1
|
|
761
|
+
});
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
// Convert to array, sort by count (descending), take first 5
|
|
765
|
+
return Array.from(entityCounts.values())
|
|
766
|
+
.sort((a, b) => b.count - a.count)
|
|
767
|
+
.slice(0, 5)
|
|
768
|
+
.map(e => ({
|
|
769
|
+
...e,
|
|
770
|
+
icon: this.getEntityIconById(e.entityId)
|
|
771
|
+
}));
|
|
772
|
+
}
|
|
773
|
+
/**
|
|
774
|
+
* Check if we should show the entity filter strip for recent records.
|
|
775
|
+
* Only show when there are 2+ unique entities.
|
|
776
|
+
*/
|
|
777
|
+
get showRecentRecordsEntityFilter() {
|
|
778
|
+
return this.uniqueRecentRecordEntities.length >= 2;
|
|
779
|
+
}
|
|
780
|
+
/**
|
|
781
|
+
* Get filtered recent records based on entity filter.
|
|
782
|
+
*/
|
|
783
|
+
get filteredRecentRecords() {
|
|
784
|
+
if (!this.recentRecordsEntityFilter) {
|
|
785
|
+
return this.recentRecords;
|
|
786
|
+
}
|
|
787
|
+
return this.recentRecords.filter(r => r.entityId === this.recentRecordsEntityFilter);
|
|
788
|
+
}
|
|
789
|
+
/**
|
|
790
|
+
* Set the entity filter for recent records
|
|
791
|
+
*/
|
|
792
|
+
setRecentRecordsEntityFilter(entityId) {
|
|
793
|
+
this.recentRecordsEntityFilter = entityId;
|
|
794
|
+
}
|
|
795
|
+
/**
|
|
796
|
+
* Get the display title for the header.
|
|
797
|
+
* Priority: contextName > entityFilter.applicationName > "Data Explorer"
|
|
798
|
+
*/
|
|
799
|
+
get displayTitle() {
|
|
800
|
+
return this.contextName || this.entityFilter?.applicationName || 'Data Explorer';
|
|
801
|
+
}
|
|
802
|
+
/**
|
|
803
|
+
* Get the display icon for the header (when at home level).
|
|
804
|
+
* Returns contextIcon if provided, otherwise null.
|
|
805
|
+
*/
|
|
806
|
+
get displayIcon() {
|
|
807
|
+
return this.contextIcon;
|
|
808
|
+
}
|
|
809
|
+
/**
|
|
810
|
+
* Configuration for mj-entity-viewer composite component
|
|
811
|
+
* Hides the built-in header since we have a custom header in the dashboard
|
|
812
|
+
* Uses server-side pagination with 100 records per page (default)
|
|
813
|
+
*/
|
|
814
|
+
viewerConfig = {
|
|
815
|
+
showFilter: false, // We have our own filter in the dashboard header
|
|
816
|
+
showViewModeToggle: false, // We have our own toggle in the dashboard header
|
|
817
|
+
showRecordCount: false, // We show count in the dashboard header
|
|
818
|
+
showPagination: true, // Show the pagination UI with "Load More" button
|
|
819
|
+
serverSideFiltering: true, // Use RunView's UserSearchString for filtering
|
|
820
|
+
serverSideSorting: true, // Use RunView's OrderBy for sorting
|
|
821
|
+
height: '100%'
|
|
822
|
+
};
|
|
823
|
+
/**
|
|
824
|
+
* Current grid state (built from view entity or local state changes)
|
|
825
|
+
* This is passed to mj-entity-viewer to control column display
|
|
826
|
+
*/
|
|
827
|
+
currentGridState = null;
|
|
828
|
+
constructor(stateService, cdr, router, recentAccessService) {
|
|
829
|
+
super();
|
|
830
|
+
this.stateService = stateService;
|
|
831
|
+
this.cdr = cdr;
|
|
832
|
+
this.router = router;
|
|
833
|
+
this.recentAccessService = recentAccessService;
|
|
834
|
+
this.state = this.stateService.CurrentState;
|
|
835
|
+
}
|
|
836
|
+
async ngOnInit() {
|
|
837
|
+
// Parse URL state FIRST - URL wins over persisted state
|
|
838
|
+
// This must happen before loading entities to prevent race conditions
|
|
839
|
+
const urlState = this.parseUrlState();
|
|
840
|
+
// Set context for state service (enables context-specific settings)
|
|
841
|
+
await this.stateService.setContext(this.entityFilter);
|
|
842
|
+
this.state = this.stateService.CurrentState;
|
|
843
|
+
// Initialize debounced filter from persisted state (only if no URL state)
|
|
844
|
+
if (!urlState && this.state.smartFilterPrompt) {
|
|
845
|
+
this.debouncedFilterText = this.state.smartFilterPrompt;
|
|
846
|
+
}
|
|
847
|
+
// Load available entities (async to support applicationId filter)
|
|
848
|
+
// Pass urlState so we don't restore persisted entity if URL specifies one
|
|
849
|
+
await this.loadEntities(urlState);
|
|
850
|
+
// Apply URL state after entities are loaded
|
|
851
|
+
if (urlState) {
|
|
852
|
+
// URL has state - apply it (overrides persisted state)
|
|
853
|
+
this.applyUrlState(urlState);
|
|
854
|
+
}
|
|
855
|
+
else if (this.deepLink) {
|
|
856
|
+
// No URL state but @Input deepLink provided - use that
|
|
857
|
+
await this.applyDeepLink(this.deepLink);
|
|
858
|
+
}
|
|
859
|
+
// Subscribe to state changes
|
|
860
|
+
this.stateService.State
|
|
861
|
+
.pipe(takeUntil(this.destroy$))
|
|
862
|
+
.subscribe(state => {
|
|
863
|
+
const entityChanged = state.selectedEntityName !== this.state.selectedEntityName;
|
|
864
|
+
this.state = state;
|
|
865
|
+
// When entity changes, immediately update the debounced filter text
|
|
866
|
+
if (entityChanged && state.smartFilterPrompt !== this.debouncedFilterText) {
|
|
867
|
+
this.debouncedFilterText = state.smartFilterPrompt;
|
|
868
|
+
}
|
|
869
|
+
this.onStateChanged();
|
|
870
|
+
// Update URL to reflect current state (for deep linking)
|
|
871
|
+
if (!this.skipUrlUpdates) {
|
|
872
|
+
this.updateUrl();
|
|
873
|
+
}
|
|
874
|
+
this.cdr.detectChanges();
|
|
875
|
+
});
|
|
876
|
+
// Subscribe to breadcrumb changes
|
|
877
|
+
this.stateService.Breadcrumbs
|
|
878
|
+
.pipe(takeUntil(this.destroy$))
|
|
879
|
+
.subscribe(breadcrumbs => {
|
|
880
|
+
this.breadcrumbs = breadcrumbs;
|
|
881
|
+
this.cdr.detectChanges();
|
|
882
|
+
});
|
|
883
|
+
// Setup debounced filter
|
|
884
|
+
this.filterInput$
|
|
885
|
+
.pipe(debounceTime(250), distinctUntilChanged(), takeUntil(this.destroy$))
|
|
886
|
+
.subscribe(filterText => {
|
|
887
|
+
this.debouncedFilterText = filterText;
|
|
888
|
+
this.cdr.detectChanges();
|
|
889
|
+
});
|
|
890
|
+
// Subscribe to recent records changes
|
|
891
|
+
this.stateService.RecentRecords
|
|
892
|
+
.pipe(takeUntil(this.destroy$))
|
|
893
|
+
.subscribe(records => {
|
|
894
|
+
this.recentRecords = records;
|
|
895
|
+
this.isLoadingRecentRecords = false;
|
|
896
|
+
this.cdr.detectChanges();
|
|
897
|
+
});
|
|
898
|
+
// Subscribe to favorite records changes
|
|
899
|
+
this.stateService.FavoriteRecords
|
|
900
|
+
.pipe(takeUntil(this.destroy$))
|
|
901
|
+
.subscribe(records => {
|
|
902
|
+
this.favoriteRecords = records;
|
|
903
|
+
this.cdr.detectChanges();
|
|
904
|
+
});
|
|
905
|
+
// Subscribe to router NavigationEnd events for back/forward button support
|
|
906
|
+
this.router.events
|
|
907
|
+
.pipe(filter((event) => event instanceof NavigationEnd), takeUntil(this.destroy$))
|
|
908
|
+
.subscribe(event => {
|
|
909
|
+
// Only react to navigation events that weren't triggered by us
|
|
910
|
+
// Normalize URLs by decoding to handle + vs %20 encoding differences
|
|
911
|
+
// Note: decodeURIComponent doesn't decode +, so we also replace + with space
|
|
912
|
+
const currentUrl = event.urlAfterRedirects || event.url;
|
|
913
|
+
const normalizedCurrentUrl = decodeURIComponent(currentUrl).replace(/\+/g, ' ');
|
|
914
|
+
const normalizedLastUrl = decodeURIComponent(this.lastNavigatedUrl).replace(/\+/g, ' ');
|
|
915
|
+
const isExternal = normalizedCurrentUrl !== normalizedLastUrl;
|
|
916
|
+
if (isExternal) {
|
|
917
|
+
this.onExternalNavigation(currentUrl);
|
|
918
|
+
}
|
|
919
|
+
});
|
|
920
|
+
// Enable URL updates now that initialization is complete
|
|
921
|
+
this.skipUrlUpdates = false;
|
|
922
|
+
// Update URL to reflect current state (whether from URL, deepLink, or persisted)
|
|
923
|
+
this.updateUrl();
|
|
924
|
+
}
|
|
925
|
+
/**
|
|
926
|
+
* Handle keyboard shortcuts
|
|
927
|
+
* "/" or Cmd+K focuses the filter input
|
|
928
|
+
*/
|
|
929
|
+
handleKeyboardShortcut(event) {
|
|
930
|
+
// Skip if user is typing in an input field
|
|
931
|
+
const target = event.target;
|
|
932
|
+
if (target.tagName === 'INPUT' || target.tagName === 'TEXTAREA' || target.isContentEditable) {
|
|
933
|
+
// Allow Escape to blur the input
|
|
934
|
+
if (event.key === 'Escape') {
|
|
935
|
+
target.blur();
|
|
936
|
+
}
|
|
937
|
+
return;
|
|
938
|
+
}
|
|
939
|
+
// "/" to focus filter
|
|
940
|
+
if (event.key === '/') {
|
|
941
|
+
event.preventDefault();
|
|
942
|
+
this.focusFilterInput();
|
|
943
|
+
}
|
|
944
|
+
// Cmd+K or Ctrl+K to focus filter
|
|
945
|
+
if ((event.metaKey || event.ctrlKey) && event.key === 'k') {
|
|
946
|
+
event.preventDefault();
|
|
947
|
+
this.focusFilterInput();
|
|
948
|
+
}
|
|
949
|
+
}
|
|
950
|
+
/**
|
|
951
|
+
* Focus the filter input
|
|
952
|
+
*/
|
|
953
|
+
focusFilterInput() {
|
|
954
|
+
if (this.filterInputRef) {
|
|
955
|
+
this.filterInputRef.nativeElement.focus();
|
|
956
|
+
this.filterInputRef.nativeElement.select();
|
|
957
|
+
}
|
|
958
|
+
}
|
|
959
|
+
ngOnDestroy() {
|
|
960
|
+
this.destroy$.next();
|
|
961
|
+
this.destroy$.complete();
|
|
962
|
+
super.ngOnDestroy();
|
|
963
|
+
}
|
|
964
|
+
async ngOnChanges(changes) {
|
|
965
|
+
// Re-apply filter when entityFilter changes
|
|
966
|
+
if (changes['entityFilter'] && !changes['entityFilter'].firstChange) {
|
|
967
|
+
// Update context for new filter (loads context-specific state)
|
|
968
|
+
await this.stateService.setContext(this.entityFilter);
|
|
969
|
+
this.state = this.stateService.CurrentState;
|
|
970
|
+
await this.loadEntities();
|
|
971
|
+
}
|
|
972
|
+
}
|
|
973
|
+
initDashboard() {
|
|
974
|
+
// Called by BaseDashboard
|
|
975
|
+
}
|
|
976
|
+
loadData() {
|
|
977
|
+
// Data loading is handled by mj-entity-viewer
|
|
978
|
+
}
|
|
979
|
+
// ========================================
|
|
980
|
+
// ENTITY MANAGEMENT
|
|
981
|
+
// ========================================
|
|
982
|
+
/**
|
|
983
|
+
* Load all available entities the user can access, applying any configured filter
|
|
984
|
+
* @param urlState Optional URL state - if provided, skip restoring persisted entity to avoid race conditions
|
|
985
|
+
*/
|
|
986
|
+
async loadEntities(urlState) {
|
|
987
|
+
console.log('[DataExplorer] loadEntities called, entityFilter:', this.entityFilter);
|
|
988
|
+
this.isLoadingEntities = true;
|
|
989
|
+
try {
|
|
990
|
+
// First, load all entities the user can access
|
|
991
|
+
this.allEntities = this.metadata.Entities
|
|
992
|
+
.filter(e => {
|
|
993
|
+
const perms = e.GetUserPermisions(this.metadata.CurrentUser);
|
|
994
|
+
return perms.CanRead && e.IncludeInAPI;
|
|
995
|
+
})
|
|
996
|
+
.sort((a, b) => a.Name.localeCompare(b.Name));
|
|
997
|
+
console.log('[DataExplorer] allEntities count:', this.allEntities.length);
|
|
998
|
+
// If we have an applicationId filter, load the application entities
|
|
999
|
+
if (this.entityFilter?.applicationId) {
|
|
1000
|
+
await this.loadApplicationEntityIds(this.entityFilter.applicationId);
|
|
1001
|
+
console.log('[DataExplorer] applicationEntityIds count:', this.applicationEntityIds.size);
|
|
1002
|
+
}
|
|
1003
|
+
// Apply filter to get the final entity list
|
|
1004
|
+
this.entities = this.applyEntityFilter(this.allEntities);
|
|
1005
|
+
console.log('[DataExplorer] filtered entities count:', this.entities.length);
|
|
1006
|
+
// Only restore entity from persisted state if there's no URL state
|
|
1007
|
+
// This prevents race conditions where persisted entity triggers data load
|
|
1008
|
+
// before URL state can override it
|
|
1009
|
+
if (!urlState && this.state.selectedEntityName) {
|
|
1010
|
+
this.selectedEntity = this.entities.find(e => e.Name === this.state.selectedEntityName) || null;
|
|
1011
|
+
}
|
|
1012
|
+
}
|
|
1013
|
+
finally {
|
|
1014
|
+
this.isLoadingEntities = false;
|
|
1015
|
+
this.cdr.detectChanges();
|
|
1016
|
+
}
|
|
1017
|
+
}
|
|
1018
|
+
/**
|
|
1019
|
+
* Load entity IDs associated with a specific application
|
|
1020
|
+
*/
|
|
1021
|
+
async loadApplicationEntityIds(applicationId) {
|
|
1022
|
+
this.applicationEntityIds.clear();
|
|
1023
|
+
const rv = new RunView();
|
|
1024
|
+
const result = await rv.RunView({
|
|
1025
|
+
EntityName: 'Application Entities',
|
|
1026
|
+
ExtraFilter: `ApplicationID = '${applicationId}'`,
|
|
1027
|
+
ResultType: 'entity_object'
|
|
1028
|
+
});
|
|
1029
|
+
if (result.Success && result.Results) {
|
|
1030
|
+
for (const appEntity of result.Results) {
|
|
1031
|
+
this.applicationEntityIds.add(appEntity.EntityID);
|
|
1032
|
+
}
|
|
1033
|
+
}
|
|
1034
|
+
}
|
|
1035
|
+
/**
|
|
1036
|
+
* Apply the configured filter to the entity list
|
|
1037
|
+
*/
|
|
1038
|
+
applyEntityFilter(entities) {
|
|
1039
|
+
if (!this.entityFilter) {
|
|
1040
|
+
return entities;
|
|
1041
|
+
}
|
|
1042
|
+
return entities.filter(entity => {
|
|
1043
|
+
// Filter by application (via ApplicationEntities)
|
|
1044
|
+
if (this.entityFilter.applicationId) {
|
|
1045
|
+
if (!this.applicationEntityIds.has(entity.ID)) {
|
|
1046
|
+
return false;
|
|
1047
|
+
}
|
|
1048
|
+
}
|
|
1049
|
+
// Filter by schema names
|
|
1050
|
+
if (this.entityFilter.schemaNames && this.entityFilter.schemaNames.length > 0) {
|
|
1051
|
+
if (!this.entityFilter.schemaNames.includes(entity.SchemaName)) {
|
|
1052
|
+
return false;
|
|
1053
|
+
}
|
|
1054
|
+
}
|
|
1055
|
+
// Filter by explicit entity names
|
|
1056
|
+
if (this.entityFilter.entityNames && this.entityFilter.entityNames.length > 0) {
|
|
1057
|
+
if (!this.entityFilter.entityNames.includes(entity.Name)) {
|
|
1058
|
+
return false;
|
|
1059
|
+
}
|
|
1060
|
+
}
|
|
1061
|
+
// Filter out system entities unless explicitly included
|
|
1062
|
+
if (!this.entityFilter.includeSystemEntities) {
|
|
1063
|
+
// Skip entities with names starting with __ (MJ system entities)
|
|
1064
|
+
if (entity.Name.startsWith('__')) {
|
|
1065
|
+
return false;
|
|
1066
|
+
}
|
|
1067
|
+
// Could add more system schema checks here if needed
|
|
1068
|
+
}
|
|
1069
|
+
return true;
|
|
1070
|
+
});
|
|
1071
|
+
}
|
|
1072
|
+
/**
|
|
1073
|
+
* Handle entity selection from navigation panel or home screen
|
|
1074
|
+
*/
|
|
1075
|
+
onEntitySelected(entity) {
|
|
1076
|
+
this.resetRecordCounts();
|
|
1077
|
+
this.selectedEntity = entity;
|
|
1078
|
+
this.stateService.selectEntity(entity.Name);
|
|
1079
|
+
// Track entity access for recent entities
|
|
1080
|
+
this.stateService.trackEntityAccess(entity.Name, entity.ID);
|
|
1081
|
+
// mj-entity-viewer will automatically load data when entity changes
|
|
1082
|
+
}
|
|
1083
|
+
/**
|
|
1084
|
+
* Handle state changes from external sources
|
|
1085
|
+
*/
|
|
1086
|
+
onStateChanged() {
|
|
1087
|
+
if (this.state.selectedEntityName !== this.selectedEntity?.Name) {
|
|
1088
|
+
this.resetRecordCounts();
|
|
1089
|
+
this.selectedEntity = this.entities.find(e => e.Name === this.state.selectedEntityName) || null;
|
|
1090
|
+
}
|
|
1091
|
+
}
|
|
1092
|
+
/**
|
|
1093
|
+
* Reset record counts when entity changes.
|
|
1094
|
+
* The actual counts will be updated when mj-entity-viewer emits dataLoaded event.
|
|
1095
|
+
*/
|
|
1096
|
+
resetRecordCounts() {
|
|
1097
|
+
this.totalRecordCount = 0;
|
|
1098
|
+
this.filteredRecordCount = 0;
|
|
1099
|
+
}
|
|
1100
|
+
// ========================================
|
|
1101
|
+
// VIEW MANAGEMENT
|
|
1102
|
+
// ========================================
|
|
1103
|
+
/**
|
|
1104
|
+
* Handle view selection from view selector dropdown
|
|
1105
|
+
*/
|
|
1106
|
+
onViewSelected(event) {
|
|
1107
|
+
this.selectedViewEntity = event.view;
|
|
1108
|
+
this.stateService.selectView(event.viewId);
|
|
1109
|
+
// When a view is selected, apply its configuration
|
|
1110
|
+
if (event.view) {
|
|
1111
|
+
// Parse and apply the view's grid state
|
|
1112
|
+
this.currentGridState = this.parseViewGridState(event.view);
|
|
1113
|
+
// Apply the view's filter - for Smart Filter views, use SmartFilterPrompt
|
|
1114
|
+
// For regular filter views, the WhereClause is applied in the entity-viewer
|
|
1115
|
+
if (event.view.SmartFilterEnabled && event.view.SmartFilterPrompt) {
|
|
1116
|
+
this.stateService.setSmartFilterPrompt(event.view.SmartFilterPrompt);
|
|
1117
|
+
this.debouncedFilterText = event.view.SmartFilterPrompt;
|
|
1118
|
+
}
|
|
1119
|
+
else {
|
|
1120
|
+
// Clear the quick filter when switching to a view with regular filters
|
|
1121
|
+
this.stateService.setSmartFilterPrompt('');
|
|
1122
|
+
this.debouncedFilterText = '';
|
|
1123
|
+
}
|
|
1124
|
+
}
|
|
1125
|
+
else {
|
|
1126
|
+
// Switching to default view - clear grid state and filters
|
|
1127
|
+
this.currentGridState = null;
|
|
1128
|
+
this.stateService.setSmartFilterPrompt('');
|
|
1129
|
+
this.debouncedFilterText = '';
|
|
1130
|
+
}
|
|
1131
|
+
}
|
|
1132
|
+
/**
|
|
1133
|
+
* Parse GridState JSON from a UserView entity
|
|
1134
|
+
* Returns null if no valid GridState is present
|
|
1135
|
+
*/
|
|
1136
|
+
parseViewGridState(view) {
|
|
1137
|
+
if (!view.GridState) {
|
|
1138
|
+
return null;
|
|
1139
|
+
}
|
|
1140
|
+
try {
|
|
1141
|
+
const parsed = JSON.parse(view.GridState);
|
|
1142
|
+
// Validate structure - expect columnSettings array
|
|
1143
|
+
if (parsed && Array.isArray(parsed.columnSettings)) {
|
|
1144
|
+
return {
|
|
1145
|
+
columnSettings: parsed.columnSettings,
|
|
1146
|
+
sortSettings: parsed.sortSettings || []
|
|
1147
|
+
};
|
|
1148
|
+
}
|
|
1149
|
+
return null;
|
|
1150
|
+
}
|
|
1151
|
+
catch (error) {
|
|
1152
|
+
console.warn('[DataExplorer] Failed to parse GridState:', error);
|
|
1153
|
+
return null;
|
|
1154
|
+
}
|
|
1155
|
+
}
|
|
1156
|
+
/**
|
|
1157
|
+
* Handle grid state changes from entity-viewer (column resize, reorder, etc.)
|
|
1158
|
+
* Updates the local currentGridState and marks view as modified
|
|
1159
|
+
*/
|
|
1160
|
+
onGridStateChanged(event) {
|
|
1161
|
+
this.currentGridState = event.gridState;
|
|
1162
|
+
// Mark view as modified if we have a selected view
|
|
1163
|
+
if (this.state.selectedViewId) {
|
|
1164
|
+
this.stateService.setViewModified(true);
|
|
1165
|
+
}
|
|
1166
|
+
}
|
|
1167
|
+
/**
|
|
1168
|
+
* Handle save view request from view selector
|
|
1169
|
+
*/
|
|
1170
|
+
onSaveViewRequested(event) {
|
|
1171
|
+
// TODO: Implement in Phase 4 - View CRUD Operations
|
|
1172
|
+
// For now, just open the config panel where save functionality will be
|
|
1173
|
+
this.stateService.openViewConfigPanel();
|
|
1174
|
+
}
|
|
1175
|
+
/**
|
|
1176
|
+
* Handle manage views request - opens view browser
|
|
1177
|
+
*/
|
|
1178
|
+
onManageViewsRequested() {
|
|
1179
|
+
// TODO: Implement navigation to view management
|
|
1180
|
+
// For now, just log
|
|
1181
|
+
console.log('[DataExplorer] Manage views requested');
|
|
1182
|
+
}
|
|
1183
|
+
/**
|
|
1184
|
+
* Handle open in tab request
|
|
1185
|
+
*/
|
|
1186
|
+
onOpenInTabRequested(viewId) {
|
|
1187
|
+
// Use OpenEntityRecord to open the view as a resource in a new tab
|
|
1188
|
+
// Views are a known resource type in MJ
|
|
1189
|
+
const compositeKey = new CompositeKey();
|
|
1190
|
+
compositeKey.KeyValuePairs = [{ FieldName: 'ID', Value: viewId }];
|
|
1191
|
+
this.OpenEntityRecord.emit({
|
|
1192
|
+
EntityName: 'User Views',
|
|
1193
|
+
RecordPKey: compositeKey
|
|
1194
|
+
});
|
|
1195
|
+
}
|
|
1196
|
+
/**
|
|
1197
|
+
* Handle configure view request - opens the configuration panel
|
|
1198
|
+
*/
|
|
1199
|
+
onConfigureViewRequested() {
|
|
1200
|
+
this.stateService.openViewConfigPanel();
|
|
1201
|
+
}
|
|
1202
|
+
/**
|
|
1203
|
+
* Close the view configuration panel
|
|
1204
|
+
*/
|
|
1205
|
+
onCloseViewConfigPanel() {
|
|
1206
|
+
this.stateService.closeViewConfigPanel();
|
|
1207
|
+
}
|
|
1208
|
+
/**
|
|
1209
|
+
* Handle save view from config panel
|
|
1210
|
+
*/
|
|
1211
|
+
async onSaveView(event) {
|
|
1212
|
+
if (!this.selectedEntity)
|
|
1213
|
+
return;
|
|
1214
|
+
try {
|
|
1215
|
+
const md = new Metadata();
|
|
1216
|
+
// Build GridState in Kendo-compatible format
|
|
1217
|
+
const gridState = this.buildGridState(event);
|
|
1218
|
+
// Build SortState in Kendo-compatible format
|
|
1219
|
+
const sortState = this.buildSortState(event);
|
|
1220
|
+
if (event.saveAsNew || !this.selectedViewEntity) {
|
|
1221
|
+
// Create new view
|
|
1222
|
+
const newView = await md.GetEntityObject('User Views');
|
|
1223
|
+
newView.Name = event.name || 'Custom';
|
|
1224
|
+
newView.Description = event.description;
|
|
1225
|
+
newView.EntityID = this.selectedEntity.ID;
|
|
1226
|
+
newView.UserID = md.CurrentUser.ID;
|
|
1227
|
+
newView.IsShared = event.isShared;
|
|
1228
|
+
newView.IsDefault = false;
|
|
1229
|
+
// Set GridState and SortState
|
|
1230
|
+
if (gridState) {
|
|
1231
|
+
newView.GridState = JSON.stringify(gridState);
|
|
1232
|
+
}
|
|
1233
|
+
if (sortState) {
|
|
1234
|
+
newView.SortState = JSON.stringify(sortState);
|
|
1235
|
+
}
|
|
1236
|
+
// Set Smart Filter settings
|
|
1237
|
+
newView.SmartFilterEnabled = event.smartFilterEnabled;
|
|
1238
|
+
newView.SmartFilterPrompt = event.smartFilterPrompt;
|
|
1239
|
+
const saved = await newView.Save();
|
|
1240
|
+
if (saved) {
|
|
1241
|
+
this.selectedViewEntity = newView;
|
|
1242
|
+
this.stateService.selectView(newView.ID);
|
|
1243
|
+
this.stateService.setViewModified(false);
|
|
1244
|
+
// Update currentGridState from the saved view to refresh the grid
|
|
1245
|
+
this.currentGridState = this.parseViewGridState(newView);
|
|
1246
|
+
// Refresh the view selector dropdown
|
|
1247
|
+
await this.viewSelectorRef?.loadViews();
|
|
1248
|
+
// Note: For new views, ngOnChanges will trigger refresh automatically
|
|
1249
|
+
// because selectedViewEntity reference changes
|
|
1250
|
+
}
|
|
1251
|
+
}
|
|
1252
|
+
else {
|
|
1253
|
+
// Update existing view
|
|
1254
|
+
this.selectedViewEntity.Name = event.name;
|
|
1255
|
+
this.selectedViewEntity.Description = event.description;
|
|
1256
|
+
this.selectedViewEntity.IsShared = event.isShared;
|
|
1257
|
+
// Update GridState and SortState
|
|
1258
|
+
if (gridState) {
|
|
1259
|
+
this.selectedViewEntity.GridState = JSON.stringify(gridState);
|
|
1260
|
+
}
|
|
1261
|
+
if (sortState) {
|
|
1262
|
+
this.selectedViewEntity.SortState = JSON.stringify(sortState);
|
|
1263
|
+
}
|
|
1264
|
+
// Update Smart Filter settings
|
|
1265
|
+
this.selectedViewEntity.SmartFilterEnabled = event.smartFilterEnabled;
|
|
1266
|
+
this.selectedViewEntity.SmartFilterPrompt = event.smartFilterPrompt;
|
|
1267
|
+
const saved = await this.selectedViewEntity.Save();
|
|
1268
|
+
if (saved) {
|
|
1269
|
+
this.stateService.setViewModified(false);
|
|
1270
|
+
// Update currentGridState from the saved view to refresh the grid columns
|
|
1271
|
+
this.currentGridState = this.parseViewGridState(this.selectedViewEntity);
|
|
1272
|
+
// Force change detection to ensure grid picks up the new gridState
|
|
1273
|
+
this.cdr.detectChanges();
|
|
1274
|
+
// Refresh the view selector dropdown
|
|
1275
|
+
await this.viewSelectorRef?.loadViews();
|
|
1276
|
+
// Note: Grid will rebuild columns via ngOnChanges when currentGridState changes
|
|
1277
|
+
}
|
|
1278
|
+
}
|
|
1279
|
+
this.stateService.closeViewConfigPanel();
|
|
1280
|
+
this.cdr.detectChanges();
|
|
1281
|
+
}
|
|
1282
|
+
catch (error) {
|
|
1283
|
+
console.error('[DataExplorer] Error saving view:', error);
|
|
1284
|
+
}
|
|
1285
|
+
}
|
|
1286
|
+
/**
|
|
1287
|
+
* Build GridState in Kendo-compatible format
|
|
1288
|
+
* Format: { columnSettings: [{ID, Name, DisplayName, hidden, width, orderIndex}], sortSettings: [{field, dir}] }
|
|
1289
|
+
*
|
|
1290
|
+
* Priority for column settings:
|
|
1291
|
+
* 1. If event.columns provided (from config panel) - use those
|
|
1292
|
+
* 2. If currentGridState exists (from grid interactions) - use that
|
|
1293
|
+
* 3. Otherwise return null
|
|
1294
|
+
*/
|
|
1295
|
+
buildGridState(event) {
|
|
1296
|
+
let columnSettings;
|
|
1297
|
+
// First check if the event has columns configured (from config panel)
|
|
1298
|
+
if (event.columns.length > 0) {
|
|
1299
|
+
columnSettings = event.columns.map((col, idx) => ({
|
|
1300
|
+
ID: col.fieldId,
|
|
1301
|
+
Name: col.fieldName,
|
|
1302
|
+
DisplayName: col.displayName,
|
|
1303
|
+
hidden: false, // Visible columns only
|
|
1304
|
+
width: col.width || null,
|
|
1305
|
+
orderIndex: idx
|
|
1306
|
+
}));
|
|
1307
|
+
}
|
|
1308
|
+
// Otherwise, use the current grid state if available (from grid interactions)
|
|
1309
|
+
else if (this.currentGridState?.columnSettings && this.currentGridState.columnSettings.length > 0) {
|
|
1310
|
+
columnSettings = this.currentGridState.columnSettings;
|
|
1311
|
+
}
|
|
1312
|
+
// No columns to save
|
|
1313
|
+
else {
|
|
1314
|
+
return null;
|
|
1315
|
+
}
|
|
1316
|
+
// Build sort settings - prefer event.sortField, fall back to currentGridState
|
|
1317
|
+
let sortSettings;
|
|
1318
|
+
if (event.sortField) {
|
|
1319
|
+
sortSettings = [{
|
|
1320
|
+
field: event.sortField,
|
|
1321
|
+
dir: event.sortDirection // 'asc' or 'desc'
|
|
1322
|
+
}];
|
|
1323
|
+
}
|
|
1324
|
+
else if (this.currentGridState?.sortSettings && this.currentGridState.sortSettings.length > 0) {
|
|
1325
|
+
sortSettings = this.currentGridState.sortSettings;
|
|
1326
|
+
}
|
|
1327
|
+
return { columnSettings, sortSettings };
|
|
1328
|
+
}
|
|
1329
|
+
/**
|
|
1330
|
+
* Build SortState in Kendo-compatible format
|
|
1331
|
+
* Format: [{field, direction}] where direction is 'asc' or 'desc'
|
|
1332
|
+
*/
|
|
1333
|
+
buildSortState(event) {
|
|
1334
|
+
if (!event.sortField)
|
|
1335
|
+
return null;
|
|
1336
|
+
return [{
|
|
1337
|
+
field: event.sortField,
|
|
1338
|
+
direction: event.sortDirection // 'asc' or 'desc'
|
|
1339
|
+
}];
|
|
1340
|
+
}
|
|
1341
|
+
/**
|
|
1342
|
+
* Handle delete view from config panel
|
|
1343
|
+
*/
|
|
1344
|
+
async onDeleteView() {
|
|
1345
|
+
if (!this.selectedViewEntity)
|
|
1346
|
+
return;
|
|
1347
|
+
try {
|
|
1348
|
+
const deleted = await this.selectedViewEntity.Delete();
|
|
1349
|
+
if (deleted) {
|
|
1350
|
+
this.selectedViewEntity = null;
|
|
1351
|
+
this.stateService.selectView(null);
|
|
1352
|
+
this.stateService.closeViewConfigPanel();
|
|
1353
|
+
}
|
|
1354
|
+
}
|
|
1355
|
+
catch (error) {
|
|
1356
|
+
console.error('[DataExplorer] Error deleting view:', error);
|
|
1357
|
+
}
|
|
1358
|
+
}
|
|
1359
|
+
// ========================================
|
|
1360
|
+
// VIEW MODE & FILTERING (Dashboard Header)
|
|
1361
|
+
// ========================================
|
|
1362
|
+
/**
|
|
1363
|
+
* Handle view mode toggle from dashboard header
|
|
1364
|
+
*/
|
|
1365
|
+
onViewModeChanged(mode) {
|
|
1366
|
+
this.stateService.setViewMode(mode);
|
|
1367
|
+
// Mark view as modified when view mode changes
|
|
1368
|
+
if (this.state.selectedViewId) {
|
|
1369
|
+
this.stateService.setViewModified(true);
|
|
1370
|
+
}
|
|
1371
|
+
}
|
|
1372
|
+
/**
|
|
1373
|
+
* Handle smart filter change from dashboard header
|
|
1374
|
+
*/
|
|
1375
|
+
onSmartFilterChanged(prompt) {
|
|
1376
|
+
this.stateService.setSmartFilterPrompt(prompt);
|
|
1377
|
+
this.filterInput$.next(prompt);
|
|
1378
|
+
}
|
|
1379
|
+
/**
|
|
1380
|
+
* Handle filter text change from mj-entity-viewer (two-way binding)
|
|
1381
|
+
*/
|
|
1382
|
+
onFilterTextChanged(filterText) {
|
|
1383
|
+
this.stateService.setSmartFilterPrompt(filterText);
|
|
1384
|
+
}
|
|
1385
|
+
// ========================================
|
|
1386
|
+
// ENTITY VIEWER EVENT HANDLERS
|
|
1387
|
+
// ========================================
|
|
1388
|
+
/**
|
|
1389
|
+
* Handle record selection from mj-entity-viewer
|
|
1390
|
+
*/
|
|
1391
|
+
onViewerRecordSelected(event) {
|
|
1392
|
+
this.selectedRecord = event.record;
|
|
1393
|
+
// When selecting from grid, detail panel entity matches the grid entity
|
|
1394
|
+
this.detailPanelEntity = this.selectedEntity;
|
|
1395
|
+
const recordName = this.getRecordDisplayName(event.record);
|
|
1396
|
+
this.stateService.selectRecord(event.record.PrimaryKey.ToConcatenatedString(), recordName);
|
|
1397
|
+
// Add to recent items (local state for navigation panel)
|
|
1398
|
+
if (this.selectedEntity) {
|
|
1399
|
+
this.stateService.addRecentItem({
|
|
1400
|
+
entityName: this.selectedEntity.Name,
|
|
1401
|
+
compositeKeyString: event.record.PrimaryKey.ToConcatenatedString(),
|
|
1402
|
+
displayName: recordName
|
|
1403
|
+
});
|
|
1404
|
+
// Update local recent records immediately for instant home screen updates
|
|
1405
|
+
const recordId = event.record.PrimaryKey.KeyValuePairs[0]?.Value?.toString() || '';
|
|
1406
|
+
this.stateService.addLocalRecentRecord(this.selectedEntity.Name, this.selectedEntity.ID, recordId, recordName);
|
|
1407
|
+
// Log to User Record Logs for persistence (fire-and-forget)
|
|
1408
|
+
this.recentAccessService.logAccess(this.selectedEntity.Name, event.record.PrimaryKey.Values(), 'record');
|
|
1409
|
+
}
|
|
1410
|
+
}
|
|
1411
|
+
/**
|
|
1412
|
+
* Handle record opened from mj-entity-viewer (double-click or open button)
|
|
1413
|
+
*/
|
|
1414
|
+
onViewerRecordOpened(event) {
|
|
1415
|
+
this.OpenEntityRecord.emit({
|
|
1416
|
+
EntityName: event.entity.Name,
|
|
1417
|
+
RecordPKey: event.compositeKey
|
|
1418
|
+
});
|
|
1419
|
+
}
|
|
1420
|
+
/**
|
|
1421
|
+
* Handle data loaded from mj-entity-viewer
|
|
1422
|
+
*/
|
|
1423
|
+
onDataLoaded(event) {
|
|
1424
|
+
this.totalRecordCount = event.totalRowCount;
|
|
1425
|
+
this.filteredRecordCount = event.loadedRowCount;
|
|
1426
|
+
// Store loaded records for back/forward navigation lookup
|
|
1427
|
+
this.loadedRecords = event.records;
|
|
1428
|
+
// Handle pending record selection from deep link
|
|
1429
|
+
if (this.pendingRecordSelection) {
|
|
1430
|
+
const recordId = this.pendingRecordSelection;
|
|
1431
|
+
this.pendingRecordSelection = null; // Clear it so we don't keep trying
|
|
1432
|
+
// Try to find the record by primary key or concatenated string
|
|
1433
|
+
const record = event.records.find(r => {
|
|
1434
|
+
const pkString = r.PrimaryKey.ToConcatenatedString();
|
|
1435
|
+
const pkValue = r.PrimaryKey.KeyValuePairs[0]?.Value?.toString();
|
|
1436
|
+
return pkString === recordId || pkValue === recordId;
|
|
1437
|
+
});
|
|
1438
|
+
if (record) {
|
|
1439
|
+
this.selectedRecord = record;
|
|
1440
|
+
this.detailPanelEntity = this.selectedEntity;
|
|
1441
|
+
const recordName = this.getRecordDisplayName(record);
|
|
1442
|
+
this.stateService.selectRecord(record.PrimaryKey.ToConcatenatedString(), recordName);
|
|
1443
|
+
}
|
|
1444
|
+
else {
|
|
1445
|
+
console.warn(`[DataExplorer] Deep link record not found: ${recordId}`);
|
|
1446
|
+
}
|
|
1447
|
+
}
|
|
1448
|
+
// Restore selected record if we have a persisted selectedRecordId
|
|
1449
|
+
else if (this.state.selectedRecordId && this.state.detailPanelOpen && !this.selectedRecord) {
|
|
1450
|
+
const record = event.records.find(r => r.PrimaryKey.ToConcatenatedString() === this.state.selectedRecordId);
|
|
1451
|
+
if (record) {
|
|
1452
|
+
this.selectedRecord = record;
|
|
1453
|
+
this.detailPanelEntity = this.selectedEntity;
|
|
1454
|
+
}
|
|
1455
|
+
}
|
|
1456
|
+
this.cdr.detectChanges();
|
|
1457
|
+
}
|
|
1458
|
+
/**
|
|
1459
|
+
* Handle filtered count change from mj-entity-viewer
|
|
1460
|
+
*/
|
|
1461
|
+
onFilteredCountChanged(event) {
|
|
1462
|
+
this.filteredRecordCount = event.filteredCount;
|
|
1463
|
+
this.totalRecordCount = event.totalCount;
|
|
1464
|
+
this.cdr.detectChanges();
|
|
1465
|
+
}
|
|
1466
|
+
// ========================================
|
|
1467
|
+
// DETAIL PANEL
|
|
1468
|
+
// ========================================
|
|
1469
|
+
/**
|
|
1470
|
+
* Handle detail panel close
|
|
1471
|
+
*/
|
|
1472
|
+
onDetailPanelClosed() {
|
|
1473
|
+
this.selectedRecord = null;
|
|
1474
|
+
this.detailPanelEntity = null;
|
|
1475
|
+
this.stateService.closeDetailPanel();
|
|
1476
|
+
}
|
|
1477
|
+
/**
|
|
1478
|
+
* Handle opening a record in full view (from detail panel)
|
|
1479
|
+
* Uses detailPanelEntity since the panel may be showing a different entity than the grid
|
|
1480
|
+
*/
|
|
1481
|
+
onOpenRecord(record) {
|
|
1482
|
+
if (!this.detailPanelEntity)
|
|
1483
|
+
return;
|
|
1484
|
+
this.OpenEntityRecord.emit({
|
|
1485
|
+
EntityName: this.detailPanelEntity.Name,
|
|
1486
|
+
RecordPKey: record.PrimaryKey
|
|
1487
|
+
});
|
|
1488
|
+
}
|
|
1489
|
+
/**
|
|
1490
|
+
* Handle navigation to a related entity from detail panel.
|
|
1491
|
+
* Navigates within the explorer and applies filter to show related records.
|
|
1492
|
+
*/
|
|
1493
|
+
onNavigateToRelated(event) {
|
|
1494
|
+
const entity = this.entities.find(e => e.Name === event.entityName);
|
|
1495
|
+
if (!entity) {
|
|
1496
|
+
// Entity not in our filtered list - it may exist in the system but not be part of this app
|
|
1497
|
+
console.warn(`Entity not found in explorer: ${event.entityName}`);
|
|
1498
|
+
return;
|
|
1499
|
+
}
|
|
1500
|
+
// Close detail panel and clear current record
|
|
1501
|
+
this.selectedRecord = null;
|
|
1502
|
+
this.detailPanelEntity = null;
|
|
1503
|
+
this.stateService.closeDetailPanel();
|
|
1504
|
+
// Navigate to the entity
|
|
1505
|
+
this.selectedEntity = entity;
|
|
1506
|
+
this.stateService.selectEntity(entity.Name);
|
|
1507
|
+
// Apply the filter to show related records
|
|
1508
|
+
// The filter is in SQL format like "ParentID='xxx'" - we just show it in the filter box
|
|
1509
|
+
// The entity viewer will apply it as a smart filter
|
|
1510
|
+
if (event.filter) {
|
|
1511
|
+
// For now, we'll apply the filter as-is
|
|
1512
|
+
// A future enhancement could parse and display it more user-friendly
|
|
1513
|
+
this.stateService.setSmartFilterPrompt(event.filter);
|
|
1514
|
+
this.debouncedFilterText = event.filter;
|
|
1515
|
+
}
|
|
1516
|
+
}
|
|
1517
|
+
/**
|
|
1518
|
+
* Handle opening a related record - display in detail panel (not new tab)
|
|
1519
|
+
* The record is already loaded, so just update the detail panel
|
|
1520
|
+
*/
|
|
1521
|
+
onOpenRelatedRecord(event) {
|
|
1522
|
+
this.showRecordInDetailPanel(event.entityName, event.record);
|
|
1523
|
+
}
|
|
1524
|
+
/**
|
|
1525
|
+
* Handle opening a foreign key record (from FK field link in detail panel)
|
|
1526
|
+
* Loads the record and displays it in the detail panel
|
|
1527
|
+
*/
|
|
1528
|
+
async onOpenForeignKeyRecord(event) {
|
|
1529
|
+
await this.loadAndShowRecordInDetailPanel(event.entityName, event.recordId);
|
|
1530
|
+
}
|
|
1531
|
+
/**
|
|
1532
|
+
* Show an already-loaded record in the detail panel
|
|
1533
|
+
* Note: This does NOT change selectedEntity (the main grid's entity)
|
|
1534
|
+
* It only updates detailPanelEntity which is used by the detail panel
|
|
1535
|
+
*/
|
|
1536
|
+
showRecordInDetailPanel(entityName, record) {
|
|
1537
|
+
const entityInfo = this.metadata.Entities.find(e => e.Name === entityName);
|
|
1538
|
+
if (!entityInfo) {
|
|
1539
|
+
console.warn(`Entity not found: ${entityName}`);
|
|
1540
|
+
return;
|
|
1541
|
+
}
|
|
1542
|
+
// Update the detail panel entity and record
|
|
1543
|
+
// detailPanelEntity may differ from selectedEntity when viewing FK/related records
|
|
1544
|
+
this.detailPanelEntity = entityInfo;
|
|
1545
|
+
this.selectedRecord = record;
|
|
1546
|
+
// Use selectRecord to open the panel with proper state tracking
|
|
1547
|
+
const recordName = this.getRecordDisplayName(record);
|
|
1548
|
+
this.stateService.selectRecord(record.PrimaryKey.ToConcatenatedString(), recordName);
|
|
1549
|
+
this.cdr.detectChanges();
|
|
1550
|
+
}
|
|
1551
|
+
/**
|
|
1552
|
+
* Load a record by ID and show it in the detail panel
|
|
1553
|
+
*/
|
|
1554
|
+
async loadAndShowRecordInDetailPanel(entityName, recordId) {
|
|
1555
|
+
const entityInfo = this.metadata.Entities.find(e => e.Name === entityName);
|
|
1556
|
+
if (!entityInfo) {
|
|
1557
|
+
console.warn(`Entity not found: ${entityName}`);
|
|
1558
|
+
return;
|
|
1559
|
+
}
|
|
1560
|
+
try {
|
|
1561
|
+
// Load the record
|
|
1562
|
+
const rv = new RunView();
|
|
1563
|
+
const result = await rv.RunView({
|
|
1564
|
+
EntityName: entityName,
|
|
1565
|
+
ExtraFilter: `ID='${recordId}'`,
|
|
1566
|
+
ResultType: 'entity_object',
|
|
1567
|
+
MaxRows: 1
|
|
1568
|
+
});
|
|
1569
|
+
if (result.Success && result.Results.length > 0) {
|
|
1570
|
+
this.showRecordInDetailPanel(entityName, result.Results[0]);
|
|
1571
|
+
}
|
|
1572
|
+
else {
|
|
1573
|
+
console.warn(`Record not found: ${entityName} ID=${recordId}`);
|
|
1574
|
+
}
|
|
1575
|
+
}
|
|
1576
|
+
catch (error) {
|
|
1577
|
+
console.error(`Failed to load record: ${entityName} ID=${recordId}`, error);
|
|
1578
|
+
}
|
|
1579
|
+
}
|
|
1580
|
+
// ========================================
|
|
1581
|
+
// NAVIGATION PANEL
|
|
1582
|
+
// ========================================
|
|
1583
|
+
/**
|
|
1584
|
+
* Handle opening a record from navigation panel (recent/favorites)
|
|
1585
|
+
*/
|
|
1586
|
+
onOpenRecordFromNav(event) {
|
|
1587
|
+
this.OpenEntityRecord.emit({
|
|
1588
|
+
EntityName: event.entityName,
|
|
1589
|
+
RecordPKey: event.compositeKey
|
|
1590
|
+
});
|
|
1591
|
+
}
|
|
1592
|
+
/**
|
|
1593
|
+
* Handle selecting a record from navigation panel (recent/favorites).
|
|
1594
|
+
* Navigates to the entity within Data Explorer and selects the record
|
|
1595
|
+
* in the detail panel (instead of opening full record view).
|
|
1596
|
+
*/
|
|
1597
|
+
onSelectRecordFromNav(event) {
|
|
1598
|
+
const entity = this.entities.find(e => e.Name === event.entityName);
|
|
1599
|
+
if (entity) {
|
|
1600
|
+
// Set pending record selection - will be resolved in onDataLoaded
|
|
1601
|
+
this.pendingRecordSelection = event.recordId;
|
|
1602
|
+
this.onEntitySelected(entity);
|
|
1603
|
+
}
|
|
1604
|
+
}
|
|
1605
|
+
/**
|
|
1606
|
+
* Toggle navigation panel
|
|
1607
|
+
*/
|
|
1608
|
+
toggleNavigationPanel() {
|
|
1609
|
+
this.stateService.toggleNavigationPanel();
|
|
1610
|
+
}
|
|
1611
|
+
/**
|
|
1612
|
+
* Handle expand and focus from collapsed nav icon click
|
|
1613
|
+
*/
|
|
1614
|
+
onExpandAndFocus(section) {
|
|
1615
|
+
this.stateService.expandAndFocusSection(section);
|
|
1616
|
+
}
|
|
1617
|
+
// ========================================
|
|
1618
|
+
// DEEP LINK HANDLING
|
|
1619
|
+
// ========================================
|
|
1620
|
+
/**
|
|
1621
|
+
* Apply a deep link to navigate to a specific entity/record
|
|
1622
|
+
*/
|
|
1623
|
+
async applyDeepLink(deepLink) {
|
|
1624
|
+
// Apply view mode if specified
|
|
1625
|
+
if (deepLink.viewMode) {
|
|
1626
|
+
this.stateService.setViewMode(deepLink.viewMode);
|
|
1627
|
+
}
|
|
1628
|
+
// Navigate to entity if specified
|
|
1629
|
+
if (deepLink.entity) {
|
|
1630
|
+
const entity = this.entities.find(e => e.Name.toLowerCase() === deepLink.entity.toLowerCase());
|
|
1631
|
+
if (entity) {
|
|
1632
|
+
// Reset counts before setting entity to prevent stale data display
|
|
1633
|
+
this.resetRecordCounts();
|
|
1634
|
+
this.selectedEntity = entity;
|
|
1635
|
+
this.stateService.selectEntity(entity.Name);
|
|
1636
|
+
// Apply filter if specified
|
|
1637
|
+
if (deepLink.filter) {
|
|
1638
|
+
this.stateService.setSmartFilterPrompt(deepLink.filter);
|
|
1639
|
+
this.debouncedFilterText = deepLink.filter;
|
|
1640
|
+
}
|
|
1641
|
+
// Note: Record selection is handled after data loads via onDataLoaded
|
|
1642
|
+
// We store the record ID to select once data is available
|
|
1643
|
+
if (deepLink.record) {
|
|
1644
|
+
this.pendingRecordSelection = deepLink.record;
|
|
1645
|
+
}
|
|
1646
|
+
}
|
|
1647
|
+
else {
|
|
1648
|
+
console.warn(`[DataExplorer] Deep link entity not found: ${deepLink.entity}`);
|
|
1649
|
+
}
|
|
1650
|
+
}
|
|
1651
|
+
}
|
|
1652
|
+
/** Record ID to select once data loads (from deep link) */
|
|
1653
|
+
pendingRecordSelection = null;
|
|
1654
|
+
// ========================================
|
|
1655
|
+
// BREADCRUMB NAVIGATION
|
|
1656
|
+
// ========================================
|
|
1657
|
+
/**
|
|
1658
|
+
* Handle breadcrumb click - navigate to that level
|
|
1659
|
+
*/
|
|
1660
|
+
onBreadcrumbClick(breadcrumb, index) {
|
|
1661
|
+
// Don't navigate if it's the last (current) breadcrumb
|
|
1662
|
+
if (index === this.breadcrumbs.length - 1) {
|
|
1663
|
+
return;
|
|
1664
|
+
}
|
|
1665
|
+
this.stateService.navigateToBreadcrumb(breadcrumb);
|
|
1666
|
+
// If navigating to application level, clear entity selection
|
|
1667
|
+
if (breadcrumb.type === 'application') {
|
|
1668
|
+
this.selectedEntity = null;
|
|
1669
|
+
this.selectedRecord = null;
|
|
1670
|
+
this.detailPanelEntity = null;
|
|
1671
|
+
}
|
|
1672
|
+
}
|
|
1673
|
+
// ========================================
|
|
1674
|
+
// HELPERS
|
|
1675
|
+
// ========================================
|
|
1676
|
+
/**
|
|
1677
|
+
* Get the set of allowed entity names for filtering favorites/recents.
|
|
1678
|
+
* Returns null if no filter is active (all entities allowed).
|
|
1679
|
+
*/
|
|
1680
|
+
get allowedEntityNames() {
|
|
1681
|
+
if (!this.entityFilter) {
|
|
1682
|
+
return null;
|
|
1683
|
+
}
|
|
1684
|
+
return new Set(this.entities.map(e => e.Name));
|
|
1685
|
+
}
|
|
1686
|
+
/**
|
|
1687
|
+
* Get display name for a record
|
|
1688
|
+
*/
|
|
1689
|
+
getRecordDisplayName(record) {
|
|
1690
|
+
if (!this.selectedEntity)
|
|
1691
|
+
return 'Unknown';
|
|
1692
|
+
if (this.selectedEntity.NameField) {
|
|
1693
|
+
const nameValue = record.Get(this.selectedEntity.NameField.Name);
|
|
1694
|
+
if (nameValue)
|
|
1695
|
+
return String(nameValue);
|
|
1696
|
+
}
|
|
1697
|
+
return record.PrimaryKey.ToString();
|
|
1698
|
+
}
|
|
1699
|
+
/**
|
|
1700
|
+
* Get the icon class for an entity
|
|
1701
|
+
*/
|
|
1702
|
+
getEntityIcon(entity) {
|
|
1703
|
+
if (entity.Icon) {
|
|
1704
|
+
return this.formatEntityIcon(entity.Icon);
|
|
1705
|
+
}
|
|
1706
|
+
return 'fa-solid fa-table';
|
|
1707
|
+
}
|
|
1708
|
+
/**
|
|
1709
|
+
* Format entity icon to ensure proper Font Awesome class format
|
|
1710
|
+
*/
|
|
1711
|
+
formatEntityIcon(icon) {
|
|
1712
|
+
if (!icon) {
|
|
1713
|
+
return 'fa-solid fa-table';
|
|
1714
|
+
}
|
|
1715
|
+
if (icon.startsWith('fa-') || icon.startsWith('fa ')) {
|
|
1716
|
+
if (icon.startsWith('fa-solid') || icon.startsWith('fa-regular') ||
|
|
1717
|
+
icon.startsWith('fa-light') || icon.startsWith('fa-brands') ||
|
|
1718
|
+
icon.startsWith('fa ')) {
|
|
1719
|
+
return icon;
|
|
1720
|
+
}
|
|
1721
|
+
return `fa-solid ${icon}`;
|
|
1722
|
+
}
|
|
1723
|
+
return `fa-solid fa-${icon}`;
|
|
1724
|
+
}
|
|
1725
|
+
// ========================================
|
|
1726
|
+
// URL DEEP LINKING
|
|
1727
|
+
// ========================================
|
|
1728
|
+
/**
|
|
1729
|
+
* Parse URL query string and return a deep link object.
|
|
1730
|
+
* Returns null if no relevant params found.
|
|
1731
|
+
*
|
|
1732
|
+
* Query params:
|
|
1733
|
+
* - entity: Selected entity name
|
|
1734
|
+
* - record: Selected record ID (URL segment format via CompositeKey)
|
|
1735
|
+
* - filter: Current filter text
|
|
1736
|
+
* - view: View mode (grid or cards)
|
|
1737
|
+
*/
|
|
1738
|
+
parseUrlState() {
|
|
1739
|
+
const url = this.router.url;
|
|
1740
|
+
const queryIndex = url.indexOf('?');
|
|
1741
|
+
if (queryIndex === -1) {
|
|
1742
|
+
return null;
|
|
1743
|
+
}
|
|
1744
|
+
const queryString = url.substring(queryIndex + 1);
|
|
1745
|
+
const params = new URLSearchParams(queryString);
|
|
1746
|
+
const entity = params.get('entity');
|
|
1747
|
+
const record = params.get('record');
|
|
1748
|
+
const filter = params.get('filter');
|
|
1749
|
+
const view = params.get('view');
|
|
1750
|
+
// If no params, return null
|
|
1751
|
+
if (!entity && !record && !filter && !view) {
|
|
1752
|
+
return null;
|
|
1753
|
+
}
|
|
1754
|
+
return {
|
|
1755
|
+
entity: entity || undefined,
|
|
1756
|
+
record: record || undefined,
|
|
1757
|
+
filter: filter || undefined,
|
|
1758
|
+
viewMode: view || undefined
|
|
1759
|
+
};
|
|
1760
|
+
}
|
|
1761
|
+
/**
|
|
1762
|
+
* Apply URL state to the component.
|
|
1763
|
+
* Used both during init and for popstate handling.
|
|
1764
|
+
*/
|
|
1765
|
+
applyUrlState(urlState) {
|
|
1766
|
+
// Apply view mode if specified
|
|
1767
|
+
if (urlState.viewMode) {
|
|
1768
|
+
this.stateService.setViewMode(urlState.viewMode);
|
|
1769
|
+
}
|
|
1770
|
+
// Navigate to entity if specified
|
|
1771
|
+
if (urlState.entity) {
|
|
1772
|
+
const entity = this.entities.find(e => e.Name.toLowerCase() === urlState.entity.toLowerCase());
|
|
1773
|
+
if (entity) {
|
|
1774
|
+
const entityChanged = this.selectedEntity?.Name !== entity.Name;
|
|
1775
|
+
if (entityChanged) {
|
|
1776
|
+
// Entity changed - reset counts and select new entity
|
|
1777
|
+
this.resetRecordCounts();
|
|
1778
|
+
this.selectedEntity = entity;
|
|
1779
|
+
this.stateService.selectEntity(entity.Name);
|
|
1780
|
+
}
|
|
1781
|
+
// Apply filter if specified
|
|
1782
|
+
if (urlState.filter) {
|
|
1783
|
+
this.stateService.setSmartFilterPrompt(urlState.filter);
|
|
1784
|
+
this.debouncedFilterText = urlState.filter;
|
|
1785
|
+
}
|
|
1786
|
+
else if (entityChanged) {
|
|
1787
|
+
// Only clear filter if entity changed (selectEntity already handles this)
|
|
1788
|
+
this.stateService.setSmartFilterPrompt('');
|
|
1789
|
+
this.debouncedFilterText = '';
|
|
1790
|
+
}
|
|
1791
|
+
// Handle record selection
|
|
1792
|
+
if (urlState.record) {
|
|
1793
|
+
if (entityChanged) {
|
|
1794
|
+
// Entity changed - need to wait for data to load
|
|
1795
|
+
this.pendingRecordSelection = urlState.record;
|
|
1796
|
+
}
|
|
1797
|
+
else {
|
|
1798
|
+
// Entity is the same - find record from already-loaded data
|
|
1799
|
+
const record = this.loadedRecords.find(r => {
|
|
1800
|
+
const pkString = r.PrimaryKey.ToConcatenatedString();
|
|
1801
|
+
const pkValue = r.PrimaryKey.KeyValuePairs[0]?.Value?.toString();
|
|
1802
|
+
return pkString === urlState.record || pkValue === urlState.record;
|
|
1803
|
+
});
|
|
1804
|
+
if (record) {
|
|
1805
|
+
this.selectedRecord = record;
|
|
1806
|
+
this.detailPanelEntity = this.selectedEntity;
|
|
1807
|
+
const recordName = this.getRecordDisplayName(record);
|
|
1808
|
+
this.stateService.selectRecord(record.PrimaryKey.ToConcatenatedString(), recordName);
|
|
1809
|
+
}
|
|
1810
|
+
else {
|
|
1811
|
+
// Record not in current page - update state but panel won't show
|
|
1812
|
+
this.stateService.selectRecord(urlState.record, this.state.selectedRecordName || undefined);
|
|
1813
|
+
}
|
|
1814
|
+
}
|
|
1815
|
+
}
|
|
1816
|
+
else {
|
|
1817
|
+
// Clear record selection if not in URL
|
|
1818
|
+
this.selectedRecord = null;
|
|
1819
|
+
this.detailPanelEntity = null;
|
|
1820
|
+
this.stateService.closeDetailPanel();
|
|
1821
|
+
}
|
|
1822
|
+
}
|
|
1823
|
+
}
|
|
1824
|
+
else {
|
|
1825
|
+
// No entity in URL - go to home view
|
|
1826
|
+
this.selectedEntity = null;
|
|
1827
|
+
this.selectedRecord = null;
|
|
1828
|
+
this.detailPanelEntity = null;
|
|
1829
|
+
this.stateService.selectEntity(null);
|
|
1830
|
+
this.stateService.closeDetailPanel();
|
|
1831
|
+
}
|
|
1832
|
+
this.cdr.detectChanges();
|
|
1833
|
+
}
|
|
1834
|
+
/**
|
|
1835
|
+
* Update the URL query string to reflect current navigation state.
|
|
1836
|
+
* This enables deep linking - users can bookmark or share URLs to specific views.
|
|
1837
|
+
* Called immediately on state changes (not debounced).
|
|
1838
|
+
* Uses Angular Router for proper browser history integration.
|
|
1839
|
+
*/
|
|
1840
|
+
updateUrl() {
|
|
1841
|
+
const params = new URLSearchParams();
|
|
1842
|
+
// Add entity if selected
|
|
1843
|
+
if (this.state.selectedEntityName) {
|
|
1844
|
+
params.set('entity', this.state.selectedEntityName);
|
|
1845
|
+
}
|
|
1846
|
+
// Add record if selected (only if entity is also selected)
|
|
1847
|
+
// Use the stored selectedRecordId which is already in URL segment format
|
|
1848
|
+
if (this.state.selectedRecordId && this.state.selectedEntityName) {
|
|
1849
|
+
// The selectedRecordId is stored using ToConcatenatedString which uses the same format as ToURLSegment
|
|
1850
|
+
params.set('record', this.state.selectedRecordId);
|
|
1851
|
+
}
|
|
1852
|
+
// Add filter if present (only if entity is also selected)
|
|
1853
|
+
if (this.state.smartFilterPrompt && this.state.selectedEntityName) {
|
|
1854
|
+
params.set('filter', this.state.smartFilterPrompt);
|
|
1855
|
+
}
|
|
1856
|
+
// Add view mode if not default (grid is default)
|
|
1857
|
+
if (this.state.viewMode && this.state.viewMode !== 'grid') {
|
|
1858
|
+
params.set('view', this.state.viewMode);
|
|
1859
|
+
}
|
|
1860
|
+
// Get the current path without query string
|
|
1861
|
+
const currentUrl = this.router.url;
|
|
1862
|
+
const currentPath = currentUrl.split('?')[0];
|
|
1863
|
+
// Build the new URL
|
|
1864
|
+
const queryString = params.toString();
|
|
1865
|
+
const newUrl = queryString ? `${currentPath}?${queryString}` : currentPath;
|
|
1866
|
+
// Track this URL so we don't react to our own navigation
|
|
1867
|
+
this.lastNavigatedUrl = newUrl;
|
|
1868
|
+
// Use Angular Router for proper browser history integration
|
|
1869
|
+
// This allows back/forward buttons to work correctly
|
|
1870
|
+
this.router.navigateByUrl(newUrl, { replaceUrl: false });
|
|
1871
|
+
}
|
|
1872
|
+
/**
|
|
1873
|
+
* Handle external navigation (back/forward buttons).
|
|
1874
|
+
* Parses the URL and applies the state without triggering a new navigation.
|
|
1875
|
+
*/
|
|
1876
|
+
onExternalNavigation(url) {
|
|
1877
|
+
// Check if this URL is for our component (contains our base path)
|
|
1878
|
+
const currentPath = this.router.url.split('?')[0];
|
|
1879
|
+
const newPath = url.split('?')[0];
|
|
1880
|
+
// Only handle if we're still on the same base path (same dashboard instance)
|
|
1881
|
+
if (currentPath !== newPath) {
|
|
1882
|
+
return; // Different route entirely, shell will handle it
|
|
1883
|
+
}
|
|
1884
|
+
// Parse the new URL state
|
|
1885
|
+
const urlState = this.parseUrlFromString(url);
|
|
1886
|
+
// Apply the state without triggering URL updates
|
|
1887
|
+
this.skipUrlUpdates = true;
|
|
1888
|
+
if (urlState) {
|
|
1889
|
+
this.applyUrlState(urlState);
|
|
1890
|
+
}
|
|
1891
|
+
else {
|
|
1892
|
+
// No params means go to home view
|
|
1893
|
+
this.selectedEntity = null;
|
|
1894
|
+
this.selectedRecord = null;
|
|
1895
|
+
this.detailPanelEntity = null;
|
|
1896
|
+
this.stateService.selectEntity(null);
|
|
1897
|
+
this.stateService.closeDetailPanel();
|
|
1898
|
+
}
|
|
1899
|
+
this.skipUrlUpdates = false;
|
|
1900
|
+
// Update the tracked URL
|
|
1901
|
+
this.lastNavigatedUrl = url;
|
|
1902
|
+
this.cdr.detectChanges();
|
|
1903
|
+
}
|
|
1904
|
+
/**
|
|
1905
|
+
* Parse URL state from a URL string (used for external navigation).
|
|
1906
|
+
*/
|
|
1907
|
+
parseUrlFromString(url) {
|
|
1908
|
+
const queryIndex = url.indexOf('?');
|
|
1909
|
+
if (queryIndex === -1) {
|
|
1910
|
+
return null;
|
|
1911
|
+
}
|
|
1912
|
+
const queryString = url.substring(queryIndex + 1);
|
|
1913
|
+
const params = new URLSearchParams(queryString);
|
|
1914
|
+
const entity = params.get('entity');
|
|
1915
|
+
const record = params.get('record');
|
|
1916
|
+
const filterParam = params.get('filter');
|
|
1917
|
+
const view = params.get('view');
|
|
1918
|
+
// If no params, return null
|
|
1919
|
+
if (!entity && !record && !filterParam && !view) {
|
|
1920
|
+
return null;
|
|
1921
|
+
}
|
|
1922
|
+
return {
|
|
1923
|
+
entity: entity || undefined,
|
|
1924
|
+
record: record || undefined,
|
|
1925
|
+
filter: filterParam || undefined,
|
|
1926
|
+
viewMode: view || undefined
|
|
1927
|
+
};
|
|
1928
|
+
}
|
|
1929
|
+
// ========================================
|
|
1930
|
+
// HOME SCREEN ACTIONS
|
|
1931
|
+
// ========================================
|
|
1932
|
+
/**
|
|
1933
|
+
* Toggle entity favorite status
|
|
1934
|
+
*/
|
|
1935
|
+
async toggleEntityFavorite(entity, event) {
|
|
1936
|
+
event.stopPropagation(); // Prevent card click
|
|
1937
|
+
if (this.stateService.isEntityFavorited(entity.ID)) {
|
|
1938
|
+
await this.stateService.removeEntityFromFavorites(entity.ID);
|
|
1939
|
+
}
|
|
1940
|
+
else {
|
|
1941
|
+
await this.stateService.addEntityToFavorites(entity.Name, entity.ID);
|
|
1942
|
+
}
|
|
1943
|
+
this.cdr.detectChanges();
|
|
1944
|
+
}
|
|
1945
|
+
/**
|
|
1946
|
+
* Check if entity is favorited (for template)
|
|
1947
|
+
*/
|
|
1948
|
+
isEntityFavorited(entity) {
|
|
1949
|
+
return this.stateService.isEntityFavorited(entity.ID);
|
|
1950
|
+
}
|
|
1951
|
+
/**
|
|
1952
|
+
* Toggle show all entities vs common entities
|
|
1953
|
+
*/
|
|
1954
|
+
toggleShowAllEntities() {
|
|
1955
|
+
this.stateService.toggleShowAllEntities();
|
|
1956
|
+
}
|
|
1957
|
+
/**
|
|
1958
|
+
* Handle clicking on a recent record from home screen.
|
|
1959
|
+
* Navigates to the entity and sets up pending selection to select the record
|
|
1960
|
+
* and open the detail panel once data loads.
|
|
1961
|
+
*/
|
|
1962
|
+
onRecentRecordClick(record) {
|
|
1963
|
+
const entity = this.entities.find(e => e.ID === record.entityId);
|
|
1964
|
+
if (entity) {
|
|
1965
|
+
// Set pending record selection - will be resolved in onDataLoaded
|
|
1966
|
+
this.pendingRecordSelection = record.recordId;
|
|
1967
|
+
this.onEntitySelected(entity);
|
|
1968
|
+
}
|
|
1969
|
+
}
|
|
1970
|
+
/**
|
|
1971
|
+
* Handle clicking on a favorite record from home screen.
|
|
1972
|
+
* Navigates to the entity and sets up pending selection to select the record
|
|
1973
|
+
* and open the detail panel once data loads.
|
|
1974
|
+
*/
|
|
1975
|
+
onFavoriteRecordClick(record) {
|
|
1976
|
+
const entity = this.entities.find(e => e.ID === record.entityId);
|
|
1977
|
+
if (entity) {
|
|
1978
|
+
// Set pending record selection - will be resolved in onDataLoaded
|
|
1979
|
+
this.pendingRecordSelection = record.recordId;
|
|
1980
|
+
this.onEntitySelected(entity);
|
|
1981
|
+
}
|
|
1982
|
+
}
|
|
1983
|
+
/**
|
|
1984
|
+
* Get the icon for an entity by ID (for recent records)
|
|
1985
|
+
*/
|
|
1986
|
+
getEntityIconById(entityId) {
|
|
1987
|
+
const entity = this.metadata.Entities.find(e => e.ID === entityId);
|
|
1988
|
+
if (entity) {
|
|
1989
|
+
return this.getEntityIcon(entity);
|
|
1990
|
+
}
|
|
1991
|
+
return 'fa-solid fa-table';
|
|
1992
|
+
}
|
|
1993
|
+
/**
|
|
1994
|
+
* Format relative time for display (e.g., "2 hours ago")
|
|
1995
|
+
*/
|
|
1996
|
+
formatRelativeTime(date) {
|
|
1997
|
+
const now = new Date();
|
|
1998
|
+
const diff = now.getTime() - new Date(date).getTime();
|
|
1999
|
+
const minutes = Math.floor(diff / 60000);
|
|
2000
|
+
const hours = Math.floor(diff / 3600000);
|
|
2001
|
+
const days = Math.floor(diff / 86400000);
|
|
2002
|
+
if (minutes < 1)
|
|
2003
|
+
return 'Just now';
|
|
2004
|
+
if (minutes < 60)
|
|
2005
|
+
return `${minutes}m ago`;
|
|
2006
|
+
if (hours < 24)
|
|
2007
|
+
return `${hours}h ago`;
|
|
2008
|
+
if (days < 7)
|
|
2009
|
+
return `${days}d ago`;
|
|
2010
|
+
return new Date(date).toLocaleDateString();
|
|
2011
|
+
}
|
|
2012
|
+
/**
|
|
2013
|
+
* Check if we're at the home level (no entity selected)
|
|
2014
|
+
*/
|
|
2015
|
+
get isAtHomeLevel() {
|
|
2016
|
+
return !this.selectedEntity;
|
|
2017
|
+
}
|
|
2018
|
+
static ɵfac = function DataExplorerDashboardComponent_Factory(t) { return new (t || DataExplorerDashboardComponent)(i0.ɵɵdirectiveInject(i1.ExplorerStateService), i0.ɵɵdirectiveInject(i0.ChangeDetectorRef), i0.ɵɵdirectiveInject(i2.Router), i0.ɵɵdirectiveInject(i3.RecentAccessService)); };
|
|
2019
|
+
static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: DataExplorerDashboardComponent, selectors: [["mj-data-explorer-dashboard"]], viewQuery: function DataExplorerDashboardComponent_Query(rf, ctx) { if (rf & 1) {
|
|
2020
|
+
i0.ɵɵviewQuery(_c0, 5);
|
|
2021
|
+
i0.ɵɵviewQuery(ViewSelectorComponent, 5);
|
|
2022
|
+
i0.ɵɵviewQuery(EntityViewerComponent, 5);
|
|
2023
|
+
} if (rf & 2) {
|
|
2024
|
+
let _t;
|
|
2025
|
+
i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.filterInputRef = _t.first);
|
|
2026
|
+
i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.viewSelectorRef = _t.first);
|
|
2027
|
+
i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.entityViewerRef = _t.first);
|
|
2028
|
+
} }, hostBindings: function DataExplorerDashboardComponent_HostBindings(rf, ctx) { if (rf & 1) {
|
|
2029
|
+
i0.ɵɵlistener("keydown", function DataExplorerDashboardComponent_keydown_HostBindingHandler($event) { return ctx.handleKeyboardShortcut($event); }, false, i0.ɵɵresolveDocument);
|
|
2030
|
+
} }, inputs: { entityFilter: "entityFilter", deepLink: "deepLink", contextName: "contextName", contextIcon: "contextIcon" }, features: [i0.ɵɵInheritDefinitionFeature, i0.ɵɵNgOnChangesFeature], decls: 18, vars: 15, consts: [["filterInput", ""], [1, "data-explorer-container"], [1, "navigation-panel", 3, "collapsed", "width"], [1, "content-area"], [1, "breadcrumb-bar"], [1, "content-header"], [1, "header-left"], [1, "header-center"], [1, "smart-filter-container"], [1, "header-right"], [1, "view-mode-toggle"], [1, "content-body"], [1, "entity-home-view"], [3, "entity", "viewEntity", "viewMode", "filterText", "selectedRecordId", "config", "gridState"], [1, "detail-panel", 3, "width"], [3, "close", "save", "delete", "entity", "viewEntity", "isOpen", "currentGridState"], [1, "navigation-panel"], [3, "entitySelected", "toggleCollapse", "sectionToggled", "openRecord", "selectRecord", "expandAndFocus", "entities", "selectedEntityName", "favorites", "recentItems", "collapsed", "allowedEntityNames", "favoritesSectionExpanded", "recentSectionExpanded", "entitiesSectionExpanded", "viewsSectionExpanded"], [1, "breadcrumb-item", 3, "click", "title"], [1, "breadcrumb-icon", 3, "class"], [1, "breadcrumb-label"], [1, "fa-solid", "fa-chevron-right", "breadcrumb-separator"], [1, "breadcrumb-icon"], [1, "entity-icon"], [1, "entity-title"], [1, "record-count"], [3, "viewSelected", "saveViewRequested", "manageViewsRequested", "openInTabRequested", "configureViewRequested", "entity", "selectedViewId", "viewModified"], [1, "entity-icon", 3, "class"], [1, "fa-solid", "fa-search", "filter-icon"], ["type", "text", "placeholder", "Filter records... (press / to focus)", 1, "smart-filter-input", 3, "ngModelChange", "ngModel"], [1, "clear-filter-btn"], [1, "clear-filter-btn", 3, "click"], [1, "fa-solid", "fa-times"], ["type", "text", "placeholder", "Filter entities... (press / to focus)", 1, "smart-filter-input", 3, "ngModelChange", "ngModel"], ["title", "Grid View", 1, "toggle-btn", 3, "click"], [1, "fa-solid", "fa-list"], ["title", "Cards View", 1, "toggle-btn", 3, "click"], [1, "fa-solid", "fa-grip"], [1, "loading-state"], [1, "home-section"], ["text", "Loading entities...", "size", "medium"], [1, "home-two-column-layout"], [1, "section-header-with-toggle"], [1, "section-header-left"], [1, "fa-solid", "fa-database", "section-icon"], [1, "section-title"], [1, "section-count"], [1, "inline-toggle"], [1, "entity-card-grid"], [1, "entity-card", 3, "title"], [1, "empty-state", "small"], [1, "empty-state"], [1, "home-column"], [1, "section-header"], [1, "fa-solid", "fa-history", "section-icon"], [1, "entity-filter-strip"], [1, "recent-records-list"], [1, "recent-record-item", 3, "title"], ["title", "Show all entities", 1, "entity-filter-btn", 3, "click"], [1, "fa-solid", "fa-layer-group"], [1, "entity-filter-btn", 3, "active", "title"], [1, "entity-filter-btn", 3, "click", "title"], [1, "recent-record-item", 3, "click", "title"], [1, "recent-record-icon"], [1, "recent-record-content"], [1, "recent-record-name"], [1, "recent-record-entity"], [1, "recent-record-time"], [1, "fa-solid", "fa-star", "section-icon"], [1, "fa-solid", "fa-clock", "section-icon"], [1, "entity-list"], [1, "entity-list-item", 3, "title"], [1, "entity-list-item", 3, "click", "title"], [1, "entity-list-icon"], [1, "entity-list-content"], [1, "entity-list-name"], [1, "favorite-btn", 3, "click", "title"], ["title", "Remove from favorites", 1, "favorite-btn", "favorited", 3, "click"], [1, "fa-solid", "fa-star"], [1, "toggle-btn", 3, "click", "title"], [1, "entity-card", 3, "click", "title"], [1, "entity-card-icon"], [1, "entity-card-content"], [1, "entity-card-title"], [1, "entity-card-description"], [1, "fa-solid", "fa-search", "empty-icon"], [1, "fa-solid", "fa-database", "empty-icon"], [3, "viewModeChange", "filterTextChange", "recordSelected", "recordOpened", "dataLoaded", "filteredCountChanged", "gridStateChanged", "entity", "viewEntity", "viewMode", "filterText", "selectedRecordId", "config", "gridState"], [1, "detail-panel"], [3, "close", "openRecord", "navigateToRelated", "openRelatedRecord", "openForeignKeyRecord", "entity", "record"]], template: function DataExplorerDashboardComponent_Template(rf, ctx) { if (rf & 1) {
|
|
2031
|
+
i0.ɵɵelementStart(0, "div", 1);
|
|
2032
|
+
i0.ɵɵtemplate(1, DataExplorerDashboardComponent_Conditional_1_Template, 2, 15, "div", 2);
|
|
2033
|
+
i0.ɵɵelementStart(2, "div", 3);
|
|
2034
|
+
i0.ɵɵtemplate(3, DataExplorerDashboardComponent_Conditional_3_Template, 3, 0, "div", 4);
|
|
2035
|
+
i0.ɵɵelementStart(4, "div", 5)(5, "div", 6);
|
|
2036
|
+
i0.ɵɵtemplate(6, DataExplorerDashboardComponent_Conditional_6_Template, 6, 7)(7, DataExplorerDashboardComponent_Conditional_7_Template, 5, 3);
|
|
2037
|
+
i0.ɵɵelementEnd();
|
|
2038
|
+
i0.ɵɵelementStart(8, "div", 7);
|
|
2039
|
+
i0.ɵɵtemplate(9, DataExplorerDashboardComponent_Conditional_9_Template, 5, 2, "div", 8)(10, DataExplorerDashboardComponent_Conditional_10_Template, 5, 2, "div", 8);
|
|
2040
|
+
i0.ɵɵelementEnd();
|
|
2041
|
+
i0.ɵɵelementStart(11, "div", 9);
|
|
2042
|
+
i0.ɵɵtemplate(12, DataExplorerDashboardComponent_Conditional_12_Template, 5, 4, "div", 10);
|
|
2043
|
+
i0.ɵɵelementEnd()();
|
|
2044
|
+
i0.ɵɵelementStart(13, "div", 11);
|
|
2045
|
+
i0.ɵɵtemplate(14, DataExplorerDashboardComponent_Conditional_14_Template, 3, 1, "div", 12)(15, DataExplorerDashboardComponent_Conditional_15_Template, 1, 7, "mj-entity-viewer", 13);
|
|
2046
|
+
i0.ɵɵelementEnd()();
|
|
2047
|
+
i0.ɵɵtemplate(16, DataExplorerDashboardComponent_Conditional_16_Template, 2, 4, "div", 14);
|
|
2048
|
+
i0.ɵɵelementStart(17, "mj-view-config-panel", 15);
|
|
2049
|
+
i0.ɵɵlistener("close", function DataExplorerDashboardComponent_Template_mj_view_config_panel_close_17_listener() { return ctx.onCloseViewConfigPanel(); })("save", function DataExplorerDashboardComponent_Template_mj_view_config_panel_save_17_listener($event) { return ctx.onSaveView($event); })("delete", function DataExplorerDashboardComponent_Template_mj_view_config_panel_delete_17_listener() { return ctx.onDeleteView(); });
|
|
2050
|
+
i0.ɵɵelementEnd()();
|
|
2051
|
+
} if (rf & 2) {
|
|
2052
|
+
i0.ɵɵadvance();
|
|
2053
|
+
i0.ɵɵconditional(!ctx.isAtHomeLevel ? 1 : -1);
|
|
2054
|
+
i0.ɵɵadvance(2);
|
|
2055
|
+
i0.ɵɵconditional(!ctx.isAtHomeLevel && ctx.breadcrumbs.length > 0 ? 3 : -1);
|
|
2056
|
+
i0.ɵɵadvance();
|
|
2057
|
+
i0.ɵɵclassProp("home-header", ctx.isAtHomeLevel);
|
|
2058
|
+
i0.ɵɵadvance(2);
|
|
2059
|
+
i0.ɵɵconditional(ctx.selectedEntity ? 6 : 7);
|
|
2060
|
+
i0.ɵɵadvance(3);
|
|
2061
|
+
i0.ɵɵconditional(ctx.selectedEntity ? 9 : 10);
|
|
2062
|
+
i0.ɵɵadvance(3);
|
|
2063
|
+
i0.ɵɵconditional(ctx.selectedEntity ? 12 : -1);
|
|
2064
|
+
i0.ɵɵadvance();
|
|
2065
|
+
i0.ɵɵclassProp("home-content", ctx.isAtHomeLevel);
|
|
2066
|
+
i0.ɵɵadvance();
|
|
2067
|
+
i0.ɵɵconditional(!ctx.selectedEntity ? 14 : 15);
|
|
2068
|
+
i0.ɵɵadvance(2);
|
|
2069
|
+
i0.ɵɵconditional(ctx.state.detailPanelOpen && ctx.selectedRecord ? 16 : -1);
|
|
2070
|
+
i0.ɵɵadvance();
|
|
2071
|
+
i0.ɵɵproperty("entity", ctx.selectedEntity)("viewEntity", ctx.selectedViewEntity)("isOpen", ctx.state.viewConfigPanelOpen)("currentGridState", ctx.currentGridState);
|
|
2072
|
+
} }, dependencies: [i4.DefaultValueAccessor, i4.NgControlStatus, i4.NgModel, i5.EntityViewerComponent, i5.EntityRecordDetailPanelComponent, i3.LoadingComponent, i6.NavigationPanelComponent, i7.ViewSelectorComponent, i8.ViewConfigPanelComponent, i9.DecimalPipe], styles: [".data-explorer-container[_ngcontent-%COMP%] {\n display: flex;\n height: 100%;\n width: 100%;\n background: #f5f7fa;\n overflow: hidden;\n}\n\n.navigation-panel[_ngcontent-%COMP%] {\n flex-shrink: 0;\n height: 100%;\n background: white;\n border-right: 1px solid #e0e0e0;\n transition: width 0.2s ease-in-out;\n overflow: hidden;\n box-shadow: 2px 0 8px rgba(0, 0, 0, 0.04);\n}\n.navigation-panel.collapsed[_ngcontent-%COMP%] {\n width: 48px;\n}\n\n.content-area[_ngcontent-%COMP%] {\n flex: 1;\n display: flex;\n flex-direction: column;\n height: 100%;\n min-width: 0;\n overflow: hidden;\n background: #f5f7fa;\n}\n\n\n\n.breadcrumb-bar[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 10px 24px;\n background: white;\n border-bottom: 1px solid #eee;\n flex-shrink: 0;\n font-size: 13px;\n min-height: 40px;\n}\n\n.breadcrumb-item[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 6px;\n color: #666;\n padding: 4px 8px;\n border-radius: 4px;\n transition: all 0.15s ease;\n max-width: 200px;\n}\n\n.breadcrumb-item.clickable[_ngcontent-%COMP%] {\n cursor: pointer;\n}\n\n.breadcrumb-item.clickable[_ngcontent-%COMP%]:hover {\n background: #f0f0f0;\n color: #1976d2;\n}\n\n.breadcrumb-item.current[_ngcontent-%COMP%] {\n color: #1a1a1a;\n font-weight: 500;\n cursor: default;\n}\n\n.breadcrumb-icon[_ngcontent-%COMP%] {\n font-size: 12px;\n flex-shrink: 0;\n}\n\n.breadcrumb-label[_ngcontent-%COMP%] {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.breadcrumb-separator[_ngcontent-%COMP%] {\n font-size: 10px;\n color: #ccc;\n flex-shrink: 0;\n}\n\n.content-header[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 16px 24px;\n background: white;\n border-bottom: 1px solid #e0e0e0;\n flex-shrink: 0;\n gap: 24px;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.04);\n}\n\n.header-left[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 12px;\n flex-shrink: 0;\n flex-wrap: wrap;\n}\n\n\n\n.header-left[_ngcontent-%COMP%] mj-view-selector {\n margin-left: 8px;\n}\n\n.entity-icon[_ngcontent-%COMP%] {\n font-size: 20px;\n color: #1976d2;\n}\n\n.entity-title[_ngcontent-%COMP%] {\n margin: 0;\n font-size: 20px;\n font-weight: 600;\n color: #1a1a1a;\n}\n\n.record-count[_ngcontent-%COMP%] {\n font-size: 13px;\n color: #666;\n font-weight: 500;\n}\n\n.header-center[_ngcontent-%COMP%] {\n flex: 1;\n max-width: 600px;\n}\n\n.smart-filter-container[_ngcontent-%COMP%] {\n position: relative;\n width: 100%;\n}\n\n.smart-filter-input[_ngcontent-%COMP%] {\n width: 100%;\n padding: 10px 40px 10px 16px;\n border: 1px solid #e0e0e0;\n border-radius: 8px;\n font-size: 14px;\n background: #fafafa;\n transition: all 0.15s ease;\n}\n.smart-filter-input[_ngcontent-%COMP%]:focus {\n outline: none;\n border-color: #1976d2;\n background: white;\n box-shadow: 0 0 0 3px rgba(25, 118, 210, 0.1);\n}\n.smart-filter-input[_ngcontent-%COMP%]::placeholder {\n color: #999;\n}\n\n.clear-filter-btn[_ngcontent-%COMP%] {\n position: absolute;\n right: 8px;\n top: 50%;\n transform: translateY(-50%);\n width: 28px;\n height: 28px;\n border: none;\n background: transparent;\n border-radius: 50%;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n color: #757575;\n transition: all 0.15s ease;\n}\n.clear-filter-btn[_ngcontent-%COMP%]:hover {\n background: #e0e0e0;\n color: #424242;\n}\n\n.header-right[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 16px;\n flex-shrink: 0;\n}\n\n.view-mode-toggle[_ngcontent-%COMP%] {\n display: flex;\n background: #f0f0f0;\n border-radius: 8px;\n padding: 3px;\n}\n\n.toggle-btn[_ngcontent-%COMP%] {\n width: 36px;\n height: 32px;\n border: none;\n background: transparent;\n border-radius: 6px;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n color: #666;\n transition: all 0.15s ease;\n}\n.toggle-btn[_ngcontent-%COMP%]:hover {\n color: #333;\n}\n.toggle-btn.active[_ngcontent-%COMP%] {\n background: white;\n color: #1976d2;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);\n}\n\n.content-body[_ngcontent-%COMP%] {\n flex: 1;\n overflow: hidden;\n padding: 20px 24px;\n display: flex;\n flex-direction: column;\n}\n\n.loading-container[_ngcontent-%COMP%], \n.loading-state[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n height: 100%;\n gap: 16px;\n background: white;\n border-radius: 8px;\n}\n\n.loading-spinner[_ngcontent-%COMP%] {\n font-size: 32px;\n color: #1976d2;\n}\n\n.loading-message[_ngcontent-%COMP%] {\n margin: 0;\n font-size: 14px;\n color: #666;\n}\n\n.empty-state[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n height: 100%;\n text-align: center;\n color: #757575;\n background: white;\n border-radius: 8px;\n padding: 40px;\n}\n\n.empty-icon[_ngcontent-%COMP%] {\n font-size: 64px;\n color: #e0e0e0;\n margin-bottom: 24px;\n}\n\n.empty-state[_ngcontent-%COMP%] h3[_ngcontent-%COMP%] {\n margin: 0 0 8px 0;\n font-size: 20px;\n font-weight: 600;\n color: #333;\n}\n\n.empty-state[_ngcontent-%COMP%] p[_ngcontent-%COMP%] {\n margin: 0;\n font-size: 14px;\n max-width: 400px;\n color: #666;\n}\n\n\n\n.entity-home-view[_ngcontent-%COMP%] {\n flex: 1;\n overflow: auto;\n padding: 4px;\n}\n\n.entity-card-grid[_ngcontent-%COMP%] {\n display: grid;\n grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));\n gap: 16px;\n padding: 0;\n}\n\n.entity-card[_ngcontent-%COMP%] {\n display: flex;\n align-items: flex-start;\n gap: 16px;\n padding: 20px;\n background: white;\n border-radius: 10px;\n cursor: pointer;\n transition: all 0.2s ease;\n border: 1px solid #e8e8e8;\n}\n\n.entity-card[_ngcontent-%COMP%]:hover {\n transform: translateY(-2px);\n box-shadow: 0 6px 20px rgba(0, 0, 0, 0.08);\n border-color: #1976d2;\n}\n\n.entity-card[_ngcontent-%COMP%]:active {\n transform: translateY(0);\n}\n\n.entity-card-icon[_ngcontent-%COMP%] {\n width: 48px;\n height: 48px;\n border-radius: 10px;\n background: linear-gradient(135deg, #e3f2fd, #bbdefb);\n display: flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n}\n\n.entity-card-icon[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 20px;\n color: #1976d2;\n}\n\n.entity-card[_ngcontent-%COMP%]:hover .entity-card-icon[_ngcontent-%COMP%] {\n background: linear-gradient(135deg, #1976d2, #1565c0);\n}\n\n.entity-card[_ngcontent-%COMP%]:hover .entity-card-icon[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n color: white;\n}\n\n.entity-card-content[_ngcontent-%COMP%] {\n flex: 1;\n min-width: 0;\n}\n\n.entity-card-title[_ngcontent-%COMP%] {\n margin: 0 0 6px 0;\n font-size: 15px;\n font-weight: 600;\n color: #1a1a1a;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.entity-card-description[_ngcontent-%COMP%] {\n margin: 0;\n font-size: 13px;\n color: #666;\n line-height: 1.4;\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n overflow: hidden;\n}\n\n.detail-panel[_ngcontent-%COMP%] {\n flex-shrink: 0;\n height: 100%;\n background: white;\n border-left: 1px solid #e0e0e0;\n box-shadow: -4px 0 16px rgba(0, 0, 0, 0.08);\n overflow: hidden;\n animation: _ngcontent-%COMP%_slideIn 0.2s ease-out;\n}\n\n@keyframes _ngcontent-%COMP%_slideIn {\n from {\n transform: translateX(100%);\n opacity: 0;\n }\n to {\n transform: translateX(0);\n opacity: 1;\n }\n}\n[_nghost-%COMP%] mj-explorer-grid-view, \n[_nghost-%COMP%] mj-explorer-cards-view {\n flex: 1;\n display: flex;\n flex-direction: column;\n min-height: 0;\n}\n\n\n\n\n\n\n\n\n.content-header.home-header[_ngcontent-%COMP%] {\n border-bottom: none;\n background: transparent;\n box-shadow: none;\n padding: 24px 24px 16px;\n}\n\n.content-body.home-content[_ngcontent-%COMP%] {\n padding-top: 0;\n}\n\n\n\n.filter-icon[_ngcontent-%COMP%] {\n position: absolute;\n left: 14px;\n top: 50%;\n transform: translateY(-50%);\n color: #999;\n font-size: 14px;\n pointer-events: none;\n}\n\n.smart-filter-container[_ngcontent-%COMP%] .smart-filter-input[_ngcontent-%COMP%] {\n padding-left: 40px;\n}\n\n\n\n\n\n\n.home-section[_ngcontent-%COMP%] {\n margin-bottom: 28px;\n}\n\n.section-header[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 10px;\n margin-bottom: 14px;\n padding-left: 4px;\n}\n\n.section-icon[_ngcontent-%COMP%] {\n font-size: 14px;\n color: #1976d2;\n width: 20px;\n text-align: center;\n}\n\n.section-title[_ngcontent-%COMP%] {\n margin: 0;\n font-size: 14px;\n font-weight: 600;\n color: #333;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n}\n\n.section-count[_ngcontent-%COMP%] {\n font-size: 12px;\n color: #888;\n font-weight: 500;\n margin-left: 8px;\n}\n\n\n\n\n\n\n.home-two-column-layout[_ngcontent-%COMP%] {\n display: grid;\n grid-template-columns: 1fr 1fr;\n gap: 24px;\n margin-bottom: 28px;\n}\n\n.home-column[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 20px;\n}\n\n\n\n.section-header-with-toggle[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: space-between;\n margin-bottom: 14px;\n padding-left: 4px;\n}\n\n.section-header-left[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 10px;\n}\n\n\n\n.inline-toggle[_ngcontent-%COMP%] {\n display: flex;\n background: #f0f0f0;\n border-radius: 6px;\n padding: 2px;\n}\n\n.inline-toggle[_ngcontent-%COMP%] .toggle-btn[_ngcontent-%COMP%] {\n width: auto;\n height: 26px;\n padding: 0 10px;\n border: none;\n background: transparent;\n border-radius: 4px;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n color: #666;\n font-size: 11px;\n font-weight: 500;\n transition: all 0.15s ease;\n}\n\n.inline-toggle[_ngcontent-%COMP%] .toggle-btn[_ngcontent-%COMP%]:hover {\n color: #333;\n}\n\n.inline-toggle[_ngcontent-%COMP%] .toggle-btn.active[_ngcontent-%COMP%] {\n background: white;\n color: #1976d2;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);\n}\n\n\n\n\n\n\n.entity-card-row[_ngcontent-%COMP%] {\n display: flex;\n flex-wrap: wrap;\n gap: 12px;\n padding: 0;\n}\n\n.entity-card.compact[_ngcontent-%COMP%] {\n flex: 0 0 auto;\n width: auto;\n min-width: 180px;\n max-width: 280px;\n padding: 12px 16px;\n gap: 12px;\n}\n\n.entity-card.compact[_ngcontent-%COMP%] .entity-card-icon[_ngcontent-%COMP%] {\n width: 36px;\n height: 36px;\n border-radius: 8px;\n}\n\n.entity-card.compact[_ngcontent-%COMP%] .entity-card-icon[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 16px;\n}\n\n.entity-card.compact[_ngcontent-%COMP%] .entity-card-title[_ngcontent-%COMP%] {\n margin: 0;\n font-size: 14px;\n}\n\n.entity-card.compact[_ngcontent-%COMP%] .entity-card-content[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n}\n\n\n\n\n\n\n.favorite-btn[_ngcontent-%COMP%] {\n flex-shrink: 0;\n width: 32px;\n height: 32px;\n border: none;\n background: transparent;\n border-radius: 50%;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n color: #ccc;\n transition: all 0.15s ease;\n margin-left: auto;\n}\n\n.favorite-btn[_ngcontent-%COMP%]:hover {\n background: rgba(255, 193, 7, 0.1);\n color: #ffc107;\n transform: scale(1.1);\n}\n\n.favorite-btn.favorited[_ngcontent-%COMP%] {\n color: #ffc107;\n}\n\n.favorite-btn.favorited[_ngcontent-%COMP%]:hover {\n background: rgba(255, 193, 7, 0.15);\n color: #ffb300;\n}\n\n.entity-card[_ngcontent-%COMP%] .favorite-btn[_ngcontent-%COMP%] {\n opacity: 0;\n transition: opacity 0.15s ease, background 0.15s ease, color 0.15s ease, transform 0.15s ease;\n}\n\n.entity-card[_ngcontent-%COMP%]:hover .favorite-btn[_ngcontent-%COMP%] {\n opacity: 1;\n}\n\n.entity-card[_ngcontent-%COMP%] .favorite-btn.favorited[_ngcontent-%COMP%] {\n opacity: 1;\n}\n\n\n\n\n\n\n.recent-records-list[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 4px;\n background: white;\n border-radius: 10px;\n padding: 8px;\n border: 1px solid #e8e8e8;\n}\n\n.recent-record-item[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 10px 12px;\n border-radius: 8px;\n cursor: pointer;\n transition: all 0.15s ease;\n}\n\n.recent-record-item[_ngcontent-%COMP%]:hover {\n background: #f5f7fa;\n}\n\n.recent-record-icon[_ngcontent-%COMP%] {\n width: 32px;\n height: 32px;\n border-radius: 8px;\n background: linear-gradient(135deg, #e8f5e9, #c8e6c9);\n display: flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n}\n\n.recent-record-icon[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 14px;\n color: #4caf50;\n}\n\n.recent-record-content[_ngcontent-%COMP%] {\n flex: 1;\n min-width: 0;\n display: flex;\n flex-direction: column;\n gap: 2px;\n}\n\n.recent-record-name[_ngcontent-%COMP%] {\n font-size: 14px;\n font-weight: 500;\n color: #1a1a1a;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.recent-record-entity[_ngcontent-%COMP%] {\n font-size: 12px;\n color: #888;\n}\n\n.recent-record-time[_ngcontent-%COMP%] {\n font-size: 11px;\n color: #aaa;\n flex-shrink: 0;\n}\n\n\n\n\n\n\n.entity-list[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 4px;\n background: white;\n border-radius: 10px;\n padding: 8px;\n border: 1px solid #e8e8e8;\n}\n\n.entity-list-item[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 10px 12px;\n border-radius: 8px;\n cursor: pointer;\n transition: all 0.15s ease;\n}\n\n.entity-list-item[_ngcontent-%COMP%]:hover {\n background: #f5f7fa;\n}\n\n.entity-list-icon[_ngcontent-%COMP%] {\n width: 32px;\n height: 32px;\n border-radius: 8px;\n background: linear-gradient(135deg, #e3f2fd, #bbdefb);\n display: flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n}\n\n.entity-list-icon[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 14px;\n color: #1976d2;\n}\n\n.entity-list-content[_ngcontent-%COMP%] {\n flex: 1;\n min-width: 0;\n display: flex;\n flex-direction: column;\n gap: 2px;\n}\n\n.entity-list-name[_ngcontent-%COMP%] {\n font-size: 14px;\n font-weight: 500;\n color: #1a1a1a;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.entity-list-item[_ngcontent-%COMP%] .favorite-btn[_ngcontent-%COMP%] {\n opacity: 0;\n transition: opacity 0.15s ease;\n}\n\n.entity-list-item[_ngcontent-%COMP%]:hover .favorite-btn[_ngcontent-%COMP%] {\n opacity: 1;\n}\n\n.entity-list-item[_ngcontent-%COMP%] .favorite-btn.favorited[_ngcontent-%COMP%] {\n opacity: 1;\n}\n\n\n\n\n\n\n.entity-filter-strip[_ngcontent-%COMP%] {\n display: flex;\n gap: 4px;\n margin-left: auto;\n background: #f0f0f0;\n border-radius: 6px;\n padding: 3px;\n}\n\n.entity-filter-btn[_ngcontent-%COMP%] {\n width: 28px;\n height: 26px;\n border: none;\n background: transparent;\n border-radius: 4px;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n color: #666;\n font-size: 12px;\n transition: all 0.15s ease;\n}\n\n.entity-filter-btn[_ngcontent-%COMP%]:hover {\n color: #333;\n background: rgba(0, 0, 0, 0.05);\n}\n\n.entity-filter-btn.active[_ngcontent-%COMP%] {\n background: white;\n color: #1976d2;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);\n}\n\n\n\n\n\n\n.view-mode-toggle[_ngcontent-%COMP%] .toggle-btn[_ngcontent-%COMP%] {\n width: auto;\n padding: 0 12px;\n gap: 6px;\n}\n\n.toggle-label[_ngcontent-%COMP%] {\n font-size: 12px;\n font-weight: 500;\n}\n\n\n\n\n\n\n.empty-state.small[_ngcontent-%COMP%] {\n height: auto;\n padding: 32px;\n background: #fafafa;\n}\n\n.empty-state.small[_ngcontent-%COMP%] .empty-icon[_ngcontent-%COMP%] {\n font-size: 40px;\n margin-bottom: 16px;\n}\n\n.empty-state.small[_ngcontent-%COMP%] h3[_ngcontent-%COMP%] {\n font-size: 16px;\n}\n\n.empty-state.small[_ngcontent-%COMP%] p[_ngcontent-%COMP%] {\n font-size: 13px;\n}\n\n\n\n\n\n\n@media (max-width: 1200px) {\n .entity-card-grid[_ngcontent-%COMP%] {\n grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));\n }\n}\n\n@media (max-width: 900px) {\n .content-header[_ngcontent-%COMP%] {\n flex-wrap: wrap;\n gap: 12px;\n }\n\n .header-center[_ngcontent-%COMP%] {\n order: 3;\n flex-basis: 100%;\n max-width: 100%;\n }\n\n .entity-card.compact[_ngcontent-%COMP%] {\n min-width: 160px;\n }\n\n \n\n .home-two-column-layout[_ngcontent-%COMP%] {\n grid-template-columns: 1fr;\n gap: 20px;\n }\n}\n\n@media (max-width: 600px) {\n .data-explorer-container[_ngcontent-%COMP%] {\n flex-direction: column;\n }\n\n .navigation-panel[_ngcontent-%COMP%] {\n display: none;\n }\n\n .content-header[_ngcontent-%COMP%] {\n padding: 12px 16px;\n }\n\n .content-body[_ngcontent-%COMP%] {\n padding: 12px 16px;\n }\n\n .entity-card-grid[_ngcontent-%COMP%] {\n grid-template-columns: 1fr;\n gap: 12px;\n }\n\n .entity-card[_ngcontent-%COMP%] {\n padding: 16px;\n }\n\n .entity-card.compact[_ngcontent-%COMP%] {\n min-width: 100%;\n max-width: 100%;\n }\n\n .entity-card-row[_ngcontent-%COMP%] {\n flex-direction: column;\n }\n\n .view-mode-toggle[_ngcontent-%COMP%] {\n display: none;\n }\n\n .section-title[_ngcontent-%COMP%] {\n font-size: 13px;\n }\n\n .home-section[_ngcontent-%COMP%] {\n margin-bottom: 20px;\n }\n}"], data: { animation: [
|
|
2073
|
+
trigger('slideInLeft', [
|
|
2074
|
+
transition(':enter', [
|
|
2075
|
+
style({ transform: 'translateX(-100%)', opacity: 0 }),
|
|
2076
|
+
animate('200ms ease-out', style({ transform: 'translateX(0)', opacity: 1 }))
|
|
2077
|
+
]),
|
|
2078
|
+
transition(':leave', [
|
|
2079
|
+
animate('200ms ease-in', style({ transform: 'translateX(-100%)', opacity: 0 }))
|
|
2080
|
+
])
|
|
2081
|
+
])
|
|
2082
|
+
] } });
|
|
2083
|
+
};
|
|
2084
|
+
DataExplorerDashboardComponent = __decorate([
|
|
2085
|
+
RegisterClass(BaseDashboard, 'DataExplorer')
|
|
2086
|
+
], DataExplorerDashboardComponent);
|
|
2087
|
+
export { DataExplorerDashboardComponent };
|
|
2088
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(DataExplorerDashboardComponent, [{
|
|
2089
|
+
type: Component,
|
|
2090
|
+
args: [{ selector: 'mj-data-explorer-dashboard', animations: [
|
|
2091
|
+
trigger('slideInLeft', [
|
|
2092
|
+
transition(':enter', [
|
|
2093
|
+
style({ transform: 'translateX(-100%)', opacity: 0 }),
|
|
2094
|
+
animate('200ms ease-out', style({ transform: 'translateX(0)', opacity: 1 }))
|
|
2095
|
+
]),
|
|
2096
|
+
transition(':leave', [
|
|
2097
|
+
animate('200ms ease-in', style({ transform: 'translateX(-100%)', opacity: 0 }))
|
|
2098
|
+
])
|
|
2099
|
+
])
|
|
2100
|
+
], template: "<div class=\"data-explorer-container\">\n <!-- Navigation Panel (Left) - Hidden at home level, animated -->\n @if (!isAtHomeLevel) {\n <div\n class=\"navigation-panel\"\n [class.collapsed]=\"state.navigationPanelCollapsed\"\n [style.width.px]=\"state.navigationPanelCollapsed ? 48 : state.navigationPanelWidth\"\n [@slideInLeft]>\n\n <mj-explorer-navigation-panel\n [entities]=\"entities\"\n [selectedEntityName]=\"state.selectedEntityName\"\n [favorites]=\"state.favorites\"\n [recentItems]=\"state.recentItems\"\n [collapsed]=\"state.navigationPanelCollapsed\"\n [allowedEntityNames]=\"allowedEntityNames\"\n [favoritesSectionExpanded]=\"state.favoritesSectionExpanded\"\n [recentSectionExpanded]=\"state.recentSectionExpanded\"\n [entitiesSectionExpanded]=\"state.entitiesSectionExpanded\"\n [viewsSectionExpanded]=\"state.viewsSectionExpanded\"\n (entitySelected)=\"onEntitySelected($event)\"\n (toggleCollapse)=\"toggleNavigationPanel()\"\n (sectionToggled)=\"stateService.toggleSection($event)\"\n (openRecord)=\"onOpenRecordFromNav($event)\"\n (selectRecord)=\"onSelectRecordFromNav($event)\"\n (expandAndFocus)=\"onExpandAndFocus($event)\">\n </mj-explorer-navigation-panel>\n </div>\n }\n\n <!-- Main Content Area -->\n <div class=\"content-area\">\n <!-- Breadcrumb Bar - Hidden at home level -->\n @if (!isAtHomeLevel && breadcrumbs.length > 0) {\n <div class=\"breadcrumb-bar\">\n @for (crumb of breadcrumbs; track crumb.label; let i = $index; let last = $last) {\n <span\n class=\"breadcrumb-item\"\n [class.clickable]=\"!last\"\n [class.current]=\"last\"\n (click)=\"onBreadcrumbClick(crumb, i)\"\n [title]=\"crumb.label\">\n @if (crumb.icon) {\n <i [class]=\"crumb.icon\" class=\"breadcrumb-icon\"></i>\n }\n <span class=\"breadcrumb-label\">{{ crumb.label }}</span>\n </span>\n @if (!last) {\n <i class=\"fa-solid fa-chevron-right breadcrumb-separator\"></i>\n }\n }\n </div>\n }\n\n <!-- Header -->\n <div class=\"content-header\" [class.home-header]=\"isAtHomeLevel\">\n <div class=\"header-left\">\n @if (selectedEntity) {\n <i [class]=\"getEntityIcon(selectedEntity)\" class=\"entity-icon\"></i>\n <h2 class=\"entity-title\">{{ selectedEntity.Name }}</h2>\n @if (debouncedFilterText && filteredRecordCount !== totalRecordCount) {\n <span class=\"record-count\">{{ filteredRecordCount | number }} of {{ totalRecordCount | number }} records</span>\n } @else {\n <span class=\"record-count\">{{ totalRecordCount | number }} records</span>\n }\n\n <!-- View Selector -->\n <mj-view-selector\n [entity]=\"selectedEntity\"\n [selectedViewId]=\"state.selectedViewId\"\n [viewModified]=\"state.viewModified\"\n (viewSelected)=\"onViewSelected($event)\"\n (saveViewRequested)=\"onSaveViewRequested($event)\"\n (manageViewsRequested)=\"onManageViewsRequested()\"\n (openInTabRequested)=\"onOpenInTabRequested($event)\"\n (configureViewRequested)=\"onConfigureViewRequested()\">\n </mj-view-selector>\n } @else {\n @if (displayIcon) {\n <i [class]=\"displayIcon\" class=\"entity-icon\"></i>\n }\n <h2 class=\"entity-title\">{{ displayTitle }}</h2>\n <span class=\"record-count\">{{ entities.length }} entities available</span>\n }\n </div>\n\n <div class=\"header-center\">\n @if (selectedEntity) {\n <div class=\"smart-filter-container\">\n <i class=\"fa-solid fa-search filter-icon\"></i>\n <input\n #filterInput\n type=\"text\"\n class=\"smart-filter-input\"\n placeholder=\"Filter records... (press / to focus)\"\n [ngModel]=\"state.smartFilterPrompt\"\n (ngModelChange)=\"onSmartFilterChanged($event)\"\n />\n @if (state.smartFilterPrompt) {\n <button class=\"clear-filter-btn\" (click)=\"onSmartFilterChanged('')\">\n <i class=\"fa-solid fa-times\"></i>\n </button>\n }\n </div>\n } @else {\n <!-- Entity filter for home screen -->\n <div class=\"smart-filter-container\">\n <i class=\"fa-solid fa-search filter-icon\"></i>\n <input\n #filterInput\n type=\"text\"\n class=\"smart-filter-input\"\n placeholder=\"Filter entities... (press / to focus)\"\n [(ngModel)]=\"entityFilterText\"\n />\n @if (entityFilterText) {\n <button class=\"clear-filter-btn\" (click)=\"entityFilterText = ''\">\n <i class=\"fa-solid fa-times\"></i>\n </button>\n }\n </div>\n }\n </div>\n\n <div class=\"header-right\">\n @if (selectedEntity) {\n <!-- View Mode Toggle -->\n <div class=\"view-mode-toggle\">\n <button\n class=\"toggle-btn\"\n [class.active]=\"state.viewMode === 'grid'\"\n (click)=\"onViewModeChanged('grid')\"\n title=\"Grid View\">\n <i class=\"fa-solid fa-list\"></i>\n </button>\n <button\n class=\"toggle-btn\"\n [class.active]=\"state.viewMode === 'cards'\"\n (click)=\"onViewModeChanged('cards')\"\n title=\"Cards View\">\n <i class=\"fa-solid fa-grip\"></i>\n </button>\n </div>\n }\n <!-- Common/All toggle moved to inline with All Entities section header -->\n </div>\n </div>\n\n <!-- Content Body - Using mj-entity-viewer composite -->\n <div class=\"content-body\" [class.home-content]=\"isAtHomeLevel\">\n @if (!selectedEntity) {\n <!-- Entity Home View - Two-Column Layout -->\n <div class=\"entity-home-view\">\n @if (isLoadingEntities) {\n <div class=\"loading-state\">\n <mj-loading text=\"Loading entities...\" size=\"medium\"></mj-loading>\n </div>\n } @else {\n <!-- Two-Column Layout: Records (left) | Entities (right) -->\n @if (hasTopSectionContent) {\n <div class=\"home-two-column-layout\">\n <!-- Left Column: Recent & Favorite Records (most actionable) -->\n <div class=\"home-column\">\n <!-- Recent Records Section -->\n @if (recentRecords.length > 0) {\n <div class=\"home-section\">\n <div class=\"section-header\">\n <i class=\"fa-solid fa-history section-icon\"></i>\n <h3 class=\"section-title\">Recent Records</h3>\n <!-- Entity filter strip - only show with 2+ entities -->\n @if (showRecentRecordsEntityFilter) {\n <div class=\"entity-filter-strip\">\n <button\n class=\"entity-filter-btn\"\n [class.active]=\"recentRecordsEntityFilter === null\"\n (click)=\"setRecentRecordsEntityFilter(null)\"\n title=\"Show all entities\">\n <i class=\"fa-solid fa-layer-group\"></i>\n </button>\n @for (entity of uniqueRecentRecordEntities; track entity.entityId) {\n <button\n class=\"entity-filter-btn\"\n [class.active]=\"recentRecordsEntityFilter === entity.entityId\"\n (click)=\"setRecentRecordsEntityFilter(entity.entityId)\"\n [title]=\"entity.entityName\">\n <i [class]=\"entity.icon\"></i>\n </button>\n }\n </div>\n }\n </div>\n <div class=\"recent-records-list\">\n @for (record of filteredRecentRecords; track record.entityId + '|' + record.recordId) {\n <div\n class=\"recent-record-item\"\n (click)=\"onRecentRecordClick(record)\"\n [title]=\"record.entityName + ' - ' + record.recordId\">\n <div class=\"recent-record-icon\">\n <i [class]=\"getEntityIconById(record.entityId)\"></i>\n </div>\n <div class=\"recent-record-content\">\n <span class=\"recent-record-name\">{{ record.recordName || record.recordId }}</span>\n <span class=\"recent-record-entity\">{{ record.entityName }}</span>\n </div>\n <span class=\"recent-record-time\">{{ formatRelativeTime(record.latestAt) }}</span>\n </div>\n }\n </div>\n </div>\n }\n\n <!-- Favorite Records Section -->\n @if (favoriteRecords.length > 0) {\n <div class=\"home-section\">\n <div class=\"section-header\">\n <i class=\"fa-solid fa-star section-icon\"></i>\n <h3 class=\"section-title\">Favorite Records</h3>\n </div>\n <div class=\"recent-records-list\">\n @for (record of favoriteRecords; track record.userFavoriteId) {\n <div\n class=\"recent-record-item\"\n (click)=\"onFavoriteRecordClick(record)\"\n [title]=\"record.entityName + ' - ' + record.recordId\">\n <div class=\"recent-record-icon\">\n <i [class]=\"getEntityIconById(record.entityId)\"></i>\n </div>\n <div class=\"recent-record-content\">\n <span class=\"recent-record-name\">{{ record.recordName || record.recordId }}</span>\n <span class=\"recent-record-entity\">{{ record.entityName }}</span>\n </div>\n </div>\n }\n </div>\n </div>\n }\n </div>\n\n <!-- Right Column: Recent & Favorite Entities -->\n <div class=\"home-column\">\n <!-- Recent Entities Section (list format) -->\n @if (recentEntities.length > 0) {\n <div class=\"home-section\">\n <div class=\"section-header\">\n <i class=\"fa-solid fa-clock section-icon\"></i>\n <h3 class=\"section-title\">Recent Entities</h3>\n </div>\n <div class=\"entity-list\">\n @for (entity of recentEntities; track entity.ID) {\n <div\n class=\"entity-list-item\"\n (click)=\"onEntitySelected(entity)\"\n [title]=\"entity.Description || entity.Name\">\n <div class=\"entity-list-icon\">\n <i [class]=\"getEntityIcon(entity)\"></i>\n </div>\n <div class=\"entity-list-content\">\n <span class=\"entity-list-name\">{{ entity.Name }}</span>\n </div>\n <button\n class=\"favorite-btn\"\n [class.favorited]=\"isEntityFavorited(entity)\"\n (click)=\"toggleEntityFavorite(entity, $event)\"\n [title]=\"isEntityFavorited(entity) ? 'Remove from favorites' : 'Add to favorites'\">\n <i [class]=\"isEntityFavorited(entity) ? 'fa-solid fa-star' : 'fa-regular fa-star'\"></i>\n </button>\n </div>\n }\n </div>\n </div>\n }\n\n <!-- Favorite Entities Section (list format) -->\n @if (favoriteEntities.length > 0) {\n <div class=\"home-section\">\n <div class=\"section-header\">\n <i class=\"fa-solid fa-star section-icon\"></i>\n <h3 class=\"section-title\">Favorite Entities</h3>\n </div>\n <div class=\"entity-list\">\n @for (entity of favoriteEntities; track entity.ID) {\n <div\n class=\"entity-list-item\"\n (click)=\"onEntitySelected(entity)\"\n [title]=\"entity.Description || entity.Name\">\n <div class=\"entity-list-icon\">\n <i [class]=\"getEntityIcon(entity)\"></i>\n </div>\n <div class=\"entity-list-content\">\n <span class=\"entity-list-name\">{{ entity.Name }}</span>\n </div>\n <button\n class=\"favorite-btn favorited\"\n (click)=\"toggleEntityFavorite(entity, $event)\"\n title=\"Remove from favorites\">\n <i class=\"fa-solid fa-star\"></i>\n </button>\n </div>\n }\n </div>\n </div>\n }\n </div>\n </div>\n }\n\n <!-- All/Common Entities Section - Full Width -->\n <div class=\"home-section\">\n <div class=\"section-header-with-toggle\">\n <div class=\"section-header-left\">\n <i class=\"fa-solid fa-database section-icon\"></i>\n <h3 class=\"section-title\">\n @if (state.showAllEntities || !showCommonAllToggle) {\n All Entities\n } @else {\n Common Entities\n }\n </h3>\n @if (entityFilterText && filteredEntities.length !== entities.length) {\n <span class=\"section-count\">{{ filteredEntities.length }} matching</span>\n }\n </div>\n @if (showCommonAllToggle) {\n <div class=\"inline-toggle\">\n <button\n class=\"toggle-btn\"\n [class.active]=\"!state.showAllEntities\"\n (click)=\"toggleShowAllEntities()\"\n title=\"Show common entities ({{ commonEntitiesCount }})\">\n Common\n </button>\n <button\n class=\"toggle-btn\"\n [class.active]=\"state.showAllEntities\"\n (click)=\"toggleShowAllEntities()\"\n title=\"Show all entities ({{ allEntitiesCount }})\">\n All\n </button>\n </div>\n }\n </div>\n <div class=\"entity-card-grid\">\n @for (entity of filteredEntities; track entity.ID) {\n <div\n class=\"entity-card\"\n (click)=\"onEntitySelected(entity)\"\n [title]=\"entity.Description || entity.Name\">\n <div class=\"entity-card-icon\">\n <i [class]=\"getEntityIcon(entity)\"></i>\n </div>\n <div class=\"entity-card-content\">\n <h4 class=\"entity-card-title\">{{ entity.Name }}</h4>\n @if (entity.Description) {\n <p class=\"entity-card-description\">{{ entity.Description }}</p>\n }\n </div>\n <button\n class=\"favorite-btn\"\n [class.favorited]=\"isEntityFavorited(entity)\"\n (click)=\"toggleEntityFavorite(entity, $event)\"\n [title]=\"isEntityFavorited(entity) ? 'Remove from favorites' : 'Add to favorites'\">\n <i [class]=\"isEntityFavorited(entity) ? 'fa-solid fa-star' : 'fa-regular fa-star'\"></i>\n </button>\n </div>\n }\n </div>\n @if (filteredEntities.length === 0 && entities.length > 0) {\n <div class=\"empty-state small\">\n <i class=\"fa-solid fa-search empty-icon\"></i>\n <h3>No Matching Entities</h3>\n <p>No entities match \"{{ entityFilterText }}\"</p>\n </div>\n }\n @if (entities.length === 0) {\n <div class=\"empty-state\">\n <i class=\"fa-solid fa-database empty-icon\"></i>\n <h3>No Entities Available</h3>\n <p>There are no entities configured for this application.</p>\n </div>\n }\n </div>\n }\n </div>\n } @else {\n <mj-entity-viewer\n [entity]=\"selectedEntity\"\n [viewEntity]=\"selectedViewEntity\"\n [viewMode]=\"state.viewMode\"\n [filterText]=\"debouncedFilterText\"\n [selectedRecordId]=\"state.selectedRecordId\"\n [config]=\"viewerConfig\"\n [gridState]=\"currentGridState\"\n (viewModeChange)=\"onViewModeChanged($event)\"\n (filterTextChange)=\"onFilterTextChanged($event)\"\n (recordSelected)=\"onViewerRecordSelected($event)\"\n (recordOpened)=\"onViewerRecordOpened($event)\"\n (dataLoaded)=\"onDataLoaded($event)\"\n (filteredCountChanged)=\"onFilteredCountChanged($event)\"\n (gridStateChanged)=\"onGridStateChanged($event)\">\n </mj-entity-viewer>\n }\n </div>\n </div>\n\n <!-- Detail Panel (Right - Slide In) -->\n @if (state.detailPanelOpen && selectedRecord) {\n <div class=\"detail-panel\" [style.width.px]=\"state.detailPanelWidth\">\n <mj-entity-record-detail-panel\n [entity]=\"detailPanelEntity\"\n [record]=\"selectedRecord\"\n (close)=\"onDetailPanelClosed()\"\n (openRecord)=\"onOpenRecord($event)\"\n (navigateToRelated)=\"onNavigateToRelated($event)\"\n (openRelatedRecord)=\"onOpenRelatedRecord($event)\"\n (openForeignKeyRecord)=\"onOpenForeignKeyRecord($event)\">\n </mj-entity-record-detail-panel>\n </div>\n }\n\n <!-- View Configuration Panel -->\n <mj-view-config-panel\n [entity]=\"selectedEntity\"\n [viewEntity]=\"selectedViewEntity\"\n [isOpen]=\"state.viewConfigPanelOpen\"\n [currentGridState]=\"currentGridState\"\n (close)=\"onCloseViewConfigPanel()\"\n (save)=\"onSaveView($event)\"\n (delete)=\"onDeleteView()\">\n </mj-view-config-panel>\n</div>\n", styles: [".data-explorer-container {\n display: flex;\n height: 100%;\n width: 100%;\n background: #f5f7fa;\n overflow: hidden;\n}\n\n.navigation-panel {\n flex-shrink: 0;\n height: 100%;\n background: white;\n border-right: 1px solid #e0e0e0;\n transition: width 0.2s ease-in-out;\n overflow: hidden;\n box-shadow: 2px 0 8px rgba(0, 0, 0, 0.04);\n}\n.navigation-panel.collapsed {\n width: 48px;\n}\n\n.content-area {\n flex: 1;\n display: flex;\n flex-direction: column;\n height: 100%;\n min-width: 0;\n overflow: hidden;\n background: #f5f7fa;\n}\n\n/* Breadcrumb Bar */\n.breadcrumb-bar {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 10px 24px;\n background: white;\n border-bottom: 1px solid #eee;\n flex-shrink: 0;\n font-size: 13px;\n min-height: 40px;\n}\n\n.breadcrumb-item {\n display: flex;\n align-items: center;\n gap: 6px;\n color: #666;\n padding: 4px 8px;\n border-radius: 4px;\n transition: all 0.15s ease;\n max-width: 200px;\n}\n\n.breadcrumb-item.clickable {\n cursor: pointer;\n}\n\n.breadcrumb-item.clickable:hover {\n background: #f0f0f0;\n color: #1976d2;\n}\n\n.breadcrumb-item.current {\n color: #1a1a1a;\n font-weight: 500;\n cursor: default;\n}\n\n.breadcrumb-icon {\n font-size: 12px;\n flex-shrink: 0;\n}\n\n.breadcrumb-label {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.breadcrumb-separator {\n font-size: 10px;\n color: #ccc;\n flex-shrink: 0;\n}\n\n.content-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 16px 24px;\n background: white;\n border-bottom: 1px solid #e0e0e0;\n flex-shrink: 0;\n gap: 24px;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.04);\n}\n\n.header-left {\n display: flex;\n align-items: center;\n gap: 12px;\n flex-shrink: 0;\n flex-wrap: wrap;\n}\n\n/* View Selector within header */\n.header-left ::ng-deep mj-view-selector {\n margin-left: 8px;\n}\n\n.entity-icon {\n font-size: 20px;\n color: #1976d2;\n}\n\n.entity-title {\n margin: 0;\n font-size: 20px;\n font-weight: 600;\n color: #1a1a1a;\n}\n\n.record-count {\n font-size: 13px;\n color: #666;\n font-weight: 500;\n}\n\n.header-center {\n flex: 1;\n max-width: 600px;\n}\n\n.smart-filter-container {\n position: relative;\n width: 100%;\n}\n\n.smart-filter-input {\n width: 100%;\n padding: 10px 40px 10px 16px;\n border: 1px solid #e0e0e0;\n border-radius: 8px;\n font-size: 14px;\n background: #fafafa;\n transition: all 0.15s ease;\n}\n.smart-filter-input:focus {\n outline: none;\n border-color: #1976d2;\n background: white;\n box-shadow: 0 0 0 3px rgba(25, 118, 210, 0.1);\n}\n.smart-filter-input::placeholder {\n color: #999;\n}\n\n.clear-filter-btn {\n position: absolute;\n right: 8px;\n top: 50%;\n transform: translateY(-50%);\n width: 28px;\n height: 28px;\n border: none;\n background: transparent;\n border-radius: 50%;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n color: #757575;\n transition: all 0.15s ease;\n}\n.clear-filter-btn:hover {\n background: #e0e0e0;\n color: #424242;\n}\n\n.header-right {\n display: flex;\n align-items: center;\n gap: 16px;\n flex-shrink: 0;\n}\n\n.view-mode-toggle {\n display: flex;\n background: #f0f0f0;\n border-radius: 8px;\n padding: 3px;\n}\n\n.toggle-btn {\n width: 36px;\n height: 32px;\n border: none;\n background: transparent;\n border-radius: 6px;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n color: #666;\n transition: all 0.15s ease;\n}\n.toggle-btn:hover {\n color: #333;\n}\n.toggle-btn.active {\n background: white;\n color: #1976d2;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);\n}\n\n.content-body {\n flex: 1;\n overflow: hidden;\n padding: 20px 24px;\n display: flex;\n flex-direction: column;\n}\n\n.loading-container,\n.loading-state {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n height: 100%;\n gap: 16px;\n background: white;\n border-radius: 8px;\n}\n\n.loading-spinner {\n font-size: 32px;\n color: #1976d2;\n}\n\n.loading-message {\n margin: 0;\n font-size: 14px;\n color: #666;\n}\n\n.empty-state {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n height: 100%;\n text-align: center;\n color: #757575;\n background: white;\n border-radius: 8px;\n padding: 40px;\n}\n\n.empty-icon {\n font-size: 64px;\n color: #e0e0e0;\n margin-bottom: 24px;\n}\n\n.empty-state h3 {\n margin: 0 0 8px 0;\n font-size: 20px;\n font-weight: 600;\n color: #333;\n}\n\n.empty-state p {\n margin: 0;\n font-size: 14px;\n max-width: 400px;\n color: #666;\n}\n\n/* Entity Home View - Card Grid */\n.entity-home-view {\n flex: 1;\n overflow: auto;\n padding: 4px;\n}\n\n.entity-card-grid {\n display: grid;\n grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));\n gap: 16px;\n padding: 0;\n}\n\n.entity-card {\n display: flex;\n align-items: flex-start;\n gap: 16px;\n padding: 20px;\n background: white;\n border-radius: 10px;\n cursor: pointer;\n transition: all 0.2s ease;\n border: 1px solid #e8e8e8;\n}\n\n.entity-card:hover {\n transform: translateY(-2px);\n box-shadow: 0 6px 20px rgba(0, 0, 0, 0.08);\n border-color: #1976d2;\n}\n\n.entity-card:active {\n transform: translateY(0);\n}\n\n.entity-card-icon {\n width: 48px;\n height: 48px;\n border-radius: 10px;\n background: linear-gradient(135deg, #e3f2fd, #bbdefb);\n display: flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n}\n\n.entity-card-icon i {\n font-size: 20px;\n color: #1976d2;\n}\n\n.entity-card:hover .entity-card-icon {\n background: linear-gradient(135deg, #1976d2, #1565c0);\n}\n\n.entity-card:hover .entity-card-icon i {\n color: white;\n}\n\n.entity-card-content {\n flex: 1;\n min-width: 0;\n}\n\n.entity-card-title {\n margin: 0 0 6px 0;\n font-size: 15px;\n font-weight: 600;\n color: #1a1a1a;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.entity-card-description {\n margin: 0;\n font-size: 13px;\n color: #666;\n line-height: 1.4;\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n overflow: hidden;\n}\n\n.detail-panel {\n flex-shrink: 0;\n height: 100%;\n background: white;\n border-left: 1px solid #e0e0e0;\n box-shadow: -4px 0 16px rgba(0, 0, 0, 0.08);\n overflow: hidden;\n animation: slideIn 0.2s ease-out;\n}\n\n@keyframes slideIn {\n from {\n transform: translateX(100%);\n opacity: 0;\n }\n to {\n transform: translateX(0);\n opacity: 1;\n }\n}\n:host ::ng-deep mj-explorer-grid-view,\n:host ::ng-deep mj-explorer-cards-view {\n flex: 1;\n display: flex;\n flex-direction: column;\n min-height: 0;\n}\n\n/* ============================================\n HOME SCREEN STYLES\n ============================================ */\n\n/* Home-level header adjustments */\n.content-header.home-header {\n border-bottom: none;\n background: transparent;\n box-shadow: none;\n padding: 24px 24px 16px;\n}\n\n.content-body.home-content {\n padding-top: 0;\n}\n\n/* Smart filter with search icon */\n.filter-icon {\n position: absolute;\n left: 14px;\n top: 50%;\n transform: translateY(-50%);\n color: #999;\n font-size: 14px;\n pointer-events: none;\n}\n\n.smart-filter-container .smart-filter-input {\n padding-left: 40px;\n}\n\n/* ============================================\n HOME SECTIONS\n ============================================ */\n\n.home-section {\n margin-bottom: 28px;\n}\n\n.section-header {\n display: flex;\n align-items: center;\n gap: 10px;\n margin-bottom: 14px;\n padding-left: 4px;\n}\n\n.section-icon {\n font-size: 14px;\n color: #1976d2;\n width: 20px;\n text-align: center;\n}\n\n.section-title {\n margin: 0;\n font-size: 14px;\n font-weight: 600;\n color: #333;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n}\n\n.section-count {\n font-size: 12px;\n color: #888;\n font-weight: 500;\n margin-left: 8px;\n}\n\n/* ============================================\n TWO-COLUMN LAYOUT (Entities left, Records right)\n ============================================ */\n\n.home-two-column-layout {\n display: grid;\n grid-template-columns: 1fr 1fr;\n gap: 24px;\n margin-bottom: 28px;\n}\n\n.home-column {\n display: flex;\n flex-direction: column;\n gap: 20px;\n}\n\n/* Section header with inline toggle */\n.section-header-with-toggle {\n display: flex;\n align-items: center;\n justify-content: space-between;\n margin-bottom: 14px;\n padding-left: 4px;\n}\n\n.section-header-left {\n display: flex;\n align-items: center;\n gap: 10px;\n}\n\n/* Inline Common/All toggle (compact) */\n.inline-toggle {\n display: flex;\n background: #f0f0f0;\n border-radius: 6px;\n padding: 2px;\n}\n\n.inline-toggle .toggle-btn {\n width: auto;\n height: 26px;\n padding: 0 10px;\n border: none;\n background: transparent;\n border-radius: 4px;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n color: #666;\n font-size: 11px;\n font-weight: 500;\n transition: all 0.15s ease;\n}\n\n.inline-toggle .toggle-btn:hover {\n color: #333;\n}\n\n.inline-toggle .toggle-btn.active {\n background: white;\n color: #1976d2;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);\n}\n\n/* ============================================\n COMPACT ENTITY CARDS (Recent/Favorites rows)\n ============================================ */\n\n.entity-card-row {\n display: flex;\n flex-wrap: wrap;\n gap: 12px;\n padding: 0;\n}\n\n.entity-card.compact {\n flex: 0 0 auto;\n width: auto;\n min-width: 180px;\n max-width: 280px;\n padding: 12px 16px;\n gap: 12px;\n}\n\n.entity-card.compact .entity-card-icon {\n width: 36px;\n height: 36px;\n border-radius: 8px;\n}\n\n.entity-card.compact .entity-card-icon i {\n font-size: 16px;\n}\n\n.entity-card.compact .entity-card-title {\n margin: 0;\n font-size: 14px;\n}\n\n.entity-card.compact .entity-card-content {\n display: flex;\n align-items: center;\n}\n\n/* ============================================\n FAVORITE BUTTON\n ============================================ */\n\n.favorite-btn {\n flex-shrink: 0;\n width: 32px;\n height: 32px;\n border: none;\n background: transparent;\n border-radius: 50%;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n color: #ccc;\n transition: all 0.15s ease;\n margin-left: auto;\n}\n\n.favorite-btn:hover {\n background: rgba(255, 193, 7, 0.1);\n color: #ffc107;\n transform: scale(1.1);\n}\n\n.favorite-btn.favorited {\n color: #ffc107;\n}\n\n.favorite-btn.favorited:hover {\n background: rgba(255, 193, 7, 0.15);\n color: #ffb300;\n}\n\n.entity-card .favorite-btn {\n opacity: 0;\n transition: opacity 0.15s ease, background 0.15s ease, color 0.15s ease, transform 0.15s ease;\n}\n\n.entity-card:hover .favorite-btn {\n opacity: 1;\n}\n\n.entity-card .favorite-btn.favorited {\n opacity: 1;\n}\n\n/* ============================================\n RECENT RECORDS LIST\n ============================================ */\n\n.recent-records-list {\n display: flex;\n flex-direction: column;\n gap: 4px;\n background: white;\n border-radius: 10px;\n padding: 8px;\n border: 1px solid #e8e8e8;\n}\n\n.recent-record-item {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 10px 12px;\n border-radius: 8px;\n cursor: pointer;\n transition: all 0.15s ease;\n}\n\n.recent-record-item:hover {\n background: #f5f7fa;\n}\n\n.recent-record-icon {\n width: 32px;\n height: 32px;\n border-radius: 8px;\n background: linear-gradient(135deg, #e8f5e9, #c8e6c9);\n display: flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n}\n\n.recent-record-icon i {\n font-size: 14px;\n color: #4caf50;\n}\n\n.recent-record-content {\n flex: 1;\n min-width: 0;\n display: flex;\n flex-direction: column;\n gap: 2px;\n}\n\n.recent-record-name {\n font-size: 14px;\n font-weight: 500;\n color: #1a1a1a;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.recent-record-entity {\n font-size: 12px;\n color: #888;\n}\n\n.recent-record-time {\n font-size: 11px;\n color: #aaa;\n flex-shrink: 0;\n}\n\n/* ============================================\n ENTITY LIST (for Recent/Favorite Entities)\n ============================================ */\n\n.entity-list {\n display: flex;\n flex-direction: column;\n gap: 4px;\n background: white;\n border-radius: 10px;\n padding: 8px;\n border: 1px solid #e8e8e8;\n}\n\n.entity-list-item {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 10px 12px;\n border-radius: 8px;\n cursor: pointer;\n transition: all 0.15s ease;\n}\n\n.entity-list-item:hover {\n background: #f5f7fa;\n}\n\n.entity-list-icon {\n width: 32px;\n height: 32px;\n border-radius: 8px;\n background: linear-gradient(135deg, #e3f2fd, #bbdefb);\n display: flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n}\n\n.entity-list-icon i {\n font-size: 14px;\n color: #1976d2;\n}\n\n.entity-list-content {\n flex: 1;\n min-width: 0;\n display: flex;\n flex-direction: column;\n gap: 2px;\n}\n\n.entity-list-name {\n font-size: 14px;\n font-weight: 500;\n color: #1a1a1a;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.entity-list-item .favorite-btn {\n opacity: 0;\n transition: opacity 0.15s ease;\n}\n\n.entity-list-item:hover .favorite-btn {\n opacity: 1;\n}\n\n.entity-list-item .favorite-btn.favorited {\n opacity: 1;\n}\n\n/* ============================================\n ENTITY FILTER STRIP (for Recent Records)\n ============================================ */\n\n.entity-filter-strip {\n display: flex;\n gap: 4px;\n margin-left: auto;\n background: #f0f0f0;\n border-radius: 6px;\n padding: 3px;\n}\n\n.entity-filter-btn {\n width: 28px;\n height: 26px;\n border: none;\n background: transparent;\n border-radius: 4px;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n color: #666;\n font-size: 12px;\n transition: all 0.15s ease;\n}\n\n.entity-filter-btn:hover {\n color: #333;\n background: rgba(0, 0, 0, 0.05);\n}\n\n.entity-filter-btn.active {\n background: white;\n color: #1976d2;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);\n}\n\n/* ============================================\n COMMON/ALL TOGGLE WITH LABELS\n ============================================ */\n\n.view-mode-toggle .toggle-btn {\n width: auto;\n padding: 0 12px;\n gap: 6px;\n}\n\n.toggle-label {\n font-size: 12px;\n font-weight: 500;\n}\n\n/* ============================================\n EMPTY STATE VARIANTS\n ============================================ */\n\n.empty-state.small {\n height: auto;\n padding: 32px;\n background: #fafafa;\n}\n\n.empty-state.small .empty-icon {\n font-size: 40px;\n margin-bottom: 16px;\n}\n\n.empty-state.small h3 {\n font-size: 16px;\n}\n\n.empty-state.small p {\n font-size: 13px;\n}\n\n/* ============================================\n RESPONSIVE STYLES\n ============================================ */\n\n@media (max-width: 1200px) {\n .entity-card-grid {\n grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));\n }\n}\n\n@media (max-width: 900px) {\n .content-header {\n flex-wrap: wrap;\n gap: 12px;\n }\n\n .header-center {\n order: 3;\n flex-basis: 100%;\n max-width: 100%;\n }\n\n .entity-card.compact {\n min-width: 160px;\n }\n\n /* Stack two-column layout on smaller screens */\n .home-two-column-layout {\n grid-template-columns: 1fr;\n gap: 20px;\n }\n}\n\n@media (max-width: 600px) {\n .data-explorer-container {\n flex-direction: column;\n }\n\n .navigation-panel {\n display: none;\n }\n\n .content-header {\n padding: 12px 16px;\n }\n\n .content-body {\n padding: 12px 16px;\n }\n\n .entity-card-grid {\n grid-template-columns: 1fr;\n gap: 12px;\n }\n\n .entity-card {\n padding: 16px;\n }\n\n .entity-card.compact {\n min-width: 100%;\n max-width: 100%;\n }\n\n .entity-card-row {\n flex-direction: column;\n }\n\n .view-mode-toggle {\n display: none;\n }\n\n .section-title {\n font-size: 13px;\n }\n\n .home-section {\n margin-bottom: 20px;\n }\n}\n"] }]
|
|
2101
|
+
}], () => [{ type: i1.ExplorerStateService }, { type: i0.ChangeDetectorRef }, { type: i2.Router }, { type: i3.RecentAccessService }], { filterInputRef: [{
|
|
2102
|
+
type: ViewChild,
|
|
2103
|
+
args: ['filterInput']
|
|
2104
|
+
}], viewSelectorRef: [{
|
|
2105
|
+
type: ViewChild,
|
|
2106
|
+
args: [ViewSelectorComponent]
|
|
2107
|
+
}], entityViewerRef: [{
|
|
2108
|
+
type: ViewChild,
|
|
2109
|
+
args: [EntityViewerComponent]
|
|
2110
|
+
}], entityFilter: [{
|
|
2111
|
+
type: Input
|
|
2112
|
+
}], deepLink: [{
|
|
2113
|
+
type: Input
|
|
2114
|
+
}], contextName: [{
|
|
2115
|
+
type: Input
|
|
2116
|
+
}], contextIcon: [{
|
|
2117
|
+
type: Input
|
|
2118
|
+
}], handleKeyboardShortcut: [{
|
|
2119
|
+
type: HostListener,
|
|
2120
|
+
args: ['document:keydown', ['$event']]
|
|
2121
|
+
}] }); })();
|
|
2122
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(DataExplorerDashboardComponent, { className: "DataExplorerDashboardComponent", filePath: "src/DataExplorer/data-explorer-dashboard.component.ts", lineNumber: 55 }); })();
|
|
2123
|
+
/**
|
|
2124
|
+
* Tree-shaking prevention
|
|
2125
|
+
*/
|
|
2126
|
+
export function LoadDataExplorerDashboard() {
|
|
2127
|
+
// Force inclusion in production builds
|
|
2128
|
+
}
|
|
2129
|
+
//# sourceMappingURL=data-explorer-dashboard.component.js.map
|