@memberjunction/ng-dashboards 5.29.0 → 5.30.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.map +1 -1
- package/dist/AI/components/agents/agent-configuration.component.js +11 -9
- package/dist/AI/components/agents/agent-configuration.component.js.map +1 -1
- package/dist/AI/components/agents/agent-editor.component.d.ts.map +1 -1
- package/dist/AI/components/agents/agent-editor.component.js +21 -19
- package/dist/AI/components/agents/agent-editor.component.js.map +1 -1
- package/dist/AI/components/autotagging/autotagging-pipeline-resource.component.js +154 -154
- package/dist/AI/components/autotagging/autotagging-pipeline-resource.component.js.map +1 -1
- package/dist/AI/components/duplicates/duplicate-detection-resource.component.d.ts.map +1 -1
- package/dist/AI/components/duplicates/duplicate-detection-resource.component.js +8 -0
- package/dist/AI/components/duplicates/duplicate-detection-resource.component.js.map +1 -1
- package/dist/AI/components/vectors/vector-management-resource.component.d.ts +1 -1
- package/dist/AI/components/vectors/vector-management-resource.component.d.ts.map +1 -1
- package/dist/AI/components/vectors/vector-management-resource.component.js +2 -2
- package/dist/AI/components/vectors/vector-management-resource.component.js.map +1 -1
- package/dist/APIKeys/api-applications-panel.component.d.ts +1 -1
- package/dist/APIKeys/api-applications-panel.component.d.ts.map +1 -1
- package/dist/APIKeys/api-applications-panel.component.js +15 -2
- package/dist/APIKeys/api-applications-panel.component.js.map +1 -1
- package/dist/APIKeys/api-key-edit-panel.component.d.ts.map +1 -1
- package/dist/APIKeys/api-key-edit-panel.component.js +17 -5
- package/dist/APIKeys/api-key-edit-panel.component.js.map +1 -1
- package/dist/Credentials/components/credentials-list-resource.component.d.ts.map +1 -1
- package/dist/Credentials/components/credentials-list-resource.component.js +56 -71
- package/dist/Credentials/components/credentials-list-resource.component.js.map +1 -1
- package/dist/DashboardBrowser/dashboard-browser-resource.component.d.ts +0 -8
- package/dist/DashboardBrowser/dashboard-browser-resource.component.d.ts.map +1 -1
- package/dist/DashboardBrowser/dashboard-browser-resource.component.js +73 -74
- package/dist/DashboardBrowser/dashboard-browser-resource.component.js.map +1 -1
- package/dist/DashboardBrowser/dashboard-share-adapter.d.ts +25 -0
- package/dist/DashboardBrowser/dashboard-share-adapter.d.ts.map +1 -0
- package/dist/DashboardBrowser/dashboard-share-adapter.js +99 -0
- package/dist/DashboardBrowser/dashboard-share-adapter.js.map +1 -0
- package/dist/DashboardBrowser/dashboard-share-dialog.component.d.ts +21 -104
- package/dist/DashboardBrowser/dashboard-share-dialog.component.d.ts.map +1 -1
- package/dist/DashboardBrowser/dashboard-share-dialog.component.js +48 -530
- package/dist/DashboardBrowser/dashboard-share-dialog.component.js.map +1 -1
- package/dist/DataExplorer/data-explorer-dashboard.component.d.ts +5 -0
- package/dist/DataExplorer/data-explorer-dashboard.component.d.ts.map +1 -1
- package/dist/DataExplorer/data-explorer-dashboard.component.js +17 -0
- package/dist/DataExplorer/data-explorer-dashboard.component.js.map +1 -1
- package/dist/Integration/components/connections/connections.component.d.ts +18 -1
- package/dist/Integration/components/connections/connections.component.d.ts.map +1 -1
- package/dist/Integration/components/connections/connections.component.js +251 -199
- package/dist/Integration/components/connections/connections.component.js.map +1 -1
- package/dist/Integration/services/integration-data.service.d.ts +7 -2
- package/dist/Integration/services/integration-data.service.d.ts.map +1 -1
- package/dist/Integration/services/integration-data.service.js +10 -2
- package/dist/Integration/services/integration-data.service.js.map +1 -1
- package/dist/KnowledgeHub/components/analytics/analytics-resource.component.js +144 -144
- package/dist/KnowledgeHub/components/analytics/analytics-resource.component.js.map +1 -1
- package/dist/Lists/components/lists-operations-resource.component.d.ts.map +1 -1
- package/dist/Lists/components/lists-operations-resource.component.js +12 -14
- package/dist/Lists/components/lists-operations-resource.component.js.map +1 -1
- package/dist/MCP/mcp-dashboard.component.d.ts.map +1 -1
- package/dist/MCP/mcp-dashboard.component.js +28 -57
- package/dist/MCP/mcp-dashboard.component.js.map +1 -1
- package/dist/Permissions/audit-log-resource.component.d.ts +38 -0
- package/dist/Permissions/audit-log-resource.component.d.ts.map +1 -0
- package/dist/Permissions/audit-log-resource.component.js +380 -0
- package/dist/Permissions/audit-log-resource.component.js.map +1 -0
- package/dist/Permissions/permissions-shared.d.ts +51 -0
- package/dist/Permissions/permissions-shared.d.ts.map +1 -0
- package/dist/Permissions/permissions-shared.js +91 -0
- package/dist/Permissions/permissions-shared.js.map +1 -0
- package/dist/Permissions/resource-access-resource.component.d.ts +45 -0
- package/dist/Permissions/resource-access-resource.component.d.ts.map +1 -0
- package/dist/Permissions/resource-access-resource.component.js +342 -0
- package/dist/Permissions/resource-access-resource.component.js.map +1 -0
- package/dist/Permissions/user-access-resource.component.d.ts +39 -0
- package/dist/Permissions/user-access-resource.component.d.ts.map +1 -0
- package/dist/Permissions/user-access-resource.component.js +346 -0
- package/dist/Permissions/user-access-resource.component.js.map +1 -0
- package/dist/component-studio-dashboards.module.d.ts +1 -0
- package/dist/component-studio-dashboards.module.d.ts.map +1 -1
- package/dist/component-studio-dashboards.module.js +7 -0
- package/dist/component-studio-dashboards.module.js.map +1 -1
- package/dist/core-dashboards.module.d.ts +24 -20
- package/dist/core-dashboards.module.d.ts.map +1 -1
- package/dist/core-dashboards.module.js +26 -1
- package/dist/core-dashboards.module.js.map +1 -1
- package/dist/public-api.d.ts +4 -1
- package/dist/public-api.d.ts.map +1 -1
- package/dist/public-api.js +4 -0
- package/dist/public-api.js.map +1 -1
- package/dist/shared/pipes/highlight-search.pipe.d.ts +1 -1
- package/dist/shared/pipes/highlight-search.pipe.d.ts.map +1 -1
- package/dist/shared/pipes/highlight-search.pipe.js +14 -4
- package/dist/shared/pipes/highlight-search.pipe.js.map +1 -1
- package/package.json +51 -50
|
@@ -722,14 +722,14 @@ function AnalyticsResourceComponent_Conditional_1_Conditional_23_Conditional_13_
|
|
|
722
722
|
} }
|
|
723
723
|
function AnalyticsResourceComponent_Conditional_1_Conditional_23_Conditional_13_Conditional_10_For_9_Conditional_3_Conditional_1_Template(rf, ctx) { if (rf & 1) {
|
|
724
724
|
const _r38 = i0.ɵɵgetCurrentView();
|
|
725
|
-
i0.ɵɵelementStart(0, "button",
|
|
725
|
+
i0.ɵɵelementStart(0, "button", 136);
|
|
726
726
|
i0.ɵɵlistener("click", function AnalyticsResourceComponent_Conditional_1_Conditional_23_Conditional_13_Conditional_10_For_9_Conditional_3_Conditional_1_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r38); const row_r37 = i0.ɵɵnextContext(2).$implicit; const ctx_r2 = i0.ɵɵnextContext(5); return i0.ɵɵresetView(ctx_r2.OpenDrillDownRecord(row_r37)); });
|
|
727
727
|
i0.ɵɵelement(1, "i", 99);
|
|
728
728
|
i0.ɵɵelementEnd();
|
|
729
729
|
} }
|
|
730
730
|
function AnalyticsResourceComponent_Conditional_1_Conditional_23_Conditional_13_Conditional_10_For_9_Conditional_3_Template(rf, ctx) { if (rf & 1) {
|
|
731
731
|
i0.ɵɵelementStart(0, "td", 96);
|
|
732
|
-
i0.ɵɵconditionalCreate(1, AnalyticsResourceComponent_Conditional_1_Conditional_23_Conditional_13_Conditional_10_For_9_Conditional_3_Conditional_1_Template, 2, 0, "button",
|
|
732
|
+
i0.ɵɵconditionalCreate(1, AnalyticsResourceComponent_Conditional_1_Conditional_23_Conditional_13_Conditional_10_For_9_Conditional_3_Conditional_1_Template, 2, 0, "button", 135);
|
|
733
733
|
i0.ɵɵelementEnd();
|
|
734
734
|
} if (rf & 2) {
|
|
735
735
|
const row_r37 = i0.ɵɵnextContext().$implicit;
|
|
@@ -794,7 +794,7 @@ function AnalyticsResourceComponent_Conditional_1_Conditional_23_Conditional_13_
|
|
|
794
794
|
i0.ɵɵconditional(ctx_r2.IsDrillDownLoading ? 9 : ctx_r2.DrillDownData.length > 0 ? 10 : 11);
|
|
795
795
|
} }
|
|
796
796
|
function AnalyticsResourceComponent_Conditional_1_Conditional_23_Conditional_14_For_6_For_5_Template(rf, ctx) { if (rf & 1) {
|
|
797
|
-
i0.ɵɵelementStart(0, "div",
|
|
797
|
+
i0.ɵɵelementStart(0, "div", 144);
|
|
798
798
|
i0.ɵɵtext(1);
|
|
799
799
|
i0.ɵɵelementEnd();
|
|
800
800
|
} if (rf & 2) {
|
|
@@ -805,11 +805,11 @@ function AnalyticsResourceComponent_Conditional_1_Conditional_23_Conditional_14_
|
|
|
805
805
|
i0.ɵɵtextInterpolate(seg_r39.Label);
|
|
806
806
|
} }
|
|
807
807
|
function AnalyticsResourceComponent_Conditional_1_Conditional_23_Conditional_14_For_6_Template(rf, ctx) { if (rf & 1) {
|
|
808
|
-
i0.ɵɵelementStart(0, "div",
|
|
808
|
+
i0.ɵɵelementStart(0, "div", 139)(1, "div", 141);
|
|
809
809
|
i0.ɵɵtext(2);
|
|
810
810
|
i0.ɵɵelementEnd();
|
|
811
|
-
i0.ɵɵelementStart(3, "div",
|
|
812
|
-
i0.ɵɵrepeaterCreate(4, AnalyticsResourceComponent_Conditional_1_Conditional_23_Conditional_14_For_6_For_5_Template, 2, 6, "div",
|
|
811
|
+
i0.ɵɵelementStart(3, "div", 142);
|
|
812
|
+
i0.ɵɵrepeaterCreate(4, AnalyticsResourceComponent_Conditional_1_Conditional_23_Conditional_14_For_6_For_5_Template, 2, 6, "div", 143, i0.ɵɵcomponentInstance().TrackByIndex, true);
|
|
813
813
|
i0.ɵɵelementEnd()();
|
|
814
814
|
} if (rf & 2) {
|
|
815
815
|
const row_r40 = ctx.$implicit;
|
|
@@ -820,7 +820,7 @@ function AnalyticsResourceComponent_Conditional_1_Conditional_23_Conditional_14_
|
|
|
820
820
|
} }
|
|
821
821
|
function AnalyticsResourceComponent_Conditional_1_Conditional_23_Conditional_14_For_9_Template(rf, ctx) { if (rf & 1) {
|
|
822
822
|
i0.ɵɵelementStart(0, "div", 61);
|
|
823
|
-
i0.ɵɵelement(1, "div",
|
|
823
|
+
i0.ɵɵelement(1, "div", 145);
|
|
824
824
|
i0.ɵɵtext(2);
|
|
825
825
|
i0.ɵɵelementEnd();
|
|
826
826
|
} if (rf & 2) {
|
|
@@ -832,13 +832,13 @@ function AnalyticsResourceComponent_Conditional_1_Conditional_23_Conditional_14_
|
|
|
832
832
|
} }
|
|
833
833
|
function AnalyticsResourceComponent_Conditional_1_Conditional_23_Conditional_14_Template(rf, ctx) { if (rf & 1) {
|
|
834
834
|
i0.ɵɵelementStart(0, "div", 122)(1, "h3");
|
|
835
|
-
i0.ɵɵelement(2, "i",
|
|
835
|
+
i0.ɵɵelement(2, "i", 137);
|
|
836
836
|
i0.ɵɵtext(3, " Tag Distribution by Entity");
|
|
837
837
|
i0.ɵɵelementEnd();
|
|
838
|
-
i0.ɵɵelementStart(4, "div",
|
|
839
|
-
i0.ɵɵrepeaterCreate(5, AnalyticsResourceComponent_Conditional_1_Conditional_23_Conditional_14_For_6_Template, 6, 1, "div",
|
|
838
|
+
i0.ɵɵelementStart(4, "div", 138);
|
|
839
|
+
i0.ɵɵrepeaterCreate(5, AnalyticsResourceComponent_Conditional_1_Conditional_23_Conditional_14_For_6_Template, 6, 1, "div", 139, _forTrack3);
|
|
840
840
|
i0.ɵɵelementEnd();
|
|
841
|
-
i0.ɵɵelementStart(7, "div",
|
|
841
|
+
i0.ɵɵelementStart(7, "div", 140);
|
|
842
842
|
i0.ɵɵrepeaterCreate(8, AnalyticsResourceComponent_Conditional_1_Conditional_23_Conditional_14_For_9_Template, 3, 3, "div", 61, _forTrack2);
|
|
843
843
|
i0.ɵɵelementEnd()();
|
|
844
844
|
} if (rf & 2) {
|
|
@@ -849,10 +849,10 @@ function AnalyticsResourceComponent_Conditional_1_Conditional_23_Conditional_14_
|
|
|
849
849
|
i0.ɵɵrepeater(ctx_r2.DistributionLegend);
|
|
850
850
|
} }
|
|
851
851
|
function AnalyticsResourceComponent_Conditional_1_Conditional_23_Conditional_15_For_6_Template(rf, ctx) { if (rf & 1) {
|
|
852
|
-
i0.ɵɵelementStart(0, "div",
|
|
852
|
+
i0.ɵɵelementStart(0, "div", 148)(1, "div", 150);
|
|
853
853
|
i0.ɵɵtext(2);
|
|
854
854
|
i0.ɵɵelementEnd();
|
|
855
|
-
i0.ɵɵelementStart(3, "div",
|
|
855
|
+
i0.ɵɵelementStart(3, "div", 151);
|
|
856
856
|
i0.ɵɵtext(4);
|
|
857
857
|
i0.ɵɵelementEnd()();
|
|
858
858
|
} if (rf & 2) {
|
|
@@ -866,13 +866,13 @@ function AnalyticsResourceComponent_Conditional_1_Conditional_23_Conditional_15_
|
|
|
866
866
|
} }
|
|
867
867
|
function AnalyticsResourceComponent_Conditional_1_Conditional_23_Conditional_15_Template(rf, ctx) { if (rf & 1) {
|
|
868
868
|
i0.ɵɵelementStart(0, "div", 122)(1, "h3");
|
|
869
|
-
i0.ɵɵelement(2, "i",
|
|
869
|
+
i0.ɵɵelement(2, "i", 146);
|
|
870
870
|
i0.ɵɵtext(3, " Tag Depth Distribution");
|
|
871
871
|
i0.ɵɵelementEnd();
|
|
872
|
-
i0.ɵɵelementStart(4, "div",
|
|
873
|
-
i0.ɵɵrepeaterCreate(5, AnalyticsResourceComponent_Conditional_1_Conditional_23_Conditional_15_For_6_Template, 5, 6, "div",
|
|
872
|
+
i0.ɵɵelementStart(4, "div", 147);
|
|
873
|
+
i0.ɵɵrepeaterCreate(5, AnalyticsResourceComponent_Conditional_1_Conditional_23_Conditional_15_For_6_Template, 5, 6, "div", 148, _forTrack2);
|
|
874
874
|
i0.ɵɵelementEnd();
|
|
875
|
-
i0.ɵɵelementStart(7, "div",
|
|
875
|
+
i0.ɵɵelementStart(7, "div", 149);
|
|
876
876
|
i0.ɵɵtext(8, " Taxonomy hierarchy depth -- Most tags at depth 2-3 indicating a healthy mid-level structure ");
|
|
877
877
|
i0.ɵɵelementEnd()();
|
|
878
878
|
} if (rf & 2) {
|
|
@@ -882,7 +882,7 @@ function AnalyticsResourceComponent_Conditional_1_Conditional_23_Conditional_15_
|
|
|
882
882
|
} }
|
|
883
883
|
function AnalyticsResourceComponent_Conditional_1_Conditional_23_Conditional_22_Template(rf, ctx) { if (rf & 1) {
|
|
884
884
|
i0.ɵɵelementStart(0, "span", 126);
|
|
885
|
-
i0.ɵɵelement(1, "i",
|
|
885
|
+
i0.ɵɵelement(1, "i", 152);
|
|
886
886
|
i0.ɵɵtext(2);
|
|
887
887
|
i0.ɵɵelementEnd();
|
|
888
888
|
} if (rf & 2) {
|
|
@@ -891,11 +891,11 @@ function AnalyticsResourceComponent_Conditional_1_Conditional_23_Conditional_22_
|
|
|
891
891
|
i0.ɵɵtextInterpolate1(" Last computed: ", ctx_r2.CoOccurrenceLastComputed, " ");
|
|
892
892
|
} }
|
|
893
893
|
function AnalyticsResourceComponent_Conditional_1_Conditional_23_Conditional_24_Template(rf, ctx) { if (rf & 1) {
|
|
894
|
-
i0.ɵɵelement(0, "i",
|
|
894
|
+
i0.ɵɵelement(0, "i", 153);
|
|
895
895
|
i0.ɵɵtext(1, " Computing... ");
|
|
896
896
|
} }
|
|
897
897
|
function AnalyticsResourceComponent_Conditional_1_Conditional_23_Conditional_25_Template(rf, ctx) { if (rf & 1) {
|
|
898
|
-
i0.ɵɵelement(0, "i",
|
|
898
|
+
i0.ɵɵelement(0, "i", 154);
|
|
899
899
|
i0.ɵɵtext(1, " Recompute ");
|
|
900
900
|
} }
|
|
901
901
|
function AnalyticsResourceComponent_Conditional_1_Conditional_23_Conditional_26_For_16_Template(rf, ctx) { if (rf & 1) {
|
|
@@ -912,7 +912,7 @@ function AnalyticsResourceComponent_Conditional_1_Conditional_23_Conditional_26_
|
|
|
912
912
|
i0.ɵɵtext(10);
|
|
913
913
|
i0.ɵɵelementEnd();
|
|
914
914
|
i0.ɵɵelementStart(11, "td");
|
|
915
|
-
i0.ɵɵelement(12, "span",
|
|
915
|
+
i0.ɵɵelement(12, "span", 155);
|
|
916
916
|
i0.ɵɵelementEnd()();
|
|
917
917
|
} if (rf & 2) {
|
|
918
918
|
const pair_r43 = ctx.$implicit;
|
|
@@ -1019,7 +1019,7 @@ function AnalyticsResourceComponent_Conditional_1_Conditional_24_Conditional_9_F
|
|
|
1019
1019
|
i0.ɵɵtext(3);
|
|
1020
1020
|
i0.ɵɵelementEnd()();
|
|
1021
1021
|
i0.ɵɵelementStart(4, "td");
|
|
1022
|
-
i0.ɵɵelement(5, "i",
|
|
1022
|
+
i0.ɵɵelement(5, "i", 158);
|
|
1023
1023
|
i0.ɵɵtext(6);
|
|
1024
1024
|
i0.ɵɵelementEnd();
|
|
1025
1025
|
i0.ɵɵelementStart(7, "td", 128);
|
|
@@ -1037,8 +1037,8 @@ function AnalyticsResourceComponent_Conditional_1_Conditional_24_Conditional_9_F
|
|
|
1037
1037
|
i0.ɵɵelementStart(15, "td", 134);
|
|
1038
1038
|
i0.ɵɵtext(16);
|
|
1039
1039
|
i0.ɵɵelementEnd();
|
|
1040
|
-
i0.ɵɵelementStart(17, "td")(18, "span",
|
|
1041
|
-
i0.ɵɵelement(19, "i",
|
|
1040
|
+
i0.ɵɵelementStart(17, "td")(18, "span", 159);
|
|
1041
|
+
i0.ɵɵelement(19, "i", 160);
|
|
1042
1042
|
i0.ɵɵtext(20);
|
|
1043
1043
|
i0.ɵɵelementEnd()()();
|
|
1044
1044
|
} if (rf & 2) {
|
|
@@ -1093,7 +1093,7 @@ function AnalyticsResourceComponent_Conditional_1_Conditional_24_Conditional_9_T
|
|
|
1093
1093
|
i0.ɵɵtext(19, "Status");
|
|
1094
1094
|
i0.ɵɵelementEnd()()();
|
|
1095
1095
|
i0.ɵɵelementStart(20, "tbody");
|
|
1096
|
-
i0.ɵɵrepeaterCreate(21, AnalyticsResourceComponent_Conditional_1_Conditional_24_Conditional_9_For_22_Template, 21, 16, "tr",
|
|
1096
|
+
i0.ɵɵrepeaterCreate(21, AnalyticsResourceComponent_Conditional_1_Conditional_24_Conditional_9_For_22_Template, 21, 16, "tr", 157, _forTrack1);
|
|
1097
1097
|
i0.ɵɵelementEnd()()();
|
|
1098
1098
|
} if (rf & 2) {
|
|
1099
1099
|
const ctx_r2 = i0.ɵɵnextContext(3);
|
|
@@ -1102,7 +1102,7 @@ function AnalyticsResourceComponent_Conditional_1_Conditional_24_Conditional_9_T
|
|
|
1102
1102
|
} }
|
|
1103
1103
|
function AnalyticsResourceComponent_Conditional_1_Conditional_24_Conditional_10_Template(rf, ctx) { if (rf & 1) {
|
|
1104
1104
|
i0.ɵɵelementStart(0, "div", 121);
|
|
1105
|
-
i0.ɵɵelement(1, "i",
|
|
1105
|
+
i0.ɵɵelement(1, "i", 156);
|
|
1106
1106
|
i0.ɵɵelementStart(2, "p");
|
|
1107
1107
|
i0.ɵɵtext(3, "No content sources configured yet.");
|
|
1108
1108
|
i0.ɵɵelementEnd()();
|
|
@@ -1142,14 +1142,14 @@ function AnalyticsResourceComponent_Conditional_1_Conditional_24_Conditional_11_
|
|
|
1142
1142
|
} }
|
|
1143
1143
|
function AnalyticsResourceComponent_Conditional_1_Conditional_24_Conditional_11_Conditional_10_For_9_Conditional_3_Conditional_1_Template(rf, ctx) { if (rf & 1) {
|
|
1144
1144
|
const _r52 = i0.ɵɵgetCurrentView();
|
|
1145
|
-
i0.ɵɵelementStart(0, "button",
|
|
1145
|
+
i0.ɵɵelementStart(0, "button", 136);
|
|
1146
1146
|
i0.ɵɵlistener("click", function AnalyticsResourceComponent_Conditional_1_Conditional_24_Conditional_11_Conditional_10_For_9_Conditional_3_Conditional_1_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r52); const row_r51 = i0.ɵɵnextContext(2).$implicit; const ctx_r2 = i0.ɵɵnextContext(5); return i0.ɵɵresetView(ctx_r2.OpenDrillDownRecord(row_r51)); });
|
|
1147
1147
|
i0.ɵɵelement(1, "i", 99);
|
|
1148
1148
|
i0.ɵɵelementEnd();
|
|
1149
1149
|
} }
|
|
1150
1150
|
function AnalyticsResourceComponent_Conditional_1_Conditional_24_Conditional_11_Conditional_10_For_9_Conditional_3_Template(rf, ctx) { if (rf & 1) {
|
|
1151
1151
|
i0.ɵɵelementStart(0, "td", 96);
|
|
1152
|
-
i0.ɵɵconditionalCreate(1, AnalyticsResourceComponent_Conditional_1_Conditional_24_Conditional_11_Conditional_10_For_9_Conditional_3_Conditional_1_Template, 2, 0, "button",
|
|
1152
|
+
i0.ɵɵconditionalCreate(1, AnalyticsResourceComponent_Conditional_1_Conditional_24_Conditional_11_Conditional_10_For_9_Conditional_3_Conditional_1_Template, 2, 0, "button", 135);
|
|
1153
1153
|
i0.ɵɵelementEnd();
|
|
1154
1154
|
} if (rf & 2) {
|
|
1155
1155
|
const row_r51 = i0.ɵɵnextContext().$implicit;
|
|
@@ -1214,7 +1214,7 @@ function AnalyticsResourceComponent_Conditional_1_Conditional_24_Conditional_11_
|
|
|
1214
1214
|
i0.ɵɵconditional(ctx_r2.IsDrillDownLoading ? 9 : ctx_r2.DrillDownData.length > 0 ? 10 : 11);
|
|
1215
1215
|
} }
|
|
1216
1216
|
function AnalyticsResourceComponent_Conditional_1_Conditional_24_Conditional_12_For_11_Template(rf, ctx) { if (rf & 1) {
|
|
1217
|
-
i0.ɵɵelementStart(0, "div", 33)(1, "div",
|
|
1217
|
+
i0.ɵɵelementStart(0, "div", 33)(1, "div", 170);
|
|
1218
1218
|
i0.ɵɵtext(2);
|
|
1219
1219
|
i0.ɵɵelementEnd();
|
|
1220
1220
|
i0.ɵɵelement(3, "div", 101);
|
|
@@ -1231,10 +1231,10 @@ function AnalyticsResourceComponent_Conditional_1_Conditional_24_Conditional_12_
|
|
|
1231
1231
|
i0.ɵɵtextInterpolate(bar_r53.Label);
|
|
1232
1232
|
} }
|
|
1233
1233
|
function AnalyticsResourceComponent_Conditional_1_Conditional_24_Conditional_12_For_18_Template(rf, ctx) { if (rf & 1) {
|
|
1234
|
-
i0.ɵɵelementStart(0, "div",
|
|
1234
|
+
i0.ɵɵelementStart(0, "div", 167)(1, "span", 171);
|
|
1235
1235
|
i0.ɵɵtext(2);
|
|
1236
1236
|
i0.ɵɵelementEnd();
|
|
1237
|
-
i0.ɵɵelementStart(3, "div",
|
|
1237
|
+
i0.ɵɵelementStart(3, "div", 172)(4, "div", 173);
|
|
1238
1238
|
i0.ɵɵtext(5);
|
|
1239
1239
|
i0.ɵɵelementEnd()()();
|
|
1240
1240
|
} if (rf & 2) {
|
|
@@ -1248,25 +1248,25 @@ function AnalyticsResourceComponent_Conditional_1_Conditional_24_Conditional_12_
|
|
|
1248
1248
|
} }
|
|
1249
1249
|
function AnalyticsResourceComponent_Conditional_1_Conditional_24_Conditional_12_Template(rf, ctx) { if (rf & 1) {
|
|
1250
1250
|
i0.ɵɵelementStart(0, "div", 122)(1, "h3");
|
|
1251
|
-
i0.ɵɵelement(2, "i",
|
|
1251
|
+
i0.ɵɵelement(2, "i", 161);
|
|
1252
1252
|
i0.ɵɵtext(3);
|
|
1253
1253
|
i0.ɵɵelementEnd();
|
|
1254
|
-
i0.ɵɵelementStart(4, "div",
|
|
1255
|
-
i0.ɵɵelement(7, "i",
|
|
1254
|
+
i0.ɵɵelementStart(4, "div", 162)(5, "div", 163)(6, "div", 31);
|
|
1255
|
+
i0.ɵɵelement(7, "i", 137);
|
|
1256
1256
|
i0.ɵɵtext(8, " Items Processed (Last 8 Weeks)");
|
|
1257
1257
|
i0.ɵɵelementEnd();
|
|
1258
|
-
i0.ɵɵelementStart(9, "div",
|
|
1258
|
+
i0.ɵɵelementStart(9, "div", 164);
|
|
1259
1259
|
i0.ɵɵrepeaterCreate(10, AnalyticsResourceComponent_Conditional_1_Conditional_24_Conditional_12_For_11_Template, 6, 6, "div", 33, _forTrack2);
|
|
1260
1260
|
i0.ɵɵelementEnd()();
|
|
1261
|
-
i0.ɵɵelementStart(12, "div",
|
|
1262
|
-
i0.ɵɵelement(14, "i",
|
|
1261
|
+
i0.ɵɵelementStart(12, "div", 163)(13, "div", 31);
|
|
1262
|
+
i0.ɵɵelement(14, "i", 165);
|
|
1263
1263
|
i0.ɵɵtext(15, " Tag Quality Distribution");
|
|
1264
1264
|
i0.ɵɵelementEnd();
|
|
1265
|
-
i0.ɵɵelementStart(16, "div",
|
|
1266
|
-
i0.ɵɵrepeaterCreate(17, AnalyticsResourceComponent_Conditional_1_Conditional_24_Conditional_12_For_18_Template, 6, 6, "div",
|
|
1265
|
+
i0.ɵɵelementStart(16, "div", 166);
|
|
1266
|
+
i0.ɵɵrepeaterCreate(17, AnalyticsResourceComponent_Conditional_1_Conditional_24_Conditional_12_For_18_Template, 6, 6, "div", 167, _forTrack2);
|
|
1267
1267
|
i0.ɵɵelementEnd();
|
|
1268
|
-
i0.ɵɵelementStart(19, "div",
|
|
1269
|
-
i0.ɵɵelement(20, "i",
|
|
1268
|
+
i0.ɵɵelementStart(19, "div", 168);
|
|
1269
|
+
i0.ɵɵelement(20, "i", 169);
|
|
1270
1270
|
i0.ɵɵtext(21);
|
|
1271
1271
|
i0.ɵɵelementEnd()()()();
|
|
1272
1272
|
} if (rf & 2) {
|
|
@@ -1281,13 +1281,13 @@ function AnalyticsResourceComponent_Conditional_1_Conditional_24_Conditional_12_
|
|
|
1281
1281
|
i0.ɵɵtextInterpolate1(" ", ctx_r2.SourceQualityNote, " ");
|
|
1282
1282
|
} }
|
|
1283
1283
|
function AnalyticsResourceComponent_Conditional_1_Conditional_24_Conditional_13_For_6_Template(rf, ctx) { if (rf & 1) {
|
|
1284
|
-
i0.ɵɵelementStart(0, "div",
|
|
1284
|
+
i0.ɵɵelementStart(0, "div", 177)(1, "div", 178);
|
|
1285
1285
|
i0.ɵɵtext(2);
|
|
1286
1286
|
i0.ɵɵelementEnd();
|
|
1287
|
-
i0.ɵɵelementStart(3, "div",
|
|
1287
|
+
i0.ɵɵelementStart(3, "div", 179);
|
|
1288
1288
|
i0.ɵɵtext(4);
|
|
1289
1289
|
i0.ɵɵelementEnd();
|
|
1290
|
-
i0.ɵɵelementStart(5, "div",
|
|
1290
|
+
i0.ɵɵelementStart(5, "div", 180);
|
|
1291
1291
|
i0.ɵɵtext(6, "uptime");
|
|
1292
1292
|
i0.ɵɵelementEnd()();
|
|
1293
1293
|
} if (rf & 2) {
|
|
@@ -1302,11 +1302,11 @@ function AnalyticsResourceComponent_Conditional_1_Conditional_24_Conditional_13_
|
|
|
1302
1302
|
} }
|
|
1303
1303
|
function AnalyticsResourceComponent_Conditional_1_Conditional_24_Conditional_13_Template(rf, ctx) { if (rf & 1) {
|
|
1304
1304
|
i0.ɵɵelementStart(0, "div", 122)(1, "h3");
|
|
1305
|
-
i0.ɵɵelement(2, "i",
|
|
1305
|
+
i0.ɵɵelement(2, "i", 174);
|
|
1306
1306
|
i0.ɵɵtext(3, " Source Health Summary");
|
|
1307
1307
|
i0.ɵɵelementEnd();
|
|
1308
|
-
i0.ɵɵelementStart(4, "div",
|
|
1309
|
-
i0.ɵɵrepeaterCreate(5, AnalyticsResourceComponent_Conditional_1_Conditional_24_Conditional_13_For_6_Template, 7, 6, "div",
|
|
1308
|
+
i0.ɵɵelementStart(4, "div", 175);
|
|
1309
|
+
i0.ɵɵrepeaterCreate(5, AnalyticsResourceComponent_Conditional_1_Conditional_24_Conditional_13_For_6_Template, 7, 6, "div", 176, _forTrack1);
|
|
1310
1310
|
i0.ɵɵelementEnd()();
|
|
1311
1311
|
} if (rf & 2) {
|
|
1312
1312
|
const ctx_r2 = i0.ɵɵnextContext(3);
|
|
@@ -1317,7 +1317,7 @@ function AnalyticsResourceComponent_Conditional_1_Conditional_24_Conditional_13_
|
|
|
1317
1317
|
} }
|
|
1318
1318
|
function AnalyticsResourceComponent_Conditional_1_Conditional_24_Template(rf, ctx) { if (rf & 1) {
|
|
1319
1319
|
i0.ɵɵelementStart(0, "section", 17)(1, "div", 19);
|
|
1320
|
-
i0.ɵɵelement(2, "i",
|
|
1320
|
+
i0.ɵɵelement(2, "i", 156);
|
|
1321
1321
|
i0.ɵɵelementStart(3, "h1");
|
|
1322
1322
|
i0.ɵɵtext(4, "Sources");
|
|
1323
1323
|
i0.ɵɵelementEnd()();
|
|
@@ -1344,7 +1344,7 @@ function AnalyticsResourceComponent_Conditional_1_Conditional_24_Template(rf, ct
|
|
|
1344
1344
|
} }
|
|
1345
1345
|
function AnalyticsResourceComponent_Conditional_1_Conditional_25_For_12_Template(rf, ctx) { if (rf & 1) {
|
|
1346
1346
|
const _r56 = i0.ɵɵgetCurrentView();
|
|
1347
|
-
i0.ɵɵelementStart(0, "div",
|
|
1347
|
+
i0.ɵɵelementStart(0, "div", 196);
|
|
1348
1348
|
i0.ɵɵlistener("click", function AnalyticsResourceComponent_Conditional_1_Conditional_25_For_12_Template_div_click_0_listener() { const $index_r57 = i0.ɵɵrestoreView(_r56).$index; const ctx_r2 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r2.OpenDrillDown("pipeline-throughput:" + $index_r57)); });
|
|
1349
1349
|
i0.ɵɵelementEnd();
|
|
1350
1350
|
} if (rf & 2) {
|
|
@@ -1395,14 +1395,14 @@ function AnalyticsResourceComponent_Conditional_1_Conditional_25_Conditional_16_
|
|
|
1395
1395
|
} }
|
|
1396
1396
|
function AnalyticsResourceComponent_Conditional_1_Conditional_25_Conditional_16_Conditional_10_For_9_Conditional_3_Conditional_1_Template(rf, ctx) { if (rf & 1) {
|
|
1397
1397
|
const _r65 = i0.ɵɵgetCurrentView();
|
|
1398
|
-
i0.ɵɵelementStart(0, "button",
|
|
1398
|
+
i0.ɵɵelementStart(0, "button", 136);
|
|
1399
1399
|
i0.ɵɵlistener("click", function AnalyticsResourceComponent_Conditional_1_Conditional_25_Conditional_16_Conditional_10_For_9_Conditional_3_Conditional_1_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r65); const row_r64 = i0.ɵɵnextContext(2).$implicit; const ctx_r2 = i0.ɵɵnextContext(5); return i0.ɵɵresetView(ctx_r2.OpenDrillDownRecord(row_r64)); });
|
|
1400
1400
|
i0.ɵɵelement(1, "i", 99);
|
|
1401
1401
|
i0.ɵɵelementEnd();
|
|
1402
1402
|
} }
|
|
1403
1403
|
function AnalyticsResourceComponent_Conditional_1_Conditional_25_Conditional_16_Conditional_10_For_9_Conditional_3_Template(rf, ctx) { if (rf & 1) {
|
|
1404
1404
|
i0.ɵɵelementStart(0, "td", 96);
|
|
1405
|
-
i0.ɵɵconditionalCreate(1, AnalyticsResourceComponent_Conditional_1_Conditional_25_Conditional_16_Conditional_10_For_9_Conditional_3_Conditional_1_Template, 2, 0, "button",
|
|
1405
|
+
i0.ɵɵconditionalCreate(1, AnalyticsResourceComponent_Conditional_1_Conditional_25_Conditional_16_Conditional_10_For_9_Conditional_3_Conditional_1_Template, 2, 0, "button", 135);
|
|
1406
1406
|
i0.ɵɵelementEnd();
|
|
1407
1407
|
} if (rf & 2) {
|
|
1408
1408
|
const row_r64 = i0.ɵɵnextContext().$implicit;
|
|
@@ -1471,13 +1471,13 @@ function AnalyticsResourceComponent_Conditional_1_Conditional_25_For_24_Conditio
|
|
|
1471
1471
|
i0.ɵɵtextInterpolate1(" ", stage_r66.Time, "s ");
|
|
1472
1472
|
} }
|
|
1473
1473
|
function AnalyticsResourceComponent_Conditional_1_Conditional_25_For_24_Template(rf, ctx) { if (rf & 1) {
|
|
1474
|
-
i0.ɵɵelementStart(0, "div",
|
|
1474
|
+
i0.ɵɵelementStart(0, "div", 187)(1, "div", 197);
|
|
1475
1475
|
i0.ɵɵtext(2);
|
|
1476
1476
|
i0.ɵɵelementEnd();
|
|
1477
|
-
i0.ɵɵelementStart(3, "div",
|
|
1477
|
+
i0.ɵɵelementStart(3, "div", 198)(4, "div", 199);
|
|
1478
1478
|
i0.ɵɵconditionalCreate(5, AnalyticsResourceComponent_Conditional_1_Conditional_25_For_24_Conditional_5_Template, 1, 1);
|
|
1479
1479
|
i0.ɵɵelementEnd()();
|
|
1480
|
-
i0.ɵɵelementStart(6, "div",
|
|
1480
|
+
i0.ɵɵelementStart(6, "div", 200);
|
|
1481
1481
|
i0.ɵɵtext(7);
|
|
1482
1482
|
i0.ɵɵelementEnd()();
|
|
1483
1483
|
} if (rf & 2) {
|
|
@@ -1492,8 +1492,8 @@ function AnalyticsResourceComponent_Conditional_1_Conditional_25_For_24_Template
|
|
|
1492
1492
|
i0.ɵɵtextInterpolate1("", stage_r66.Time, "s");
|
|
1493
1493
|
} }
|
|
1494
1494
|
function AnalyticsResourceComponent_Conditional_1_Conditional_25_Conditional_30_Template(rf, ctx) { if (rf & 1) {
|
|
1495
|
-
i0.ɵɵelementStart(0, "span",
|
|
1496
|
-
i0.ɵɵelement(1, "i",
|
|
1495
|
+
i0.ɵɵelementStart(0, "span", 189);
|
|
1496
|
+
i0.ɵɵelement(1, "i", 201);
|
|
1497
1497
|
i0.ɵɵtext(2);
|
|
1498
1498
|
i0.ɵɵelementEnd();
|
|
1499
1499
|
} if (rf & 2) {
|
|
@@ -1502,7 +1502,7 @@ function AnalyticsResourceComponent_Conditional_1_Conditional_25_Conditional_30_
|
|
|
1502
1502
|
i0.ɵɵtextInterpolate2(" ", ctx_r2.BottleneckStage, " stage is the bottleneck (", ctx_r2.BottleneckPercent, "% of total time) ");
|
|
1503
1503
|
} }
|
|
1504
1504
|
function AnalyticsResourceComponent_Conditional_1_Conditional_25_Conditional_52_For_22_Template(rf, ctx) { if (rf & 1) {
|
|
1505
|
-
i0.ɵɵelementStart(0, "tr")(1, "td",
|
|
1505
|
+
i0.ɵɵelementStart(0, "tr")(1, "td", 203);
|
|
1506
1506
|
i0.ɵɵtext(2);
|
|
1507
1507
|
i0.ɵɵelementEnd();
|
|
1508
1508
|
i0.ɵɵelementStart(3, "td");
|
|
@@ -1511,13 +1511,13 @@ function AnalyticsResourceComponent_Conditional_1_Conditional_25_Conditional_52_
|
|
|
1511
1511
|
i0.ɵɵelementStart(5, "td", 134);
|
|
1512
1512
|
i0.ɵɵtext(6);
|
|
1513
1513
|
i0.ɵɵelementEnd();
|
|
1514
|
-
i0.ɵɵelementStart(7, "td")(8, "div",
|
|
1515
|
-
i0.ɵɵelement(9, "div",
|
|
1514
|
+
i0.ɵɵelementStart(7, "td")(8, "div", 204);
|
|
1515
|
+
i0.ɵɵelement(9, "div", 205);
|
|
1516
1516
|
i0.ɵɵelementEnd();
|
|
1517
|
-
i0.ɵɵelementStart(10, "span",
|
|
1517
|
+
i0.ɵɵelementStart(10, "span", 206);
|
|
1518
1518
|
i0.ɵɵtext(11);
|
|
1519
1519
|
i0.ɵɵelementEnd()();
|
|
1520
|
-
i0.ɵɵelementStart(12, "td")(13, "span",
|
|
1520
|
+
i0.ɵɵelementStart(12, "td")(13, "span", 159);
|
|
1521
1521
|
i0.ɵɵtext(14);
|
|
1522
1522
|
i0.ɵɵelementEnd()();
|
|
1523
1523
|
i0.ɵɵelementStart(15, "td", 128);
|
|
@@ -1545,7 +1545,7 @@ function AnalyticsResourceComponent_Conditional_1_Conditional_25_Conditional_52_
|
|
|
1545
1545
|
} }
|
|
1546
1546
|
function AnalyticsResourceComponent_Conditional_1_Conditional_25_Conditional_52_Template(rf, ctx) { if (rf & 1) {
|
|
1547
1547
|
i0.ɵɵelementStart(0, "div", 122)(1, "h3");
|
|
1548
|
-
i0.ɵɵelement(2, "i",
|
|
1548
|
+
i0.ɵɵelement(2, "i", 202);
|
|
1549
1549
|
i0.ɵɵtext(3, " Active Runs");
|
|
1550
1550
|
i0.ɵɵelementEnd();
|
|
1551
1551
|
i0.ɵɵelementStart(4, "div", 120)(5, "table", 95)(6, "thead")(7, "tr")(8, "th");
|
|
@@ -1575,13 +1575,13 @@ function AnalyticsResourceComponent_Conditional_1_Conditional_25_Conditional_52_
|
|
|
1575
1575
|
i0.ɵɵrepeater(ctx_r2.ActiveRuns);
|
|
1576
1576
|
} }
|
|
1577
1577
|
function AnalyticsResourceComponent_Conditional_1_Conditional_25_Conditional_53_For_6_Template(rf, ctx) { if (rf & 1) {
|
|
1578
|
-
i0.ɵɵelementStart(0, "div",
|
|
1578
|
+
i0.ɵɵelementStart(0, "div", 209)(1, "div", 210);
|
|
1579
1579
|
i0.ɵɵtext(2);
|
|
1580
1580
|
i0.ɵɵelementEnd();
|
|
1581
|
-
i0.ɵɵelementStart(3, "div",
|
|
1581
|
+
i0.ɵɵelementStart(3, "div", 211);
|
|
1582
1582
|
i0.ɵɵtext(4);
|
|
1583
1583
|
i0.ɵɵelementEnd();
|
|
1584
|
-
i0.ɵɵelementStart(5, "div",
|
|
1584
|
+
i0.ɵɵelementStart(5, "div", 212);
|
|
1585
1585
|
i0.ɵɵtext(6);
|
|
1586
1586
|
i0.ɵɵelementEnd()();
|
|
1587
1587
|
} if (rf & 2) {
|
|
@@ -1595,11 +1595,11 @@ function AnalyticsResourceComponent_Conditional_1_Conditional_25_Conditional_53_
|
|
|
1595
1595
|
} }
|
|
1596
1596
|
function AnalyticsResourceComponent_Conditional_1_Conditional_25_Conditional_53_Template(rf, ctx) { if (rf & 1) {
|
|
1597
1597
|
i0.ɵɵelementStart(0, "div", 122)(1, "h3");
|
|
1598
|
-
i0.ɵɵelement(2, "i",
|
|
1598
|
+
i0.ɵɵelement(2, "i", 207);
|
|
1599
1599
|
i0.ɵɵtext(3, " Recent Errors");
|
|
1600
1600
|
i0.ɵɵelementEnd();
|
|
1601
|
-
i0.ɵɵelementStart(4, "div",
|
|
1602
|
-
i0.ɵɵrepeaterCreate(5, AnalyticsResourceComponent_Conditional_1_Conditional_25_Conditional_53_For_6_Template, 7, 3, "div",
|
|
1601
|
+
i0.ɵɵelementStart(4, "div", 208);
|
|
1602
|
+
i0.ɵɵrepeaterCreate(5, AnalyticsResourceComponent_Conditional_1_Conditional_25_Conditional_53_For_6_Template, 7, 3, "div", 209, i0.ɵɵcomponentInstance().TrackByIndex, true);
|
|
1603
1603
|
i0.ɵɵelementEnd()();
|
|
1604
1604
|
} if (rf & 2) {
|
|
1605
1605
|
const ctx_r2 = i0.ɵɵnextContext(3);
|
|
@@ -1608,55 +1608,55 @@ function AnalyticsResourceComponent_Conditional_1_Conditional_25_Conditional_53_
|
|
|
1608
1608
|
} }
|
|
1609
1609
|
function AnalyticsResourceComponent_Conditional_1_Conditional_25_Template(rf, ctx) { if (rf & 1) {
|
|
1610
1610
|
i0.ɵɵelementStart(0, "section", 17)(1, "div", 19);
|
|
1611
|
-
i0.ɵɵelement(2, "i",
|
|
1611
|
+
i0.ɵɵelement(2, "i", 181);
|
|
1612
1612
|
i0.ɵɵelementStart(3, "h1");
|
|
1613
1613
|
i0.ɵɵtext(4, "Pipeline");
|
|
1614
1614
|
i0.ɵɵelementEnd()();
|
|
1615
1615
|
i0.ɵɵelementStart(5, "div", 117)(6, "h3");
|
|
1616
|
-
i0.ɵɵelement(7, "i",
|
|
1616
|
+
i0.ɵɵelement(7, "i", 137);
|
|
1617
1617
|
i0.ɵɵtext(8, " Pipeline Throughput (Last 30 Days)");
|
|
1618
1618
|
i0.ɵɵelementEnd();
|
|
1619
|
-
i0.ɵɵelementStart(9, "div",
|
|
1620
|
-
i0.ɵɵrepeaterCreate(11, AnalyticsResourceComponent_Conditional_1_Conditional_25_For_12_Template, 1, 6, "div",
|
|
1619
|
+
i0.ɵɵelementStart(9, "div", 163)(10, "div", 182);
|
|
1620
|
+
i0.ɵɵrepeaterCreate(11, AnalyticsResourceComponent_Conditional_1_Conditional_25_For_12_Template, 1, 6, "div", 183, i0.ɵɵcomponentInstance().TrackByIndex, true);
|
|
1621
1621
|
i0.ɵɵelementEnd();
|
|
1622
|
-
i0.ɵɵelementStart(13, "div",
|
|
1622
|
+
i0.ɵɵelementStart(13, "div", 184);
|
|
1623
1623
|
i0.ɵɵrepeaterCreate(14, AnalyticsResourceComponent_Conditional_1_Conditional_25_For_15_Template, 2, 1, "span", null, i0.ɵɵrepeaterTrackByIdentity);
|
|
1624
1624
|
i0.ɵɵelementEnd()()();
|
|
1625
1625
|
i0.ɵɵconditionalCreate(16, AnalyticsResourceComponent_Conditional_1_Conditional_25_Conditional_16_Template, 12, 2, "div", 28);
|
|
1626
1626
|
i0.ɵɵelementStart(17, "div", 122)(18, "h3");
|
|
1627
|
-
i0.ɵɵelement(19, "i",
|
|
1627
|
+
i0.ɵɵelement(19, "i", 185);
|
|
1628
1628
|
i0.ɵɵtext(20, " Processing Time Breakdown (Avg per Item)");
|
|
1629
1629
|
i0.ɵɵelementEnd();
|
|
1630
|
-
i0.ɵɵelementStart(21, "div",
|
|
1631
|
-
i0.ɵɵrepeaterCreate(23, AnalyticsResourceComponent_Conditional_1_Conditional_25_For_24_Template, 8, 7, "div",
|
|
1630
|
+
i0.ɵɵelementStart(21, "div", 163)(22, "div", 186);
|
|
1631
|
+
i0.ɵɵrepeaterCreate(23, AnalyticsResourceComponent_Conditional_1_Conditional_25_For_24_Template, 8, 7, "div", 187, _forTrack1);
|
|
1632
1632
|
i0.ɵɵelementEnd();
|
|
1633
|
-
i0.ɵɵelementStart(25, "div",
|
|
1633
|
+
i0.ɵɵelementStart(25, "div", 188)(26, "span")(27, "strong");
|
|
1634
1634
|
i0.ɵɵtext(28, "Total avg:");
|
|
1635
1635
|
i0.ɵɵelementEnd();
|
|
1636
1636
|
i0.ɵɵtext(29);
|
|
1637
1637
|
i0.ɵɵelementEnd();
|
|
1638
|
-
i0.ɵɵconditionalCreate(30, AnalyticsResourceComponent_Conditional_1_Conditional_25_Conditional_30_Template, 3, 2, "span",
|
|
1638
|
+
i0.ɵɵconditionalCreate(30, AnalyticsResourceComponent_Conditional_1_Conditional_25_Conditional_30_Template, 3, 2, "span", 189);
|
|
1639
1639
|
i0.ɵɵelementEnd()()();
|
|
1640
1640
|
i0.ɵɵelementStart(31, "div", 122)(32, "h3");
|
|
1641
1641
|
i0.ɵɵelement(33, "i", 5);
|
|
1642
1642
|
i0.ɵɵtext(34, " Success Rate Overview");
|
|
1643
1643
|
i0.ɵɵelementEnd();
|
|
1644
|
-
i0.ɵɵelementStart(35, "div",
|
|
1644
|
+
i0.ɵɵelementStart(35, "div", 163)(36, "div", 190)(37, "div", 191)(38, "div", 192);
|
|
1645
1645
|
i0.ɵɵtext(39);
|
|
1646
1646
|
i0.ɵɵelementEnd();
|
|
1647
|
-
i0.ɵɵelementStart(40, "div",
|
|
1647
|
+
i0.ɵɵelementStart(40, "div", 193);
|
|
1648
1648
|
i0.ɵɵtext(41, "Success Rate");
|
|
1649
1649
|
i0.ɵɵelementEnd()();
|
|
1650
|
-
i0.ɵɵelementStart(42, "div",
|
|
1650
|
+
i0.ɵɵelementStart(42, "div", 191)(43, "div", 194);
|
|
1651
1651
|
i0.ɵɵtext(44);
|
|
1652
1652
|
i0.ɵɵelementEnd();
|
|
1653
|
-
i0.ɵɵelementStart(45, "div",
|
|
1653
|
+
i0.ɵɵelementStart(45, "div", 193);
|
|
1654
1654
|
i0.ɵɵtext(46, "Failure Rate");
|
|
1655
1655
|
i0.ɵɵelementEnd()();
|
|
1656
|
-
i0.ɵɵelementStart(47, "div",
|
|
1656
|
+
i0.ɵɵelementStart(47, "div", 191)(48, "div", 195);
|
|
1657
1657
|
i0.ɵɵtext(49);
|
|
1658
1658
|
i0.ɵɵelementEnd();
|
|
1659
|
-
i0.ɵɵelementStart(50, "div",
|
|
1659
|
+
i0.ɵɵelementStart(50, "div", 193);
|
|
1660
1660
|
i0.ɵɵtext(51, "Total Runs");
|
|
1661
1661
|
i0.ɵɵelementEnd()()()()();
|
|
1662
1662
|
i0.ɵɵconditionalCreate(52, AnalyticsResourceComponent_Conditional_1_Conditional_25_Conditional_52_Template, 23, 0, "div", 122);
|
|
@@ -1695,12 +1695,12 @@ function AnalyticsResourceComponent_Conditional_1_Conditional_26_For_12_Conditio
|
|
|
1695
1695
|
} }
|
|
1696
1696
|
function AnalyticsResourceComponent_Conditional_1_Conditional_26_For_12_Template(rf, ctx) { if (rf & 1) {
|
|
1697
1697
|
const _r69 = i0.ɵɵgetCurrentView();
|
|
1698
|
-
i0.ɵɵelementStart(0, "div",
|
|
1698
|
+
i0.ɵɵelementStart(0, "div", 222);
|
|
1699
1699
|
i0.ɵɵlistener("click", function AnalyticsResourceComponent_Conditional_1_Conditional_26_For_12_Template_div_click_0_listener() { const bin_r70 = i0.ɵɵrestoreView(_r69).$implicit; const ctx_r2 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r2.OpenDrillDown("quality-bin:" + bin_r70.Label)); });
|
|
1700
|
-
i0.ɵɵelementStart(1, "div",
|
|
1700
|
+
i0.ɵɵelementStart(1, "div", 223);
|
|
1701
1701
|
i0.ɵɵconditionalCreate(2, AnalyticsResourceComponent_Conditional_1_Conditional_26_For_12_Conditional_2_Template, 1, 1);
|
|
1702
1702
|
i0.ɵɵelementEnd();
|
|
1703
|
-
i0.ɵɵelementStart(3, "div",
|
|
1703
|
+
i0.ɵɵelementStart(3, "div", 224);
|
|
1704
1704
|
i0.ɵɵtext(4);
|
|
1705
1705
|
i0.ɵɵelementEnd()();
|
|
1706
1706
|
} if (rf & 2) {
|
|
@@ -1760,14 +1760,14 @@ function AnalyticsResourceComponent_Conditional_1_Conditional_26_Conditional_16_
|
|
|
1760
1760
|
} }
|
|
1761
1761
|
function AnalyticsResourceComponent_Conditional_1_Conditional_26_Conditional_16_Conditional_10_For_9_Conditional_3_Conditional_1_Template(rf, ctx) { if (rf & 1) {
|
|
1762
1762
|
const _r77 = i0.ɵɵgetCurrentView();
|
|
1763
|
-
i0.ɵɵelementStart(0, "button",
|
|
1763
|
+
i0.ɵɵelementStart(0, "button", 136);
|
|
1764
1764
|
i0.ɵɵlistener("click", function AnalyticsResourceComponent_Conditional_1_Conditional_26_Conditional_16_Conditional_10_For_9_Conditional_3_Conditional_1_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r77); const row_r76 = i0.ɵɵnextContext(2).$implicit; const ctx_r2 = i0.ɵɵnextContext(5); return i0.ɵɵresetView(ctx_r2.OpenDrillDownRecord(row_r76)); });
|
|
1765
1765
|
i0.ɵɵelement(1, "i", 99);
|
|
1766
1766
|
i0.ɵɵelementEnd();
|
|
1767
1767
|
} }
|
|
1768
1768
|
function AnalyticsResourceComponent_Conditional_1_Conditional_26_Conditional_16_Conditional_10_For_9_Conditional_3_Template(rf, ctx) { if (rf & 1) {
|
|
1769
1769
|
i0.ɵɵelementStart(0, "td", 96);
|
|
1770
|
-
i0.ɵɵconditionalCreate(1, AnalyticsResourceComponent_Conditional_1_Conditional_26_Conditional_16_Conditional_10_For_9_Conditional_3_Conditional_1_Template, 2, 0, "button",
|
|
1770
|
+
i0.ɵɵconditionalCreate(1, AnalyticsResourceComponent_Conditional_1_Conditional_26_Conditional_16_Conditional_10_For_9_Conditional_3_Conditional_1_Template, 2, 0, "button", 135);
|
|
1771
1771
|
i0.ɵɵelementEnd();
|
|
1772
1772
|
} if (rf & 2) {
|
|
1773
1773
|
const row_r76 = i0.ɵɵnextContext().$implicit;
|
|
@@ -1832,10 +1832,10 @@ function AnalyticsResourceComponent_Conditional_1_Conditional_26_Conditional_16_
|
|
|
1832
1832
|
i0.ɵɵconditional(ctx_r2.IsDrillDownLoading ? 9 : ctx_r2.DrillDownData.length > 0 ? 10 : 11);
|
|
1833
1833
|
} }
|
|
1834
1834
|
function AnalyticsResourceComponent_Conditional_1_Conditional_26_Conditional_17_For_7_Template(rf, ctx) { if (rf & 1) {
|
|
1835
|
-
i0.ɵɵelementStart(0, "div",
|
|
1836
|
-
i0.ɵɵelement(2, "div",
|
|
1835
|
+
i0.ɵɵelementStart(0, "div", 227)(1, "div", 229);
|
|
1836
|
+
i0.ɵɵelement(2, "div", 230)(3, "div", 231)(4, "div", 232);
|
|
1837
1837
|
i0.ɵɵelementEnd();
|
|
1838
|
-
i0.ɵɵelementStart(5, "div",
|
|
1838
|
+
i0.ɵɵelementStart(5, "div", 233);
|
|
1839
1839
|
i0.ɵɵtext(6);
|
|
1840
1840
|
i0.ɵɵelementEnd()();
|
|
1841
1841
|
} if (rf & 2) {
|
|
@@ -1854,7 +1854,7 @@ function AnalyticsResourceComponent_Conditional_1_Conditional_26_Conditional_17_
|
|
|
1854
1854
|
} }
|
|
1855
1855
|
function AnalyticsResourceComponent_Conditional_1_Conditional_26_Conditional_17_For_10_Template(rf, ctx) { if (rf & 1) {
|
|
1856
1856
|
i0.ɵɵelementStart(0, "div", 61);
|
|
1857
|
-
i0.ɵɵelement(1, "div",
|
|
1857
|
+
i0.ɵɵelement(1, "div", 145);
|
|
1858
1858
|
i0.ɵɵtext(2);
|
|
1859
1859
|
i0.ɵɵelementEnd();
|
|
1860
1860
|
} if (rf & 2) {
|
|
@@ -1866,13 +1866,13 @@ function AnalyticsResourceComponent_Conditional_1_Conditional_26_Conditional_17_
|
|
|
1866
1866
|
} }
|
|
1867
1867
|
function AnalyticsResourceComponent_Conditional_1_Conditional_26_Conditional_17_Template(rf, ctx) { if (rf & 1) {
|
|
1868
1868
|
i0.ɵɵelementStart(0, "div", 122)(1, "h3");
|
|
1869
|
-
i0.ɵɵelement(2, "i",
|
|
1869
|
+
i0.ɵɵelement(2, "i", 225);
|
|
1870
1870
|
i0.ɵɵtext(3, " Weight Distribution by Entity");
|
|
1871
1871
|
i0.ɵɵelementEnd();
|
|
1872
|
-
i0.ɵɵelementStart(4, "div",
|
|
1873
|
-
i0.ɵɵrepeaterCreate(6, AnalyticsResourceComponent_Conditional_1_Conditional_26_Conditional_17_For_7_Template, 7, 10, "div",
|
|
1872
|
+
i0.ɵɵelementStart(4, "div", 163)(5, "div", 226);
|
|
1873
|
+
i0.ɵɵrepeaterCreate(6, AnalyticsResourceComponent_Conditional_1_Conditional_26_Conditional_17_For_7_Template, 7, 10, "div", 227, _forTrack1);
|
|
1874
1874
|
i0.ɵɵelementEnd();
|
|
1875
|
-
i0.ɵɵelementStart(8, "div",
|
|
1875
|
+
i0.ɵɵelementStart(8, "div", 228);
|
|
1876
1876
|
i0.ɵɵrepeaterCreate(9, AnalyticsResourceComponent_Conditional_1_Conditional_26_Conditional_17_For_10_Template, 3, 3, "div", 61, _forTrack2);
|
|
1877
1877
|
i0.ɵɵelementEnd()()();
|
|
1878
1878
|
} if (rf & 2) {
|
|
@@ -1884,7 +1884,7 @@ function AnalyticsResourceComponent_Conditional_1_Conditional_26_Conditional_17_
|
|
|
1884
1884
|
} }
|
|
1885
1885
|
function AnalyticsResourceComponent_Conditional_1_Conditional_26_Conditional_18_For_21_Template(rf, ctx) { if (rf & 1) {
|
|
1886
1886
|
i0.ɵɵnamespaceSVG();
|
|
1887
|
-
i0.ɵɵelement(0, "circle",
|
|
1887
|
+
i0.ɵɵelement(0, "circle", 241);
|
|
1888
1888
|
} if (rf & 2) {
|
|
1889
1889
|
const dot_r80 = ctx.$implicit;
|
|
1890
1890
|
i0.ɵɵattribute("cx", dot_r80.Cx)("cy", dot_r80.Cy);
|
|
@@ -1903,7 +1903,7 @@ function AnalyticsResourceComponent_Conditional_1_Conditional_26_Conditional_18_
|
|
|
1903
1903
|
i0.ɵɵelement(2, "i", 5);
|
|
1904
1904
|
i0.ɵɵtext(3, " Tag Accuracy Over Time (Weekly Avg Confidence)");
|
|
1905
1905
|
i0.ɵɵelementEnd();
|
|
1906
|
-
i0.ɵɵelementStart(4, "div",
|
|
1906
|
+
i0.ɵɵelementStart(4, "div", 163)(5, "div", 234)(6, "div", 235)(7, "div");
|
|
1907
1907
|
i0.ɵɵtext(8, "1.0");
|
|
1908
1908
|
i0.ɵɵelementEnd();
|
|
1909
1909
|
i0.ɵɵelementStart(9, "div");
|
|
@@ -1915,19 +1915,19 @@ function AnalyticsResourceComponent_Conditional_1_Conditional_26_Conditional_18_
|
|
|
1915
1915
|
i0.ɵɵelementStart(13, "div");
|
|
1916
1916
|
i0.ɵɵtext(14, "0.25");
|
|
1917
1917
|
i0.ɵɵelementEnd()();
|
|
1918
|
-
i0.ɵɵelementStart(15, "div",
|
|
1919
|
-
i0.ɵɵelement(16, "div",
|
|
1918
|
+
i0.ɵɵelementStart(15, "div", 236);
|
|
1919
|
+
i0.ɵɵelement(16, "div", 237)(17, "div", 238);
|
|
1920
1920
|
i0.ɵɵnamespaceSVG();
|
|
1921
|
-
i0.ɵɵelementStart(18, "svg",
|
|
1922
|
-
i0.ɵɵelement(19, "polyline",
|
|
1923
|
-
i0.ɵɵrepeaterCreate(20, AnalyticsResourceComponent_Conditional_1_Conditional_26_Conditional_18_For_21_Template, 1, 2, ":svg:circle",
|
|
1921
|
+
i0.ɵɵelementStart(18, "svg", 239);
|
|
1922
|
+
i0.ɵɵelement(19, "polyline", 240);
|
|
1923
|
+
i0.ɵɵrepeaterCreate(20, AnalyticsResourceComponent_Conditional_1_Conditional_26_Conditional_18_For_21_Template, 1, 2, ":svg:circle", 241, i0.ɵɵcomponentInstance().TrackByIndex, true);
|
|
1924
1924
|
i0.ɵɵelementEnd()()();
|
|
1925
1925
|
i0.ɵɵnamespaceHTML();
|
|
1926
|
-
i0.ɵɵelementStart(22, "div",
|
|
1926
|
+
i0.ɵɵelementStart(22, "div", 242);
|
|
1927
1927
|
i0.ɵɵrepeaterCreate(23, AnalyticsResourceComponent_Conditional_1_Conditional_26_Conditional_18_For_24_Template, 2, 1, "span", null, i0.ɵɵrepeaterTrackByIdentity);
|
|
1928
1928
|
i0.ɵɵelementEnd();
|
|
1929
|
-
i0.ɵɵelementStart(25, "div",
|
|
1930
|
-
i0.ɵɵelement(26, "i",
|
|
1929
|
+
i0.ɵɵelementStart(25, "div", 243);
|
|
1930
|
+
i0.ɵɵelement(26, "i", 244);
|
|
1931
1931
|
i0.ɵɵtext(27);
|
|
1932
1932
|
i0.ɵɵelementEnd()()();
|
|
1933
1933
|
} if (rf & 2) {
|
|
@@ -1954,7 +1954,7 @@ function AnalyticsResourceComponent_Conditional_1_Conditional_26_Conditional_19_
|
|
|
1954
1954
|
i0.ɵɵelementStart(8, "td");
|
|
1955
1955
|
i0.ɵɵtext(9);
|
|
1956
1956
|
i0.ɵɵelementEnd();
|
|
1957
|
-
i0.ɵɵelementStart(10, "td")(11, "span",
|
|
1957
|
+
i0.ɵɵelementStart(10, "td")(11, "span", 159);
|
|
1958
1958
|
i0.ɵɵtext(12);
|
|
1959
1959
|
i0.ɵɵelementEnd()()();
|
|
1960
1960
|
} if (rf & 2) {
|
|
@@ -1974,7 +1974,7 @@ function AnalyticsResourceComponent_Conditional_1_Conditional_26_Conditional_19_
|
|
|
1974
1974
|
} }
|
|
1975
1975
|
function AnalyticsResourceComponent_Conditional_1_Conditional_26_Conditional_19_Template(rf, ctx) { if (rf & 1) {
|
|
1976
1976
|
i0.ɵɵelementStart(0, "div", 122)(1, "h3");
|
|
1977
|
-
i0.ɵɵelement(2, "i",
|
|
1977
|
+
i0.ɵɵelement(2, "i", 245);
|
|
1978
1978
|
i0.ɵɵtext(3, " Low-Confidence Tags (Avg Weight < 0.4)");
|
|
1979
1979
|
i0.ɵɵelementEnd();
|
|
1980
1980
|
i0.ɵɵelementStart(4, "div", 120)(5, "table", 95)(6, "thead")(7, "tr")(8, "th");
|
|
@@ -2001,25 +2001,25 @@ function AnalyticsResourceComponent_Conditional_1_Conditional_26_Conditional_19_
|
|
|
2001
2001
|
i0.ɵɵrepeater(ctx_r2.LowConfidenceTags);
|
|
2002
2002
|
} }
|
|
2003
2003
|
function AnalyticsResourceComponent_Conditional_1_Conditional_26_For_26_Template(rf, ctx) { if (rf & 1) {
|
|
2004
|
-
i0.ɵɵelementStart(0, "div",
|
|
2005
|
-
i0.ɵɵelement(2, "i",
|
|
2004
|
+
i0.ɵɵelementStart(0, "div", 220)(1, "div", 246);
|
|
2005
|
+
i0.ɵɵelement(2, "i", 158);
|
|
2006
2006
|
i0.ɵɵtext(3);
|
|
2007
2007
|
i0.ɵɵelementEnd();
|
|
2008
2008
|
i0.ɵɵnamespaceSVG();
|
|
2009
|
-
i0.ɵɵelementStart(4, "svg",
|
|
2010
|
-
i0.ɵɵelement(5, "circle",
|
|
2009
|
+
i0.ɵɵelementStart(4, "svg", 247);
|
|
2010
|
+
i0.ɵɵelement(5, "circle", 248)(6, "circle", 249);
|
|
2011
2011
|
i0.ɵɵelementEnd();
|
|
2012
2012
|
i0.ɵɵnamespaceHTML();
|
|
2013
|
-
i0.ɵɵelementStart(7, "div",
|
|
2013
|
+
i0.ɵɵelementStart(7, "div", 250);
|
|
2014
2014
|
i0.ɵɵtext(8);
|
|
2015
2015
|
i0.ɵɵelementEnd();
|
|
2016
|
-
i0.ɵɵelementStart(9, "div",
|
|
2016
|
+
i0.ɵɵelementStart(9, "div", 251);
|
|
2017
2017
|
i0.ɵɵtext(10);
|
|
2018
2018
|
i0.ɵɵelementEnd();
|
|
2019
|
-
i0.ɵɵelementStart(11, "div",
|
|
2019
|
+
i0.ɵɵelementStart(11, "div", 251);
|
|
2020
2020
|
i0.ɵɵtext(12);
|
|
2021
2021
|
i0.ɵɵelementEnd();
|
|
2022
|
-
i0.ɵɵelementStart(13, "div",
|
|
2022
|
+
i0.ɵɵelementStart(13, "div", 252);
|
|
2023
2023
|
i0.ɵɵtext(14);
|
|
2024
2024
|
i0.ɵɵelementEnd()();
|
|
2025
2025
|
} if (rf & 2) {
|
|
@@ -2046,8 +2046,8 @@ function AnalyticsResourceComponent_Conditional_1_Conditional_26_For_26_Template
|
|
|
2046
2046
|
i0.ɵɵtextInterpolate(model_r83.Role);
|
|
2047
2047
|
} }
|
|
2048
2048
|
function AnalyticsResourceComponent_Conditional_1_Conditional_26_Conditional_27_Template(rf, ctx) { if (rf & 1) {
|
|
2049
|
-
i0.ɵɵelementStart(0, "div",
|
|
2050
|
-
i0.ɵɵelement(1, "i",
|
|
2049
|
+
i0.ɵɵelementStart(0, "div", 221);
|
|
2050
|
+
i0.ɵɵelement(1, "i", 253);
|
|
2051
2051
|
i0.ɵɵelementStart(2, "div")(3, "strong");
|
|
2052
2052
|
i0.ɵɵtext(4, "Recommendation:");
|
|
2053
2053
|
i0.ɵɵelementEnd();
|
|
@@ -2060,18 +2060,18 @@ function AnalyticsResourceComponent_Conditional_1_Conditional_26_Conditional_27_
|
|
|
2060
2060
|
} }
|
|
2061
2061
|
function AnalyticsResourceComponent_Conditional_1_Conditional_26_Template(rf, ctx) { if (rf & 1) {
|
|
2062
2062
|
i0.ɵɵelementStart(0, "section", 17)(1, "div", 19);
|
|
2063
|
-
i0.ɵɵelement(2, "i",
|
|
2063
|
+
i0.ɵɵelement(2, "i", 213);
|
|
2064
2064
|
i0.ɵɵelementStart(3, "h1");
|
|
2065
2065
|
i0.ɵɵtext(4, "Quality");
|
|
2066
2066
|
i0.ɵɵelementEnd()();
|
|
2067
2067
|
i0.ɵɵelementStart(5, "div", 117)(6, "h3");
|
|
2068
|
-
i0.ɵɵelement(7, "i",
|
|
2068
|
+
i0.ɵɵelement(7, "i", 214);
|
|
2069
2069
|
i0.ɵɵtext(8, " Confidence Distribution");
|
|
2070
2070
|
i0.ɵɵelementEnd();
|
|
2071
|
-
i0.ɵɵelementStart(9, "div",
|
|
2072
|
-
i0.ɵɵrepeaterCreate(11, AnalyticsResourceComponent_Conditional_1_Conditional_26_For_12_Template, 5, 6, "div",
|
|
2071
|
+
i0.ɵɵelementStart(9, "div", 163)(10, "div", 215);
|
|
2072
|
+
i0.ɵɵrepeaterCreate(11, AnalyticsResourceComponent_Conditional_1_Conditional_26_For_12_Template, 5, 6, "div", 216, _forTrack2);
|
|
2073
2073
|
i0.ɵɵelementEnd();
|
|
2074
|
-
i0.ɵɵelementStart(13, "div",
|
|
2074
|
+
i0.ɵɵelementStart(13, "div", 217);
|
|
2075
2075
|
i0.ɵɵrepeaterCreate(14, AnalyticsResourceComponent_Conditional_1_Conditional_26_For_15_Template, 4, 2, "span", null, _forTrack2);
|
|
2076
2076
|
i0.ɵɵelementEnd()()();
|
|
2077
2077
|
i0.ɵɵconditionalCreate(16, AnalyticsResourceComponent_Conditional_1_Conditional_26_Conditional_16_Template, 12, 3, "div", 28);
|
|
@@ -2079,13 +2079,13 @@ function AnalyticsResourceComponent_Conditional_1_Conditional_26_Template(rf, ct
|
|
|
2079
2079
|
i0.ɵɵconditionalCreate(18, AnalyticsResourceComponent_Conditional_1_Conditional_26_Conditional_18_Template, 28, 2, "div", 122);
|
|
2080
2080
|
i0.ɵɵconditionalCreate(19, AnalyticsResourceComponent_Conditional_1_Conditional_26_Conditional_19_Template, 21, 0, "div", 122);
|
|
2081
2081
|
i0.ɵɵelementStart(20, "div", 122)(21, "h3");
|
|
2082
|
-
i0.ɵɵelement(22, "i",
|
|
2082
|
+
i0.ɵɵelement(22, "i", 218);
|
|
2083
2083
|
i0.ɵɵtext(23, " Model Performance Comparison");
|
|
2084
2084
|
i0.ɵɵelementEnd();
|
|
2085
|
-
i0.ɵɵelementStart(24, "div",
|
|
2086
|
-
i0.ɵɵrepeaterCreate(25, AnalyticsResourceComponent_Conditional_1_Conditional_26_For_26_Template, 15, 15, "div",
|
|
2085
|
+
i0.ɵɵelementStart(24, "div", 219);
|
|
2086
|
+
i0.ɵɵrepeaterCreate(25, AnalyticsResourceComponent_Conditional_1_Conditional_26_For_26_Template, 15, 15, "div", 220, _forTrack1);
|
|
2087
2087
|
i0.ɵɵelementEnd();
|
|
2088
|
-
i0.ɵɵconditionalCreate(27, AnalyticsResourceComponent_Conditional_1_Conditional_26_Conditional_27_Template, 6, 1, "div",
|
|
2088
|
+
i0.ɵɵconditionalCreate(27, AnalyticsResourceComponent_Conditional_1_Conditional_26_Conditional_27_Template, 6, 1, "div", 221);
|
|
2089
2089
|
i0.ɵɵelementEnd()();
|
|
2090
2090
|
} if (rf & 2) {
|
|
2091
2091
|
const ctx_r2 = i0.ɵɵnextContext(2);
|
|
@@ -2120,16 +2120,16 @@ function AnalyticsResourceComponent_Conditional_1_Conditional_27_For_15_Template
|
|
|
2120
2120
|
i0.ɵɵtextInterpolate(range_r86.Label);
|
|
2121
2121
|
} }
|
|
2122
2122
|
function AnalyticsResourceComponent_Conditional_1_Conditional_27_Conditional_16_For_2_Template(rf, ctx) { if (rf & 1) {
|
|
2123
|
-
i0.ɵɵelementStart(0, "div",
|
|
2123
|
+
i0.ɵɵelementStart(0, "div", 259)(1, "div", 260);
|
|
2124
2124
|
i0.ɵɵelement(2, "i");
|
|
2125
2125
|
i0.ɵɵelementEnd();
|
|
2126
|
-
i0.ɵɵelementStart(3, "div")(4, "div",
|
|
2126
|
+
i0.ɵɵelementStart(3, "div")(4, "div", 261);
|
|
2127
2127
|
i0.ɵɵtext(5);
|
|
2128
2128
|
i0.ɵɵelementEnd();
|
|
2129
|
-
i0.ɵɵelementStart(6, "div",
|
|
2129
|
+
i0.ɵɵelementStart(6, "div", 262);
|
|
2130
2130
|
i0.ɵɵtext(7);
|
|
2131
2131
|
i0.ɵɵelementEnd();
|
|
2132
|
-
i0.ɵɵelementStart(8, "div",
|
|
2132
|
+
i0.ɵɵelementStart(8, "div", 263);
|
|
2133
2133
|
i0.ɵɵtext(9);
|
|
2134
2134
|
i0.ɵɵelementEnd()()();
|
|
2135
2135
|
} if (rf & 2) {
|
|
@@ -2144,8 +2144,8 @@ function AnalyticsResourceComponent_Conditional_1_Conditional_27_Conditional_16_
|
|
|
2144
2144
|
i0.ɵɵtextInterpolate(kpi_r87.SubLabel);
|
|
2145
2145
|
} }
|
|
2146
2146
|
function AnalyticsResourceComponent_Conditional_1_Conditional_27_Conditional_16_Template(rf, ctx) { if (rf & 1) {
|
|
2147
|
-
i0.ɵɵelementStart(0, "div",
|
|
2148
|
-
i0.ɵɵrepeaterCreate(1, AnalyticsResourceComponent_Conditional_1_Conditional_27_Conditional_16_For_2_Template, 10, 5, "div",
|
|
2147
|
+
i0.ɵɵelementStart(0, "div", 258);
|
|
2148
|
+
i0.ɵɵrepeaterCreate(1, AnalyticsResourceComponent_Conditional_1_Conditional_27_Conditional_16_For_2_Template, 10, 5, "div", 259, _forTrack2);
|
|
2149
2149
|
i0.ɵɵelementEnd();
|
|
2150
2150
|
} if (rf & 2) {
|
|
2151
2151
|
const ctx_r2 = i0.ɵɵnextContext(3);
|
|
@@ -2153,7 +2153,7 @@ function AnalyticsResourceComponent_Conditional_1_Conditional_27_Conditional_16_
|
|
|
2153
2153
|
i0.ɵɵrepeater(ctx_r2.CostKPIs);
|
|
2154
2154
|
} }
|
|
2155
2155
|
function AnalyticsResourceComponent_Conditional_1_Conditional_27_Conditional_21_For_16_Template(rf, ctx) { if (rf & 1) {
|
|
2156
|
-
i0.ɵɵelementStart(0, "tr")(1, "td",
|
|
2156
|
+
i0.ɵɵelementStart(0, "tr")(1, "td", 203);
|
|
2157
2157
|
i0.ɵɵtext(2);
|
|
2158
2158
|
i0.ɵɵelementEnd();
|
|
2159
2159
|
i0.ɵɵelementStart(3, "td");
|
|
@@ -2208,35 +2208,35 @@ function AnalyticsResourceComponent_Conditional_1_Conditional_27_Conditional_21_
|
|
|
2208
2208
|
} }
|
|
2209
2209
|
function AnalyticsResourceComponent_Conditional_1_Conditional_27_Conditional_22_Template(rf, ctx) { if (rf & 1) {
|
|
2210
2210
|
i0.ɵɵelementStart(0, "div", 121);
|
|
2211
|
-
i0.ɵɵelement(1, "i",
|
|
2211
|
+
i0.ɵɵelement(1, "i", 254);
|
|
2212
2212
|
i0.ɵɵelementStart(2, "p");
|
|
2213
2213
|
i0.ɵɵtext(3, "No cost data available yet. Run the pipeline to generate cost and token usage metrics.");
|
|
2214
2214
|
i0.ɵɵelementEnd();
|
|
2215
|
-
i0.ɵɵelementStart(4, "p",
|
|
2215
|
+
i0.ɵɵelementStart(4, "p", 264);
|
|
2216
2216
|
i0.ɵɵtext(5, "Cost data is aggregated from ContentProcessRunDetail records linked to AI Prompt Runs.");
|
|
2217
2217
|
i0.ɵɵelementEnd()();
|
|
2218
2218
|
} }
|
|
2219
2219
|
function AnalyticsResourceComponent_Conditional_1_Conditional_27_Template(rf, ctx) { if (rf & 1) {
|
|
2220
2220
|
const _r84 = i0.ɵɵgetCurrentView();
|
|
2221
2221
|
i0.ɵɵelementStart(0, "section", 17)(1, "div", 19);
|
|
2222
|
-
i0.ɵɵelement(2, "i",
|
|
2222
|
+
i0.ɵɵelement(2, "i", 254);
|
|
2223
2223
|
i0.ɵɵelementStart(3, "h1");
|
|
2224
2224
|
i0.ɵɵtext(4, "Cost & Usage");
|
|
2225
2225
|
i0.ɵɵelementEnd();
|
|
2226
|
-
i0.ɵɵelementStart(5, "div",
|
|
2226
|
+
i0.ɵɵelementStart(5, "div", 255)(6, "button", 93);
|
|
2227
2227
|
i0.ɵɵlistener("click", function AnalyticsResourceComponent_Conditional_1_Conditional_27_Template_button_click_6_listener() { i0.ɵɵrestoreView(_r84); const ctx_r2 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r2.ExportTabDataCSV("cost-usage")); });
|
|
2228
2228
|
i0.ɵɵelement(7, "i", 94);
|
|
2229
2229
|
i0.ɵɵtext(8, " CSV ");
|
|
2230
2230
|
i0.ɵɵelementEnd();
|
|
2231
|
-
i0.ɵɵelementStart(9, "button",
|
|
2231
|
+
i0.ɵɵelementStart(9, "button", 256);
|
|
2232
2232
|
i0.ɵɵlistener("click", function AnalyticsResourceComponent_Conditional_1_Conditional_27_Template_button_click_9_listener() { i0.ɵɵrestoreView(_r84); const ctx_r2 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r2.PrintCurrentTab()); });
|
|
2233
|
-
i0.ɵɵelement(10, "i",
|
|
2233
|
+
i0.ɵɵelement(10, "i", 257);
|
|
2234
2234
|
i0.ɵɵtext(11, " Print ");
|
|
2235
2235
|
i0.ɵɵelementEnd()()();
|
|
2236
2236
|
i0.ɵɵelementStart(12, "div", 21)(13, "div", 22);
|
|
2237
2237
|
i0.ɵɵrepeaterCreate(14, AnalyticsResourceComponent_Conditional_1_Conditional_27_For_15_Template, 2, 3, "button", 23, _forTrack2);
|
|
2238
2238
|
i0.ɵɵelementEnd()();
|
|
2239
|
-
i0.ɵɵconditionalCreate(16, AnalyticsResourceComponent_Conditional_1_Conditional_27_Conditional_16_Template, 3, 0, "div",
|
|
2239
|
+
i0.ɵɵconditionalCreate(16, AnalyticsResourceComponent_Conditional_1_Conditional_27_Conditional_16_Template, 3, 0, "div", 258);
|
|
2240
2240
|
i0.ɵɵelementStart(17, "div", 122)(18, "h3");
|
|
2241
2241
|
i0.ɵɵelement(19, "i", 85);
|
|
2242
2242
|
i0.ɵɵtext(20, " Cost Breakdown by Run");
|
|
@@ -4245,7 +4245,7 @@ let AnalyticsResourceComponent = class AnalyticsResourceComponent extends BaseRe
|
|
|
4245
4245
|
}
|
|
4246
4246
|
}
|
|
4247
4247
|
static ɵfac = /*@__PURE__*/ (() => { let ɵAnalyticsResourceComponent_BaseFactory; return function AnalyticsResourceComponent_Factory(__ngFactoryType__) { return (ɵAnalyticsResourceComponent_BaseFactory || (ɵAnalyticsResourceComponent_BaseFactory = i0.ɵɵgetInheritedFactory(AnalyticsResourceComponent)))(__ngFactoryType__ || AnalyticsResourceComponent); }; })();
|
|
4248
|
-
static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: AnalyticsResourceComponent, selectors: [["app-analytics-resource"]], standalone: false, features: [i0.ɵɵInheritDefinitionFeature], decls: 2, vars: 1, consts: [[1, "analytics-loading"], [1, "analytics-layout"], ["text", "Loading analytics data...", "size", "medium"], [1, "analytics-sidebar"], [1, "sidebar-header"], [1, "fa-solid", "fa-chart-line"], [1, "sidebar-nav"], [1, "nav-item", 3, "active"], [1, "sidebar-divider"], [1, "trending-section"], [1, "fa-solid", "fa-arrow-trend-up"], [1, "tag-cloud"], [3, "font-size", "font-weight"], [1, "no-trending"], [1, "pipeline-status"], [1, "status-dot"], [1, "main-content"], [1, "tab-section"], [1, "nav-item", 3, "click"], [1, "tab-section-header"], [1, "fa-solid", "fa-grip"], [1, "filter-bar"], [1, "date-chips"], [1, "date-chip", 3, "active"], [1, "filter-dropdown", 3, "ngModelChange", "ngModel"], [3, "value"], [1, "kpi-row"], [1, "kpi-card"], [1, "drill-down-panel"], [1, "cards-grid"], [1, "widget-card", 3, "click"], [1, "widget-title"], [1, "bar-chart"], [1, "bar-col"], [1, "fa-solid", "fa-bullseye"], [1, "rings-grid"], [1, "ring-item"], [1, "widget-empty"], [1, "fa-solid", "fa-gauge-high"], [1, "gauge-container"], ["width", "180", "height", "110", "viewBox", "0 0 180 110"], ["d", "M 20 95 A 70 70 0 0 1 160 95", "fill", "none", "stroke", "var(--mj-border-default)", "stroke-width", "14", "stroke-linecap", "round"], ["d", "M 20 95 A 70 70 0 0 1 48.6 35.2", "fill", "none", "stroke", "var(--mj-status-error)", "stroke-width", "14", "stroke-linecap", "round", "opacity", "0.3"], ["d", "M 48.6 35.2 A 70 70 0 0 1 118 28", "fill", "none", "stroke", "var(--mj-status-warning)", "stroke-width", "14", "opacity", "0.3"], ["d", "M 118 28 A 70 70 0 0 1 160 95", "fill", "none", "stroke", "var(--mj-status-success)", "stroke-width", "14", "stroke-linecap", "round", "opacity", "0.3"], ["x", "90", "y", "88", "text-anchor", "middle", "font-size", "26", "font-weight", "800", "fill", "var(--mj-text-primary)"], ["x", "90", "y", "105", "text-anchor", "middle", "font-size", "10", "fill", "var(--mj-text-muted)"], ["x", "28", "y", "108", "font-size", "8", "fill", "var(--mj-text-muted)"], ["x", "86", "y", "18", "font-size", "8", "fill", "var(--mj-text-muted)"], ["x", "155", "y", "108", "font-size", "8", "fill", "var(--mj-text-muted)"], [1, "mini-histogram"], [1, "mini-hist-bar", 3, "height", "background", "title"], [1, "mini-hist-label"], [1, "fa-solid", "fa-ranking-star"], [1, "h-bar-list"], [1, "h-bar-row"], [1, "widget-footnote"], [1, "fa-solid", "fa-bolt"], [1, "throughput-bars"], [1, "tp-bar", 3, "height", "background"], [1, "legend"], [1, "legend-item"], [1, "legend-dot", 2, "background", "var(--mj-status-success)"], [1, "legend-dot", 2, "background", "var(--mj-status-error)"], [1, "fa-solid", "fa-sitemap"], [1, "taxonomy-ring-container"], ["width", "140", "height", "140", "viewBox", "0 0 140 140"], ["cx", "70", "cy", "70", "r", "54", "fill", "none", "stroke", "var(--mj-border-default)", "stroke-width", "18"], ["cx", "70", "cy", "70", "r", "54", "fill", "none", "stroke-width", "18", "transform", "rotate(-90 70 70)"], ["x", "70", "y", "66", "text-anchor", "middle", "font-size", "22", "font-weight", "800", "fill", "var(--mj-text-primary)"], ["x", "70", "y", "82", "text-anchor", "middle", "font-size", "10", "fill", "var(--mj-text-muted)"], [1, "taxonomy-stats"], [1, "tax-stat", 3, "background", "color"], [1, "date-chip", 3, "click"], [1, "kpi-card", 3, "click"], [1, "kpi-label"], [1, "kpi-value"], [1, "kpi-delta"], [1, "fa-solid", "fa-arrow-up", 2, "font-size", "9px"], [1, "fa-solid", "fa-arrow-down", 2, "font-size", "9px"], [1, "kpi-sparkline"], ["width", "64", "height", "28", "viewBox", "0 0 64 28"], ["fill", "none", "stroke-width", "2", "stroke-linecap", "round", "stroke-linejoin", "round"], [1, "drill-down-header"], [1, "drill-down-title"], [1, "fa-solid", "fa-table"], [1, "drill-down-header-actions"], ["title", "Export CSV", 1, "drill-export-btn"], [1, "drill-down-close", 3, "click"], [1, "fa-solid", "fa-times"], ["text", "Loading details...", "size", "small"], [1, "drill-down-table-wrap"], [1, "drill-down-empty"], ["title", "Export CSV", 1, "drill-export-btn", 3, "click"], [1, "fa-solid", "fa-download"], [1, "data-table"], [1, "drill-action-col"], ["title", "Open record", 1, "drill-open-btn"], ["title", "Open record", 1, "drill-open-btn", 3, "click"], [1, "fa-solid", "fa-arrow-up-right-from-square"], [1, "bar-value"], [1, "bar", 2, "background", "var(--mj-brand-primary)"], [1, "bar-label"], ["width", "48", "height", "48", "viewBox", "0 0 48 48"], ["cx", "24", "cy", "24", "r", "20", "fill", "none", "stroke", "var(--mj-border-default)", "stroke-width", "5"], ["cx", "24", "cy", "24", "r", "20", "fill", "none", "stroke-width", "5", "stroke-dashoffset", "31.4", "stroke-linecap", "round", "transform", "rotate(-90 24 24)"], ["x", "24", "y", "26", "text-anchor", "middle", "font-size", "11", "font-weight", "700", "fill", "var(--mj-text-primary)"], [1, "ring-label"], [1, "ring-stat"], [1, "mini-hist-bar", 3, "title"], [1, "h-bar-name"], [1, "h-bar-track"], [1, "h-bar-fill"], [1, "tp-bar"], [1, "tp-bar-label"], [1, "tax-stat"], [1, "fa-solid", "fa-tags"], [1, "sub-section", 2, "margin-top", "0"], [1, "sub-section-header"], [1, "fa-solid", "fa-trophy"], [1, "table-scroll"], [1, "empty-state"], [1, "sub-section"], [1, "sub-section", "co-occurrence-section"], [1, "fa-solid", "fa-link"], [1, "co-occurrence-actions"], ["title", "Last computed timestamp", 1, "co-occurrence-staleness"], ["title", "Recompute co-occurrence data", 1, "drill-export-btn", 3, "click", "disabled"], [1, "num"], [2, "cursor", "pointer"], [2, "cursor", "pointer", 3, "click"], [1, "weight-bar"], ["width", "48", "height", "16"], ["fill", "none", "stroke-width", "1.5", "stroke-linecap", "round"], [1, "muted"], [1, "fa-solid", "fa-chart-bar"], [1, "stacked-bar-chart"], [1, "stacked-row"], [1, "legend", 2, "margin-top", "12px"], [1, "stacked-label"], [1, "stacked-track"], [1, "stacked-seg", 3, "width", "background", "title"], [1, "stacked-seg", 3, "title"], [1, "legend-dot"], [1, "fa-solid", "fa-layer-group"], [1, "v-bar-chart"], [1, "v-bar-col"], [1, "chart-footnote"], [1, "v-bar", 2, "background", "var(--mj-brand-primary)"], [1, "v-bar-label"], [1, "fa-regular", "fa-clock"], [1, "fa-solid", "fa-spinner", "fa-spin"], [1, "fa-solid", "fa-arrows-rotate"], [1, "weight-bar", 2, "background", "var(--mj-status-info)"], [1, "fa-solid", "fa-database"], [2, "cursor", "pointer", 3, "source-selected"], [2, "margin-right", "4px"], [1, "badge"], [1, "fa-solid", "fa-circle", 2, "font-size", "6px", "margin-right", "3px"], [1, "fa-solid", "fa-chart-area"], [1, "two-col"], [1, "widget-card"], [1, "bar-chart", 2, "height", "100px"], [1, "fa-solid", "fa-star-half-stroke"], [1, "quality-bands"], [1, "quality-band-row"], [1, "source-quality-note"], [1, "fa-solid", "fa-circle-info", 2, "color", "var(--mj-status-info)", "margin-right", "4px"], [1, "bar-value", 2, "font-size", "9px"], [1, "quality-band-label"], [1, "quality-band-track"], [1, "quality-band-fill"], [1, "fa-solid", "fa-heart-pulse"], [1, "source-health-grid"], [1, "source-health-card", 3, "border-top-color"], [1, "source-health-card"], [1, "health-card-name"], [1, "health-card-value"], [1, "health-card-label"], [1, "fa-solid", "fa-gears"], [1, "pipeline-throughput-bars"], [1, "pipeline-bar", 2, "cursor", "pointer", 3, "height", "background", "opacity"], [1, "pipeline-date-labels"], [1, "fa-solid", "fa-stopwatch"], [1, "stage-bars"], [1, "stage-row"], [1, "stage-summary"], [1, "stage-warning"], [1, "success-rate-display"], [1, "success-rate-stat"], [1, "success-rate-value", 2, "color", "var(--mj-status-success)"], [1, "success-rate-label"], [1, "success-rate-value", 2, "color", "var(--mj-status-error)"], [1, "success-rate-value", 2, "color", "var(--mj-text-primary)"], [1, "pipeline-bar", 2, "cursor", "pointer", 3, "click"], [1, "stage-name"], [1, "stage-track"], [1, "stage-fill"], [1, "stage-time"], [1, "fa-solid", "fa-triangle-exclamation", 2, "color", "var(--mj-status-warning)", "margin-right", "4px"], [1, "fa-solid", "fa-spinner"], [1, "monospace-cell"], [1, "progress-track"], [1, "progress-fill", 2, "background", "var(--mj-brand-primary)"], [1, "progress-text"], [1, "fa-solid", "fa-triangle-exclamation"], [1, "error-log"], [1, "error-entry"], [1, "error-time"], [1, "error-source"], [1, "error-msg"], [1, "fa-solid", "fa-circle-check"], [1, "fa-solid", "fa-chart-column"], [1, "histogram"], [1, "hist-bar-col", 2, "cursor", "pointer"], [1, "confidence-stats"], [1, "fa-solid", "fa-robot"], [1, "model-grid"], [1, "model-card"], [1, "model-recommendation"], [1, "hist-bar-col", 2, "cursor", "pointer", 3, "click"], [1, "hist-bar"], [1, "hist-label"], [1, "fa-solid", "fa-weight-scale"], [1, "grouped-bar-chart"], [1, "grouped-col"], [1, "legend", 2, "justify-content", "center", "margin-top", "14px"], [1, "grouped-bars"], [1, "g-bar", 2, "background", "var(--mj-brand-primary)", 3, "title"], [1, "g-bar", 2, "background", "var(--mj-status-info)", 3, "title"], [1, "g-bar", 2, "background", "var(--mj-text-muted)", 3, "title"], [1, "grouped-label"], [1, "accuracy-chart"], [1, "accuracy-y-labels"], [1, "accuracy-area"], [1, "accuracy-grid-line", 2, "top", "33%"], [1, "accuracy-grid-line", 2, "top", "66%"], ["width", "100%", "height", "110", "viewBox", "0 0 400 110", "preserveAspectRatio", "none", 1, "accuracy-svg"], ["fill", "none", "stroke", "var(--mj-brand-primary)", "stroke-width", "2.5", "vector-effect", "non-scaling-stroke", "stroke-linecap", "round", "stroke-linejoin", "round"], ["r", "3", "fill", "var(--mj-brand-primary)"], [1, "accuracy-x-labels"], [1, "accuracy-trend-text"], [1, "fa-solid", "fa-arrow-trend-up", 2, "margin-right", "4px"], [1, "fa-solid", "fa-exclamation-triangle"], [1, "model-name"], ["width", "80", "height", "80", "viewBox", "0 0 80 80", 2, "margin", "4px 0"], ["cx", "40", "cy", "40", "r", "32", "fill", "none", "stroke", "var(--mj-border-default)", "stroke-width", "8"], ["cx", "40", "cy", "40", "r", "32", "fill", "none", "stroke-width", "8", "stroke-dashoffset", "50", "transform", "rotate(-90 40 40)", "stroke-linecap", "round"], [1, "model-score"], [1, "model-detail"], [1, "model-detail", 2, "font-weight", "600"], [1, "fa-solid", "fa-lightbulb", 2, "color", "var(--mj-status-warning)"], [1, "fa-solid", "fa-coins"], [1, "tab-header-actions"], ["title", "Print (PDF alternative)", 1, "drill-export-btn", 3, "click"], [1, "fa-solid", "fa-print"], [1, "cost-kpi-row"], [1, "cost-kpi-card"], [1, "cost-kpi-icon"], [1, "cost-kpi-value"], [1, "cost-kpi-label"], [1, "cost-kpi-sub"], [1, "empty-state-hint"]], template: function AnalyticsResourceComponent_Template(rf, ctx) { if (rf & 1) {
|
|
4248
|
+
static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: AnalyticsResourceComponent, selectors: [["app-analytics-resource"]], standalone: false, features: [i0.ɵɵInheritDefinitionFeature], decls: 2, vars: 1, consts: [[1, "analytics-loading"], [1, "analytics-layout"], ["text", "Loading analytics data...", "size", "medium"], [1, "analytics-sidebar"], [1, "sidebar-header"], [1, "fa-solid", "fa-chart-line"], [1, "sidebar-nav"], [1, "nav-item", 3, "active"], [1, "sidebar-divider"], [1, "trending-section"], [1, "fa-solid", "fa-arrow-trend-up"], [1, "tag-cloud"], [3, "font-size", "font-weight"], [1, "no-trending"], [1, "pipeline-status"], [1, "status-dot"], [1, "main-content"], [1, "tab-section"], [1, "nav-item", 3, "click"], [1, "tab-section-header"], [1, "fa-solid", "fa-grip"], [1, "filter-bar"], [1, "date-chips"], [1, "date-chip", 3, "active"], [1, "filter-dropdown", 3, "ngModelChange", "ngModel"], [3, "value"], [1, "kpi-row"], [1, "kpi-card"], [1, "drill-down-panel"], [1, "cards-grid"], [1, "widget-card", 3, "click"], [1, "widget-title"], [1, "bar-chart"], [1, "bar-col"], [1, "fa-solid", "fa-bullseye"], [1, "rings-grid"], [1, "ring-item"], [1, "widget-empty"], [1, "fa-solid", "fa-gauge-high"], [1, "gauge-container"], ["width", "180", "height", "110", "viewBox", "0 0 180 110"], ["d", "M 20 95 A 70 70 0 0 1 160 95", "fill", "none", "stroke", "var(--mj-border-default)", "stroke-width", "14", "stroke-linecap", "round"], ["d", "M 20 95 A 70 70 0 0 1 48.6 35.2", "fill", "none", "stroke", "var(--mj-status-error)", "stroke-width", "14", "stroke-linecap", "round", "opacity", "0.3"], ["d", "M 48.6 35.2 A 70 70 0 0 1 118 28", "fill", "none", "stroke", "var(--mj-status-warning)", "stroke-width", "14", "opacity", "0.3"], ["d", "M 118 28 A 70 70 0 0 1 160 95", "fill", "none", "stroke", "var(--mj-status-success)", "stroke-width", "14", "stroke-linecap", "round", "opacity", "0.3"], ["x", "90", "y", "88", "text-anchor", "middle", "font-size", "26", "font-weight", "800", "fill", "var(--mj-text-primary)"], ["x", "90", "y", "105", "text-anchor", "middle", "font-size", "10", "fill", "var(--mj-text-muted)"], ["x", "28", "y", "108", "font-size", "8", "fill", "var(--mj-text-muted)"], ["x", "86", "y", "18", "font-size", "8", "fill", "var(--mj-text-muted)"], ["x", "155", "y", "108", "font-size", "8", "fill", "var(--mj-text-muted)"], [1, "mini-histogram"], [1, "mini-hist-bar", 3, "height", "background", "title"], [1, "mini-hist-label"], [1, "fa-solid", "fa-ranking-star"], [1, "h-bar-list"], [1, "h-bar-row"], [1, "widget-footnote"], [1, "fa-solid", "fa-bolt"], [1, "throughput-bars"], [1, "tp-bar", 3, "height", "background"], [1, "legend"], [1, "legend-item"], [1, "legend-dot", 2, "background", "var(--mj-status-success)"], [1, "legend-dot", 2, "background", "var(--mj-status-error)"], [1, "fa-solid", "fa-sitemap"], [1, "taxonomy-ring-container"], ["width", "140", "height", "140", "viewBox", "0 0 140 140"], ["cx", "70", "cy", "70", "r", "54", "fill", "none", "stroke", "var(--mj-border-default)", "stroke-width", "18"], ["cx", "70", "cy", "70", "r", "54", "fill", "none", "stroke-width", "18", "transform", "rotate(-90 70 70)"], ["x", "70", "y", "66", "text-anchor", "middle", "font-size", "22", "font-weight", "800", "fill", "var(--mj-text-primary)"], ["x", "70", "y", "82", "text-anchor", "middle", "font-size", "10", "fill", "var(--mj-text-muted)"], [1, "taxonomy-stats"], [1, "tax-stat", 3, "background", "color"], [1, "date-chip", 3, "click"], [1, "kpi-card", 3, "click"], [1, "kpi-label"], [1, "kpi-value"], [1, "kpi-delta"], [1, "fa-solid", "fa-arrow-up", 2, "font-size", "9px"], [1, "fa-solid", "fa-arrow-down", 2, "font-size", "9px"], [1, "kpi-sparkline"], ["width", "64", "height", "28", "viewBox", "0 0 64 28"], ["fill", "none", "stroke-width", "2", "stroke-linecap", "round", "stroke-linejoin", "round"], [1, "drill-down-header"], [1, "drill-down-title"], [1, "fa-solid", "fa-table"], [1, "drill-down-header-actions"], ["title", "Export CSV", 1, "drill-export-btn"], ["aria-label", "Close drill-down", 1, "drill-down-close", 3, "click"], [1, "fa-solid", "fa-times"], ["text", "Loading details...", "size", "small"], [1, "drill-down-table-wrap"], [1, "drill-down-empty"], ["title", "Export CSV", 1, "drill-export-btn", 3, "click"], [1, "fa-solid", "fa-download"], [1, "data-table"], [1, "drill-action-col"], ["title", "Open record", 1, "drill-open-btn"], ["title", "Open record", 1, "drill-open-btn", 3, "click"], [1, "fa-solid", "fa-arrow-up-right-from-square"], [1, "bar-value"], [1, "bar", 2, "background", "var(--mj-brand-primary)"], [1, "bar-label"], ["width", "48", "height", "48", "viewBox", "0 0 48 48"], ["cx", "24", "cy", "24", "r", "20", "fill", "none", "stroke", "var(--mj-border-default)", "stroke-width", "5"], ["cx", "24", "cy", "24", "r", "20", "fill", "none", "stroke-width", "5", "stroke-dashoffset", "31.4", "stroke-linecap", "round", "transform", "rotate(-90 24 24)"], ["x", "24", "y", "26", "text-anchor", "middle", "font-size", "11", "font-weight", "700", "fill", "var(--mj-text-primary)"], [1, "ring-label"], [1, "ring-stat"], [1, "mini-hist-bar", 3, "title"], [1, "h-bar-name"], [1, "h-bar-track"], [1, "h-bar-fill"], [1, "tp-bar"], [1, "tp-bar-label"], [1, "tax-stat"], [1, "fa-solid", "fa-tags"], [1, "sub-section", 2, "margin-top", "0"], [1, "sub-section-header"], [1, "fa-solid", "fa-trophy"], [1, "table-scroll"], [1, "empty-state"], [1, "sub-section"], [1, "sub-section", "co-occurrence-section"], [1, "fa-solid", "fa-link"], [1, "co-occurrence-actions"], ["title", "Last computed timestamp", 1, "co-occurrence-staleness"], ["title", "Recompute co-occurrence data", 1, "drill-export-btn", 3, "click", "disabled"], [1, "num"], [2, "cursor", "pointer"], [2, "cursor", "pointer", 3, "click"], [1, "weight-bar"], ["width", "48", "height", "16"], ["fill", "none", "stroke-width", "1.5", "stroke-linecap", "round"], [1, "muted"], ["aria-label", "Open record", "title", "Open record", 1, "drill-open-btn"], ["aria-label", "Open record", "title", "Open record", 1, "drill-open-btn", 3, "click"], [1, "fa-solid", "fa-chart-bar"], [1, "stacked-bar-chart"], [1, "stacked-row"], [1, "legend", 2, "margin-top", "12px"], [1, "stacked-label"], [1, "stacked-track"], [1, "stacked-seg", 3, "width", "background", "title"], [1, "stacked-seg", 3, "title"], [1, "legend-dot"], [1, "fa-solid", "fa-layer-group"], [1, "v-bar-chart"], [1, "v-bar-col"], [1, "chart-footnote"], [1, "v-bar", 2, "background", "var(--mj-brand-primary)"], [1, "v-bar-label"], [1, "fa-regular", "fa-clock"], [1, "fa-solid", "fa-spinner", "fa-spin"], [1, "fa-solid", "fa-arrows-rotate"], [1, "weight-bar", 2, "background", "var(--mj-status-info)"], [1, "fa-solid", "fa-database"], [2, "cursor", "pointer", 3, "source-selected"], [2, "margin-right", "4px"], [1, "badge"], [1, "fa-solid", "fa-circle", 2, "font-size", "6px", "margin-right", "3px"], [1, "fa-solid", "fa-chart-area"], [1, "two-col"], [1, "widget-card"], [1, "bar-chart", 2, "height", "100px"], [1, "fa-solid", "fa-star-half-stroke"], [1, "quality-bands"], [1, "quality-band-row"], [1, "source-quality-note"], [1, "fa-solid", "fa-circle-info", 2, "color", "var(--mj-status-info)", "margin-right", "4px"], [1, "bar-value", 2, "font-size", "9px"], [1, "quality-band-label"], [1, "quality-band-track"], [1, "quality-band-fill"], [1, "fa-solid", "fa-heart-pulse"], [1, "source-health-grid"], [1, "source-health-card", 3, "border-top-color"], [1, "source-health-card"], [1, "health-card-name"], [1, "health-card-value"], [1, "health-card-label"], [1, "fa-solid", "fa-gears"], [1, "pipeline-throughput-bars"], [1, "pipeline-bar", 2, "cursor", "pointer", 3, "height", "background", "opacity"], [1, "pipeline-date-labels"], [1, "fa-solid", "fa-stopwatch"], [1, "stage-bars"], [1, "stage-row"], [1, "stage-summary"], [1, "stage-warning"], [1, "success-rate-display"], [1, "success-rate-stat"], [1, "success-rate-value", 2, "color", "var(--mj-status-success)"], [1, "success-rate-label"], [1, "success-rate-value", 2, "color", "var(--mj-status-error)"], [1, "success-rate-value", 2, "color", "var(--mj-text-primary)"], [1, "pipeline-bar", 2, "cursor", "pointer", 3, "click"], [1, "stage-name"], [1, "stage-track"], [1, "stage-fill"], [1, "stage-time"], [1, "fa-solid", "fa-triangle-exclamation", 2, "color", "var(--mj-status-warning)", "margin-right", "4px"], [1, "fa-solid", "fa-spinner"], [1, "monospace-cell"], [1, "progress-track"], [1, "progress-fill", 2, "background", "var(--mj-brand-primary)"], [1, "progress-text"], [1, "fa-solid", "fa-triangle-exclamation"], [1, "error-log"], [1, "error-entry"], [1, "error-time"], [1, "error-source"], [1, "error-msg"], [1, "fa-solid", "fa-circle-check"], [1, "fa-solid", "fa-chart-column"], [1, "histogram"], [1, "hist-bar-col", 2, "cursor", "pointer"], [1, "confidence-stats"], [1, "fa-solid", "fa-robot"], [1, "model-grid"], [1, "model-card"], [1, "model-recommendation"], [1, "hist-bar-col", 2, "cursor", "pointer", 3, "click"], [1, "hist-bar"], [1, "hist-label"], [1, "fa-solid", "fa-weight-scale"], [1, "grouped-bar-chart"], [1, "grouped-col"], [1, "legend", 2, "justify-content", "center", "margin-top", "14px"], [1, "grouped-bars"], [1, "g-bar", 2, "background", "var(--mj-brand-primary)", 3, "title"], [1, "g-bar", 2, "background", "var(--mj-status-info)", 3, "title"], [1, "g-bar", 2, "background", "var(--mj-text-muted)", 3, "title"], [1, "grouped-label"], [1, "accuracy-chart"], [1, "accuracy-y-labels"], [1, "accuracy-area"], [1, "accuracy-grid-line", 2, "top", "33%"], [1, "accuracy-grid-line", 2, "top", "66%"], ["width", "100%", "height", "110", "viewBox", "0 0 400 110", "preserveAspectRatio", "none", 1, "accuracy-svg"], ["fill", "none", "stroke", "var(--mj-brand-primary)", "stroke-width", "2.5", "vector-effect", "non-scaling-stroke", "stroke-linecap", "round", "stroke-linejoin", "round"], ["r", "3", "fill", "var(--mj-brand-primary)"], [1, "accuracy-x-labels"], [1, "accuracy-trend-text"], [1, "fa-solid", "fa-arrow-trend-up", 2, "margin-right", "4px"], [1, "fa-solid", "fa-exclamation-triangle"], [1, "model-name"], ["width", "80", "height", "80", "viewBox", "0 0 80 80", 2, "margin", "4px 0"], ["cx", "40", "cy", "40", "r", "32", "fill", "none", "stroke", "var(--mj-border-default)", "stroke-width", "8"], ["cx", "40", "cy", "40", "r", "32", "fill", "none", "stroke-width", "8", "stroke-dashoffset", "50", "transform", "rotate(-90 40 40)", "stroke-linecap", "round"], [1, "model-score"], [1, "model-detail"], [1, "model-detail", 2, "font-weight", "600"], [1, "fa-solid", "fa-lightbulb", 2, "color", "var(--mj-status-warning)"], [1, "fa-solid", "fa-coins"], [1, "tab-header-actions"], ["title", "Print (PDF alternative)", 1, "drill-export-btn", 3, "click"], [1, "fa-solid", "fa-print"], [1, "cost-kpi-row"], [1, "cost-kpi-card"], [1, "cost-kpi-icon"], [1, "cost-kpi-value"], [1, "cost-kpi-label"], [1, "cost-kpi-sub"], [1, "empty-state-hint"]], template: function AnalyticsResourceComponent_Template(rf, ctx) { if (rf & 1) {
|
|
4249
4249
|
i0.ɵɵconditionalCreate(0, AnalyticsResourceComponent_Conditional_0_Template, 2, 0, "div", 0)(1, AnalyticsResourceComponent_Conditional_1_Template, 28, 10, "div", 1);
|
|
4250
4250
|
} if (rf & 2) {
|
|
4251
4251
|
i0.ɵɵconditional(ctx.IsLoading ? 0 : 1);
|
|
@@ -4257,7 +4257,7 @@ AnalyticsResourceComponent = AnalyticsResourceComponent_1 = __decorate([
|
|
|
4257
4257
|
export { AnalyticsResourceComponent };
|
|
4258
4258
|
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(AnalyticsResourceComponent, [{
|
|
4259
4259
|
type: Component,
|
|
4260
|
-
args: [{ standalone: false, selector: 'app-analytics-resource', template: "@if (IsLoading) {\n <div class=\"analytics-loading\">\n <mj-loading text=\"Loading analytics data...\" size=\"medium\"></mj-loading>\n </div>\n} @else {\n <div class=\"analytics-layout\">\n <!-- =============== LEFT SIDEBAR =============== -->\n <aside class=\"analytics-sidebar\">\n <div class=\"sidebar-header\">\n <h2><i class=\"fa-solid fa-chart-line\"></i> Analytics</h2>\n </div>\n\n <nav class=\"sidebar-nav\">\n @for (nav of NavItems; track nav.ID) {\n <button\n class=\"nav-item\"\n [class.active]=\"ActiveTab === nav.ID\"\n (click)=\"SelectTab(nav.ID)\"\n >\n <i [class]=\"nav.Icon\"></i> {{ nav.Label }}\n </button>\n }\n </nav>\n\n <div class=\"sidebar-divider\"></div>\n\n <!-- Trending Tags Cloud -->\n <div class=\"trending-section\">\n <h3><i class=\"fa-solid fa-arrow-trend-up\"></i> Trending Tags</h3>\n <div class=\"tag-cloud\">\n @for (tag of TrendingTags; track tag.Name) {\n <span [style.font-size.px]=\"tag.Size\" [style.font-weight]=\"tag.Weight\">{{ tag.Name }}</span>\n }\n @if (TrendingTags.length === 0) {\n <span class=\"no-trending\">No trending tags yet</span>\n }\n </div>\n </div>\n\n <!-- Pipeline Status -->\n <div class=\"pipeline-status\">\n <span class=\"status-dot\" [class.status-dot-error]=\"!PipelineStatusOk\"></span>\n {{ PipelineStatusText }}\n </div>\n </aside>\n\n <!-- =============== MAIN CONTENT =============== -->\n <div class=\"main-content\">\n\n <!-- ====================================================== -->\n <!-- TAB 1: OVERVIEW -->\n <!-- ====================================================== -->\n @if (ActiveTab === 'overview') {\n <section class=\"tab-section\">\n <div class=\"tab-section-header\">\n <i class=\"fa-solid fa-grip\"></i>\n <h1>Overview</h1>\n </div>\n\n <!-- Filter Bar -->\n <div class=\"filter-bar\">\n <div class=\"date-chips\">\n @for (range of DateRanges; track range.Label) {\n <button\n class=\"date-chip\"\n [class.active]=\"ActiveDateRange === range.Label\"\n (click)=\"SetDateRange(range.Label)\"\n >{{ range.Label }}</button>\n }\n </div>\n <select class=\"filter-dropdown\" [ngModel]=\"EntityFilter\" (ngModelChange)=\"SetEntityFilter($event)\">\n @for (opt of EntityFilterOptions; track opt) {\n <option [value]=\"opt\">{{ opt }}</option>\n }\n </select>\n </div>\n\n <!-- KPI Cards -->\n <div class=\"kpi-row\">\n @for (kpi of KPIs; track kpi.Label) {\n <div class=\"kpi-card\" (click)=\"OpenDrillDown(kpi.DrillDownKey)\">\n <div>\n <div class=\"kpi-label\">{{ kpi.Label }}</div>\n <div class=\"kpi-value\">{{ kpi.Value }}</div>\n <div class=\"kpi-delta\" [class.up]=\"kpi.DeltaDirection === 'up'\" [class.down]=\"kpi.DeltaDirection === 'down'\">\n @if (kpi.DeltaDirection === 'up') {\n <i class=\"fa-solid fa-arrow-up\" style=\"font-size:9px\"></i>\n } @else if (kpi.DeltaDirection === 'down') {\n <i class=\"fa-solid fa-arrow-down\" style=\"font-size:9px\"></i>\n }\n {{ kpi.Delta }}\n </div>\n </div>\n <div class=\"kpi-sparkline\">\n <svg width=\"64\" height=\"28\" viewBox=\"0 0 64 28\">\n <polyline [attr.points]=\"kpi.SparklinePoints\" fill=\"none\" [attr.stroke]=\"kpi.SparklineColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n </svg>\n </div>\n </div>\n }\n </div>\n\n <!-- KPI Drill-Down -->\n @if (DrillDownTarget && DrillDownTarget.startsWith('kpi-')) {\n <div class=\"drill-down-panel\">\n <div class=\"drill-down-header\">\n <span class=\"drill-down-title\"><i class=\"fa-solid fa-table\"></i> Detail View</span>\n <div class=\"drill-down-header-actions\">\n @if (DrillDownData.length > 0) {\n <button class=\"drill-export-btn\" (click)=\"ExportDrillDownCSV()\" title=\"Export CSV\">\n <i class=\"fa-solid fa-download\"></i> CSV\n </button>\n }\n <button class=\"drill-down-close\" (click)=\"CloseDrillDown()\">\n <i class=\"fa-solid fa-times\"></i>\n </button>\n </div>\n </div>\n @if (IsDrillDownLoading) {\n <mj-loading text=\"Loading details...\" size=\"small\"></mj-loading>\n } @else if (DrillDownData.length > 0) {\n <div class=\"drill-down-table-wrap\">\n <table class=\"data-table\">\n <thead>\n <tr>\n @for (col of DrillDownColumns; track col) {\n <th>{{ col }}</th>\n }\n @if (DrillDownHasActions) {\n <th class=\"drill-action-col\"></th>\n }\n </tr>\n </thead>\n <tbody>\n @for (row of DrillDownData; track TrackByIndex($index)) {\n <tr>\n @for (col of DrillDownColumns; track col) {\n <td>{{ row[col] }}</td>\n }\n @if (DrillDownHasActions) {\n <td class=\"drill-action-col\">\n @if (row['_RecordID']) {\n <button class=\"drill-open-btn\" (click)=\"OpenDrillDownRecord(row)\" title=\"Open record\">\n <i class=\"fa-solid fa-arrow-up-right-from-square\"></i>\n </button>\n }\n </td>\n }\n </tr>\n }\n </tbody>\n </table>\n </div>\n } @else {\n <div class=\"drill-down-empty\">No data available</div>\n }\n </div>\n }\n\n <!-- Widget Cards Grid (2x3) -->\n <div class=\"cards-grid\">\n\n <!-- Card 1: Tag Growth -->\n <div class=\"widget-card\" (click)=\"OpenDrillDown('tagGrowth')\">\n <div class=\"widget-title\"><i class=\"fa-solid fa-chart-line\"></i> Tag Growth</div>\n <div class=\"bar-chart\">\n @for (bar of TagGrowthData; track bar.Label) {\n <div class=\"bar-col\">\n <div class=\"bar-value\">{{ bar.Count }}</div>\n <div class=\"bar\" [style.height.%]=\"bar.Percentage\" style=\"background:var(--mj-brand-primary)\" [style.opacity]=\"0.5 + (bar.Percentage / 200)\"></div>\n <div class=\"bar-label\">{{ bar.Label }}</div>\n </div>\n }\n </div>\n </div>\n\n <!-- Card 2: Content Coverage -->\n <div class=\"widget-card\" (click)=\"OpenDrillDown('contentCoverage')\">\n <div class=\"widget-title\"><i class=\"fa-solid fa-bullseye\"></i> Content Coverage</div>\n <div class=\"rings-grid\">\n @for (entity of CoverageData; track entity.Name) {\n <div class=\"ring-item\">\n <svg width=\"48\" height=\"48\" viewBox=\"0 0 48 48\">\n <circle cx=\"24\" cy=\"24\" r=\"20\" fill=\"none\" stroke=\"var(--mj-border-default)\" stroke-width=\"5\"/>\n <circle cx=\"24\" cy=\"24\" r=\"20\" fill=\"none\" [attr.stroke]=\"entity.Color\" stroke-width=\"5\"\n [attr.stroke-dasharray]=\"entity.StrokeDash\" stroke-dashoffset=\"31.4\" stroke-linecap=\"round\"\n transform=\"rotate(-90 24 24)\"/>\n <text x=\"24\" y=\"26\" text-anchor=\"middle\" font-size=\"11\" font-weight=\"700\" fill=\"var(--mj-text-primary)\">{{ entity.Percentage }}%</text>\n </svg>\n <div>\n <div class=\"ring-label\">{{ entity.Name }}</div>\n <div class=\"ring-stat\">{{ FormatNumber(entity.Tagged) }} / {{ FormatNumber(entity.Total) }}</div>\n </div>\n </div>\n }\n @if (CoverageData.length === 0) {\n <div class=\"widget-empty\">No content types found</div>\n }\n </div>\n </div>\n\n <!-- Card 3: Quality Score Gauge -->\n <div class=\"widget-card\" (click)=\"OpenDrillDown('qualityScore')\">\n <div class=\"widget-title\"><i class=\"fa-solid fa-gauge-high\"></i> Quality Score</div>\n <div class=\"gauge-container\">\n <svg width=\"180\" height=\"110\" viewBox=\"0 0 180 110\">\n <!-- Background arc -->\n <path d=\"M 20 95 A 70 70 0 0 1 160 95\" fill=\"none\" stroke=\"var(--mj-border-default)\" stroke-width=\"14\" stroke-linecap=\"round\"/>\n <!-- Zone arcs -->\n <path d=\"M 20 95 A 70 70 0 0 1 48.6 35.2\" fill=\"none\" stroke=\"var(--mj-status-error)\" stroke-width=\"14\" stroke-linecap=\"round\" opacity=\"0.3\"/>\n <path d=\"M 48.6 35.2 A 70 70 0 0 1 118 28\" fill=\"none\" stroke=\"var(--mj-status-warning)\" stroke-width=\"14\" opacity=\"0.3\"/>\n <path d=\"M 118 28 A 70 70 0 0 1 160 95\" fill=\"none\" stroke=\"var(--mj-status-success)\" stroke-width=\"14\" stroke-linecap=\"round\" opacity=\"0.3\"/>\n <!-- Score text -->\n <text x=\"90\" y=\"88\" text-anchor=\"middle\" font-size=\"26\" font-weight=\"800\" fill=\"var(--mj-text-primary)\">{{ QualityScore }}</text>\n <text x=\"90\" y=\"105\" text-anchor=\"middle\" font-size=\"10\" fill=\"var(--mj-text-muted)\">out of 100</text>\n <text x=\"28\" y=\"108\" font-size=\"8\" fill=\"var(--mj-text-muted)\">0</text>\n <text x=\"86\" y=\"18\" font-size=\"8\" fill=\"var(--mj-text-muted)\">50</text>\n <text x=\"155\" y=\"108\" font-size=\"8\" fill=\"var(--mj-text-muted)\">100</text>\n </svg>\n </div>\n <!-- Mini confidence histogram -->\n <div class=\"mini-histogram\">\n @for (bin of MiniConfidenceBins; track TrackByIndex($index)) {\n <div class=\"mini-hist-bar\" [style.height.px]=\"bin.Height\" [style.background]=\"bin.Color\" [title]=\"bin.Title\"></div>\n }\n </div>\n <div class=\"mini-hist-label\">Confidence Distribution</div>\n </div>\n\n <!-- Card 4: Source Performance -->\n <div class=\"widget-card\" (click)=\"OpenDrillDown('sourcePerformance')\">\n <div class=\"widget-title\"><i class=\"fa-solid fa-ranking-star\"></i> Source Performance</div>\n <div class=\"h-bar-list\">\n @for (source of SourcePerfData; track source.Name) {\n <div class=\"h-bar-row\">\n <div class=\"h-bar-name\">{{ source.Name }}</div>\n <div class=\"h-bar-track\">\n <div class=\"h-bar-fill\" [style.width.%]=\"source.Percentage\" [style.background]=\"source.Color\">{{ source.AvgTagsPerItem }}</div>\n </div>\n </div>\n }\n @if (SourcePerfData.length === 0) {\n <div class=\"widget-empty\">No source data</div>\n }\n </div>\n <div class=\"widget-footnote\">Average tags per item</div>\n </div>\n\n <!-- Card 5: Daily Throughput -->\n <div class=\"widget-card\" (click)=\"OpenDrillDown('dailyThroughput')\">\n <div class=\"widget-title\"><i class=\"fa-solid fa-bolt\"></i> Daily Throughput</div>\n <div class=\"throughput-bars\">\n @for (day of ThroughputData; track day.Label) {\n <div class=\"tp-bar\"\n [style.height.%]=\"day.Percentage\"\n [style.background]=\"day.IsError ? 'var(--mj-status-error)' : 'var(--mj-status-success)'\"\n >\n <span class=\"tp-bar-label\">{{ day.Label }}</span>\n </div>\n }\n </div>\n <div class=\"legend\">\n <div class=\"legend-item\"><div class=\"legend-dot\" style=\"background:var(--mj-status-success)\"></div> Success</div>\n <div class=\"legend-item\"><div class=\"legend-dot\" style=\"background:var(--mj-status-error)\"></div> Failures</div>\n </div>\n </div>\n\n <!-- Card 6: Taxonomy Health -->\n <div class=\"widget-card\" (click)=\"OpenDrillDown('taxonomyHealth')\">\n <div class=\"widget-title\"><i class=\"fa-solid fa-sitemap\"></i> Taxonomy Health</div>\n <div class=\"taxonomy-ring-container\">\n <svg width=\"140\" height=\"140\" viewBox=\"0 0 140 140\">\n <circle cx=\"70\" cy=\"70\" r=\"54\" fill=\"none\" stroke=\"var(--mj-border-default)\" stroke-width=\"18\"/>\n @for (seg of TaxonomyRingSegments; track TrackByIndex($index)) {\n <circle cx=\"70\" cy=\"70\" r=\"54\" fill=\"none\"\n [attr.stroke]=\"seg.Color\" stroke-width=\"18\"\n [attr.stroke-dasharray]=\"seg.StrokeDash\"\n [attr.stroke-dashoffset]=\"seg.StrokeOffset\"\n transform=\"rotate(-90 70 70)\"/>\n }\n <text x=\"70\" y=\"66\" text-anchor=\"middle\" font-size=\"22\" font-weight=\"800\" fill=\"var(--mj-text-primary)\">{{ TaxonomyTotal }}</text>\n <text x=\"70\" y=\"82\" text-anchor=\"middle\" font-size=\"10\" fill=\"var(--mj-text-muted)\">total tags</text>\n </svg>\n </div>\n <div class=\"taxonomy-stats\">\n @for (stat of TaxonomyStats; track stat.Label) {\n <div class=\"tax-stat\" [style.background]=\"stat.BgColor\" [style.color]=\"stat.Color\">\n {{ stat.Count }}<small>{{ stat.Label }}</small>\n </div>\n }\n </div>\n </div>\n\n </div><!-- /cards-grid -->\n\n <!-- Widget Drill-Down -->\n @if (DrillDownTarget && !DrillDownTarget.startsWith('kpi-')) {\n <div class=\"drill-down-panel\">\n <div class=\"drill-down-header\">\n <span class=\"drill-down-title\"><i class=\"fa-solid fa-table\"></i> {{ DrillDownTarget }} Detail</span>\n <button class=\"drill-down-close\" (click)=\"CloseDrillDown()\">\n <i class=\"fa-solid fa-times\"></i>\n </button>\n </div>\n @if (IsDrillDownLoading) {\n <mj-loading text=\"Loading details...\" size=\"small\"></mj-loading>\n } @else if (DrillDownData.length > 0) {\n <div class=\"drill-down-table-wrap\">\n <table class=\"data-table\">\n <thead>\n <tr>\n @for (col of DrillDownColumns; track col) {\n <th>{{ col }}</th>\n }\n @if (DrillDownHasActions) {\n <th class=\"drill-action-col\"></th>\n }\n </tr>\n </thead>\n <tbody>\n @for (row of DrillDownData; track TrackByIndex($index)) {\n <tr>\n @for (col of DrillDownColumns; track col) {\n <td>{{ row[col] }}</td>\n }\n @if (DrillDownHasActions) {\n <td class=\"drill-action-col\">\n @if (row['_RecordID']) {\n <button class=\"drill-open-btn\" (click)=\"OpenDrillDownRecord(row)\" title=\"Open record\">\n <i class=\"fa-solid fa-arrow-up-right-from-square\"></i>\n </button>\n }\n </td>\n }\n </tr>\n }\n </tbody>\n </table>\n </div>\n } @else {\n <div class=\"drill-down-empty\">No data available</div>\n }\n </div>\n }\n </section>\n }\n\n <!-- ====================================================== -->\n <!-- TAB 2: TAGS -->\n <!-- ====================================================== -->\n @if (ActiveTab === 'tags') {\n <section class=\"tab-section\">\n <div class=\"tab-section-header\">\n <i class=\"fa-solid fa-tags\"></i>\n <h1>Tags</h1>\n </div>\n\n <!-- Top 20 Tags Table -->\n <div class=\"sub-section\" style=\"margin-top:0\">\n <div class=\"sub-section-header\">\n <h3><i class=\"fa-solid fa-trophy\"></i> Top 20 Tags</h3>\n @if (TopTags.length > 0) {\n <button class=\"drill-export-btn\" (click)=\"ExportTabDataCSV('top-tags')\" title=\"Export CSV\">\n <i class=\"fa-solid fa-download\"></i> CSV\n </button>\n }\n </div>\n @if (TopTags.length > 0) {\n <div class=\"table-scroll\">\n <table class=\"data-table\">\n <thead>\n <tr>\n <th>#</th>\n <th>Tag Name</th>\n <th class=\"num\">Usage Count</th>\n <th>Avg Weight</th>\n <th>Trend</th>\n <th>Top Entity</th>\n <th>First Seen</th>\n </tr>\n </thead>\n <tbody>\n @for (tag of TopTags; track tag.Name) {\n <tr style=\"cursor:pointer\" (click)=\"OpenDrillDown('tag-row:' + tag.Name)\">\n <td>{{ tag.Rank }}</td>\n <td><strong>{{ tag.Name }}</strong></td>\n <td class=\"num\">{{ FormatNumber(tag.UsageCount) }}</td>\n <td>\n <span class=\"weight-bar\" [style.width.px]=\"tag.WeightBarWidth\" [style.background]=\"tag.WeightBarColor\"></span>\n {{ tag.AvgWeight }}\n </td>\n <td>\n <svg width=\"48\" height=\"16\">\n <polyline [attr.points]=\"tag.TrendPoints\" fill=\"none\" [attr.stroke]=\"tag.TrendColor\" stroke-width=\"1.5\" stroke-linecap=\"round\"/>\n </svg>\n </td>\n <td>{{ tag.TopEntity }}</td>\n <td class=\"muted\">{{ tag.FirstSeen }}</td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n } @else {\n <div class=\"empty-state\">\n <i class=\"fa-solid fa-tags\"></i>\n <p>No tag data available yet. Process content to generate tags.</p>\n </div>\n }\n </div>\n\n <!-- D10: Tags tab drill-down -->\n @if (DrillDownTarget && DrillDownTarget.startsWith('tag-row:')) {\n <div class=\"drill-down-panel\">\n <div class=\"drill-down-header\">\n <span class=\"drill-down-title\"><i class=\"fa-solid fa-table\"></i> Content Items Tagged \"{{ DrillDownTarget.replace('tag-row:', '') }}\"</span>\n <div class=\"drill-down-header-actions\">\n @if (DrillDownData.length > 0) {\n <button class=\"drill-export-btn\" (click)=\"ExportDrillDownCSV()\" title=\"Export CSV\">\n <i class=\"fa-solid fa-download\"></i> CSV\n </button>\n }\n <button class=\"drill-down-close\" (click)=\"CloseDrillDown()\"><i class=\"fa-solid fa-times\"></i></button>\n </div>\n </div>\n @if (IsDrillDownLoading) {\n <mj-loading text=\"Loading details...\" size=\"small\"></mj-loading>\n } @else if (DrillDownData.length > 0) {\n <div class=\"drill-down-table-wrap\">\n <table class=\"data-table\">\n <thead><tr>\n @for (col of DrillDownColumns; track col) { <th>{{ col }}</th> }\n @if (DrillDownHasActions) { <th class=\"drill-action-col\"></th> }\n </tr></thead>\n <tbody>\n @for (row of DrillDownData; track TrackByIndex($index)) {\n <tr>\n @for (col of DrillDownColumns; track col) { <td>{{ row[col] }}</td> }\n @if (DrillDownHasActions) {\n <td class=\"drill-action-col\">\n @if (row['_RecordID']) {\n <button class=\"drill-open-btn\" (click)=\"OpenDrillDownRecord(row)\" title=\"Open record\"><i class=\"fa-solid fa-arrow-up-right-from-square\"></i></button>\n }\n </td>\n }\n </tr>\n }\n </tbody>\n </table>\n </div>\n } @else {\n <div class=\"drill-down-empty\">No items found for this tag</div>\n }\n </div>\n }\n\n <!-- Tag Distribution by Entity -->\n @if (EntityDistribution.length > 0) {\n <div class=\"sub-section\">\n <h3><i class=\"fa-solid fa-chart-bar\"></i> Tag Distribution by Entity</h3>\n <div class=\"stacked-bar-chart\">\n @for (row of EntityDistribution; track row.EntityName) {\n <div class=\"stacked-row\">\n <div class=\"stacked-label\">{{ row.EntityName }}</div>\n <div class=\"stacked-track\">\n @for (seg of row.Segments; track TrackByIndex($index)) {\n <div class=\"stacked-seg\" [style.width.%]=\"seg.Percentage\" [style.background]=\"seg.Color\" [title]=\"seg.Label + ': ' + seg.Percentage + '%'\">{{ seg.Label }}</div>\n }\n </div>\n </div>\n }\n </div>\n <div class=\"legend\" style=\"margin-top:12px\">\n @for (item of DistributionLegend; track item.Label) {\n <div class=\"legend-item\"><div class=\"legend-dot\" [style.background]=\"item.Color\"></div> {{ item.Label }}</div>\n }\n </div>\n </div>\n }\n\n <!-- Tag Depth Distribution -->\n @if (TagDepthBars.length > 0) {\n <div class=\"sub-section\">\n <h3><i class=\"fa-solid fa-layer-group\"></i> Tag Depth Distribution</h3>\n <div class=\"v-bar-chart\">\n @for (bar of TagDepthBars; track bar.Label) {\n <div class=\"v-bar-col\">\n <div class=\"v-bar\" [style.height.%]=\"bar.Percentage\" style=\"background:var(--mj-brand-primary)\" [style.min-height.px]=\"bar.Count > 0 ? 24 : 0\">{{ bar.Count }}</div>\n <div class=\"v-bar-label\">{{ bar.Label }}</div>\n </div>\n }\n </div>\n <div class=\"chart-footnote\">\n Taxonomy hierarchy depth -- Most tags at depth 2-3 indicating a healthy mid-level structure\n </div>\n </div>\n }\n\n <!-- Frequently Paired Tags (Co-Occurrence) -->\n <div class=\"sub-section co-occurrence-section\">\n <div class=\"sub-section-header\">\n <h3><i class=\"fa-solid fa-link\"></i> Frequently Paired Tags</h3>\n <div class=\"co-occurrence-actions\">\n @if (CoOccurrenceLastComputed) {\n <span class=\"co-occurrence-staleness\" title=\"Last computed timestamp\">\n <i class=\"fa-regular fa-clock\"></i>\n Last computed: {{ CoOccurrenceLastComputed }}\n </span>\n }\n <button class=\"drill-export-btn\" (click)=\"RecomputeCoOccurrence()\" [disabled]=\"IsRecomputingCoOccurrence\" title=\"Recompute co-occurrence data\">\n @if (IsRecomputingCoOccurrence) {\n <i class=\"fa-solid fa-spinner fa-spin\"></i> Computing...\n } @else {\n <i class=\"fa-solid fa-arrows-rotate\"></i> Recompute\n }\n </button>\n </div>\n </div>\n @if (CoOccurrencePairs.length > 0) {\n <div class=\"table-scroll\">\n <table class=\"data-table\">\n <thead>\n <tr>\n <th>#</th>\n <th>Tag A</th>\n <th>Tag B</th>\n <th class=\"num\">Co-Occurrences</th>\n <th>Frequency</th>\n </tr>\n </thead>\n <tbody>\n @for (pair of CoOccurrencePairs; track pair.TagAName + pair.TagBName; let i = $index) {\n <tr>\n <td class=\"muted\">{{ i + 1 }}</td>\n <td><strong>{{ pair.TagAName }}</strong></td>\n <td><strong>{{ pair.TagBName }}</strong></td>\n <td class=\"num\">{{ FormatNumber(pair.Count) }}</td>\n <td>\n <span class=\"weight-bar\" [style.width.px]=\"pair.BarWidth\" style=\"background: var(--mj-status-info)\"></span>\n </td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n } @else {\n <div class=\"empty-state\">\n <i class=\"fa-solid fa-link\"></i>\n <p>No co-occurrence data available yet. Process content and recompute to see which tags frequently appear together.</p>\n </div>\n }\n </div>\n </section>\n }\n\n <!-- ====================================================== -->\n <!-- TAB 3: SOURCES -->\n <!-- ====================================================== -->\n @if (ActiveTab === 'sources') {\n <section class=\"tab-section\">\n <div class=\"tab-section-header\">\n <i class=\"fa-solid fa-database\"></i>\n <h1>Sources</h1>\n </div>\n\n <!-- Source Comparison Table -->\n <div class=\"sub-section\" style=\"margin-top:0\">\n <h3><i class=\"fa-solid fa-table\"></i> Source Comparison</h3>\n @if (SourceComparison.length > 0) {\n <div class=\"table-scroll\">\n <table class=\"data-table\">\n <thead>\n <tr>\n <th>Source Name</th>\n <th>Type</th>\n <th class=\"num\">Items</th>\n <th class=\"num\">Tags Generated</th>\n <th class=\"num\">Avg Tags/Item</th>\n <th class=\"num\">Avg Weight</th>\n <th>Last Run</th>\n <th>Status</th>\n </tr>\n </thead>\n <tbody>\n @for (source of SourceComparison; track source.Name) {\n <tr (click)=\"SelectSource(source.Name); OpenDrillDown('source-row:' + source.Name)\" style=\"cursor:pointer\" [class.source-selected]=\"SelectedSourceName === source.Name\">\n <td><strong>{{ source.Name }}</strong></td>\n <td><i [class]=\"source.TypeIcon\" [style.color]=\"source.TypeColor\" style=\"margin-right:4px\"></i> {{ source.Type }}</td>\n <td class=\"num\">{{ FormatNumber(source.Items) }}</td>\n <td class=\"num\">{{ FormatNumber(source.TagsGenerated) }}</td>\n <td class=\"num\">{{ source.AvgTagsPerItem }}</td>\n <td class=\"num\">{{ source.AvgWeight }}</td>\n <td class=\"muted\">{{ source.LastRun }}</td>\n <td><span class=\"badge\" [class]=\"source.StatusClass\"><i class=\"fa-solid fa-circle\" style=\"font-size:6px;margin-right:3px\"></i> {{ source.Status }}</span></td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n } @else {\n <div class=\"empty-state\">\n <i class=\"fa-solid fa-database\"></i>\n <p>No content sources configured yet.</p>\n </div>\n }\n </div>\n\n <!-- D10: Sources tab drill-down (recent runs) -->\n @if (DrillDownTarget && DrillDownTarget.startsWith('source-row:')) {\n <div class=\"drill-down-panel\">\n <div class=\"drill-down-header\">\n <span class=\"drill-down-title\"><i class=\"fa-solid fa-table\"></i> Recent Runs: {{ DrillDownTarget.replace('source-row:', '') }}</span>\n <div class=\"drill-down-header-actions\">\n @if (DrillDownData.length > 0) {\n <button class=\"drill-export-btn\" (click)=\"ExportDrillDownCSV()\" title=\"Export CSV\"><i class=\"fa-solid fa-download\"></i> CSV</button>\n }\n <button class=\"drill-down-close\" (click)=\"CloseDrillDown()\"><i class=\"fa-solid fa-times\"></i></button>\n </div>\n </div>\n @if (IsDrillDownLoading) {\n <mj-loading text=\"Loading details...\" size=\"small\"></mj-loading>\n } @else if (DrillDownData.length > 0) {\n <div class=\"drill-down-table-wrap\">\n <table class=\"data-table\">\n <thead><tr>\n @for (col of DrillDownColumns; track col) { <th>{{ col }}</th> }\n @if (DrillDownHasActions) { <th class=\"drill-action-col\"></th> }\n </tr></thead>\n <tbody>\n @for (row of DrillDownData; track TrackByIndex($index)) {\n <tr>\n @for (col of DrillDownColumns; track col) { <td>{{ row[col] }}</td> }\n @if (DrillDownHasActions) {\n <td class=\"drill-action-col\">\n @if (row['_RecordID']) {\n <button class=\"drill-open-btn\" (click)=\"OpenDrillDownRecord(row)\" title=\"Open record\"><i class=\"fa-solid fa-arrow-up-right-from-square\"></i></button>\n }\n </td>\n }\n </tr>\n }\n </tbody>\n </table>\n </div>\n } @else {\n <div class=\"drill-down-empty\">No run data for this source</div>\n }\n </div>\n }\n\n <!-- Per-Source Detail -->\n @if (SelectedSourceName && SourceComparison.length > 0) {\n <div class=\"sub-section\">\n <h3><i class=\"fa-solid fa-chart-area\"></i> Source Detail: {{ SelectedSourceName }}</h3>\n <div class=\"two-col\">\n <!-- Items Processed Over Time -->\n <div class=\"widget-card\">\n <div class=\"widget-title\"><i class=\"fa-solid fa-chart-bar\"></i> Items Processed (Last 8 Weeks)</div>\n <div class=\"bar-chart\" style=\"height:100px\">\n @for (bar of SourceWeeklyBars; track bar.Label) {\n <div class=\"bar-col\">\n <div class=\"bar-value\" style=\"font-size:9px\">{{ bar.Value }}</div>\n <div class=\"bar\" [style.height.%]=\"bar.Percentage\" style=\"background:var(--mj-brand-primary)\" [style.opacity]=\"0.5 + (bar.Percentage / 200)\"></div>\n <div class=\"bar-label\">{{ bar.Label }}</div>\n </div>\n }\n </div>\n </div>\n\n <!-- Tag Quality Distribution -->\n <div class=\"widget-card\">\n <div class=\"widget-title\"><i class=\"fa-solid fa-star-half-stroke\"></i> Tag Quality Distribution</div>\n <div class=\"quality-bands\">\n @for (band of SourceQualityBands; track band.Label) {\n <div class=\"quality-band-row\">\n <span class=\"quality-band-label\">{{ band.Label }}</span>\n <div class=\"quality-band-track\">\n <div class=\"quality-band-fill\" [style.width.%]=\"band.Percentage\" [style.background]=\"band.Color\">{{ band.Percentage }}%</div>\n </div>\n </div>\n }\n </div>\n <div class=\"source-quality-note\">\n <i class=\"fa-solid fa-circle-info\" style=\"color:var(--mj-status-info);margin-right:4px\"></i>\n {{ SourceQualityNote }}\n </div>\n </div>\n </div>\n </div>\n }\n\n <!-- Source Health -->\n @if (SourceHealthCards.length > 0) {\n <div class=\"sub-section\">\n <h3><i class=\"fa-solid fa-heart-pulse\"></i> Source Health Summary</h3>\n <div class=\"source-health-grid\" [style.grid-template-columns]=\"'repeat(' + SourceHealthCards.length + ', 1fr)'\">\n @for (card of SourceHealthCards; track card.Name) {\n <div class=\"source-health-card\" [style.border-top-color]=\"card.Color\">\n <div class=\"health-card-name\">{{ card.Name }}</div>\n <div class=\"health-card-value\" [style.color]=\"card.Color\">{{ card.Uptime }}%</div>\n <div class=\"health-card-label\">uptime</div>\n </div>\n }\n </div>\n </div>\n }\n </section>\n }\n\n <!-- ====================================================== -->\n <!-- TAB 4: PIPELINE -->\n <!-- ====================================================== -->\n @if (ActiveTab === 'pipeline') {\n <section class=\"tab-section\">\n <div class=\"tab-section-header\">\n <i class=\"fa-solid fa-gears\"></i>\n <h1>Pipeline</h1>\n </div>\n\n <!-- Pipeline Throughput Chart -->\n <div class=\"sub-section\" style=\"margin-top:0\">\n <h3><i class=\"fa-solid fa-chart-bar\"></i> Pipeline Throughput (Last 30 Days)</h3>\n <div class=\"widget-card\">\n <div class=\"pipeline-throughput-bars\">\n @for (bar of PipelineThroughputBars; track TrackByIndex($index)) {\n <div class=\"pipeline-bar\"\n [style.height.%]=\"bar.Percentage\"\n [style.background]=\"bar.IsError ? 'var(--mj-status-error)' : 'var(--mj-brand-primary)'\"\n [style.opacity]=\"bar.IsError ? 0.7 : (0.6 + bar.Percentage / 300)\"\n style=\"cursor:pointer\"\n (click)=\"OpenDrillDown('pipeline-throughput:' + $index)\"\n ></div>\n }\n </div>\n <div class=\"pipeline-date-labels\">\n @for (label of PipelineDateLabels; track label) {\n <span>{{ label }}</span>\n }\n </div>\n </div>\n </div>\n\n <!-- D10: Pipeline throughput drill-down -->\n @if (DrillDownTarget && DrillDownTarget.startsWith('pipeline-throughput:')) {\n <div class=\"drill-down-panel\">\n <div class=\"drill-down-header\">\n <span class=\"drill-down-title\"><i class=\"fa-solid fa-table\"></i> Runs for Selected Day</span>\n <div class=\"drill-down-header-actions\">\n @if (DrillDownData.length > 0) {\n <button class=\"drill-export-btn\" (click)=\"ExportDrillDownCSV()\" title=\"Export CSV\"><i class=\"fa-solid fa-download\"></i> CSV</button>\n }\n <button class=\"drill-down-close\" (click)=\"CloseDrillDown()\"><i class=\"fa-solid fa-times\"></i></button>\n </div>\n </div>\n @if (IsDrillDownLoading) {\n <mj-loading text=\"Loading details...\" size=\"small\"></mj-loading>\n } @else if (DrillDownData.length > 0) {\n <div class=\"drill-down-table-wrap\">\n <table class=\"data-table\">\n <thead><tr>\n @for (col of DrillDownColumns; track col) { <th>{{ col }}</th> }\n @if (DrillDownHasActions) { <th class=\"drill-action-col\"></th> }\n </tr></thead>\n <tbody>\n @for (row of DrillDownData; track TrackByIndex($index)) {\n <tr>\n @for (col of DrillDownColumns; track col) { <td>{{ row[col] }}</td> }\n @if (DrillDownHasActions) {\n <td class=\"drill-action-col\">\n @if (row['_RecordID']) {\n <button class=\"drill-open-btn\" (click)=\"OpenDrillDownRecord(row)\" title=\"Open record\"><i class=\"fa-solid fa-arrow-up-right-from-square\"></i></button>\n }\n </td>\n }\n </tr>\n }\n </tbody>\n </table>\n </div>\n } @else {\n <div class=\"drill-down-empty\">No runs for this day</div>\n }\n </div>\n }\n\n <!-- Processing Time Breakdown -->\n <div class=\"sub-section\">\n <h3><i class=\"fa-solid fa-stopwatch\"></i> Processing Time Breakdown (Avg per Item)</h3>\n <div class=\"widget-card\">\n <div class=\"stage-bars\">\n @for (stage of ProcessingStages; track stage.Name) {\n <div class=\"stage-row\">\n <div class=\"stage-name\">{{ stage.Name }}</div>\n <div class=\"stage-track\">\n <div class=\"stage-fill\" [style.width.%]=\"stage.Percentage\" [style.background]=\"stage.Color\">\n @if (stage.Percentage > 15) {\n {{ stage.Time }}s\n }\n </div>\n </div>\n <div class=\"stage-time\">{{ stage.Time }}s</div>\n </div>\n }\n </div>\n <div class=\"stage-summary\">\n <span><strong>Total avg:</strong> {{ TotalAvgProcessingTime }}s per item</span>\n @if (BottleneckStage) {\n <span class=\"stage-warning\">\n <i class=\"fa-solid fa-triangle-exclamation\" style=\"color:var(--mj-status-warning);margin-right:4px\"></i>\n {{ BottleneckStage }} stage is the bottleneck ({{ BottleneckPercent }}% of total time)\n </span>\n }\n </div>\n </div>\n </div>\n\n <!-- Success Rate -->\n <div class=\"sub-section\">\n <h3><i class=\"fa-solid fa-chart-line\"></i> Success Rate Overview</h3>\n <div class=\"widget-card\">\n <div class=\"success-rate-display\">\n <div class=\"success-rate-stat\">\n <div class=\"success-rate-value\" style=\"color:var(--mj-status-success)\">\n {{ rawProcessRuns.length > 0 ? (100 - (ErrorLog.length / rawProcessRuns.length * 100)).toFixed(1) : '100' }}%\n </div>\n <div class=\"success-rate-label\">Success Rate</div>\n </div>\n <div class=\"success-rate-stat\">\n <div class=\"success-rate-value\" style=\"color:var(--mj-status-error)\">\n {{ rawProcessRuns.length > 0 ? (ErrorLog.length / rawProcessRuns.length * 100).toFixed(1) : '0' }}%\n </div>\n <div class=\"success-rate-label\">Failure Rate</div>\n </div>\n <div class=\"success-rate-stat\">\n <div class=\"success-rate-value\" style=\"color:var(--mj-text-primary)\">\n {{ FormatNumber(rawProcessRuns.length) }}\n </div>\n <div class=\"success-rate-label\">Total Runs</div>\n </div>\n </div>\n </div>\n </div>\n\n <!-- Active Runs Table -->\n @if (ActiveRuns.length > 0) {\n <div class=\"sub-section\">\n <h3><i class=\"fa-solid fa-spinner\"></i> Active Runs</h3>\n <div class=\"table-scroll\">\n <table class=\"data-table\">\n <thead>\n <tr>\n <th>Run ID</th>\n <th>Source</th>\n <th>Started</th>\n <th>Progress</th>\n <th>Stage</th>\n <th class=\"num\">Items</th>\n </tr>\n </thead>\n <tbody>\n @for (run of ActiveRuns; track run.RunID) {\n <tr>\n <td class=\"monospace-cell\">{{ run.RunID }}</td>\n <td>{{ run.Source }}</td>\n <td class=\"muted\">{{ run.Started }}</td>\n <td>\n <div class=\"progress-track\"><div class=\"progress-fill\" [style.width.%]=\"run.Progress\" style=\"background:var(--mj-brand-primary)\"></div></div>\n <span class=\"progress-text\">{{ run.Progress }}%</span>\n </td>\n <td><span class=\"badge\" [class]=\"run.StageClass\">{{ run.Stage }}</span></td>\n <td class=\"num\">{{ FormatNumber(run.Items) }}</td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n </div>\n }\n\n <!-- Error Log -->\n @if (ErrorLog.length > 0) {\n <div class=\"sub-section\">\n <h3><i class=\"fa-solid fa-triangle-exclamation\"></i> Recent Errors</h3>\n <div class=\"error-log\">\n @for (entry of ErrorLog; track TrackByIndex($index)) {\n <div class=\"error-entry\">\n <div class=\"error-time\">{{ entry.Time }}</div>\n <div class=\"error-source\">{{ entry.Source }}</div>\n <div class=\"error-msg\">{{ entry.Message }}</div>\n </div>\n }\n </div>\n </div>\n }\n </section>\n }\n\n <!-- ====================================================== -->\n <!-- TAB 5: QUALITY -->\n <!-- ====================================================== -->\n @if (ActiveTab === 'quality') {\n <section class=\"tab-section\">\n <div class=\"tab-section-header\">\n <i class=\"fa-solid fa-circle-check\"></i>\n <h1>Quality</h1>\n </div>\n\n <!-- Confidence Distribution Histogram -->\n <div class=\"sub-section\" style=\"margin-top:0\">\n <h3><i class=\"fa-solid fa-chart-column\"></i> Confidence Distribution</h3>\n <div class=\"widget-card\">\n <div class=\"histogram\">\n @for (bin of ConfidenceHistogram; track bin.Label) {\n <div class=\"hist-bar-col\" style=\"cursor:pointer\" (click)=\"OpenDrillDown('quality-bin:' + bin.Label)\">\n <div class=\"hist-bar\" [style.height.%]=\"bin.Percentage\" [style.background]=\"bin.Color\">\n @if (bin.Count > 0) { {{ bin.Count }} }\n </div>\n <div class=\"hist-label\">{{ bin.Label }}</div>\n </div>\n }\n </div>\n <div class=\"confidence-stats\">\n @for (stat of ConfidenceStats; track stat.Label) {\n <span><strong>{{ stat.Label }}:</strong> {{ stat.Value }}</span>\n }\n </div>\n </div>\n </div>\n\n <!-- D10: Quality bin drill-down -->\n @if (DrillDownTarget && DrillDownTarget.startsWith('quality-bin:')) {\n <div class=\"drill-down-panel\">\n <div class=\"drill-down-header\">\n <span class=\"drill-down-title\"><i class=\"fa-solid fa-table\"></i> Items in Confidence Range {{ DrillDownTarget.replace('quality-bin:', '') }}</span>\n <div class=\"drill-down-header-actions\">\n @if (DrillDownData.length > 0) {\n <button class=\"drill-export-btn\" (click)=\"ExportDrillDownCSV()\" title=\"Export CSV\"><i class=\"fa-solid fa-download\"></i> CSV</button>\n }\n <button class=\"drill-down-close\" (click)=\"CloseDrillDown()\"><i class=\"fa-solid fa-times\"></i></button>\n </div>\n </div>\n @if (IsDrillDownLoading) {\n <mj-loading text=\"Loading details...\" size=\"small\"></mj-loading>\n } @else if (DrillDownData.length > 0) {\n <div class=\"drill-down-table-wrap\">\n <table class=\"data-table\">\n <thead><tr>\n @for (col of DrillDownColumns; track col) { <th>{{ col }}</th> }\n @if (DrillDownHasActions) { <th class=\"drill-action-col\"></th> }\n </tr></thead>\n <tbody>\n @for (row of DrillDownData; track TrackByIndex($index)) {\n <tr>\n @for (col of DrillDownColumns; track col) { <td>{{ row[col] }}</td> }\n @if (DrillDownHasActions) {\n <td class=\"drill-action-col\">\n @if (row['_RecordID']) {\n <button class=\"drill-open-btn\" (click)=\"OpenDrillDownRecord(row)\" title=\"Open record\"><i class=\"fa-solid fa-arrow-up-right-from-square\"></i></button>\n }\n </td>\n }\n </tr>\n }\n </tbody>\n </table>\n </div>\n } @else {\n <div class=\"drill-down-empty\">No items in this confidence range</div>\n }\n </div>\n }\n\n <!-- Weight Distribution by Entity -->\n @if (WeightByEntity.length > 0) {\n <div class=\"sub-section\">\n <h3><i class=\"fa-solid fa-weight-scale\"></i> Weight Distribution by Entity</h3>\n <div class=\"widget-card\">\n <div class=\"grouped-bar-chart\">\n @for (entity of WeightByEntity; track entity.Name) {\n <div class=\"grouped-col\">\n <div class=\"grouped-bars\">\n <div class=\"g-bar\" [style.height.%]=\"entity.High\" style=\"background:var(--mj-brand-primary)\" [title]=\"'High: ' + entity.High + '%'\"></div>\n <div class=\"g-bar\" [style.height.%]=\"entity.Med\" style=\"background:var(--mj-status-info)\" [title]=\"'Med: ' + entity.Med + '%'\"></div>\n <div class=\"g-bar\" [style.height.%]=\"entity.Low\" style=\"background:var(--mj-text-muted)\" [title]=\"'Low: ' + entity.Low + '%'\"></div>\n </div>\n <div class=\"grouped-label\">{{ entity.Name }}</div>\n </div>\n }\n </div>\n <div class=\"legend\" style=\"justify-content:center;margin-top:14px\">\n @for (item of WeightLegend; track item.Label) {\n <div class=\"legend-item\"><div class=\"legend-dot\" [style.background]=\"item.Color\"></div> {{ item.Label }}</div>\n }\n </div>\n </div>\n </div>\n }\n\n <!-- Tag Accuracy Over Time -->\n @if (AccuracyLinePoints) {\n <div class=\"sub-section\">\n <h3><i class=\"fa-solid fa-chart-line\"></i> Tag Accuracy Over Time (Weekly Avg Confidence)</h3>\n <div class=\"widget-card\">\n <div class=\"accuracy-chart\">\n <div class=\"accuracy-y-labels\">\n <div>1.0</div>\n <div>0.75</div>\n <div>0.50</div>\n <div>0.25</div>\n </div>\n <div class=\"accuracy-area\">\n <div class=\"accuracy-grid-line\" style=\"top:33%\"></div>\n <div class=\"accuracy-grid-line\" style=\"top:66%\"></div>\n <svg width=\"100%\" height=\"110\" viewBox=\"0 0 400 110\" preserveAspectRatio=\"none\" class=\"accuracy-svg\">\n <polyline [attr.points]=\"AccuracyLinePoints\"\n fill=\"none\" stroke=\"var(--mj-brand-primary)\" stroke-width=\"2.5\" vector-effect=\"non-scaling-stroke\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n @for (dot of AccuracyDots; track TrackByIndex($index)) {\n <circle [attr.cx]=\"dot.Cx\" [attr.cy]=\"dot.Cy\" r=\"3\" fill=\"var(--mj-brand-primary)\"/>\n }\n </svg>\n </div>\n </div>\n <div class=\"accuracy-x-labels\">\n @for (label of AccuracyMonthLabels; track label) {\n <span>{{ label }}</span>\n }\n </div>\n <div class=\"accuracy-trend-text\">\n <i class=\"fa-solid fa-arrow-trend-up\" style=\"margin-right:4px\"></i>\n {{ AccuracyTrendText }}\n </div>\n </div>\n </div>\n }\n\n <!-- Low-Confidence Tags Table -->\n @if (LowConfidenceTags.length > 0) {\n <div class=\"sub-section\">\n <h3><i class=\"fa-solid fa-exclamation-triangle\"></i> Low-Confidence Tags (Avg Weight < 0.4)</h3>\n <div class=\"table-scroll\">\n <table class=\"data-table\">\n <thead>\n <tr>\n <th>Tag Name</th>\n <th class=\"num\">Avg Weight</th>\n <th class=\"num\">Usage Count</th>\n <th>Top Entity</th>\n <th>Suggested Action</th>\n </tr>\n </thead>\n <tbody>\n @for (tag of LowConfidenceTags; track tag.Name) {\n <tr>\n <td><strong>{{ tag.Name }}</strong></td>\n <td class=\"num\">{{ tag.AvgWeight }}</td>\n <td class=\"num\">{{ tag.UsageCount }}</td>\n <td>{{ tag.TopEntity }}</td>\n <td><span class=\"badge\" [class]=\"tag.ActionClass\">{{ tag.SuggestedAction }}</span></td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n </div>\n }\n\n <!-- Model Performance Comparison -->\n <div class=\"sub-section\">\n <h3><i class=\"fa-solid fa-robot\"></i> Model Performance Comparison</h3>\n <div class=\"model-grid\">\n @for (model of ModelComparisons; track model.Name) {\n <div class=\"model-card\">\n <div class=\"model-name\"><i [class]=\"model.Icon\" [style.color]=\"model.IconColor\" style=\"margin-right:4px\"></i> {{ model.Name }}</div>\n <svg width=\"80\" height=\"80\" viewBox=\"0 0 80 80\" style=\"margin:4px 0\">\n <circle cx=\"40\" cy=\"40\" r=\"32\" fill=\"none\" stroke=\"var(--mj-border-default)\" stroke-width=\"8\"/>\n <circle cx=\"40\" cy=\"40\" r=\"32\" fill=\"none\" [attr.stroke]=\"model.ScoreColor\" stroke-width=\"8\"\n [attr.stroke-dasharray]=\"model.StrokeDash\" stroke-dashoffset=\"50\"\n transform=\"rotate(-90 40 40)\" stroke-linecap=\"round\"/>\n </svg>\n <div class=\"model-score\" [style.color]=\"model.ScoreColor\">{{ model.ScorePercentage }}%</div>\n <div class=\"model-detail\">Avg confidence: {{ model.AvgConfidence }}</div>\n <div class=\"model-detail\">{{ FormatNumber(model.TagsGenerated) }} tags generated</div>\n <div class=\"model-detail\" [style.color]=\"model.RoleColor\" style=\"font-weight:600\">{{ model.Role }}</div>\n </div>\n }\n </div>\n @if (ModelRecommendation) {\n <div class=\"model-recommendation\">\n <i class=\"fa-solid fa-lightbulb\" style=\"color:var(--mj-status-warning)\"></i>\n <div>\n <strong>Recommendation:</strong> {{ ModelRecommendation }}\n </div>\n </div>\n }\n </div>\n </section>\n }\n\n <!-- ====================================================== -->\n <!-- TAB 6: COST & USAGE (D1) -->\n <!-- ====================================================== -->\n @if (ActiveTab === 'cost') {\n <section class=\"tab-section\">\n <div class=\"tab-section-header\">\n <i class=\"fa-solid fa-coins\"></i>\n <h1>Cost & Usage</h1>\n <div class=\"tab-header-actions\">\n <button class=\"drill-export-btn\" (click)=\"ExportTabDataCSV('cost-usage')\" title=\"Export CSV\">\n <i class=\"fa-solid fa-download\"></i> CSV\n </button>\n <button class=\"drill-export-btn\" (click)=\"PrintCurrentTab()\" title=\"Print (PDF alternative)\">\n <i class=\"fa-solid fa-print\"></i> Print\n </button>\n </div>\n </div>\n\n <!-- Filter Bar -->\n <div class=\"filter-bar\">\n <div class=\"date-chips\">\n @for (range of DateRanges; track range.Label) {\n <button\n class=\"date-chip\"\n [class.active]=\"ActiveDateRange === range.Label\"\n (click)=\"SetDateRange(range.Label)\"\n >{{ range.Label }}</button>\n }\n </div>\n </div>\n\n <!-- KPI Cards -->\n @if (CostKPIs.length > 0) {\n <div class=\"cost-kpi-row\">\n @for (kpi of CostKPIs; track kpi.Label) {\n <div class=\"cost-kpi-card\">\n <div class=\"cost-kpi-icon\"><i [class]=\"kpi.Icon\"></i></div>\n <div>\n <div class=\"cost-kpi-value\">{{ kpi.Value }}</div>\n <div class=\"cost-kpi-label\">{{ kpi.Label }}</div>\n <div class=\"cost-kpi-sub\">{{ kpi.SubLabel }}</div>\n </div>\n </div>\n }\n </div>\n }\n\n <!-- Per-Run Cost Table -->\n <div class=\"sub-section\">\n <h3><i class=\"fa-solid fa-table\"></i> Cost Breakdown by Run</h3>\n @if (CostPerRunRows.length > 0) {\n <div class=\"table-scroll\">\n <table class=\"data-table\">\n <thead>\n <tr>\n <th>Run ID</th>\n <th>Source</th>\n <th class=\"num\">Tokens</th>\n <th class=\"num\">Cost</th>\n <th>Started</th>\n </tr>\n </thead>\n <tbody>\n @for (row of CostPerRunRows; track row.RunID) {\n <tr>\n <td class=\"monospace-cell\">{{ row.RunID }}</td>\n <td>{{ row.Source }}</td>\n <td class=\"num\">{{ FormatNumber(row.Tokens) }}</td>\n <td class=\"num\">{{ row.Cost > 0 ? '$' + row.Cost.toFixed(4) : '$0.00' }}</td>\n <td class=\"muted\">{{ row.Started }}</td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n } @else {\n <div class=\"empty-state\">\n <i class=\"fa-solid fa-coins\"></i>\n <p>No cost data available yet. Run the pipeline to generate cost and token usage metrics.</p>\n <p class=\"empty-state-hint\">Cost data is aggregated from ContentProcessRunDetail records linked to AI Prompt Runs.</p>\n </div>\n }\n </div>\n </section>\n }\n\n </div><!-- /main-content -->\n </div><!-- /analytics-layout -->\n}\n", styles: ["/* ===================================================================\n Analytics & Insights Resource Component\n ================================================================= */\n\n.analytics-loading {\n display: flex;\n justify-content: center;\n align-items: center;\n height: 400px;\n}\n\n.analytics-layout {\n display: flex;\n height: 100%;\n min-height: 600px;\n}\n\n/* ===== LEFT SIDEBAR ===== */\n.analytics-sidebar {\n width: 220px;\n min-width: 220px;\n background: var(--mj-bg-surface);\n border-right: 1px solid var(--mj-border-default);\n display: flex;\n flex-direction: column;\n overflow-y: auto;\n}\n\n.sidebar-header {\n padding: 20px 16px 12px;\n border-bottom: 1px solid var(--mj-border-default);\n}\n\n.sidebar-header h2 {\n font-size: 13px;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n color: var(--mj-text-muted);\n display: flex;\n align-items: center;\n gap: 8px;\n margin: 0;\n}\n\n.sidebar-header h2 i {\n font-size: 14px;\n color: var(--mj-brand-primary);\n}\n\n.sidebar-nav {\n padding: 8px 0;\n flex: 1;\n display: flex;\n flex-direction: column;\n}\n\n.nav-item {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 10px 16px;\n font-size: 13.5px;\n font-weight: 500;\n color: var(--mj-text-secondary);\n cursor: pointer;\n border: none;\n background: transparent;\n border-left: 3px solid transparent;\n transition: all 0.15s ease;\n text-align: left;\n width: 100%;\n font-family: inherit;\n}\n\n.nav-item:hover {\n background: var(--mj-bg-surface-card);\n color: var(--mj-text-primary);\n}\n\n.nav-item.active {\n border-left-color: var(--mj-brand-primary);\n color: var(--mj-brand-primary);\n background: color-mix(in srgb, var(--mj-brand-primary) 6%, var(--mj-bg-surface));\n font-weight: 600;\n}\n\n.nav-item i {\n width: 16px;\n text-align: center;\n font-size: 13px;\n}\n\n.sidebar-divider {\n height: 1px;\n background: var(--mj-border-default);\n margin: 8px 16px;\n}\n\n.trending-section {\n padding: 12px 16px 16px;\n}\n\n.trending-section h3 {\n font-size: 11px;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n color: var(--mj-text-muted);\n margin: 0 0 10px;\n display: flex;\n align-items: center;\n gap: 4px;\n}\n\n.tag-cloud {\n display: flex;\n flex-wrap: wrap;\n gap: 4px 6px;\n line-height: 1.7;\n}\n\n.tag-cloud span {\n color: var(--mj-brand-primary);\n cursor: pointer;\n transition: opacity 0.15s;\n}\n\n.tag-cloud span:hover {\n opacity: 0.7;\n}\n\n.no-trending {\n color: var(--mj-text-muted) !important;\n font-size: 12px !important;\n font-style: italic;\n}\n\n.pipeline-status {\n padding: 12px 16px;\n border-top: 1px solid var(--mj-border-default);\n display: flex;\n align-items: center;\n gap: 8px;\n font-size: 11.5px;\n color: var(--mj-text-muted);\n}\n\n.status-dot {\n width: 8px;\n height: 8px;\n border-radius: 50%;\n background: var(--mj-status-success);\n flex-shrink: 0;\n}\n\n.status-dot-error {\n background: var(--mj-status-error);\n}\n\n/* ===== MAIN CONTENT ===== */\n.main-content {\n flex: 1;\n overflow-y: auto;\n min-width: 0;\n}\n\n.tab-section {\n padding: 28px 32px 40px;\n}\n\n.tab-section-header {\n display: flex;\n align-items: center;\n gap: 10px;\n margin-bottom: 20px;\n padding-bottom: 12px;\n border-bottom: 2px solid var(--mj-brand-primary);\n}\n\n.tab-section-header i {\n font-size: 18px;\n color: var(--mj-brand-primary);\n}\n\n.tab-section-header h1 {\n font-size: 20px;\n font-weight: 700;\n color: var(--mj-text-primary);\n margin: 0;\n}\n\n/* ===== FILTER BAR ===== */\n.filter-bar {\n display: flex;\n align-items: center;\n gap: 12px;\n margin-bottom: 20px;\n flex-wrap: wrap;\n}\n\n.date-chips {\n display: flex;\n gap: 0;\n border: 1px solid var(--mj-border-default);\n border-radius: 6px;\n overflow: hidden;\n}\n\n.date-chip {\n padding: 6px 14px;\n font-size: 12px;\n font-weight: 500;\n color: var(--mj-text-secondary);\n background: var(--mj-bg-surface);\n cursor: pointer;\n border: none;\n border-right: 1px solid var(--mj-border-default);\n transition: all 0.15s;\n font-family: inherit;\n}\n\n.date-chip:last-child {\n border-right: none;\n}\n\n.date-chip:hover {\n background: var(--mj-bg-surface-card);\n}\n\n.date-chip.active {\n background: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n}\n\n.filter-dropdown {\n padding: 6px 12px;\n font-size: 12px;\n border: 1px solid var(--mj-border-default);\n border-radius: 6px;\n background: var(--mj-bg-surface);\n color: var(--mj-text-secondary);\n cursor: pointer;\n font-family: inherit;\n}\n\n/* ===== KPI CARDS ===== */\n.kpi-row {\n display: grid;\n grid-template-columns: repeat(4, 1fr);\n gap: 16px;\n margin-bottom: 20px;\n}\n\n.kpi-card {\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n padding: 16px 18px;\n display: flex;\n align-items: flex-start;\n justify-content: space-between;\n transition: transform 0.15s, box-shadow 0.15s;\n cursor: pointer;\n}\n\n.kpi-card:hover {\n transform: translateY(-2px);\n box-shadow: 0 4px 12px color-mix(in srgb, var(--mj-text-primary) 8%, transparent);\n}\n\n.kpi-label {\n font-size: 12px;\n font-weight: 500;\n color: var(--mj-text-muted);\n margin-bottom: 4px;\n}\n\n.kpi-value {\n font-size: 28px;\n font-weight: 700;\n color: var(--mj-text-primary);\n line-height: 1.1;\n}\n\n.kpi-delta {\n font-size: 11px;\n font-weight: 500;\n margin-top: 4px;\n color: var(--mj-text-muted);\n}\n\n.kpi-delta.up {\n color: var(--mj-status-success);\n}\n\n.kpi-delta.down {\n color: var(--mj-status-error);\n}\n\n.kpi-sparkline {\n flex-shrink: 0;\n margin-left: 12px;\n margin-top: 6px;\n}\n\n/* ===== CARDS GRID ===== */\n.cards-grid {\n display: grid;\n grid-template-columns: repeat(3, 1fr);\n gap: 16px;\n}\n\n.widget-card {\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n padding: 18px 20px;\n transition: transform 0.15s, box-shadow 0.15s;\n cursor: pointer;\n}\n\n.widget-card:hover {\n transform: translateY(-2px);\n box-shadow: 0 4px 12px color-mix(in srgb, var(--mj-text-primary) 8%, transparent);\n}\n\n.widget-title {\n font-size: 13px;\n font-weight: 600;\n color: var(--mj-text-secondary);\n margin-bottom: 14px;\n display: flex;\n align-items: center;\n gap: 6px;\n}\n\n.widget-title i {\n color: var(--mj-brand-primary);\n font-size: 12px;\n}\n\n.widget-empty {\n text-align: center;\n color: var(--mj-text-muted);\n font-size: 12px;\n padding: 20px;\n}\n\n.widget-footnote {\n text-align: center;\n font-size: 10px;\n color: var(--mj-text-muted);\n margin-top: 10px;\n}\n\n/* ===== BAR CHART ===== */\n.bar-chart {\n display: flex;\n align-items: flex-end;\n gap: 8px;\n height: 120px;\n padding-top: 10px;\n}\n\n.bar-col {\n flex: 1;\n display: flex;\n flex-direction: column;\n align-items: center;\n height: 100%;\n justify-content: flex-end;\n}\n\n.bar {\n width: 100%;\n border-radius: 4px 4px 0 0;\n transition: height 0.3s;\n min-height: 2px;\n}\n\n.bar-label {\n font-size: 10px;\n color: var(--mj-text-muted);\n margin-top: 6px;\n}\n\n.bar-value {\n font-size: 10px;\n font-weight: 600;\n color: var(--mj-text-secondary);\n margin-bottom: 4px;\n}\n\n/* ===== PROGRESS RINGS ===== */\n.rings-grid {\n display: grid;\n grid-template-columns: repeat(2, 1fr);\n gap: 12px;\n}\n\n.ring-item {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 6px 0;\n}\n\n.ring-label {\n font-size: 12px;\n color: var(--mj-text-primary);\n font-weight: 500;\n}\n\n.ring-stat {\n font-size: 11px;\n color: var(--mj-text-muted);\n}\n\n/* ===== GAUGE ===== */\n.gauge-container {\n display: flex;\n justify-content: center;\n margin-bottom: 8px;\n}\n\n/* ===== MINI HISTOGRAM ===== */\n.mini-histogram {\n display: flex;\n align-items: flex-end;\n gap: 3px;\n height: 32px;\n justify-content: center;\n}\n\n.mini-hist-bar {\n width: 14px;\n border-radius: 2px;\n}\n\n.mini-hist-label {\n text-align: center;\n font-size: 10px;\n color: var(--mj-text-muted);\n margin-top: 4px;\n}\n\n/* ===== HORIZONTAL BARS ===== */\n.h-bar-list {\n display: flex;\n flex-direction: column;\n gap: 10px;\n}\n\n.h-bar-row {\n display: flex;\n align-items: center;\n gap: 10px;\n}\n\n.h-bar-name {\n font-size: 12px;\n color: var(--mj-text-secondary);\n width: 100px;\n flex-shrink: 0;\n text-align: right;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.h-bar-track {\n flex: 1;\n height: 20px;\n background: var(--mj-bg-surface-card);\n border-radius: 4px;\n overflow: hidden;\n}\n\n.h-bar-fill {\n height: 100%;\n border-radius: 4px;\n display: flex;\n align-items: center;\n padding-left: 8px;\n font-size: 11px;\n font-weight: 600;\n color: var(--mj-text-inverse);\n min-width: 36px;\n}\n\n/* ===== THROUGHPUT BARS ===== */\n.throughput-bars {\n display: flex;\n align-items: flex-end;\n gap: 4px;\n height: 110px;\n margin-bottom: 22px;\n}\n\n.tp-bar {\n flex: 1;\n border-radius: 3px 3px 0 0;\n position: relative;\n min-height: 4px;\n}\n\n.tp-bar-label {\n position: absolute;\n bottom: -18px;\n left: 50%;\n transform: translateX(-50%);\n font-size: 8px;\n color: var(--mj-text-muted);\n white-space: nowrap;\n}\n\n/* ===== TAXONOMY ===== */\n.taxonomy-ring-container {\n display: flex;\n justify-content: center;\n}\n\n.taxonomy-stats {\n display: flex;\n gap: 8px;\n margin-top: 12px;\n}\n\n.tax-stat {\n flex: 1;\n text-align: center;\n padding: 8px 4px;\n border-radius: 6px;\n font-size: 20px;\n font-weight: 700;\n}\n\n.tax-stat small {\n display: block;\n font-size: 10px;\n font-weight: 500;\n margin-top: 2px;\n}\n\n/* ===== LEGEND ===== */\n.legend {\n display: flex;\n gap: 16px;\n margin-top: 10px;\n flex-wrap: wrap;\n justify-content: center;\n}\n\n.legend-item {\n display: flex;\n align-items: center;\n gap: 5px;\n font-size: 11px;\n color: var(--mj-text-muted);\n}\n\n.legend-dot {\n width: 10px;\n height: 10px;\n border-radius: 3px;\n flex-shrink: 0;\n}\n\n/* ===== DATA TABLES ===== */\n.table-scroll {\n overflow-x: auto;\n}\n\n.data-table {\n width: 100%;\n border-collapse: collapse;\n font-size: 12.5px;\n}\n\n.data-table thead th {\n text-align: left;\n padding: 10px 12px;\n font-size: 11px;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.04em;\n color: var(--mj-text-muted);\n border-bottom: 2px solid var(--mj-border-default);\n background: var(--mj-bg-surface-card);\n white-space: nowrap;\n}\n\n.data-table tbody td {\n padding: 10px 12px;\n border-bottom: 1px solid var(--mj-border-subtle);\n color: var(--mj-text-primary);\n vertical-align: middle;\n}\n\n.data-table tbody tr:hover {\n background: var(--mj-bg-surface-card);\n}\n\n.data-table .num {\n text-align: right;\n font-variant-numeric: tabular-nums;\n}\n\n.muted {\n color: var(--mj-text-muted);\n}\n\n.monospace-cell {\n font-family: monospace;\n font-size: 11px;\n color: var(--mj-brand-primary);\n}\n\n.weight-bar {\n display: inline-block;\n height: 6px;\n border-radius: 3px;\n vertical-align: middle;\n margin-right: 6px;\n}\n\n/* ===== BADGES ===== */\n.badge {\n display: inline-block;\n padding: 2px 8px;\n border-radius: 10px;\n font-size: 10.5px;\n font-weight: 600;\n}\n\n.badge-success {\n background: color-mix(in srgb, var(--mj-status-success) 12%, var(--mj-bg-surface));\n color: var(--mj-status-success);\n}\n\n.badge-warning {\n background: color-mix(in srgb, var(--mj-status-warning) 12%, var(--mj-bg-surface));\n color: var(--mj-status-warning);\n}\n\n.badge-error {\n background: color-mix(in srgb, var(--mj-status-error) 12%, var(--mj-bg-surface));\n color: var(--mj-status-error);\n}\n\n.badge-info {\n background: color-mix(in srgb, var(--mj-status-info) 12%, var(--mj-bg-surface));\n color: var(--mj-status-info);\n}\n\n/* ===== SUB-SECTIONS ===== */\n.sub-section {\n margin-top: 24px;\n}\n\n.sub-section h3 {\n font-size: 14px;\n font-weight: 600;\n color: var(--mj-text-primary);\n margin: 0 0 12px;\n display: flex;\n align-items: center;\n gap: 6px;\n}\n\n.sub-section h3 i {\n font-size: 13px;\n color: var(--mj-brand-primary);\n}\n\n/* ===== EMPTY STATE ===== */\n.empty-state {\n display: flex;\n flex-direction: column;\n align-items: center;\n padding: 3rem 2rem;\n text-align: center;\n gap: 0.75rem;\n border: 2px dashed var(--mj-border-default);\n border-radius: 12px;\n background: var(--mj-bg-surface-sunken);\n color: var(--mj-text-muted);\n}\n\n.empty-state i {\n font-size: 2.5rem;\n color: var(--mj-text-disabled);\n}\n\n.empty-state p {\n margin: 0;\n font-size: 0.85rem;\n max-width: 400px;\n}\n\n/* ===== DRILL-DOWN PANEL ===== */\n.drill-down-panel {\n margin-top: 16px;\n margin-bottom: 16px;\n background: var(--mj-bg-surface);\n border: 2px solid var(--mj-brand-primary);\n border-radius: 10px;\n overflow: hidden;\n animation: slideDown 0.2s ease-out;\n}\n\n@keyframes slideDown {\n from {\n opacity: 0;\n max-height: 0;\n }\n to {\n opacity: 1;\n max-height: 600px;\n }\n}\n\n.drill-down-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 10px 16px;\n background: color-mix(in srgb, var(--mj-brand-primary) 6%, var(--mj-bg-surface));\n border-bottom: 1px solid var(--mj-border-default);\n}\n\n.drill-down-title {\n font-size: 13px;\n font-weight: 600;\n color: var(--mj-brand-primary);\n display: flex;\n align-items: center;\n gap: 6px;\n}\n\n.drill-down-close {\n border: none;\n background: transparent;\n color: var(--mj-text-muted);\n cursor: pointer;\n font-size: 14px;\n padding: 4px 8px;\n border-radius: 4px;\n transition: all 0.15s;\n}\n\n.drill-down-close:hover {\n background: var(--mj-bg-surface-hover);\n color: var(--mj-text-primary);\n}\n\n.drill-down-table-wrap {\n max-height: 400px;\n overflow-y: auto;\n padding: 8px;\n}\n\n.drill-action-col {\n width: 40px;\n text-align: center;\n padding: 6px !important;\n}\n\n.drill-open-btn {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 26px;\n height: 26px;\n border: 1px solid var(--mj-border-default);\n border-radius: 5px;\n background: transparent;\n color: var(--mj-text-muted);\n font-size: 10px;\n cursor: pointer;\n transition: all 0.15s;\n}\n\n.drill-open-btn:hover {\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n border-color: var(--mj-brand-primary);\n color: var(--mj-brand-primary);\n}\n\n.drill-down-header-actions {\n display: flex;\n align-items: center;\n gap: 6px;\n}\n\n.drill-export-btn {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n padding: 4px 10px;\n border: 1px solid var(--mj-border-default);\n border-radius: 5px;\n background: transparent;\n color: var(--mj-text-secondary);\n font-size: 11px;\n cursor: pointer;\n transition: all 0.15s;\n}\n\n.drill-export-btn:hover {\n background: color-mix(in srgb, var(--mj-brand-primary) 8%, var(--mj-bg-surface));\n border-color: var(--mj-brand-primary);\n color: var(--mj-brand-primary);\n}\n\n.sub-section-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n margin-bottom: 0;\n}\n\n.sub-section-header h3 {\n margin: 0;\n}\n\n.drill-down-empty {\n padding: 24px;\n text-align: center;\n color: var(--mj-text-muted);\n font-size: 13px;\n}\n\n/* ===== STACKED BAR CHART ===== */\n.stacked-bar-chart {\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n\n.stacked-row {\n display: flex;\n align-items: center;\n gap: 10px;\n}\n\n.stacked-label {\n width: 90px;\n font-size: 12px;\n color: var(--mj-text-secondary);\n text-align: right;\n flex-shrink: 0;\n}\n\n.stacked-track {\n flex: 1;\n height: 22px;\n display: flex;\n border-radius: 4px;\n overflow: hidden;\n}\n\n.stacked-seg {\n height: 100%;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 9px;\n font-weight: 600;\n color: var(--mj-text-inverse);\n min-width: 20px;\n}\n\n/* ===== VERTICAL BAR CHART ===== */\n.v-bar-chart {\n display: flex;\n align-items: flex-end;\n gap: 24px;\n height: 140px;\n padding-top: 10px;\n justify-content: center;\n}\n\n.v-bar-col {\n display: flex;\n flex-direction: column;\n align-items: center;\n height: 100%;\n justify-content: flex-end;\n min-width: 60px;\n}\n\n.v-bar {\n width: 48px;\n border-radius: 4px 4px 0 0;\n display: flex;\n align-items: flex-start;\n justify-content: center;\n padding-top: 4px;\n font-size: 11px;\n font-weight: 700;\n color: var(--mj-text-inverse);\n}\n\n.v-bar-label {\n font-size: 11px;\n color: var(--mj-text-muted);\n margin-top: 8px;\n text-align: center;\n}\n\n.chart-footnote {\n text-align: center;\n font-size: 11px;\n color: var(--mj-text-muted);\n margin-top: 14px;\n}\n\n/* ===== TWO COLUMN ===== */\n.two-col {\n display: grid;\n grid-template-columns: 1fr 1fr;\n gap: 20px;\n}\n\n/* ===== SOURCE SELECTED ===== */\n.source-selected {\n background: color-mix(in srgb, var(--mj-brand-primary) 5%, var(--mj-bg-surface));\n}\n\n/* ===== QUALITY BANDS ===== */\n.quality-bands {\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n\n.quality-band-row {\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.quality-band-label {\n font-size: 11px;\n color: var(--mj-text-muted);\n width: 65px;\n text-align: right;\n flex-shrink: 0;\n}\n\n.quality-band-track {\n flex: 1;\n height: 18px;\n background: var(--mj-bg-surface-card);\n border-radius: 4px;\n overflow: hidden;\n}\n\n.quality-band-fill {\n height: 100%;\n border-radius: 4px;\n display: flex;\n align-items: center;\n padding-left: 8px;\n font-size: 10px;\n font-weight: 600;\n color: var(--mj-text-inverse);\n min-width: 30px;\n}\n\n.source-quality-note {\n margin-top: 14px;\n padding: 10px;\n background: var(--mj-bg-surface-card);\n border-radius: 6px;\n font-size: 11px;\n color: var(--mj-text-secondary);\n}\n\n/* ===== SOURCE HEALTH ===== */\n.source-health-grid {\n display: grid;\n gap: 12px;\n}\n\n.source-health-card {\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n padding: 14px;\n text-align: center;\n border-top: 3px solid;\n}\n\n.health-card-name {\n font-size: 11px;\n color: var(--mj-text-muted);\n margin-bottom: 4px;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.health-card-value {\n font-size: 22px;\n font-weight: 700;\n}\n\n.health-card-label {\n font-size: 10px;\n color: var(--mj-text-muted);\n}\n\n/* ===== PIPELINE ===== */\n.pipeline-throughput-bars {\n display: flex;\n align-items: flex-end;\n gap: 3px;\n height: 110px;\n}\n\n.pipeline-bar {\n flex: 1;\n border-radius: 2px 2px 0 0;\n min-height: 3px;\n}\n\n.pipeline-date-labels {\n display: flex;\n justify-content: space-between;\n margin-top: 6px;\n font-size: 10px;\n color: var(--mj-text-muted);\n}\n\n/* ===== PROCESSING STAGES ===== */\n.stage-bars {\n display: flex;\n flex-direction: column;\n gap: 10px;\n}\n\n.stage-row {\n display: flex;\n align-items: center;\n gap: 10px;\n}\n\n.stage-name {\n width: 80px;\n font-size: 12px;\n font-weight: 500;\n color: var(--mj-text-secondary);\n text-align: right;\n flex-shrink: 0;\n}\n\n.stage-track {\n flex: 1;\n height: 24px;\n background: var(--mj-bg-surface-card);\n border-radius: 4px;\n overflow: hidden;\n}\n\n.stage-fill {\n height: 100%;\n border-radius: 4px;\n display: flex;\n align-items: center;\n justify-content: flex-end;\n padding-right: 8px;\n font-size: 11px;\n font-weight: 600;\n color: var(--mj-text-inverse);\n}\n\n.stage-time {\n width: 50px;\n font-size: 12px;\n font-weight: 600;\n color: var(--mj-text-primary);\n text-align: right;\n flex-shrink: 0;\n}\n\n.stage-summary {\n display: flex;\n justify-content: space-between;\n margin-top: 14px;\n padding-top: 10px;\n border-top: 1px solid var(--mj-border-default);\n font-size: 12px;\n color: var(--mj-text-secondary);\n}\n\n.stage-warning {\n color: var(--mj-text-muted);\n}\n\n/* ===== SUCCESS RATE ===== */\n.success-rate-display {\n display: flex;\n justify-content: center;\n gap: 48px;\n padding: 20px;\n}\n\n.success-rate-stat {\n text-align: center;\n}\n\n.success-rate-value {\n font-size: 32px;\n font-weight: 700;\n line-height: 1.1;\n}\n\n.success-rate-label {\n font-size: 12px;\n color: var(--mj-text-muted);\n margin-top: 4px;\n}\n\n/* ===== PROGRESS ===== */\n.progress-track {\n width: 100px;\n height: 6px;\n background: var(--mj-bg-surface-sunken);\n border-radius: 3px;\n overflow: hidden;\n display: inline-block;\n vertical-align: middle;\n margin-right: 6px;\n}\n\n.progress-fill {\n height: 100%;\n border-radius: 3px;\n}\n\n.progress-text {\n font-size: 11px;\n font-weight: 600;\n}\n\n/* ===== ERROR LOG ===== */\n.error-log {\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n\n.error-entry {\n display: flex;\n gap: 12px;\n padding: 10px 14px;\n background: color-mix(in srgb, var(--mj-status-error) 4%, var(--mj-bg-surface));\n border-left: 3px solid var(--mj-status-error);\n border-radius: 0 6px 6px 0;\n font-size: 12px;\n}\n\n.error-time {\n color: var(--mj-text-muted);\n font-size: 11px;\n white-space: nowrap;\n flex-shrink: 0;\n width: 130px;\n}\n\n.error-source {\n color: var(--mj-brand-primary);\n font-weight: 600;\n flex-shrink: 0;\n width: 120px;\n}\n\n.error-msg {\n color: var(--mj-text-secondary);\n}\n\n/* ===== HISTOGRAM ===== */\n.histogram {\n display: flex;\n align-items: flex-end;\n gap: 6px;\n height: 130px;\n padding-bottom: 24px;\n}\n\n.hist-bar-col {\n flex: 1;\n display: flex;\n flex-direction: column;\n align-items: center;\n height: 100%;\n justify-content: flex-end;\n}\n\n.hist-bar {\n width: 100%;\n border-radius: 3px 3px 0 0;\n display: flex;\n align-items: flex-start;\n justify-content: center;\n padding-top: 3px;\n font-size: 10px;\n font-weight: 600;\n color: var(--mj-text-inverse);\n min-height: 2px;\n}\n\n.hist-label {\n font-size: 9px;\n color: var(--mj-text-muted);\n margin-top: 6px;\n text-align: center;\n}\n\n.confidence-stats {\n display: flex;\n justify-content: space-between;\n margin-top: 8px;\n padding: 10px;\n background: var(--mj-bg-surface-card);\n border-radius: 6px;\n font-size: 11px;\n color: var(--mj-text-muted);\n flex-wrap: wrap;\n gap: 8px;\n}\n\n/* ===== GROUPED BAR CHART ===== */\n.grouped-bar-chart {\n display: flex;\n align-items: flex-end;\n gap: 20px;\n height: 140px;\n justify-content: center;\n}\n\n.grouped-col {\n display: flex;\n flex-direction: column;\n align-items: center;\n height: 100%;\n justify-content: flex-end;\n}\n\n.grouped-bars {\n display: flex;\n gap: 3px;\n align-items: flex-end;\n height: 100%;\n}\n\n.g-bar {\n width: 18px;\n border-radius: 3px 3px 0 0;\n min-height: 2px;\n}\n\n.grouped-label {\n font-size: 10px;\n color: var(--mj-text-muted);\n margin-top: 6px;\n text-align: center;\n}\n\n/* ===== ACCURACY CHART ===== */\n.accuracy-chart {\n position: relative;\n display: flex;\n margin: 0 10px;\n}\n\n.accuracy-y-labels {\n display: flex;\n flex-direction: column;\n justify-content: space-between;\n font-size: 9px;\n color: var(--mj-text-muted);\n margin-right: 8px;\n height: 110px;\n}\n\n.accuracy-area {\n flex: 1;\n position: relative;\n height: 110px;\n border-left: 1px solid var(--mj-border-default);\n border-bottom: 1px solid var(--mj-border-default);\n}\n\n.accuracy-grid-line {\n position: absolute;\n left: 0;\n right: 0;\n border-top: 1px dashed var(--mj-border-subtle);\n}\n\n.accuracy-svg {\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n}\n\n.accuracy-x-labels {\n display: flex;\n justify-content: space-between;\n margin-top: 8px;\n padding: 0 40px 0 40px;\n font-size: 10px;\n color: var(--mj-text-muted);\n}\n\n.accuracy-trend-text {\n text-align: center;\n margin-top: 10px;\n font-size: 12px;\n color: var(--mj-status-success);\n}\n\n/* ===== MODEL COMPARISON ===== */\n.model-grid {\n display: grid;\n grid-template-columns: repeat(3, 1fr);\n gap: 12px;\n}\n\n.model-card {\n background: var(--mj-bg-surface-card);\n border-radius: 8px;\n padding: 14px;\n text-align: center;\n border: 1px solid var(--mj-border-default);\n}\n\n.model-name {\n font-size: 12px;\n font-weight: 600;\n color: var(--mj-text-primary);\n margin-bottom: 8px;\n}\n\n.model-score {\n font-size: 28px;\n font-weight: 700;\n line-height: 1;\n}\n\n.model-detail {\n font-size: 11px;\n color: var(--mj-text-muted);\n margin-top: 4px;\n}\n\n.model-recommendation {\n margin-top: 14px;\n padding: 12px 16px;\n background: var(--mj-bg-surface-card);\n border-radius: 8px;\n font-size: 12px;\n color: var(--mj-text-secondary);\n display: flex;\n align-items: flex-start;\n gap: 8px;\n}\n\n/* ===== RESPONSIVE ===== */\n@media (max-width: 1200px) {\n .cards-grid {\n grid-template-columns: repeat(2, 1fr);\n }\n\n .kpi-row {\n grid-template-columns: repeat(2, 1fr);\n }\n\n .model-grid {\n grid-template-columns: repeat(2, 1fr);\n }\n}\n\n@media (max-width: 768px) {\n .analytics-layout {\n flex-direction: column;\n }\n\n .analytics-sidebar {\n width: 100%;\n min-width: 100%;\n border-right: none;\n border-bottom: 1px solid var(--mj-border-default);\n }\n\n .sidebar-nav {\n flex-direction: row;\n overflow-x: auto;\n }\n\n .nav-item {\n border-left: none;\n border-bottom: 3px solid transparent;\n white-space: nowrap;\n }\n\n .nav-item.active {\n border-left-color: transparent;\n border-bottom-color: var(--mj-brand-primary);\n }\n\n .trending-section,\n .sidebar-divider {\n display: none;\n }\n\n .tab-section {\n padding: 16px;\n }\n\n .cards-grid {\n grid-template-columns: 1fr;\n }\n\n .kpi-row {\n grid-template-columns: 1fr;\n }\n\n .two-col {\n grid-template-columns: 1fr;\n }\n\n .cost-kpi-row {\n grid-template-columns: 1fr;\n }\n}\n\n/* ===== COST & USAGE TAB (D1) ===== */\n\n.tab-header-actions {\n display: flex;\n gap: 8px;\n margin-left: auto;\n}\n\n.cost-kpi-row {\n display: grid;\n grid-template-columns: repeat(3, 1fr);\n gap: 16px;\n margin-bottom: 24px;\n}\n\n.cost-kpi-card {\n display: flex;\n gap: 14px;\n align-items: center;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: 10px;\n padding: 18px 20px;\n transition: box-shadow 0.15s ease;\n}\n\n.cost-kpi-card:hover {\n box-shadow: 0 2px 8px color-mix(in srgb, var(--mj-text-primary) 8%, transparent);\n}\n\n.cost-kpi-icon {\n width: 44px;\n height: 44px;\n border-radius: 10px;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 18px;\n color: var(--mj-brand-primary);\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n flex-shrink: 0;\n}\n\n.cost-kpi-value {\n font-size: 22px;\n font-weight: 700;\n color: var(--mj-text-primary);\n line-height: 1.2;\n}\n\n.cost-kpi-label {\n font-size: 12px;\n font-weight: 600;\n color: var(--mj-text-secondary);\n text-transform: uppercase;\n letter-spacing: 0.03em;\n}\n\n.cost-kpi-sub {\n font-size: 11px;\n color: var(--mj-text-muted);\n margin-top: 2px;\n}\n\n.empty-state-hint {\n font-size: 12px;\n color: var(--mj-text-muted);\n margin-top: 4px;\n}\n\n/* ===== D5: PRINT STYLES ===== */\n\n@media print {\n .analytics-sidebar,\n .filter-bar,\n .tab-header-actions,\n .drill-down-close,\n .drill-export-btn,\n .drill-open-btn {\n display: none !important;\n }\n\n .analytics-layout {\n display: block;\n }\n\n .main-content {\n padding: 0;\n }\n\n .tab-section {\n padding: 0;\n }\n\n .widget-card,\n .cost-kpi-card,\n .kpi-card {\n break-inside: avoid;\n box-shadow: none;\n border: 1px solid var(--mj-border-default);\n }\n\n .data-table {\n font-size: 10px;\n }\n}\n\n/* \u2500\u2500 Co-Occurrence Section \u2500\u2500 */\n\n/* Constrain the co-occurrence sub-section so an empty state or large table\n does not blow out the page height and render the app unusable. */\n.co-occurrence-section {\n max-height: 500px;\n display: flex;\n flex-direction: column;\n overflow: hidden;\n}\n\n.co-occurrence-section .table-scroll {\n overflow-y: auto;\n max-height: 400px;\n flex: 1;\n min-height: 0;\n}\n\n.co-occurrence-section .empty-state {\n max-height: 200px;\n overflow: hidden;\n}\n\n.co-occurrence-actions {\n display: flex;\n align-items: center;\n gap: 12px;\n}\n\n.co-occurrence-staleness {\n display: flex;\n align-items: center;\n gap: 5px;\n font-size: 0.78rem;\n color: var(--mj-text-muted);\n}\n"] }]
|
|
4260
|
+
args: [{ standalone: false, selector: 'app-analytics-resource', template: "@if (IsLoading) {\n <div class=\"analytics-loading\">\n <mj-loading text=\"Loading analytics data...\" size=\"medium\"></mj-loading>\n </div>\n} @else {\n <div class=\"analytics-layout\">\n <!-- =============== LEFT SIDEBAR =============== -->\n <aside class=\"analytics-sidebar\">\n <div class=\"sidebar-header\">\n <h2><i class=\"fa-solid fa-chart-line\"></i> Analytics</h2>\n </div>\n\n <nav class=\"sidebar-nav\">\n @for (nav of NavItems; track nav.ID) {\n <button\n class=\"nav-item\"\n [class.active]=\"ActiveTab === nav.ID\"\n (click)=\"SelectTab(nav.ID)\"\n >\n <i [class]=\"nav.Icon\"></i> {{ nav.Label }}\n </button>\n }\n </nav>\n\n <div class=\"sidebar-divider\"></div>\n\n <!-- Trending Tags Cloud -->\n <div class=\"trending-section\">\n <h3><i class=\"fa-solid fa-arrow-trend-up\"></i> Trending Tags</h3>\n <div class=\"tag-cloud\">\n @for (tag of TrendingTags; track tag.Name) {\n <span [style.font-size.px]=\"tag.Size\" [style.font-weight]=\"tag.Weight\">{{ tag.Name }}</span>\n }\n @if (TrendingTags.length === 0) {\n <span class=\"no-trending\">No trending tags yet</span>\n }\n </div>\n </div>\n\n <!-- Pipeline Status -->\n <div class=\"pipeline-status\">\n <span class=\"status-dot\" [class.status-dot-error]=\"!PipelineStatusOk\"></span>\n {{ PipelineStatusText }}\n </div>\n </aside>\n\n <!-- =============== MAIN CONTENT =============== -->\n <div class=\"main-content\">\n\n <!-- ====================================================== -->\n <!-- TAB 1: OVERVIEW -->\n <!-- ====================================================== -->\n @if (ActiveTab === 'overview') {\n <section class=\"tab-section\">\n <div class=\"tab-section-header\">\n <i class=\"fa-solid fa-grip\"></i>\n <h1>Overview</h1>\n </div>\n\n <!-- Filter Bar -->\n <div class=\"filter-bar\">\n <div class=\"date-chips\">\n @for (range of DateRanges; track range.Label) {\n <button\n class=\"date-chip\"\n [class.active]=\"ActiveDateRange === range.Label\"\n (click)=\"SetDateRange(range.Label)\"\n >{{ range.Label }}</button>\n }\n </div>\n <select class=\"filter-dropdown\" [ngModel]=\"EntityFilter\" (ngModelChange)=\"SetEntityFilter($event)\">\n @for (opt of EntityFilterOptions; track opt) {\n <option [value]=\"opt\">{{ opt }}</option>\n }\n </select>\n </div>\n\n <!-- KPI Cards -->\n <div class=\"kpi-row\">\n @for (kpi of KPIs; track kpi.Label) {\n <div class=\"kpi-card\" (click)=\"OpenDrillDown(kpi.DrillDownKey)\">\n <div>\n <div class=\"kpi-label\">{{ kpi.Label }}</div>\n <div class=\"kpi-value\">{{ kpi.Value }}</div>\n <div class=\"kpi-delta\" [class.up]=\"kpi.DeltaDirection === 'up'\" [class.down]=\"kpi.DeltaDirection === 'down'\">\n @if (kpi.DeltaDirection === 'up') {\n <i class=\"fa-solid fa-arrow-up\" style=\"font-size:9px\"></i>\n } @else if (kpi.DeltaDirection === 'down') {\n <i class=\"fa-solid fa-arrow-down\" style=\"font-size:9px\"></i>\n }\n {{ kpi.Delta }}\n </div>\n </div>\n <div class=\"kpi-sparkline\">\n <svg width=\"64\" height=\"28\" viewBox=\"0 0 64 28\">\n <polyline [attr.points]=\"kpi.SparklinePoints\" fill=\"none\" [attr.stroke]=\"kpi.SparklineColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n </svg>\n </div>\n </div>\n }\n </div>\n\n <!-- KPI Drill-Down -->\n @if (DrillDownTarget && DrillDownTarget.startsWith('kpi-')) {\n <div class=\"drill-down-panel\">\n <div class=\"drill-down-header\">\n <span class=\"drill-down-title\"><i class=\"fa-solid fa-table\"></i> Detail View</span>\n <div class=\"drill-down-header-actions\">\n @if (DrillDownData.length > 0) {\n <button class=\"drill-export-btn\" (click)=\"ExportDrillDownCSV()\" title=\"Export CSV\">\n <i class=\"fa-solid fa-download\"></i> CSV\n </button>\n }\n <button class=\"drill-down-close\" aria-label=\"Close drill-down\" (click)=\"CloseDrillDown()\">\n <i class=\"fa-solid fa-times\"></i>\n </button>\n </div>\n </div>\n @if (IsDrillDownLoading) {\n <mj-loading text=\"Loading details...\" size=\"small\"></mj-loading>\n } @else if (DrillDownData.length > 0) {\n <div class=\"drill-down-table-wrap\">\n <table class=\"data-table\">\n <thead>\n <tr>\n @for (col of DrillDownColumns; track col) {\n <th>{{ col }}</th>\n }\n @if (DrillDownHasActions) {\n <th class=\"drill-action-col\"></th>\n }\n </tr>\n </thead>\n <tbody>\n @for (row of DrillDownData; track TrackByIndex($index)) {\n <tr>\n @for (col of DrillDownColumns; track col) {\n <td>{{ row[col] }}</td>\n }\n @if (DrillDownHasActions) {\n <td class=\"drill-action-col\">\n @if (row['_RecordID']) {\n <button class=\"drill-open-btn\" (click)=\"OpenDrillDownRecord(row)\" title=\"Open record\">\n <i class=\"fa-solid fa-arrow-up-right-from-square\"></i>\n </button>\n }\n </td>\n }\n </tr>\n }\n </tbody>\n </table>\n </div>\n } @else {\n <div class=\"drill-down-empty\">No data available</div>\n }\n </div>\n }\n\n <!-- Widget Cards Grid (2x3) -->\n <div class=\"cards-grid\">\n\n <!-- Card 1: Tag Growth -->\n <div class=\"widget-card\" (click)=\"OpenDrillDown('tagGrowth')\">\n <div class=\"widget-title\"><i class=\"fa-solid fa-chart-line\"></i> Tag Growth</div>\n <div class=\"bar-chart\">\n @for (bar of TagGrowthData; track bar.Label) {\n <div class=\"bar-col\">\n <div class=\"bar-value\">{{ bar.Count }}</div>\n <div class=\"bar\" [style.height.%]=\"bar.Percentage\" style=\"background:var(--mj-brand-primary)\" [style.opacity]=\"0.5 + (bar.Percentage / 200)\"></div>\n <div class=\"bar-label\">{{ bar.Label }}</div>\n </div>\n }\n </div>\n </div>\n\n <!-- Card 2: Content Coverage -->\n <div class=\"widget-card\" (click)=\"OpenDrillDown('contentCoverage')\">\n <div class=\"widget-title\"><i class=\"fa-solid fa-bullseye\"></i> Content Coverage</div>\n <div class=\"rings-grid\">\n @for (entity of CoverageData; track entity.Name) {\n <div class=\"ring-item\">\n <svg width=\"48\" height=\"48\" viewBox=\"0 0 48 48\">\n <circle cx=\"24\" cy=\"24\" r=\"20\" fill=\"none\" stroke=\"var(--mj-border-default)\" stroke-width=\"5\"/>\n <circle cx=\"24\" cy=\"24\" r=\"20\" fill=\"none\" [attr.stroke]=\"entity.Color\" stroke-width=\"5\"\n [attr.stroke-dasharray]=\"entity.StrokeDash\" stroke-dashoffset=\"31.4\" stroke-linecap=\"round\"\n transform=\"rotate(-90 24 24)\"/>\n <text x=\"24\" y=\"26\" text-anchor=\"middle\" font-size=\"11\" font-weight=\"700\" fill=\"var(--mj-text-primary)\">{{ entity.Percentage }}%</text>\n </svg>\n <div>\n <div class=\"ring-label\">{{ entity.Name }}</div>\n <div class=\"ring-stat\">{{ FormatNumber(entity.Tagged) }} / {{ FormatNumber(entity.Total) }}</div>\n </div>\n </div>\n }\n @if (CoverageData.length === 0) {\n <div class=\"widget-empty\">No content types found</div>\n }\n </div>\n </div>\n\n <!-- Card 3: Quality Score Gauge -->\n <div class=\"widget-card\" (click)=\"OpenDrillDown('qualityScore')\">\n <div class=\"widget-title\"><i class=\"fa-solid fa-gauge-high\"></i> Quality Score</div>\n <div class=\"gauge-container\">\n <svg width=\"180\" height=\"110\" viewBox=\"0 0 180 110\">\n <!-- Background arc -->\n <path d=\"M 20 95 A 70 70 0 0 1 160 95\" fill=\"none\" stroke=\"var(--mj-border-default)\" stroke-width=\"14\" stroke-linecap=\"round\"/>\n <!-- Zone arcs -->\n <path d=\"M 20 95 A 70 70 0 0 1 48.6 35.2\" fill=\"none\" stroke=\"var(--mj-status-error)\" stroke-width=\"14\" stroke-linecap=\"round\" opacity=\"0.3\"/>\n <path d=\"M 48.6 35.2 A 70 70 0 0 1 118 28\" fill=\"none\" stroke=\"var(--mj-status-warning)\" stroke-width=\"14\" opacity=\"0.3\"/>\n <path d=\"M 118 28 A 70 70 0 0 1 160 95\" fill=\"none\" stroke=\"var(--mj-status-success)\" stroke-width=\"14\" stroke-linecap=\"round\" opacity=\"0.3\"/>\n <!-- Score text -->\n <text x=\"90\" y=\"88\" text-anchor=\"middle\" font-size=\"26\" font-weight=\"800\" fill=\"var(--mj-text-primary)\">{{ QualityScore }}</text>\n <text x=\"90\" y=\"105\" text-anchor=\"middle\" font-size=\"10\" fill=\"var(--mj-text-muted)\">out of 100</text>\n <text x=\"28\" y=\"108\" font-size=\"8\" fill=\"var(--mj-text-muted)\">0</text>\n <text x=\"86\" y=\"18\" font-size=\"8\" fill=\"var(--mj-text-muted)\">50</text>\n <text x=\"155\" y=\"108\" font-size=\"8\" fill=\"var(--mj-text-muted)\">100</text>\n </svg>\n </div>\n <!-- Mini confidence histogram -->\n <div class=\"mini-histogram\">\n @for (bin of MiniConfidenceBins; track TrackByIndex($index)) {\n <div class=\"mini-hist-bar\" [style.height.px]=\"bin.Height\" [style.background]=\"bin.Color\" [title]=\"bin.Title\"></div>\n }\n </div>\n <div class=\"mini-hist-label\">Confidence Distribution</div>\n </div>\n\n <!-- Card 4: Source Performance -->\n <div class=\"widget-card\" (click)=\"OpenDrillDown('sourcePerformance')\">\n <div class=\"widget-title\"><i class=\"fa-solid fa-ranking-star\"></i> Source Performance</div>\n <div class=\"h-bar-list\">\n @for (source of SourcePerfData; track source.Name) {\n <div class=\"h-bar-row\">\n <div class=\"h-bar-name\">{{ source.Name }}</div>\n <div class=\"h-bar-track\">\n <div class=\"h-bar-fill\" [style.width.%]=\"source.Percentage\" [style.background]=\"source.Color\">{{ source.AvgTagsPerItem }}</div>\n </div>\n </div>\n }\n @if (SourcePerfData.length === 0) {\n <div class=\"widget-empty\">No source data</div>\n }\n </div>\n <div class=\"widget-footnote\">Average tags per item</div>\n </div>\n\n <!-- Card 5: Daily Throughput -->\n <div class=\"widget-card\" (click)=\"OpenDrillDown('dailyThroughput')\">\n <div class=\"widget-title\"><i class=\"fa-solid fa-bolt\"></i> Daily Throughput</div>\n <div class=\"throughput-bars\">\n @for (day of ThroughputData; track day.Label) {\n <div class=\"tp-bar\"\n [style.height.%]=\"day.Percentage\"\n [style.background]=\"day.IsError ? 'var(--mj-status-error)' : 'var(--mj-status-success)'\"\n >\n <span class=\"tp-bar-label\">{{ day.Label }}</span>\n </div>\n }\n </div>\n <div class=\"legend\">\n <div class=\"legend-item\"><div class=\"legend-dot\" style=\"background:var(--mj-status-success)\"></div> Success</div>\n <div class=\"legend-item\"><div class=\"legend-dot\" style=\"background:var(--mj-status-error)\"></div> Failures</div>\n </div>\n </div>\n\n <!-- Card 6: Taxonomy Health -->\n <div class=\"widget-card\" (click)=\"OpenDrillDown('taxonomyHealth')\">\n <div class=\"widget-title\"><i class=\"fa-solid fa-sitemap\"></i> Taxonomy Health</div>\n <div class=\"taxonomy-ring-container\">\n <svg width=\"140\" height=\"140\" viewBox=\"0 0 140 140\">\n <circle cx=\"70\" cy=\"70\" r=\"54\" fill=\"none\" stroke=\"var(--mj-border-default)\" stroke-width=\"18\"/>\n @for (seg of TaxonomyRingSegments; track TrackByIndex($index)) {\n <circle cx=\"70\" cy=\"70\" r=\"54\" fill=\"none\"\n [attr.stroke]=\"seg.Color\" stroke-width=\"18\"\n [attr.stroke-dasharray]=\"seg.StrokeDash\"\n [attr.stroke-dashoffset]=\"seg.StrokeOffset\"\n transform=\"rotate(-90 70 70)\"/>\n }\n <text x=\"70\" y=\"66\" text-anchor=\"middle\" font-size=\"22\" font-weight=\"800\" fill=\"var(--mj-text-primary)\">{{ TaxonomyTotal }}</text>\n <text x=\"70\" y=\"82\" text-anchor=\"middle\" font-size=\"10\" fill=\"var(--mj-text-muted)\">total tags</text>\n </svg>\n </div>\n <div class=\"taxonomy-stats\">\n @for (stat of TaxonomyStats; track stat.Label) {\n <div class=\"tax-stat\" [style.background]=\"stat.BgColor\" [style.color]=\"stat.Color\">\n {{ stat.Count }}<small>{{ stat.Label }}</small>\n </div>\n }\n </div>\n </div>\n\n </div><!-- /cards-grid -->\n\n <!-- Widget Drill-Down -->\n @if (DrillDownTarget && !DrillDownTarget.startsWith('kpi-')) {\n <div class=\"drill-down-panel\">\n <div class=\"drill-down-header\">\n <span class=\"drill-down-title\"><i class=\"fa-solid fa-table\"></i> {{ DrillDownTarget }} Detail</span>\n <button class=\"drill-down-close\" aria-label=\"Close drill-down\" (click)=\"CloseDrillDown()\">\n <i class=\"fa-solid fa-times\"></i>\n </button>\n </div>\n @if (IsDrillDownLoading) {\n <mj-loading text=\"Loading details...\" size=\"small\"></mj-loading>\n } @else if (DrillDownData.length > 0) {\n <div class=\"drill-down-table-wrap\">\n <table class=\"data-table\">\n <thead>\n <tr>\n @for (col of DrillDownColumns; track col) {\n <th>{{ col }}</th>\n }\n @if (DrillDownHasActions) {\n <th class=\"drill-action-col\"></th>\n }\n </tr>\n </thead>\n <tbody>\n @for (row of DrillDownData; track TrackByIndex($index)) {\n <tr>\n @for (col of DrillDownColumns; track col) {\n <td>{{ row[col] }}</td>\n }\n @if (DrillDownHasActions) {\n <td class=\"drill-action-col\">\n @if (row['_RecordID']) {\n <button class=\"drill-open-btn\" (click)=\"OpenDrillDownRecord(row)\" title=\"Open record\">\n <i class=\"fa-solid fa-arrow-up-right-from-square\"></i>\n </button>\n }\n </td>\n }\n </tr>\n }\n </tbody>\n </table>\n </div>\n } @else {\n <div class=\"drill-down-empty\">No data available</div>\n }\n </div>\n }\n </section>\n }\n\n <!-- ====================================================== -->\n <!-- TAB 2: TAGS -->\n <!-- ====================================================== -->\n @if (ActiveTab === 'tags') {\n <section class=\"tab-section\">\n <div class=\"tab-section-header\">\n <i class=\"fa-solid fa-tags\"></i>\n <h1>Tags</h1>\n </div>\n\n <!-- Top 20 Tags Table -->\n <div class=\"sub-section\" style=\"margin-top:0\">\n <div class=\"sub-section-header\">\n <h3><i class=\"fa-solid fa-trophy\"></i> Top 20 Tags</h3>\n @if (TopTags.length > 0) {\n <button class=\"drill-export-btn\" (click)=\"ExportTabDataCSV('top-tags')\" title=\"Export CSV\">\n <i class=\"fa-solid fa-download\"></i> CSV\n </button>\n }\n </div>\n @if (TopTags.length > 0) {\n <div class=\"table-scroll\">\n <table class=\"data-table\">\n <thead>\n <tr>\n <th>#</th>\n <th>Tag Name</th>\n <th class=\"num\">Usage Count</th>\n <th>Avg Weight</th>\n <th>Trend</th>\n <th>Top Entity</th>\n <th>First Seen</th>\n </tr>\n </thead>\n <tbody>\n @for (tag of TopTags; track tag.Name) {\n <tr style=\"cursor:pointer\" (click)=\"OpenDrillDown('tag-row:' + tag.Name)\">\n <td>{{ tag.Rank }}</td>\n <td><strong>{{ tag.Name }}</strong></td>\n <td class=\"num\">{{ FormatNumber(tag.UsageCount) }}</td>\n <td>\n <span class=\"weight-bar\" [style.width.px]=\"tag.WeightBarWidth\" [style.background]=\"tag.WeightBarColor\"></span>\n {{ tag.AvgWeight }}\n </td>\n <td>\n <svg width=\"48\" height=\"16\">\n <polyline [attr.points]=\"tag.TrendPoints\" fill=\"none\" [attr.stroke]=\"tag.TrendColor\" stroke-width=\"1.5\" stroke-linecap=\"round\"/>\n </svg>\n </td>\n <td>{{ tag.TopEntity }}</td>\n <td class=\"muted\">{{ tag.FirstSeen }}</td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n } @else {\n <div class=\"empty-state\">\n <i class=\"fa-solid fa-tags\"></i>\n <p>No tag data available yet. Process content to generate tags.</p>\n </div>\n }\n </div>\n\n <!-- D10: Tags tab drill-down -->\n @if (DrillDownTarget && DrillDownTarget.startsWith('tag-row:')) {\n <div class=\"drill-down-panel\">\n <div class=\"drill-down-header\">\n <span class=\"drill-down-title\"><i class=\"fa-solid fa-table\"></i> Content Items Tagged \"{{ DrillDownTarget.replace('tag-row:', '') }}\"</span>\n <div class=\"drill-down-header-actions\">\n @if (DrillDownData.length > 0) {\n <button class=\"drill-export-btn\" (click)=\"ExportDrillDownCSV()\" title=\"Export CSV\">\n <i class=\"fa-solid fa-download\"></i> CSV\n </button>\n }\n <button class=\"drill-down-close\" aria-label=\"Close drill-down\" (click)=\"CloseDrillDown()\"><i class=\"fa-solid fa-times\"></i></button>\n </div>\n </div>\n @if (IsDrillDownLoading) {\n <mj-loading text=\"Loading details...\" size=\"small\"></mj-loading>\n } @else if (DrillDownData.length > 0) {\n <div class=\"drill-down-table-wrap\">\n <table class=\"data-table\">\n <thead><tr>\n @for (col of DrillDownColumns; track col) { <th>{{ col }}</th> }\n @if (DrillDownHasActions) { <th class=\"drill-action-col\"></th> }\n </tr></thead>\n <tbody>\n @for (row of DrillDownData; track TrackByIndex($index)) {\n <tr>\n @for (col of DrillDownColumns; track col) { <td>{{ row[col] }}</td> }\n @if (DrillDownHasActions) {\n <td class=\"drill-action-col\">\n @if (row['_RecordID']) {\n <button class=\"drill-open-btn\" aria-label=\"Open record\" (click)=\"OpenDrillDownRecord(row)\" title=\"Open record\"><i class=\"fa-solid fa-arrow-up-right-from-square\"></i></button>\n }\n </td>\n }\n </tr>\n }\n </tbody>\n </table>\n </div>\n } @else {\n <div class=\"drill-down-empty\">No items found for this tag</div>\n }\n </div>\n }\n\n <!-- Tag Distribution by Entity -->\n @if (EntityDistribution.length > 0) {\n <div class=\"sub-section\">\n <h3><i class=\"fa-solid fa-chart-bar\"></i> Tag Distribution by Entity</h3>\n <div class=\"stacked-bar-chart\">\n @for (row of EntityDistribution; track row.EntityName) {\n <div class=\"stacked-row\">\n <div class=\"stacked-label\">{{ row.EntityName }}</div>\n <div class=\"stacked-track\">\n @for (seg of row.Segments; track TrackByIndex($index)) {\n <div class=\"stacked-seg\" [style.width.%]=\"seg.Percentage\" [style.background]=\"seg.Color\" [title]=\"seg.Label + ': ' + seg.Percentage + '%'\">{{ seg.Label }}</div>\n }\n </div>\n </div>\n }\n </div>\n <div class=\"legend\" style=\"margin-top:12px\">\n @for (item of DistributionLegend; track item.Label) {\n <div class=\"legend-item\"><div class=\"legend-dot\" [style.background]=\"item.Color\"></div> {{ item.Label }}</div>\n }\n </div>\n </div>\n }\n\n <!-- Tag Depth Distribution -->\n @if (TagDepthBars.length > 0) {\n <div class=\"sub-section\">\n <h3><i class=\"fa-solid fa-layer-group\"></i> Tag Depth Distribution</h3>\n <div class=\"v-bar-chart\">\n @for (bar of TagDepthBars; track bar.Label) {\n <div class=\"v-bar-col\">\n <div class=\"v-bar\" [style.height.%]=\"bar.Percentage\" style=\"background:var(--mj-brand-primary)\" [style.min-height.px]=\"bar.Count > 0 ? 24 : 0\">{{ bar.Count }}</div>\n <div class=\"v-bar-label\">{{ bar.Label }}</div>\n </div>\n }\n </div>\n <div class=\"chart-footnote\">\n Taxonomy hierarchy depth -- Most tags at depth 2-3 indicating a healthy mid-level structure\n </div>\n </div>\n }\n\n <!-- Frequently Paired Tags (Co-Occurrence) -->\n <div class=\"sub-section co-occurrence-section\">\n <div class=\"sub-section-header\">\n <h3><i class=\"fa-solid fa-link\"></i> Frequently Paired Tags</h3>\n <div class=\"co-occurrence-actions\">\n @if (CoOccurrenceLastComputed) {\n <span class=\"co-occurrence-staleness\" title=\"Last computed timestamp\">\n <i class=\"fa-regular fa-clock\"></i>\n Last computed: {{ CoOccurrenceLastComputed }}\n </span>\n }\n <button class=\"drill-export-btn\" (click)=\"RecomputeCoOccurrence()\" [disabled]=\"IsRecomputingCoOccurrence\" title=\"Recompute co-occurrence data\">\n @if (IsRecomputingCoOccurrence) {\n <i class=\"fa-solid fa-spinner fa-spin\"></i> Computing...\n } @else {\n <i class=\"fa-solid fa-arrows-rotate\"></i> Recompute\n }\n </button>\n </div>\n </div>\n @if (CoOccurrencePairs.length > 0) {\n <div class=\"table-scroll\">\n <table class=\"data-table\">\n <thead>\n <tr>\n <th>#</th>\n <th>Tag A</th>\n <th>Tag B</th>\n <th class=\"num\">Co-Occurrences</th>\n <th>Frequency</th>\n </tr>\n </thead>\n <tbody>\n @for (pair of CoOccurrencePairs; track pair.TagAName + pair.TagBName; let i = $index) {\n <tr>\n <td class=\"muted\">{{ i + 1 }}</td>\n <td><strong>{{ pair.TagAName }}</strong></td>\n <td><strong>{{ pair.TagBName }}</strong></td>\n <td class=\"num\">{{ FormatNumber(pair.Count) }}</td>\n <td>\n <span class=\"weight-bar\" [style.width.px]=\"pair.BarWidth\" style=\"background: var(--mj-status-info)\"></span>\n </td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n } @else {\n <div class=\"empty-state\">\n <i class=\"fa-solid fa-link\"></i>\n <p>No co-occurrence data available yet. Process content and recompute to see which tags frequently appear together.</p>\n </div>\n }\n </div>\n </section>\n }\n\n <!-- ====================================================== -->\n <!-- TAB 3: SOURCES -->\n <!-- ====================================================== -->\n @if (ActiveTab === 'sources') {\n <section class=\"tab-section\">\n <div class=\"tab-section-header\">\n <i class=\"fa-solid fa-database\"></i>\n <h1>Sources</h1>\n </div>\n\n <!-- Source Comparison Table -->\n <div class=\"sub-section\" style=\"margin-top:0\">\n <h3><i class=\"fa-solid fa-table\"></i> Source Comparison</h3>\n @if (SourceComparison.length > 0) {\n <div class=\"table-scroll\">\n <table class=\"data-table\">\n <thead>\n <tr>\n <th>Source Name</th>\n <th>Type</th>\n <th class=\"num\">Items</th>\n <th class=\"num\">Tags Generated</th>\n <th class=\"num\">Avg Tags/Item</th>\n <th class=\"num\">Avg Weight</th>\n <th>Last Run</th>\n <th>Status</th>\n </tr>\n </thead>\n <tbody>\n @for (source of SourceComparison; track source.Name) {\n <tr (click)=\"SelectSource(source.Name); OpenDrillDown('source-row:' + source.Name)\" style=\"cursor:pointer\" [class.source-selected]=\"SelectedSourceName === source.Name\">\n <td><strong>{{ source.Name }}</strong></td>\n <td><i [class]=\"source.TypeIcon\" [style.color]=\"source.TypeColor\" style=\"margin-right:4px\"></i> {{ source.Type }}</td>\n <td class=\"num\">{{ FormatNumber(source.Items) }}</td>\n <td class=\"num\">{{ FormatNumber(source.TagsGenerated) }}</td>\n <td class=\"num\">{{ source.AvgTagsPerItem }}</td>\n <td class=\"num\">{{ source.AvgWeight }}</td>\n <td class=\"muted\">{{ source.LastRun }}</td>\n <td><span class=\"badge\" [class]=\"source.StatusClass\"><i class=\"fa-solid fa-circle\" style=\"font-size:6px;margin-right:3px\"></i> {{ source.Status }}</span></td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n } @else {\n <div class=\"empty-state\">\n <i class=\"fa-solid fa-database\"></i>\n <p>No content sources configured yet.</p>\n </div>\n }\n </div>\n\n <!-- D10: Sources tab drill-down (recent runs) -->\n @if (DrillDownTarget && DrillDownTarget.startsWith('source-row:')) {\n <div class=\"drill-down-panel\">\n <div class=\"drill-down-header\">\n <span class=\"drill-down-title\"><i class=\"fa-solid fa-table\"></i> Recent Runs: {{ DrillDownTarget.replace('source-row:', '') }}</span>\n <div class=\"drill-down-header-actions\">\n @if (DrillDownData.length > 0) {\n <button class=\"drill-export-btn\" (click)=\"ExportDrillDownCSV()\" title=\"Export CSV\"><i class=\"fa-solid fa-download\"></i> CSV</button>\n }\n <button class=\"drill-down-close\" aria-label=\"Close drill-down\" (click)=\"CloseDrillDown()\"><i class=\"fa-solid fa-times\"></i></button>\n </div>\n </div>\n @if (IsDrillDownLoading) {\n <mj-loading text=\"Loading details...\" size=\"small\"></mj-loading>\n } @else if (DrillDownData.length > 0) {\n <div class=\"drill-down-table-wrap\">\n <table class=\"data-table\">\n <thead><tr>\n @for (col of DrillDownColumns; track col) { <th>{{ col }}</th> }\n @if (DrillDownHasActions) { <th class=\"drill-action-col\"></th> }\n </tr></thead>\n <tbody>\n @for (row of DrillDownData; track TrackByIndex($index)) {\n <tr>\n @for (col of DrillDownColumns; track col) { <td>{{ row[col] }}</td> }\n @if (DrillDownHasActions) {\n <td class=\"drill-action-col\">\n @if (row['_RecordID']) {\n <button class=\"drill-open-btn\" aria-label=\"Open record\" (click)=\"OpenDrillDownRecord(row)\" title=\"Open record\"><i class=\"fa-solid fa-arrow-up-right-from-square\"></i></button>\n }\n </td>\n }\n </tr>\n }\n </tbody>\n </table>\n </div>\n } @else {\n <div class=\"drill-down-empty\">No run data for this source</div>\n }\n </div>\n }\n\n <!-- Per-Source Detail -->\n @if (SelectedSourceName && SourceComparison.length > 0) {\n <div class=\"sub-section\">\n <h3><i class=\"fa-solid fa-chart-area\"></i> Source Detail: {{ SelectedSourceName }}</h3>\n <div class=\"two-col\">\n <!-- Items Processed Over Time -->\n <div class=\"widget-card\">\n <div class=\"widget-title\"><i class=\"fa-solid fa-chart-bar\"></i> Items Processed (Last 8 Weeks)</div>\n <div class=\"bar-chart\" style=\"height:100px\">\n @for (bar of SourceWeeklyBars; track bar.Label) {\n <div class=\"bar-col\">\n <div class=\"bar-value\" style=\"font-size:9px\">{{ bar.Value }}</div>\n <div class=\"bar\" [style.height.%]=\"bar.Percentage\" style=\"background:var(--mj-brand-primary)\" [style.opacity]=\"0.5 + (bar.Percentage / 200)\"></div>\n <div class=\"bar-label\">{{ bar.Label }}</div>\n </div>\n }\n </div>\n </div>\n\n <!-- Tag Quality Distribution -->\n <div class=\"widget-card\">\n <div class=\"widget-title\"><i class=\"fa-solid fa-star-half-stroke\"></i> Tag Quality Distribution</div>\n <div class=\"quality-bands\">\n @for (band of SourceQualityBands; track band.Label) {\n <div class=\"quality-band-row\">\n <span class=\"quality-band-label\">{{ band.Label }}</span>\n <div class=\"quality-band-track\">\n <div class=\"quality-band-fill\" [style.width.%]=\"band.Percentage\" [style.background]=\"band.Color\">{{ band.Percentage }}%</div>\n </div>\n </div>\n }\n </div>\n <div class=\"source-quality-note\">\n <i class=\"fa-solid fa-circle-info\" style=\"color:var(--mj-status-info);margin-right:4px\"></i>\n {{ SourceQualityNote }}\n </div>\n </div>\n </div>\n </div>\n }\n\n <!-- Source Health -->\n @if (SourceHealthCards.length > 0) {\n <div class=\"sub-section\">\n <h3><i class=\"fa-solid fa-heart-pulse\"></i> Source Health Summary</h3>\n <div class=\"source-health-grid\" [style.grid-template-columns]=\"'repeat(' + SourceHealthCards.length + ', 1fr)'\">\n @for (card of SourceHealthCards; track card.Name) {\n <div class=\"source-health-card\" [style.border-top-color]=\"card.Color\">\n <div class=\"health-card-name\">{{ card.Name }}</div>\n <div class=\"health-card-value\" [style.color]=\"card.Color\">{{ card.Uptime }}%</div>\n <div class=\"health-card-label\">uptime</div>\n </div>\n }\n </div>\n </div>\n }\n </section>\n }\n\n <!-- ====================================================== -->\n <!-- TAB 4: PIPELINE -->\n <!-- ====================================================== -->\n @if (ActiveTab === 'pipeline') {\n <section class=\"tab-section\">\n <div class=\"tab-section-header\">\n <i class=\"fa-solid fa-gears\"></i>\n <h1>Pipeline</h1>\n </div>\n\n <!-- Pipeline Throughput Chart -->\n <div class=\"sub-section\" style=\"margin-top:0\">\n <h3><i class=\"fa-solid fa-chart-bar\"></i> Pipeline Throughput (Last 30 Days)</h3>\n <div class=\"widget-card\">\n <div class=\"pipeline-throughput-bars\">\n @for (bar of PipelineThroughputBars; track TrackByIndex($index)) {\n <div class=\"pipeline-bar\"\n [style.height.%]=\"bar.Percentage\"\n [style.background]=\"bar.IsError ? 'var(--mj-status-error)' : 'var(--mj-brand-primary)'\"\n [style.opacity]=\"bar.IsError ? 0.7 : (0.6 + bar.Percentage / 300)\"\n style=\"cursor:pointer\"\n (click)=\"OpenDrillDown('pipeline-throughput:' + $index)\"\n ></div>\n }\n </div>\n <div class=\"pipeline-date-labels\">\n @for (label of PipelineDateLabels; track label) {\n <span>{{ label }}</span>\n }\n </div>\n </div>\n </div>\n\n <!-- D10: Pipeline throughput drill-down -->\n @if (DrillDownTarget && DrillDownTarget.startsWith('pipeline-throughput:')) {\n <div class=\"drill-down-panel\">\n <div class=\"drill-down-header\">\n <span class=\"drill-down-title\"><i class=\"fa-solid fa-table\"></i> Runs for Selected Day</span>\n <div class=\"drill-down-header-actions\">\n @if (DrillDownData.length > 0) {\n <button class=\"drill-export-btn\" (click)=\"ExportDrillDownCSV()\" title=\"Export CSV\"><i class=\"fa-solid fa-download\"></i> CSV</button>\n }\n <button class=\"drill-down-close\" aria-label=\"Close drill-down\" (click)=\"CloseDrillDown()\"><i class=\"fa-solid fa-times\"></i></button>\n </div>\n </div>\n @if (IsDrillDownLoading) {\n <mj-loading text=\"Loading details...\" size=\"small\"></mj-loading>\n } @else if (DrillDownData.length > 0) {\n <div class=\"drill-down-table-wrap\">\n <table class=\"data-table\">\n <thead><tr>\n @for (col of DrillDownColumns; track col) { <th>{{ col }}</th> }\n @if (DrillDownHasActions) { <th class=\"drill-action-col\"></th> }\n </tr></thead>\n <tbody>\n @for (row of DrillDownData; track TrackByIndex($index)) {\n <tr>\n @for (col of DrillDownColumns; track col) { <td>{{ row[col] }}</td> }\n @if (DrillDownHasActions) {\n <td class=\"drill-action-col\">\n @if (row['_RecordID']) {\n <button class=\"drill-open-btn\" aria-label=\"Open record\" (click)=\"OpenDrillDownRecord(row)\" title=\"Open record\"><i class=\"fa-solid fa-arrow-up-right-from-square\"></i></button>\n }\n </td>\n }\n </tr>\n }\n </tbody>\n </table>\n </div>\n } @else {\n <div class=\"drill-down-empty\">No runs for this day</div>\n }\n </div>\n }\n\n <!-- Processing Time Breakdown -->\n <div class=\"sub-section\">\n <h3><i class=\"fa-solid fa-stopwatch\"></i> Processing Time Breakdown (Avg per Item)</h3>\n <div class=\"widget-card\">\n <div class=\"stage-bars\">\n @for (stage of ProcessingStages; track stage.Name) {\n <div class=\"stage-row\">\n <div class=\"stage-name\">{{ stage.Name }}</div>\n <div class=\"stage-track\">\n <div class=\"stage-fill\" [style.width.%]=\"stage.Percentage\" [style.background]=\"stage.Color\">\n @if (stage.Percentage > 15) {\n {{ stage.Time }}s\n }\n </div>\n </div>\n <div class=\"stage-time\">{{ stage.Time }}s</div>\n </div>\n }\n </div>\n <div class=\"stage-summary\">\n <span><strong>Total avg:</strong> {{ TotalAvgProcessingTime }}s per item</span>\n @if (BottleneckStage) {\n <span class=\"stage-warning\">\n <i class=\"fa-solid fa-triangle-exclamation\" style=\"color:var(--mj-status-warning);margin-right:4px\"></i>\n {{ BottleneckStage }} stage is the bottleneck ({{ BottleneckPercent }}% of total time)\n </span>\n }\n </div>\n </div>\n </div>\n\n <!-- Success Rate -->\n <div class=\"sub-section\">\n <h3><i class=\"fa-solid fa-chart-line\"></i> Success Rate Overview</h3>\n <div class=\"widget-card\">\n <div class=\"success-rate-display\">\n <div class=\"success-rate-stat\">\n <div class=\"success-rate-value\" style=\"color:var(--mj-status-success)\">\n {{ rawProcessRuns.length > 0 ? (100 - (ErrorLog.length / rawProcessRuns.length * 100)).toFixed(1) : '100' }}%\n </div>\n <div class=\"success-rate-label\">Success Rate</div>\n </div>\n <div class=\"success-rate-stat\">\n <div class=\"success-rate-value\" style=\"color:var(--mj-status-error)\">\n {{ rawProcessRuns.length > 0 ? (ErrorLog.length / rawProcessRuns.length * 100).toFixed(1) : '0' }}%\n </div>\n <div class=\"success-rate-label\">Failure Rate</div>\n </div>\n <div class=\"success-rate-stat\">\n <div class=\"success-rate-value\" style=\"color:var(--mj-text-primary)\">\n {{ FormatNumber(rawProcessRuns.length) }}\n </div>\n <div class=\"success-rate-label\">Total Runs</div>\n </div>\n </div>\n </div>\n </div>\n\n <!-- Active Runs Table -->\n @if (ActiveRuns.length > 0) {\n <div class=\"sub-section\">\n <h3><i class=\"fa-solid fa-spinner\"></i> Active Runs</h3>\n <div class=\"table-scroll\">\n <table class=\"data-table\">\n <thead>\n <tr>\n <th>Run ID</th>\n <th>Source</th>\n <th>Started</th>\n <th>Progress</th>\n <th>Stage</th>\n <th class=\"num\">Items</th>\n </tr>\n </thead>\n <tbody>\n @for (run of ActiveRuns; track run.RunID) {\n <tr>\n <td class=\"monospace-cell\">{{ run.RunID }}</td>\n <td>{{ run.Source }}</td>\n <td class=\"muted\">{{ run.Started }}</td>\n <td>\n <div class=\"progress-track\"><div class=\"progress-fill\" [style.width.%]=\"run.Progress\" style=\"background:var(--mj-brand-primary)\"></div></div>\n <span class=\"progress-text\">{{ run.Progress }}%</span>\n </td>\n <td><span class=\"badge\" [class]=\"run.StageClass\">{{ run.Stage }}</span></td>\n <td class=\"num\">{{ FormatNumber(run.Items) }}</td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n </div>\n }\n\n <!-- Error Log -->\n @if (ErrorLog.length > 0) {\n <div class=\"sub-section\">\n <h3><i class=\"fa-solid fa-triangle-exclamation\"></i> Recent Errors</h3>\n <div class=\"error-log\">\n @for (entry of ErrorLog; track TrackByIndex($index)) {\n <div class=\"error-entry\">\n <div class=\"error-time\">{{ entry.Time }}</div>\n <div class=\"error-source\">{{ entry.Source }}</div>\n <div class=\"error-msg\">{{ entry.Message }}</div>\n </div>\n }\n </div>\n </div>\n }\n </section>\n }\n\n <!-- ====================================================== -->\n <!-- TAB 5: QUALITY -->\n <!-- ====================================================== -->\n @if (ActiveTab === 'quality') {\n <section class=\"tab-section\">\n <div class=\"tab-section-header\">\n <i class=\"fa-solid fa-circle-check\"></i>\n <h1>Quality</h1>\n </div>\n\n <!-- Confidence Distribution Histogram -->\n <div class=\"sub-section\" style=\"margin-top:0\">\n <h3><i class=\"fa-solid fa-chart-column\"></i> Confidence Distribution</h3>\n <div class=\"widget-card\">\n <div class=\"histogram\">\n @for (bin of ConfidenceHistogram; track bin.Label) {\n <div class=\"hist-bar-col\" style=\"cursor:pointer\" (click)=\"OpenDrillDown('quality-bin:' + bin.Label)\">\n <div class=\"hist-bar\" [style.height.%]=\"bin.Percentage\" [style.background]=\"bin.Color\">\n @if (bin.Count > 0) { {{ bin.Count }} }\n </div>\n <div class=\"hist-label\">{{ bin.Label }}</div>\n </div>\n }\n </div>\n <div class=\"confidence-stats\">\n @for (stat of ConfidenceStats; track stat.Label) {\n <span><strong>{{ stat.Label }}:</strong> {{ stat.Value }}</span>\n }\n </div>\n </div>\n </div>\n\n <!-- D10: Quality bin drill-down -->\n @if (DrillDownTarget && DrillDownTarget.startsWith('quality-bin:')) {\n <div class=\"drill-down-panel\">\n <div class=\"drill-down-header\">\n <span class=\"drill-down-title\"><i class=\"fa-solid fa-table\"></i> Items in Confidence Range {{ DrillDownTarget.replace('quality-bin:', '') }}</span>\n <div class=\"drill-down-header-actions\">\n @if (DrillDownData.length > 0) {\n <button class=\"drill-export-btn\" (click)=\"ExportDrillDownCSV()\" title=\"Export CSV\"><i class=\"fa-solid fa-download\"></i> CSV</button>\n }\n <button class=\"drill-down-close\" aria-label=\"Close drill-down\" (click)=\"CloseDrillDown()\"><i class=\"fa-solid fa-times\"></i></button>\n </div>\n </div>\n @if (IsDrillDownLoading) {\n <mj-loading text=\"Loading details...\" size=\"small\"></mj-loading>\n } @else if (DrillDownData.length > 0) {\n <div class=\"drill-down-table-wrap\">\n <table class=\"data-table\">\n <thead><tr>\n @for (col of DrillDownColumns; track col) { <th>{{ col }}</th> }\n @if (DrillDownHasActions) { <th class=\"drill-action-col\"></th> }\n </tr></thead>\n <tbody>\n @for (row of DrillDownData; track TrackByIndex($index)) {\n <tr>\n @for (col of DrillDownColumns; track col) { <td>{{ row[col] }}</td> }\n @if (DrillDownHasActions) {\n <td class=\"drill-action-col\">\n @if (row['_RecordID']) {\n <button class=\"drill-open-btn\" aria-label=\"Open record\" (click)=\"OpenDrillDownRecord(row)\" title=\"Open record\"><i class=\"fa-solid fa-arrow-up-right-from-square\"></i></button>\n }\n </td>\n }\n </tr>\n }\n </tbody>\n </table>\n </div>\n } @else {\n <div class=\"drill-down-empty\">No items in this confidence range</div>\n }\n </div>\n }\n\n <!-- Weight Distribution by Entity -->\n @if (WeightByEntity.length > 0) {\n <div class=\"sub-section\">\n <h3><i class=\"fa-solid fa-weight-scale\"></i> Weight Distribution by Entity</h3>\n <div class=\"widget-card\">\n <div class=\"grouped-bar-chart\">\n @for (entity of WeightByEntity; track entity.Name) {\n <div class=\"grouped-col\">\n <div class=\"grouped-bars\">\n <div class=\"g-bar\" [style.height.%]=\"entity.High\" style=\"background:var(--mj-brand-primary)\" [title]=\"'High: ' + entity.High + '%'\"></div>\n <div class=\"g-bar\" [style.height.%]=\"entity.Med\" style=\"background:var(--mj-status-info)\" [title]=\"'Med: ' + entity.Med + '%'\"></div>\n <div class=\"g-bar\" [style.height.%]=\"entity.Low\" style=\"background:var(--mj-text-muted)\" [title]=\"'Low: ' + entity.Low + '%'\"></div>\n </div>\n <div class=\"grouped-label\">{{ entity.Name }}</div>\n </div>\n }\n </div>\n <div class=\"legend\" style=\"justify-content:center;margin-top:14px\">\n @for (item of WeightLegend; track item.Label) {\n <div class=\"legend-item\"><div class=\"legend-dot\" [style.background]=\"item.Color\"></div> {{ item.Label }}</div>\n }\n </div>\n </div>\n </div>\n }\n\n <!-- Tag Accuracy Over Time -->\n @if (AccuracyLinePoints) {\n <div class=\"sub-section\">\n <h3><i class=\"fa-solid fa-chart-line\"></i> Tag Accuracy Over Time (Weekly Avg Confidence)</h3>\n <div class=\"widget-card\">\n <div class=\"accuracy-chart\">\n <div class=\"accuracy-y-labels\">\n <div>1.0</div>\n <div>0.75</div>\n <div>0.50</div>\n <div>0.25</div>\n </div>\n <div class=\"accuracy-area\">\n <div class=\"accuracy-grid-line\" style=\"top:33%\"></div>\n <div class=\"accuracy-grid-line\" style=\"top:66%\"></div>\n <svg width=\"100%\" height=\"110\" viewBox=\"0 0 400 110\" preserveAspectRatio=\"none\" class=\"accuracy-svg\">\n <polyline [attr.points]=\"AccuracyLinePoints\"\n fill=\"none\" stroke=\"var(--mj-brand-primary)\" stroke-width=\"2.5\" vector-effect=\"non-scaling-stroke\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n @for (dot of AccuracyDots; track TrackByIndex($index)) {\n <circle [attr.cx]=\"dot.Cx\" [attr.cy]=\"dot.Cy\" r=\"3\" fill=\"var(--mj-brand-primary)\"/>\n }\n </svg>\n </div>\n </div>\n <div class=\"accuracy-x-labels\">\n @for (label of AccuracyMonthLabels; track label) {\n <span>{{ label }}</span>\n }\n </div>\n <div class=\"accuracy-trend-text\">\n <i class=\"fa-solid fa-arrow-trend-up\" style=\"margin-right:4px\"></i>\n {{ AccuracyTrendText }}\n </div>\n </div>\n </div>\n }\n\n <!-- Low-Confidence Tags Table -->\n @if (LowConfidenceTags.length > 0) {\n <div class=\"sub-section\">\n <h3><i class=\"fa-solid fa-exclamation-triangle\"></i> Low-Confidence Tags (Avg Weight < 0.4)</h3>\n <div class=\"table-scroll\">\n <table class=\"data-table\">\n <thead>\n <tr>\n <th>Tag Name</th>\n <th class=\"num\">Avg Weight</th>\n <th class=\"num\">Usage Count</th>\n <th>Top Entity</th>\n <th>Suggested Action</th>\n </tr>\n </thead>\n <tbody>\n @for (tag of LowConfidenceTags; track tag.Name) {\n <tr>\n <td><strong>{{ tag.Name }}</strong></td>\n <td class=\"num\">{{ tag.AvgWeight }}</td>\n <td class=\"num\">{{ tag.UsageCount }}</td>\n <td>{{ tag.TopEntity }}</td>\n <td><span class=\"badge\" [class]=\"tag.ActionClass\">{{ tag.SuggestedAction }}</span></td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n </div>\n }\n\n <!-- Model Performance Comparison -->\n <div class=\"sub-section\">\n <h3><i class=\"fa-solid fa-robot\"></i> Model Performance Comparison</h3>\n <div class=\"model-grid\">\n @for (model of ModelComparisons; track model.Name) {\n <div class=\"model-card\">\n <div class=\"model-name\"><i [class]=\"model.Icon\" [style.color]=\"model.IconColor\" style=\"margin-right:4px\"></i> {{ model.Name }}</div>\n <svg width=\"80\" height=\"80\" viewBox=\"0 0 80 80\" style=\"margin:4px 0\">\n <circle cx=\"40\" cy=\"40\" r=\"32\" fill=\"none\" stroke=\"var(--mj-border-default)\" stroke-width=\"8\"/>\n <circle cx=\"40\" cy=\"40\" r=\"32\" fill=\"none\" [attr.stroke]=\"model.ScoreColor\" stroke-width=\"8\"\n [attr.stroke-dasharray]=\"model.StrokeDash\" stroke-dashoffset=\"50\"\n transform=\"rotate(-90 40 40)\" stroke-linecap=\"round\"/>\n </svg>\n <div class=\"model-score\" [style.color]=\"model.ScoreColor\">{{ model.ScorePercentage }}%</div>\n <div class=\"model-detail\">Avg confidence: {{ model.AvgConfidence }}</div>\n <div class=\"model-detail\">{{ FormatNumber(model.TagsGenerated) }} tags generated</div>\n <div class=\"model-detail\" [style.color]=\"model.RoleColor\" style=\"font-weight:600\">{{ model.Role }}</div>\n </div>\n }\n </div>\n @if (ModelRecommendation) {\n <div class=\"model-recommendation\">\n <i class=\"fa-solid fa-lightbulb\" style=\"color:var(--mj-status-warning)\"></i>\n <div>\n <strong>Recommendation:</strong> {{ ModelRecommendation }}\n </div>\n </div>\n }\n </div>\n </section>\n }\n\n <!-- ====================================================== -->\n <!-- TAB 6: COST & USAGE (D1) -->\n <!-- ====================================================== -->\n @if (ActiveTab === 'cost') {\n <section class=\"tab-section\">\n <div class=\"tab-section-header\">\n <i class=\"fa-solid fa-coins\"></i>\n <h1>Cost & Usage</h1>\n <div class=\"tab-header-actions\">\n <button class=\"drill-export-btn\" (click)=\"ExportTabDataCSV('cost-usage')\" title=\"Export CSV\">\n <i class=\"fa-solid fa-download\"></i> CSV\n </button>\n <button class=\"drill-export-btn\" (click)=\"PrintCurrentTab()\" title=\"Print (PDF alternative)\">\n <i class=\"fa-solid fa-print\"></i> Print\n </button>\n </div>\n </div>\n\n <!-- Filter Bar -->\n <div class=\"filter-bar\">\n <div class=\"date-chips\">\n @for (range of DateRanges; track range.Label) {\n <button\n class=\"date-chip\"\n [class.active]=\"ActiveDateRange === range.Label\"\n (click)=\"SetDateRange(range.Label)\"\n >{{ range.Label }}</button>\n }\n </div>\n </div>\n\n <!-- KPI Cards -->\n @if (CostKPIs.length > 0) {\n <div class=\"cost-kpi-row\">\n @for (kpi of CostKPIs; track kpi.Label) {\n <div class=\"cost-kpi-card\">\n <div class=\"cost-kpi-icon\"><i [class]=\"kpi.Icon\"></i></div>\n <div>\n <div class=\"cost-kpi-value\">{{ kpi.Value }}</div>\n <div class=\"cost-kpi-label\">{{ kpi.Label }}</div>\n <div class=\"cost-kpi-sub\">{{ kpi.SubLabel }}</div>\n </div>\n </div>\n }\n </div>\n }\n\n <!-- Per-Run Cost Table -->\n <div class=\"sub-section\">\n <h3><i class=\"fa-solid fa-table\"></i> Cost Breakdown by Run</h3>\n @if (CostPerRunRows.length > 0) {\n <div class=\"table-scroll\">\n <table class=\"data-table\">\n <thead>\n <tr>\n <th>Run ID</th>\n <th>Source</th>\n <th class=\"num\">Tokens</th>\n <th class=\"num\">Cost</th>\n <th>Started</th>\n </tr>\n </thead>\n <tbody>\n @for (row of CostPerRunRows; track row.RunID) {\n <tr>\n <td class=\"monospace-cell\">{{ row.RunID }}</td>\n <td>{{ row.Source }}</td>\n <td class=\"num\">{{ FormatNumber(row.Tokens) }}</td>\n <td class=\"num\">{{ row.Cost > 0 ? '$' + row.Cost.toFixed(4) : '$0.00' }}</td>\n <td class=\"muted\">{{ row.Started }}</td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n } @else {\n <div class=\"empty-state\">\n <i class=\"fa-solid fa-coins\"></i>\n <p>No cost data available yet. Run the pipeline to generate cost and token usage metrics.</p>\n <p class=\"empty-state-hint\">Cost data is aggregated from ContentProcessRunDetail records linked to AI Prompt Runs.</p>\n </div>\n }\n </div>\n </section>\n }\n\n </div><!-- /main-content -->\n </div><!-- /analytics-layout -->\n}\n", styles: ["/* ===================================================================\n Analytics & Insights Resource Component\n ================================================================= */\n\n.analytics-loading {\n display: flex;\n justify-content: center;\n align-items: center;\n height: 400px;\n}\n\n.analytics-layout {\n display: flex;\n height: 100%;\n min-height: 600px;\n}\n\n/* ===== LEFT SIDEBAR ===== */\n.analytics-sidebar {\n width: 220px;\n min-width: 220px;\n background: var(--mj-bg-surface);\n border-right: 1px solid var(--mj-border-default);\n display: flex;\n flex-direction: column;\n overflow-y: auto;\n}\n\n.sidebar-header {\n padding: 20px 16px 12px;\n border-bottom: 1px solid var(--mj-border-default);\n}\n\n.sidebar-header h2 {\n font-size: 13px;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n color: var(--mj-text-muted);\n display: flex;\n align-items: center;\n gap: 8px;\n margin: 0;\n}\n\n.sidebar-header h2 i {\n font-size: 14px;\n color: var(--mj-brand-primary);\n}\n\n.sidebar-nav {\n padding: 8px 0;\n flex: 1;\n display: flex;\n flex-direction: column;\n}\n\n.nav-item {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 10px 16px;\n font-size: 13.5px;\n font-weight: 500;\n color: var(--mj-text-secondary);\n cursor: pointer;\n border: none;\n background: transparent;\n border-left: 3px solid transparent;\n transition: all 0.15s ease;\n text-align: left;\n width: 100%;\n font-family: inherit;\n}\n\n.nav-item:hover {\n background: var(--mj-bg-surface-card);\n color: var(--mj-text-primary);\n}\n\n.nav-item.active {\n border-left-color: var(--mj-brand-primary);\n color: var(--mj-brand-primary);\n background: color-mix(in srgb, var(--mj-brand-primary) 6%, var(--mj-bg-surface));\n font-weight: 600;\n}\n\n.nav-item i {\n width: 16px;\n text-align: center;\n font-size: 13px;\n}\n\n.sidebar-divider {\n height: 1px;\n background: var(--mj-border-default);\n margin: 8px 16px;\n}\n\n.trending-section {\n padding: 12px 16px 16px;\n}\n\n.trending-section h3 {\n font-size: 11px;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n color: var(--mj-text-muted);\n margin: 0 0 10px;\n display: flex;\n align-items: center;\n gap: 4px;\n}\n\n.tag-cloud {\n display: flex;\n flex-wrap: wrap;\n gap: 4px 6px;\n line-height: 1.7;\n}\n\n.tag-cloud span {\n color: var(--mj-brand-primary);\n cursor: pointer;\n transition: opacity 0.15s;\n}\n\n.tag-cloud span:hover {\n opacity: 0.7;\n}\n\n.no-trending {\n color: var(--mj-text-muted) !important;\n font-size: 12px !important;\n font-style: italic;\n}\n\n.pipeline-status {\n padding: 12px 16px;\n border-top: 1px solid var(--mj-border-default);\n display: flex;\n align-items: center;\n gap: 8px;\n font-size: 11.5px;\n color: var(--mj-text-muted);\n}\n\n.status-dot {\n width: 8px;\n height: 8px;\n border-radius: 50%;\n background: var(--mj-status-success);\n flex-shrink: 0;\n}\n\n.status-dot-error {\n background: var(--mj-status-error);\n}\n\n/* ===== MAIN CONTENT ===== */\n.main-content {\n flex: 1;\n overflow-y: auto;\n min-width: 0;\n}\n\n.tab-section {\n padding: 28px 32px 40px;\n}\n\n.tab-section-header {\n display: flex;\n align-items: center;\n gap: 10px;\n margin-bottom: 20px;\n padding-bottom: 12px;\n border-bottom: 2px solid var(--mj-brand-primary);\n}\n\n.tab-section-header i {\n font-size: 18px;\n color: var(--mj-brand-primary);\n}\n\n.tab-section-header h1 {\n font-size: 20px;\n font-weight: 700;\n color: var(--mj-text-primary);\n margin: 0;\n}\n\n/* ===== FILTER BAR ===== */\n.filter-bar {\n display: flex;\n align-items: center;\n gap: 12px;\n margin-bottom: 20px;\n flex-wrap: wrap;\n}\n\n.date-chips {\n display: flex;\n gap: 0;\n border: 1px solid var(--mj-border-default);\n border-radius: 6px;\n overflow: hidden;\n}\n\n.date-chip {\n padding: 6px 14px;\n font-size: 12px;\n font-weight: 500;\n color: var(--mj-text-secondary);\n background: var(--mj-bg-surface);\n cursor: pointer;\n border: none;\n border-right: 1px solid var(--mj-border-default);\n transition: all 0.15s;\n font-family: inherit;\n}\n\n.date-chip:last-child {\n border-right: none;\n}\n\n.date-chip:hover {\n background: var(--mj-bg-surface-card);\n}\n\n.date-chip.active {\n background: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n}\n\n.filter-dropdown {\n padding: 6px 12px;\n font-size: 12px;\n border: 1px solid var(--mj-border-default);\n border-radius: 6px;\n background: var(--mj-bg-surface);\n color: var(--mj-text-secondary);\n cursor: pointer;\n font-family: inherit;\n}\n\n/* ===== KPI CARDS ===== */\n.kpi-row {\n display: grid;\n grid-template-columns: repeat(4, 1fr);\n gap: 16px;\n margin-bottom: 20px;\n}\n\n.kpi-card {\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n padding: 16px 18px;\n display: flex;\n align-items: flex-start;\n justify-content: space-between;\n transition: transform 0.15s, box-shadow 0.15s;\n cursor: pointer;\n}\n\n.kpi-card:hover {\n transform: translateY(-2px);\n box-shadow: 0 4px 12px color-mix(in srgb, var(--mj-text-primary) 8%, transparent);\n}\n\n.kpi-label {\n font-size: 12px;\n font-weight: 500;\n color: var(--mj-text-muted);\n margin-bottom: 4px;\n}\n\n.kpi-value {\n font-size: 28px;\n font-weight: 700;\n color: var(--mj-text-primary);\n line-height: 1.1;\n}\n\n.kpi-delta {\n font-size: 11px;\n font-weight: 500;\n margin-top: 4px;\n color: var(--mj-text-muted);\n}\n\n.kpi-delta.up {\n color: var(--mj-status-success);\n}\n\n.kpi-delta.down {\n color: var(--mj-status-error);\n}\n\n.kpi-sparkline {\n flex-shrink: 0;\n margin-left: 12px;\n margin-top: 6px;\n}\n\n/* ===== CARDS GRID ===== */\n.cards-grid {\n display: grid;\n grid-template-columns: repeat(3, 1fr);\n gap: 16px;\n}\n\n.widget-card {\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n padding: 18px 20px;\n transition: transform 0.15s, box-shadow 0.15s;\n cursor: pointer;\n}\n\n.widget-card:hover {\n transform: translateY(-2px);\n box-shadow: 0 4px 12px color-mix(in srgb, var(--mj-text-primary) 8%, transparent);\n}\n\n.widget-title {\n font-size: 13px;\n font-weight: 600;\n color: var(--mj-text-secondary);\n margin-bottom: 14px;\n display: flex;\n align-items: center;\n gap: 6px;\n}\n\n.widget-title i {\n color: var(--mj-brand-primary);\n font-size: 12px;\n}\n\n.widget-empty {\n text-align: center;\n color: var(--mj-text-muted);\n font-size: 12px;\n padding: 20px;\n}\n\n.widget-footnote {\n text-align: center;\n font-size: 10px;\n color: var(--mj-text-muted);\n margin-top: 10px;\n}\n\n/* ===== BAR CHART ===== */\n.bar-chart {\n display: flex;\n align-items: flex-end;\n gap: 8px;\n height: 120px;\n padding-top: 10px;\n}\n\n.bar-col {\n flex: 1;\n display: flex;\n flex-direction: column;\n align-items: center;\n height: 100%;\n justify-content: flex-end;\n}\n\n.bar {\n width: 100%;\n border-radius: 4px 4px 0 0;\n transition: height 0.3s;\n min-height: 2px;\n}\n\n.bar-label {\n font-size: 10px;\n color: var(--mj-text-muted);\n margin-top: 6px;\n}\n\n.bar-value {\n font-size: 10px;\n font-weight: 600;\n color: var(--mj-text-secondary);\n margin-bottom: 4px;\n}\n\n/* ===== PROGRESS RINGS ===== */\n.rings-grid {\n display: grid;\n grid-template-columns: repeat(2, 1fr);\n gap: 12px;\n}\n\n.ring-item {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 6px 0;\n}\n\n.ring-label {\n font-size: 12px;\n color: var(--mj-text-primary);\n font-weight: 500;\n}\n\n.ring-stat {\n font-size: 11px;\n color: var(--mj-text-muted);\n}\n\n/* ===== GAUGE ===== */\n.gauge-container {\n display: flex;\n justify-content: center;\n margin-bottom: 8px;\n}\n\n/* ===== MINI HISTOGRAM ===== */\n.mini-histogram {\n display: flex;\n align-items: flex-end;\n gap: 3px;\n height: 32px;\n justify-content: center;\n}\n\n.mini-hist-bar {\n width: 14px;\n border-radius: 2px;\n}\n\n.mini-hist-label {\n text-align: center;\n font-size: 10px;\n color: var(--mj-text-muted);\n margin-top: 4px;\n}\n\n/* ===== HORIZONTAL BARS ===== */\n.h-bar-list {\n display: flex;\n flex-direction: column;\n gap: 10px;\n}\n\n.h-bar-row {\n display: flex;\n align-items: center;\n gap: 10px;\n}\n\n.h-bar-name {\n font-size: 12px;\n color: var(--mj-text-secondary);\n width: 100px;\n flex-shrink: 0;\n text-align: right;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.h-bar-track {\n flex: 1;\n height: 20px;\n background: var(--mj-bg-surface-card);\n border-radius: 4px;\n overflow: hidden;\n}\n\n.h-bar-fill {\n height: 100%;\n border-radius: 4px;\n display: flex;\n align-items: center;\n padding-left: 8px;\n font-size: 11px;\n font-weight: 600;\n color: var(--mj-text-inverse);\n min-width: 36px;\n}\n\n/* ===== THROUGHPUT BARS ===== */\n.throughput-bars {\n display: flex;\n align-items: flex-end;\n gap: 4px;\n height: 110px;\n margin-bottom: 22px;\n}\n\n.tp-bar {\n flex: 1;\n border-radius: 3px 3px 0 0;\n position: relative;\n min-height: 4px;\n}\n\n.tp-bar-label {\n position: absolute;\n bottom: -18px;\n left: 50%;\n transform: translateX(-50%);\n font-size: 8px;\n color: var(--mj-text-muted);\n white-space: nowrap;\n}\n\n/* ===== TAXONOMY ===== */\n.taxonomy-ring-container {\n display: flex;\n justify-content: center;\n}\n\n.taxonomy-stats {\n display: flex;\n gap: 8px;\n margin-top: 12px;\n}\n\n.tax-stat {\n flex: 1;\n text-align: center;\n padding: 8px 4px;\n border-radius: 6px;\n font-size: 20px;\n font-weight: 700;\n}\n\n.tax-stat small {\n display: block;\n font-size: 10px;\n font-weight: 500;\n margin-top: 2px;\n}\n\n/* ===== LEGEND ===== */\n.legend {\n display: flex;\n gap: 16px;\n margin-top: 10px;\n flex-wrap: wrap;\n justify-content: center;\n}\n\n.legend-item {\n display: flex;\n align-items: center;\n gap: 5px;\n font-size: 11px;\n color: var(--mj-text-muted);\n}\n\n.legend-dot {\n width: 10px;\n height: 10px;\n border-radius: 3px;\n flex-shrink: 0;\n}\n\n/* ===== DATA TABLES ===== */\n.table-scroll {\n overflow-x: auto;\n}\n\n.data-table {\n width: 100%;\n border-collapse: collapse;\n font-size: 12.5px;\n}\n\n.data-table thead th {\n text-align: left;\n padding: 10px 12px;\n font-size: 11px;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.04em;\n color: var(--mj-text-muted);\n border-bottom: 2px solid var(--mj-border-default);\n background: var(--mj-bg-surface-card);\n white-space: nowrap;\n}\n\n.data-table tbody td {\n padding: 10px 12px;\n border-bottom: 1px solid var(--mj-border-subtle);\n color: var(--mj-text-primary);\n vertical-align: middle;\n}\n\n.data-table tbody tr:hover {\n background: var(--mj-bg-surface-card);\n}\n\n.data-table .num {\n text-align: right;\n font-variant-numeric: tabular-nums;\n}\n\n.muted {\n color: var(--mj-text-muted);\n}\n\n.monospace-cell {\n font-family: monospace;\n font-size: 11px;\n color: var(--mj-brand-primary);\n}\n\n.weight-bar {\n display: inline-block;\n height: 6px;\n border-radius: 3px;\n vertical-align: middle;\n margin-right: 6px;\n}\n\n/* ===== BADGES ===== */\n.badge {\n display: inline-block;\n padding: 2px 8px;\n border-radius: 10px;\n font-size: 10.5px;\n font-weight: 600;\n}\n\n.badge-success {\n background: color-mix(in srgb, var(--mj-status-success) 12%, var(--mj-bg-surface));\n color: var(--mj-status-success);\n}\n\n.badge-warning {\n background: color-mix(in srgb, var(--mj-status-warning) 12%, var(--mj-bg-surface));\n color: var(--mj-status-warning);\n}\n\n.badge-error {\n background: color-mix(in srgb, var(--mj-status-error) 12%, var(--mj-bg-surface));\n color: var(--mj-status-error);\n}\n\n.badge-info {\n background: color-mix(in srgb, var(--mj-status-info) 12%, var(--mj-bg-surface));\n color: var(--mj-status-info);\n}\n\n/* ===== SUB-SECTIONS ===== */\n.sub-section {\n margin-top: 24px;\n}\n\n.sub-section h3 {\n font-size: 14px;\n font-weight: 600;\n color: var(--mj-text-primary);\n margin: 0 0 12px;\n display: flex;\n align-items: center;\n gap: 6px;\n}\n\n.sub-section h3 i {\n font-size: 13px;\n color: var(--mj-brand-primary);\n}\n\n/* ===== EMPTY STATE ===== */\n.empty-state {\n display: flex;\n flex-direction: column;\n align-items: center;\n padding: 3rem 2rem;\n text-align: center;\n gap: 0.75rem;\n border: 2px dashed var(--mj-border-default);\n border-radius: 12px;\n background: var(--mj-bg-surface-sunken);\n color: var(--mj-text-muted);\n}\n\n.empty-state i {\n font-size: 2.5rem;\n color: var(--mj-text-disabled);\n}\n\n.empty-state p {\n margin: 0;\n font-size: 0.85rem;\n max-width: 400px;\n}\n\n/* ===== DRILL-DOWN PANEL ===== */\n.drill-down-panel {\n margin-top: 16px;\n margin-bottom: 16px;\n background: var(--mj-bg-surface);\n border: 2px solid var(--mj-brand-primary);\n border-radius: 10px;\n overflow: hidden;\n animation: slideDown 0.2s ease-out;\n}\n\n@keyframes slideDown {\n from {\n opacity: 0;\n max-height: 0;\n }\n to {\n opacity: 1;\n max-height: 600px;\n }\n}\n\n.drill-down-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 10px 16px;\n background: color-mix(in srgb, var(--mj-brand-primary) 6%, var(--mj-bg-surface));\n border-bottom: 1px solid var(--mj-border-default);\n}\n\n.drill-down-title {\n font-size: 13px;\n font-weight: 600;\n color: var(--mj-brand-primary);\n display: flex;\n align-items: center;\n gap: 6px;\n}\n\n.drill-down-close {\n border: none;\n background: transparent;\n color: var(--mj-text-muted);\n cursor: pointer;\n font-size: 14px;\n padding: 4px 8px;\n border-radius: 4px;\n transition: all 0.15s;\n}\n\n.drill-down-close:hover {\n background: var(--mj-bg-surface-hover);\n color: var(--mj-text-primary);\n}\n\n.drill-down-table-wrap {\n max-height: 400px;\n overflow-y: auto;\n padding: 8px;\n}\n\n.drill-action-col {\n width: 40px;\n text-align: center;\n padding: 6px !important;\n}\n\n.drill-open-btn {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 26px;\n height: 26px;\n border: 1px solid var(--mj-border-default);\n border-radius: 5px;\n background: transparent;\n color: var(--mj-text-muted);\n font-size: 10px;\n cursor: pointer;\n transition: all 0.15s;\n}\n\n.drill-open-btn:hover {\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n border-color: var(--mj-brand-primary);\n color: var(--mj-brand-primary);\n}\n\n.drill-down-header-actions {\n display: flex;\n align-items: center;\n gap: 6px;\n}\n\n.drill-export-btn {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n padding: 4px 10px;\n border: 1px solid var(--mj-border-default);\n border-radius: 5px;\n background: transparent;\n color: var(--mj-text-secondary);\n font-size: 11px;\n cursor: pointer;\n transition: all 0.15s;\n}\n\n.drill-export-btn:hover {\n background: color-mix(in srgb, var(--mj-brand-primary) 8%, var(--mj-bg-surface));\n border-color: var(--mj-brand-primary);\n color: var(--mj-brand-primary);\n}\n\n.sub-section-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n margin-bottom: 0;\n}\n\n.sub-section-header h3 {\n margin: 0;\n}\n\n.drill-down-empty {\n padding: 24px;\n text-align: center;\n color: var(--mj-text-muted);\n font-size: 13px;\n}\n\n/* ===== STACKED BAR CHART ===== */\n.stacked-bar-chart {\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n\n.stacked-row {\n display: flex;\n align-items: center;\n gap: 10px;\n}\n\n.stacked-label {\n width: 90px;\n font-size: 12px;\n color: var(--mj-text-secondary);\n text-align: right;\n flex-shrink: 0;\n}\n\n.stacked-track {\n flex: 1;\n height: 22px;\n display: flex;\n border-radius: 4px;\n overflow: hidden;\n}\n\n.stacked-seg {\n height: 100%;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 9px;\n font-weight: 600;\n color: var(--mj-text-inverse);\n min-width: 20px;\n}\n\n/* ===== VERTICAL BAR CHART ===== */\n.v-bar-chart {\n display: flex;\n align-items: flex-end;\n gap: 24px;\n height: 140px;\n padding-top: 10px;\n justify-content: center;\n}\n\n.v-bar-col {\n display: flex;\n flex-direction: column;\n align-items: center;\n height: 100%;\n justify-content: flex-end;\n min-width: 60px;\n}\n\n.v-bar {\n width: 48px;\n border-radius: 4px 4px 0 0;\n display: flex;\n align-items: flex-start;\n justify-content: center;\n padding-top: 4px;\n font-size: 11px;\n font-weight: 700;\n color: var(--mj-text-inverse);\n}\n\n.v-bar-label {\n font-size: 11px;\n color: var(--mj-text-muted);\n margin-top: 8px;\n text-align: center;\n}\n\n.chart-footnote {\n text-align: center;\n font-size: 11px;\n color: var(--mj-text-muted);\n margin-top: 14px;\n}\n\n/* ===== TWO COLUMN ===== */\n.two-col {\n display: grid;\n grid-template-columns: 1fr 1fr;\n gap: 20px;\n}\n\n/* ===== SOURCE SELECTED ===== */\n.source-selected {\n background: color-mix(in srgb, var(--mj-brand-primary) 5%, var(--mj-bg-surface));\n}\n\n/* ===== QUALITY BANDS ===== */\n.quality-bands {\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n\n.quality-band-row {\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.quality-band-label {\n font-size: 11px;\n color: var(--mj-text-muted);\n width: 65px;\n text-align: right;\n flex-shrink: 0;\n}\n\n.quality-band-track {\n flex: 1;\n height: 18px;\n background: var(--mj-bg-surface-card);\n border-radius: 4px;\n overflow: hidden;\n}\n\n.quality-band-fill {\n height: 100%;\n border-radius: 4px;\n display: flex;\n align-items: center;\n padding-left: 8px;\n font-size: 10px;\n font-weight: 600;\n color: var(--mj-text-inverse);\n min-width: 30px;\n}\n\n.source-quality-note {\n margin-top: 14px;\n padding: 10px;\n background: var(--mj-bg-surface-card);\n border-radius: 6px;\n font-size: 11px;\n color: var(--mj-text-secondary);\n}\n\n/* ===== SOURCE HEALTH ===== */\n.source-health-grid {\n display: grid;\n gap: 12px;\n}\n\n.source-health-card {\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n padding: 14px;\n text-align: center;\n border-top: 3px solid;\n}\n\n.health-card-name {\n font-size: 11px;\n color: var(--mj-text-muted);\n margin-bottom: 4px;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.health-card-value {\n font-size: 22px;\n font-weight: 700;\n}\n\n.health-card-label {\n font-size: 10px;\n color: var(--mj-text-muted);\n}\n\n/* ===== PIPELINE ===== */\n.pipeline-throughput-bars {\n display: flex;\n align-items: flex-end;\n gap: 3px;\n height: 110px;\n}\n\n.pipeline-bar {\n flex: 1;\n border-radius: 2px 2px 0 0;\n min-height: 3px;\n}\n\n.pipeline-date-labels {\n display: flex;\n justify-content: space-between;\n margin-top: 6px;\n font-size: 10px;\n color: var(--mj-text-muted);\n}\n\n/* ===== PROCESSING STAGES ===== */\n.stage-bars {\n display: flex;\n flex-direction: column;\n gap: 10px;\n}\n\n.stage-row {\n display: flex;\n align-items: center;\n gap: 10px;\n}\n\n.stage-name {\n width: 80px;\n font-size: 12px;\n font-weight: 500;\n color: var(--mj-text-secondary);\n text-align: right;\n flex-shrink: 0;\n}\n\n.stage-track {\n flex: 1;\n height: 24px;\n background: var(--mj-bg-surface-card);\n border-radius: 4px;\n overflow: hidden;\n}\n\n.stage-fill {\n height: 100%;\n border-radius: 4px;\n display: flex;\n align-items: center;\n justify-content: flex-end;\n padding-right: 8px;\n font-size: 11px;\n font-weight: 600;\n color: var(--mj-text-inverse);\n}\n\n.stage-time {\n width: 50px;\n font-size: 12px;\n font-weight: 600;\n color: var(--mj-text-primary);\n text-align: right;\n flex-shrink: 0;\n}\n\n.stage-summary {\n display: flex;\n justify-content: space-between;\n margin-top: 14px;\n padding-top: 10px;\n border-top: 1px solid var(--mj-border-default);\n font-size: 12px;\n color: var(--mj-text-secondary);\n}\n\n.stage-warning {\n color: var(--mj-text-muted);\n}\n\n/* ===== SUCCESS RATE ===== */\n.success-rate-display {\n display: flex;\n justify-content: center;\n gap: 48px;\n padding: 20px;\n}\n\n.success-rate-stat {\n text-align: center;\n}\n\n.success-rate-value {\n font-size: 32px;\n font-weight: 700;\n line-height: 1.1;\n}\n\n.success-rate-label {\n font-size: 12px;\n color: var(--mj-text-muted);\n margin-top: 4px;\n}\n\n/* ===== PROGRESS ===== */\n.progress-track {\n width: 100px;\n height: 6px;\n background: var(--mj-bg-surface-sunken);\n border-radius: 3px;\n overflow: hidden;\n display: inline-block;\n vertical-align: middle;\n margin-right: 6px;\n}\n\n.progress-fill {\n height: 100%;\n border-radius: 3px;\n}\n\n.progress-text {\n font-size: 11px;\n font-weight: 600;\n}\n\n/* ===== ERROR LOG ===== */\n.error-log {\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n\n.error-entry {\n display: flex;\n gap: 12px;\n padding: 10px 14px;\n background: color-mix(in srgb, var(--mj-status-error) 4%, var(--mj-bg-surface));\n border-left: 3px solid var(--mj-status-error);\n border-radius: 0 6px 6px 0;\n font-size: 12px;\n}\n\n.error-time {\n color: var(--mj-text-muted);\n font-size: 11px;\n white-space: nowrap;\n flex-shrink: 0;\n width: 130px;\n}\n\n.error-source {\n color: var(--mj-brand-primary);\n font-weight: 600;\n flex-shrink: 0;\n width: 120px;\n}\n\n.error-msg {\n color: var(--mj-text-secondary);\n}\n\n/* ===== HISTOGRAM ===== */\n.histogram {\n display: flex;\n align-items: flex-end;\n gap: 6px;\n height: 130px;\n padding-bottom: 24px;\n}\n\n.hist-bar-col {\n flex: 1;\n display: flex;\n flex-direction: column;\n align-items: center;\n height: 100%;\n justify-content: flex-end;\n}\n\n.hist-bar {\n width: 100%;\n border-radius: 3px 3px 0 0;\n display: flex;\n align-items: flex-start;\n justify-content: center;\n padding-top: 3px;\n font-size: 10px;\n font-weight: 600;\n color: var(--mj-text-inverse);\n min-height: 2px;\n}\n\n.hist-label {\n font-size: 9px;\n color: var(--mj-text-muted);\n margin-top: 6px;\n text-align: center;\n}\n\n.confidence-stats {\n display: flex;\n justify-content: space-between;\n margin-top: 8px;\n padding: 10px;\n background: var(--mj-bg-surface-card);\n border-radius: 6px;\n font-size: 11px;\n color: var(--mj-text-muted);\n flex-wrap: wrap;\n gap: 8px;\n}\n\n/* ===== GROUPED BAR CHART ===== */\n.grouped-bar-chart {\n display: flex;\n align-items: flex-end;\n gap: 20px;\n height: 140px;\n justify-content: center;\n}\n\n.grouped-col {\n display: flex;\n flex-direction: column;\n align-items: center;\n height: 100%;\n justify-content: flex-end;\n}\n\n.grouped-bars {\n display: flex;\n gap: 3px;\n align-items: flex-end;\n height: 100%;\n}\n\n.g-bar {\n width: 18px;\n border-radius: 3px 3px 0 0;\n min-height: 2px;\n}\n\n.grouped-label {\n font-size: 10px;\n color: var(--mj-text-muted);\n margin-top: 6px;\n text-align: center;\n}\n\n/* ===== ACCURACY CHART ===== */\n.accuracy-chart {\n position: relative;\n display: flex;\n margin: 0 10px;\n}\n\n.accuracy-y-labels {\n display: flex;\n flex-direction: column;\n justify-content: space-between;\n font-size: 9px;\n color: var(--mj-text-muted);\n margin-right: 8px;\n height: 110px;\n}\n\n.accuracy-area {\n flex: 1;\n position: relative;\n height: 110px;\n border-left: 1px solid var(--mj-border-default);\n border-bottom: 1px solid var(--mj-border-default);\n}\n\n.accuracy-grid-line {\n position: absolute;\n left: 0;\n right: 0;\n border-top: 1px dashed var(--mj-border-subtle);\n}\n\n.accuracy-svg {\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n}\n\n.accuracy-x-labels {\n display: flex;\n justify-content: space-between;\n margin-top: 8px;\n padding: 0 40px 0 40px;\n font-size: 10px;\n color: var(--mj-text-muted);\n}\n\n.accuracy-trend-text {\n text-align: center;\n margin-top: 10px;\n font-size: 12px;\n color: var(--mj-status-success);\n}\n\n/* ===== MODEL COMPARISON ===== */\n.model-grid {\n display: grid;\n grid-template-columns: repeat(3, 1fr);\n gap: 12px;\n}\n\n.model-card {\n background: var(--mj-bg-surface-card);\n border-radius: 8px;\n padding: 14px;\n text-align: center;\n border: 1px solid var(--mj-border-default);\n}\n\n.model-name {\n font-size: 12px;\n font-weight: 600;\n color: var(--mj-text-primary);\n margin-bottom: 8px;\n}\n\n.model-score {\n font-size: 28px;\n font-weight: 700;\n line-height: 1;\n}\n\n.model-detail {\n font-size: 11px;\n color: var(--mj-text-muted);\n margin-top: 4px;\n}\n\n.model-recommendation {\n margin-top: 14px;\n padding: 12px 16px;\n background: var(--mj-bg-surface-card);\n border-radius: 8px;\n font-size: 12px;\n color: var(--mj-text-secondary);\n display: flex;\n align-items: flex-start;\n gap: 8px;\n}\n\n/* ===== RESPONSIVE ===== */\n@media (max-width: 1200px) {\n .cards-grid {\n grid-template-columns: repeat(2, 1fr);\n }\n\n .kpi-row {\n grid-template-columns: repeat(2, 1fr);\n }\n\n .model-grid {\n grid-template-columns: repeat(2, 1fr);\n }\n}\n\n@media (max-width: 768px) {\n .analytics-layout {\n flex-direction: column;\n }\n\n .analytics-sidebar {\n width: 100%;\n min-width: 100%;\n border-right: none;\n border-bottom: 1px solid var(--mj-border-default);\n }\n\n .sidebar-nav {\n flex-direction: row;\n overflow-x: auto;\n }\n\n .nav-item {\n border-left: none;\n border-bottom: 3px solid transparent;\n white-space: nowrap;\n }\n\n .nav-item.active {\n border-left-color: transparent;\n border-bottom-color: var(--mj-brand-primary);\n }\n\n .trending-section,\n .sidebar-divider {\n display: none;\n }\n\n .tab-section {\n padding: 16px;\n }\n\n .cards-grid {\n grid-template-columns: 1fr;\n }\n\n .kpi-row {\n grid-template-columns: 1fr;\n }\n\n .two-col {\n grid-template-columns: 1fr;\n }\n\n .cost-kpi-row {\n grid-template-columns: 1fr;\n }\n}\n\n/* ===== COST & USAGE TAB (D1) ===== */\n\n.tab-header-actions {\n display: flex;\n gap: 8px;\n margin-left: auto;\n}\n\n.cost-kpi-row {\n display: grid;\n grid-template-columns: repeat(3, 1fr);\n gap: 16px;\n margin-bottom: 24px;\n}\n\n.cost-kpi-card {\n display: flex;\n gap: 14px;\n align-items: center;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: 10px;\n padding: 18px 20px;\n transition: box-shadow 0.15s ease;\n}\n\n.cost-kpi-card:hover {\n box-shadow: 0 2px 8px color-mix(in srgb, var(--mj-text-primary) 8%, transparent);\n}\n\n.cost-kpi-icon {\n width: 44px;\n height: 44px;\n border-radius: 10px;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 18px;\n color: var(--mj-brand-primary);\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n flex-shrink: 0;\n}\n\n.cost-kpi-value {\n font-size: 22px;\n font-weight: 700;\n color: var(--mj-text-primary);\n line-height: 1.2;\n}\n\n.cost-kpi-label {\n font-size: 12px;\n font-weight: 600;\n color: var(--mj-text-secondary);\n text-transform: uppercase;\n letter-spacing: 0.03em;\n}\n\n.cost-kpi-sub {\n font-size: 11px;\n color: var(--mj-text-muted);\n margin-top: 2px;\n}\n\n.empty-state-hint {\n font-size: 12px;\n color: var(--mj-text-muted);\n margin-top: 4px;\n}\n\n/* ===== D5: PRINT STYLES ===== */\n\n@media print {\n .analytics-sidebar,\n .filter-bar,\n .tab-header-actions,\n .drill-down-close,\n .drill-export-btn,\n .drill-open-btn {\n display: none !important;\n }\n\n .analytics-layout {\n display: block;\n }\n\n .main-content {\n padding: 0;\n }\n\n .tab-section {\n padding: 0;\n }\n\n .widget-card,\n .cost-kpi-card,\n .kpi-card {\n break-inside: avoid;\n box-shadow: none;\n border: 1px solid var(--mj-border-default);\n }\n\n .data-table {\n font-size: 10px;\n }\n}\n\n/* \u2500\u2500 Co-Occurrence Section \u2500\u2500 */\n\n/* Constrain the co-occurrence sub-section so an empty state or large table\n does not blow out the page height and render the app unusable. */\n.co-occurrence-section {\n max-height: 500px;\n display: flex;\n flex-direction: column;\n overflow: hidden;\n}\n\n.co-occurrence-section .table-scroll {\n overflow-y: auto;\n max-height: 400px;\n flex: 1;\n min-height: 0;\n}\n\n.co-occurrence-section .empty-state {\n max-height: 200px;\n overflow: hidden;\n}\n\n.co-occurrence-actions {\n display: flex;\n align-items: center;\n gap: 12px;\n}\n\n.co-occurrence-staleness {\n display: flex;\n align-items: center;\n gap: 5px;\n font-size: 0.78rem;\n color: var(--mj-text-muted);\n}\n"] }]
|
|
4261
4261
|
}], null, null); })();
|
|
4262
4262
|
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(AnalyticsResourceComponent, { className: "AnalyticsResourceComponent", filePath: "src/KnowledgeHub/components/analytics/analytics-resource.component.ts", lineNumber: 222 }); })();
|
|
4263
4263
|
/** Tree-shaking prevention */
|