@memberjunction/ng-archive-manager 0.0.1 → 5.30.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/lib/archive-config/archive-config-admin.component.d.ts +113 -0
- package/dist/lib/archive-config/archive-config-admin.component.d.ts.map +1 -0
- package/dist/lib/archive-config/archive-config-admin.component.js +735 -0
- package/dist/lib/archive-config/archive-config-admin.component.js.map +1 -0
- package/dist/lib/archive-manager.module.d.ts +14 -0
- package/dist/lib/archive-manager.module.d.ts.map +1 -0
- package/dist/lib/archive-manager.module.js +48 -0
- package/dist/lib/archive-manager.module.js.map +1 -0
- package/dist/lib/archive-restore/archive-restore-dialog.component.d.ts +82 -0
- package/dist/lib/archive-restore/archive-restore-dialog.component.d.ts.map +1 -0
- package/dist/lib/archive-restore/archive-restore-dialog.component.js +386 -0
- package/dist/lib/archive-restore/archive-restore-dialog.component.js.map +1 -0
- package/dist/lib/archive-run-viewer/archive-run-viewer.component.d.ts +81 -0
- package/dist/lib/archive-run-viewer/archive-run-viewer.component.d.ts.map +1 -0
- package/dist/lib/archive-run-viewer/archive-run-viewer.component.js +515 -0
- package/dist/lib/archive-run-viewer/archive-run-viewer.component.js.map +1 -0
- package/dist/lib/archive-status/archive-status-badge.component.d.ts +65 -0
- package/dist/lib/archive-status/archive-status-badge.component.d.ts.map +1 -0
- package/dist/lib/archive-status/archive-status-badge.component.js +172 -0
- package/dist/lib/archive-status/archive-status-badge.component.js.map +1 -0
- package/dist/lib/archive-types.d.ts +81 -0
- package/dist/lib/archive-types.d.ts.map +1 -0
- package/dist/lib/archive-types.js +8 -0
- package/dist/lib/archive-types.js.map +1 -0
- package/dist/public-api.d.ts +6 -0
- package/dist/public-api.d.ts.map +1 -0
- package/dist/public-api.js +11 -0
- package/dist/public-api.js.map +1 -0
- package/package.json +42 -6
- package/README.md +0 -45
|
@@ -0,0 +1,515 @@
|
|
|
1
|
+
import { Component, ChangeDetectionStrategy, ChangeDetectorRef, inject, } from '@angular/core';
|
|
2
|
+
import { RunView } from '@memberjunction/core';
|
|
3
|
+
import * as i0 from "@angular/core";
|
|
4
|
+
import * as i1 from "@angular/common";
|
|
5
|
+
import * as i2 from "@memberjunction/ng-shared-generic";
|
|
6
|
+
const _forTrack0 = ($index, $item) => $item.ID;
|
|
7
|
+
function ArchiveRunViewerComponent_Conditional_1_Template(rf, ctx) { if (rf & 1) {
|
|
8
|
+
i0.ɵɵelementStart(0, "div", 1);
|
|
9
|
+
i0.ɵɵelement(1, "mj-loading", 4);
|
|
10
|
+
i0.ɵɵelementEnd();
|
|
11
|
+
} }
|
|
12
|
+
function ArchiveRunViewerComponent_Conditional_2_Conditional_36_Template(rf, ctx) { if (rf & 1) {
|
|
13
|
+
i0.ɵɵelementStart(0, "div", 20);
|
|
14
|
+
i0.ɵɵelement(1, "i", 22);
|
|
15
|
+
i0.ɵɵelementStart(2, "p");
|
|
16
|
+
i0.ɵɵtext(3, "No archive runs found.");
|
|
17
|
+
i0.ɵɵelementEnd()();
|
|
18
|
+
} }
|
|
19
|
+
function ArchiveRunViewerComponent_Conditional_2_For_38_Conditional_13_Template(rf, ctx) { if (rf & 1) {
|
|
20
|
+
i0.ɵɵelementStart(0, "span", 31);
|
|
21
|
+
i0.ɵɵelement(1, "i", 35);
|
|
22
|
+
i0.ɵɵtext(2);
|
|
23
|
+
i0.ɵɵelementEnd();
|
|
24
|
+
} if (rf & 2) {
|
|
25
|
+
const run_r2 = i0.ɵɵnextContext().$implicit;
|
|
26
|
+
i0.ɵɵadvance(2);
|
|
27
|
+
i0.ɵɵtextInterpolate1(" ", run_r2.RecordsFailed, " failed ");
|
|
28
|
+
} }
|
|
29
|
+
function ArchiveRunViewerComponent_Conditional_2_For_38_Conditional_14_Template(rf, ctx) { if (rf & 1) {
|
|
30
|
+
i0.ɵɵelementStart(0, "span", 32);
|
|
31
|
+
i0.ɵɵelement(1, "i", 36);
|
|
32
|
+
i0.ɵɵtext(2);
|
|
33
|
+
i0.ɵɵelementEnd();
|
|
34
|
+
} if (rf & 2) {
|
|
35
|
+
const run_r2 = i0.ɵɵnextContext().$implicit;
|
|
36
|
+
i0.ɵɵadvance(2);
|
|
37
|
+
i0.ɵɵtextInterpolate1(" ", run_r2.RecordsSkipped, " skipped ");
|
|
38
|
+
} }
|
|
39
|
+
function ArchiveRunViewerComponent_Conditional_2_For_38_Template(rf, ctx) { if (rf & 1) {
|
|
40
|
+
const _r1 = i0.ɵɵgetCurrentView();
|
|
41
|
+
i0.ɵɵelementStart(0, "div", 23);
|
|
42
|
+
i0.ɵɵlistener("click", function ArchiveRunViewerComponent_Conditional_2_For_38_Template_div_click_0_listener() { const run_r2 = i0.ɵɵrestoreView(_r1).$implicit; const ctx_r2 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r2.OpenRunDrawer(run_r2)); })("keydown.enter", function ArchiveRunViewerComponent_Conditional_2_For_38_Template_div_keydown_enter_0_listener() { const run_r2 = i0.ɵɵrestoreView(_r1).$implicit; const ctx_r2 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r2.OpenRunDrawer(run_r2)); });
|
|
43
|
+
i0.ɵɵelementStart(1, "div", 24)(2, "span", 25);
|
|
44
|
+
i0.ɵɵtext(3);
|
|
45
|
+
i0.ɵɵelementEnd();
|
|
46
|
+
i0.ɵɵelementStart(4, "span", 26);
|
|
47
|
+
i0.ɵɵtext(5);
|
|
48
|
+
i0.ɵɵelementEnd()();
|
|
49
|
+
i0.ɵɵelementStart(6, "div", 27);
|
|
50
|
+
i0.ɵɵelement(7, "i", 28);
|
|
51
|
+
i0.ɵɵtext(8);
|
|
52
|
+
i0.ɵɵelementEnd();
|
|
53
|
+
i0.ɵɵelementStart(9, "div", 29)(10, "span", 30);
|
|
54
|
+
i0.ɵɵelement(11, "i", 13);
|
|
55
|
+
i0.ɵɵtext(12);
|
|
56
|
+
i0.ɵɵelementEnd();
|
|
57
|
+
i0.ɵɵconditionalCreate(13, ArchiveRunViewerComponent_Conditional_2_For_38_Conditional_13_Template, 3, 1, "span", 31);
|
|
58
|
+
i0.ɵɵconditionalCreate(14, ArchiveRunViewerComponent_Conditional_2_For_38_Conditional_14_Template, 3, 1, "span", 32);
|
|
59
|
+
i0.ɵɵelementStart(15, "span", 30);
|
|
60
|
+
i0.ɵɵelement(16, "i", 33);
|
|
61
|
+
i0.ɵɵtext(17);
|
|
62
|
+
i0.ɵɵelementEnd();
|
|
63
|
+
i0.ɵɵelementStart(18, "span", 30);
|
|
64
|
+
i0.ɵɵelement(19, "i", 34);
|
|
65
|
+
i0.ɵɵtext(20);
|
|
66
|
+
i0.ɵɵelementEnd()()();
|
|
67
|
+
} if (rf & 2) {
|
|
68
|
+
const run_r2 = ctx.$implicit;
|
|
69
|
+
const ctx_r2 = i0.ɵɵnextContext(2);
|
|
70
|
+
i0.ɵɵattribute("aria-label", "View details for run on " + ctx_r2.FormatDate(run_r2.StartedAt));
|
|
71
|
+
i0.ɵɵadvance(3);
|
|
72
|
+
i0.ɵɵtextInterpolate(run_r2.ConfigurationName);
|
|
73
|
+
i0.ɵɵadvance();
|
|
74
|
+
i0.ɵɵproperty("ngClass", ctx_r2.GetStatusClass(run_r2.Status));
|
|
75
|
+
i0.ɵɵadvance();
|
|
76
|
+
i0.ɵɵtextInterpolate(run_r2.Status);
|
|
77
|
+
i0.ɵɵadvance(3);
|
|
78
|
+
i0.ɵɵtextInterpolate1(" ", ctx_r2.FormatDate(run_r2.StartedAt), " ");
|
|
79
|
+
i0.ɵɵadvance(4);
|
|
80
|
+
i0.ɵɵtextInterpolate1(" ", run_r2.RecordsArchived, " archived ");
|
|
81
|
+
i0.ɵɵadvance();
|
|
82
|
+
i0.ɵɵconditional(run_r2.RecordsFailed > 0 ? 13 : -1);
|
|
83
|
+
i0.ɵɵadvance();
|
|
84
|
+
i0.ɵɵconditional(run_r2.RecordsSkipped > 0 ? 14 : -1);
|
|
85
|
+
i0.ɵɵadvance(3);
|
|
86
|
+
i0.ɵɵtextInterpolate1(" ", ctx_r2.FormatDuration(run_r2.DurationSeconds), " ");
|
|
87
|
+
i0.ɵɵadvance(3);
|
|
88
|
+
i0.ɵɵtextInterpolate1(" ", ctx_r2.FormatBytes(run_r2.TotalBytes), " ");
|
|
89
|
+
} }
|
|
90
|
+
function ArchiveRunViewerComponent_Conditional_2_Template(rf, ctx) { if (rf & 1) {
|
|
91
|
+
i0.ɵɵelementStart(0, "div", 5)(1, "div", 6)(2, "div", 7);
|
|
92
|
+
i0.ɵɵelement(3, "i", 8);
|
|
93
|
+
i0.ɵɵelementEnd();
|
|
94
|
+
i0.ɵɵelementStart(4, "div", 9)(5, "span", 10);
|
|
95
|
+
i0.ɵɵtext(6);
|
|
96
|
+
i0.ɵɵelementEnd();
|
|
97
|
+
i0.ɵɵelementStart(7, "span", 11);
|
|
98
|
+
i0.ɵɵtext(8, "Total Runs");
|
|
99
|
+
i0.ɵɵelementEnd()()();
|
|
100
|
+
i0.ɵɵelementStart(9, "div", 6)(10, "div", 12);
|
|
101
|
+
i0.ɵɵelement(11, "i", 13);
|
|
102
|
+
i0.ɵɵelementEnd();
|
|
103
|
+
i0.ɵɵelementStart(12, "div", 9)(13, "span", 10);
|
|
104
|
+
i0.ɵɵtext(14);
|
|
105
|
+
i0.ɵɵelementEnd();
|
|
106
|
+
i0.ɵɵelementStart(15, "span", 11);
|
|
107
|
+
i0.ɵɵtext(16, "Records Archived");
|
|
108
|
+
i0.ɵɵelementEnd()()();
|
|
109
|
+
i0.ɵɵelementStart(17, "div", 6)(18, "div", 14);
|
|
110
|
+
i0.ɵɵelement(19, "i", 15);
|
|
111
|
+
i0.ɵɵelementEnd();
|
|
112
|
+
i0.ɵɵelementStart(20, "div", 9)(21, "span", 10);
|
|
113
|
+
i0.ɵɵtext(22);
|
|
114
|
+
i0.ɵɵelementEnd();
|
|
115
|
+
i0.ɵɵelementStart(23, "span", 11);
|
|
116
|
+
i0.ɵɵtext(24, "Storage Used");
|
|
117
|
+
i0.ɵɵelementEnd()()();
|
|
118
|
+
i0.ɵɵelementStart(25, "div", 6)(26, "div", 16);
|
|
119
|
+
i0.ɵɵelement(27, "i", 17);
|
|
120
|
+
i0.ɵɵelementEnd();
|
|
121
|
+
i0.ɵɵelementStart(28, "div", 9)(29, "span", 10);
|
|
122
|
+
i0.ɵɵtext(30);
|
|
123
|
+
i0.ɵɵelementEnd();
|
|
124
|
+
i0.ɵɵelementStart(31, "span", 11);
|
|
125
|
+
i0.ɵɵtext(32, "Last Run");
|
|
126
|
+
i0.ɵɵelementEnd()()()();
|
|
127
|
+
i0.ɵɵelementStart(33, "div", 18)(34, "h3", 19);
|
|
128
|
+
i0.ɵɵtext(35, "Run History");
|
|
129
|
+
i0.ɵɵelementEnd();
|
|
130
|
+
i0.ɵɵconditionalCreate(36, ArchiveRunViewerComponent_Conditional_2_Conditional_36_Template, 4, 0, "div", 20);
|
|
131
|
+
i0.ɵɵrepeaterCreate(37, ArchiveRunViewerComponent_Conditional_2_For_38_Template, 21, 10, "div", 21, _forTrack0);
|
|
132
|
+
i0.ɵɵelementEnd();
|
|
133
|
+
} if (rf & 2) {
|
|
134
|
+
const ctx_r2 = i0.ɵɵnextContext();
|
|
135
|
+
i0.ɵɵadvance(6);
|
|
136
|
+
i0.ɵɵtextInterpolate(ctx_r2.Metrics.TotalRuns);
|
|
137
|
+
i0.ɵɵadvance(8);
|
|
138
|
+
i0.ɵɵtextInterpolate(ctx_r2.Metrics.TotalRecordsArchived);
|
|
139
|
+
i0.ɵɵadvance(8);
|
|
140
|
+
i0.ɵɵtextInterpolate(ctx_r2.FormatBytes(ctx_r2.Metrics.TotalStorageBytes));
|
|
141
|
+
i0.ɵɵadvance(8);
|
|
142
|
+
i0.ɵɵtextInterpolate(ctx_r2.FormatDate(ctx_r2.Metrics.LastRunDate));
|
|
143
|
+
i0.ɵɵadvance(6);
|
|
144
|
+
i0.ɵɵconditional(ctx_r2.Runs.length === 0 ? 36 : -1);
|
|
145
|
+
i0.ɵɵadvance();
|
|
146
|
+
i0.ɵɵrepeater(ctx_r2.Runs);
|
|
147
|
+
} }
|
|
148
|
+
function ArchiveRunViewerComponent_Conditional_3_Template(rf, ctx) { if (rf & 1) {
|
|
149
|
+
const _r4 = i0.ɵɵgetCurrentView();
|
|
150
|
+
i0.ɵɵelementStart(0, "div", 37);
|
|
151
|
+
i0.ɵɵlistener("click", function ArchiveRunViewerComponent_Conditional_3_Template_div_click_0_listener() { i0.ɵɵrestoreView(_r4); const ctx_r2 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r2.CloseDrawer()); });
|
|
152
|
+
i0.ɵɵelementEnd();
|
|
153
|
+
} }
|
|
154
|
+
function ArchiveRunViewerComponent_Conditional_5_Conditional_45_Template(rf, ctx) { if (rf & 1) {
|
|
155
|
+
i0.ɵɵelement(0, "mj-loading", 52);
|
|
156
|
+
} }
|
|
157
|
+
function ArchiveRunViewerComponent_Conditional_5_Conditional_46_Template(rf, ctx) { if (rf & 1) {
|
|
158
|
+
i0.ɵɵelementStart(0, "p", 53);
|
|
159
|
+
i0.ɵɵtext(1, "No detail records found.");
|
|
160
|
+
i0.ɵɵelementEnd();
|
|
161
|
+
} }
|
|
162
|
+
function ArchiveRunViewerComponent_Conditional_5_Conditional_47_For_14_Template(rf, ctx) { if (rf & 1) {
|
|
163
|
+
i0.ɵɵelementStart(0, "tr")(1, "td", 56);
|
|
164
|
+
i0.ɵɵtext(2);
|
|
165
|
+
i0.ɵɵelementEnd();
|
|
166
|
+
i0.ɵɵelementStart(3, "td", 57);
|
|
167
|
+
i0.ɵɵtext(4);
|
|
168
|
+
i0.ɵɵelementEnd();
|
|
169
|
+
i0.ɵɵelementStart(5, "td")(6, "span", 58);
|
|
170
|
+
i0.ɵɵtext(7);
|
|
171
|
+
i0.ɵɵelementEnd()();
|
|
172
|
+
i0.ɵɵelementStart(8, "td", 59);
|
|
173
|
+
i0.ɵɵtext(9);
|
|
174
|
+
i0.ɵɵelementEnd()();
|
|
175
|
+
} if (rf & 2) {
|
|
176
|
+
const detail_r6 = ctx.$implicit;
|
|
177
|
+
const ctx_r2 = i0.ɵɵnextContext(3);
|
|
178
|
+
i0.ɵɵadvance(2);
|
|
179
|
+
i0.ɵɵtextInterpolate(detail_r6.EntityName);
|
|
180
|
+
i0.ɵɵadvance();
|
|
181
|
+
i0.ɵɵproperty("title", detail_r6.RecordID);
|
|
182
|
+
i0.ɵɵadvance();
|
|
183
|
+
i0.ɵɵtextInterpolate(detail_r6.RecordID);
|
|
184
|
+
i0.ɵɵadvance(2);
|
|
185
|
+
i0.ɵɵproperty("ngClass", ctx_r2.GetStatusClass(detail_r6.Status));
|
|
186
|
+
i0.ɵɵadvance();
|
|
187
|
+
i0.ɵɵtextInterpolate1(" ", detail_r6.Status, " ");
|
|
188
|
+
i0.ɵɵadvance(2);
|
|
189
|
+
i0.ɵɵtextInterpolate(ctx_r2.FormatBytes(detail_r6.Bytes));
|
|
190
|
+
} }
|
|
191
|
+
function ArchiveRunViewerComponent_Conditional_5_Conditional_47_Template(rf, ctx) { if (rf & 1) {
|
|
192
|
+
i0.ɵɵelementStart(0, "div", 54)(1, "table", 55)(2, "thead")(3, "tr")(4, "th");
|
|
193
|
+
i0.ɵɵtext(5, "Entity");
|
|
194
|
+
i0.ɵɵelementEnd();
|
|
195
|
+
i0.ɵɵelementStart(6, "th");
|
|
196
|
+
i0.ɵɵtext(7, "Record ID");
|
|
197
|
+
i0.ɵɵelementEnd();
|
|
198
|
+
i0.ɵɵelementStart(8, "th");
|
|
199
|
+
i0.ɵɵtext(9, "Status");
|
|
200
|
+
i0.ɵɵelementEnd();
|
|
201
|
+
i0.ɵɵelementStart(10, "th");
|
|
202
|
+
i0.ɵɵtext(11, "Bytes");
|
|
203
|
+
i0.ɵɵelementEnd()()();
|
|
204
|
+
i0.ɵɵelementStart(12, "tbody");
|
|
205
|
+
i0.ɵɵrepeaterCreate(13, ArchiveRunViewerComponent_Conditional_5_Conditional_47_For_14_Template, 10, 6, "tr", null, _forTrack0);
|
|
206
|
+
i0.ɵɵelementEnd()()();
|
|
207
|
+
} if (rf & 2) {
|
|
208
|
+
const ctx_r2 = i0.ɵɵnextContext(2);
|
|
209
|
+
i0.ɵɵadvance(13);
|
|
210
|
+
i0.ɵɵrepeater(ctx_r2.RunDetails);
|
|
211
|
+
} }
|
|
212
|
+
function ArchiveRunViewerComponent_Conditional_5_Template(rf, ctx) { if (rf & 1) {
|
|
213
|
+
const _r5 = i0.ɵɵgetCurrentView();
|
|
214
|
+
i0.ɵɵelementStart(0, "div", 38)(1, "h3");
|
|
215
|
+
i0.ɵɵtext(2, "Run Details");
|
|
216
|
+
i0.ɵɵelementEnd();
|
|
217
|
+
i0.ɵɵelementStart(3, "button", 39);
|
|
218
|
+
i0.ɵɵlistener("click", function ArchiveRunViewerComponent_Conditional_5_Template_button_click_3_listener() { i0.ɵɵrestoreView(_r5); const ctx_r2 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r2.CloseDrawer()); });
|
|
219
|
+
i0.ɵɵelement(4, "i", 40);
|
|
220
|
+
i0.ɵɵelementEnd()();
|
|
221
|
+
i0.ɵɵelementStart(5, "div", 41)(6, "div", 42)(7, "span", 43);
|
|
222
|
+
i0.ɵɵtext(8, "Configuration");
|
|
223
|
+
i0.ɵɵelementEnd();
|
|
224
|
+
i0.ɵɵelementStart(9, "span", 44);
|
|
225
|
+
i0.ɵɵtext(10);
|
|
226
|
+
i0.ɵɵelementEnd()();
|
|
227
|
+
i0.ɵɵelementStart(11, "div", 42)(12, "span", 43);
|
|
228
|
+
i0.ɵɵtext(13, "Status");
|
|
229
|
+
i0.ɵɵelementEnd();
|
|
230
|
+
i0.ɵɵelementStart(14, "span", 26);
|
|
231
|
+
i0.ɵɵtext(15);
|
|
232
|
+
i0.ɵɵelementEnd()();
|
|
233
|
+
i0.ɵɵelementStart(16, "div", 42)(17, "span", 43);
|
|
234
|
+
i0.ɵɵtext(18, "Started");
|
|
235
|
+
i0.ɵɵelementEnd();
|
|
236
|
+
i0.ɵɵelementStart(19, "span", 44);
|
|
237
|
+
i0.ɵɵtext(20);
|
|
238
|
+
i0.ɵɵelementEnd()();
|
|
239
|
+
i0.ɵɵelementStart(21, "div", 42)(22, "span", 43);
|
|
240
|
+
i0.ɵɵtext(23, "Duration");
|
|
241
|
+
i0.ɵɵelementEnd();
|
|
242
|
+
i0.ɵɵelementStart(24, "span", 44);
|
|
243
|
+
i0.ɵɵtext(25);
|
|
244
|
+
i0.ɵɵelementEnd()()();
|
|
245
|
+
i0.ɵɵelementStart(26, "div", 45)(27, "div", 46)(28, "span", 47);
|
|
246
|
+
i0.ɵɵtext(29);
|
|
247
|
+
i0.ɵɵelementEnd();
|
|
248
|
+
i0.ɵɵelementStart(30, "span", 48);
|
|
249
|
+
i0.ɵɵtext(31, "Archived");
|
|
250
|
+
i0.ɵɵelementEnd()();
|
|
251
|
+
i0.ɵɵelementStart(32, "div", 49)(33, "span", 47);
|
|
252
|
+
i0.ɵɵtext(34);
|
|
253
|
+
i0.ɵɵelementEnd();
|
|
254
|
+
i0.ɵɵelementStart(35, "span", 48);
|
|
255
|
+
i0.ɵɵtext(36, "Failed");
|
|
256
|
+
i0.ɵɵelementEnd()();
|
|
257
|
+
i0.ɵɵelementStart(37, "div", 50)(38, "span", 47);
|
|
258
|
+
i0.ɵɵtext(39);
|
|
259
|
+
i0.ɵɵelementEnd();
|
|
260
|
+
i0.ɵɵelementStart(40, "span", 48);
|
|
261
|
+
i0.ɵɵtext(41, "Skipped");
|
|
262
|
+
i0.ɵɵelementEnd()()();
|
|
263
|
+
i0.ɵɵelementStart(42, "div", 51)(43, "h4");
|
|
264
|
+
i0.ɵɵtext(44, "Per-Record Results");
|
|
265
|
+
i0.ɵɵelementEnd();
|
|
266
|
+
i0.ɵɵconditionalCreate(45, ArchiveRunViewerComponent_Conditional_5_Conditional_45_Template, 1, 0, "mj-loading", 52);
|
|
267
|
+
i0.ɵɵconditionalCreate(46, ArchiveRunViewerComponent_Conditional_5_Conditional_46_Template, 2, 0, "p", 53);
|
|
268
|
+
i0.ɵɵconditionalCreate(47, ArchiveRunViewerComponent_Conditional_5_Conditional_47_Template, 15, 0, "div", 54);
|
|
269
|
+
i0.ɵɵelementEnd();
|
|
270
|
+
} if (rf & 2) {
|
|
271
|
+
const ctx_r2 = i0.ɵɵnextContext();
|
|
272
|
+
i0.ɵɵadvance(10);
|
|
273
|
+
i0.ɵɵtextInterpolate(ctx_r2.SelectedRun.ConfigurationName);
|
|
274
|
+
i0.ɵɵadvance(4);
|
|
275
|
+
i0.ɵɵproperty("ngClass", ctx_r2.GetStatusClass(ctx_r2.SelectedRun.Status));
|
|
276
|
+
i0.ɵɵadvance();
|
|
277
|
+
i0.ɵɵtextInterpolate(ctx_r2.SelectedRun.Status);
|
|
278
|
+
i0.ɵɵadvance(5);
|
|
279
|
+
i0.ɵɵtextInterpolate(ctx_r2.FormatDate(ctx_r2.SelectedRun.StartedAt));
|
|
280
|
+
i0.ɵɵadvance(5);
|
|
281
|
+
i0.ɵɵtextInterpolate(ctx_r2.FormatDuration(ctx_r2.SelectedRun.DurationSeconds));
|
|
282
|
+
i0.ɵɵadvance(4);
|
|
283
|
+
i0.ɵɵtextInterpolate(ctx_r2.SelectedRun.RecordsArchived);
|
|
284
|
+
i0.ɵɵadvance(5);
|
|
285
|
+
i0.ɵɵtextInterpolate(ctx_r2.SelectedRun.RecordsFailed);
|
|
286
|
+
i0.ɵɵadvance(5);
|
|
287
|
+
i0.ɵɵtextInterpolate(ctx_r2.SelectedRun.RecordsSkipped);
|
|
288
|
+
i0.ɵɵadvance(6);
|
|
289
|
+
i0.ɵɵconditional(ctx_r2.IsDrawerLoading ? 45 : -1);
|
|
290
|
+
i0.ɵɵadvance();
|
|
291
|
+
i0.ɵɵconditional(!ctx_r2.IsDrawerLoading && ctx_r2.RunDetails.length === 0 ? 46 : -1);
|
|
292
|
+
i0.ɵɵadvance();
|
|
293
|
+
i0.ɵɵconditional(!ctx_r2.IsDrawerLoading && ctx_r2.RunDetails.length > 0 ? 47 : -1);
|
|
294
|
+
} }
|
|
295
|
+
export class ArchiveRunViewerComponent {
|
|
296
|
+
cdr = inject(ChangeDetectorRef);
|
|
297
|
+
/** Summary metrics */
|
|
298
|
+
Metrics = {
|
|
299
|
+
TotalRuns: 0,
|
|
300
|
+
TotalRecordsArchived: 0,
|
|
301
|
+
TotalStorageBytes: 0,
|
|
302
|
+
LastRunDate: null,
|
|
303
|
+
};
|
|
304
|
+
/** All archive runs */
|
|
305
|
+
Runs = [];
|
|
306
|
+
/** Currently selected run for the drawer */
|
|
307
|
+
SelectedRun = null;
|
|
308
|
+
/** Detail records for the selected run */
|
|
309
|
+
RunDetails = [];
|
|
310
|
+
/** Whether the drawer is open */
|
|
311
|
+
DrawerOpen = false;
|
|
312
|
+
/** Whether the main data is loading */
|
|
313
|
+
IsLoading = true;
|
|
314
|
+
/** Whether drawer details are loading */
|
|
315
|
+
IsDrawerLoading = false;
|
|
316
|
+
destroyed = false;
|
|
317
|
+
ngOnInit() {
|
|
318
|
+
this.loadRunData();
|
|
319
|
+
}
|
|
320
|
+
ngOnDestroy() {
|
|
321
|
+
this.destroyed = true;
|
|
322
|
+
}
|
|
323
|
+
/** Open the drawer for a specific run */
|
|
324
|
+
async OpenRunDrawer(run) {
|
|
325
|
+
this.SelectedRun = run;
|
|
326
|
+
this.DrawerOpen = true;
|
|
327
|
+
this.cdr.markForCheck();
|
|
328
|
+
await this.loadRunDetails(run.ID);
|
|
329
|
+
}
|
|
330
|
+
/** Close the drawer */
|
|
331
|
+
CloseDrawer() {
|
|
332
|
+
this.DrawerOpen = false;
|
|
333
|
+
this.SelectedRun = null;
|
|
334
|
+
this.RunDetails = [];
|
|
335
|
+
this.cdr.markForCheck();
|
|
336
|
+
}
|
|
337
|
+
/** Format bytes into a readable string */
|
|
338
|
+
FormatBytes(bytes) {
|
|
339
|
+
const numBytes = Number(bytes);
|
|
340
|
+
if (!numBytes || numBytes <= 0)
|
|
341
|
+
return '0 B';
|
|
342
|
+
const units = ['B', 'KB', 'MB', 'GB', 'TB'];
|
|
343
|
+
const k = 1024;
|
|
344
|
+
const i = Math.floor(Math.log(numBytes) / Math.log(k));
|
|
345
|
+
const value = numBytes / Math.pow(k, i);
|
|
346
|
+
return `${value.toFixed(i > 0 ? 1 : 0)} ${units[i]}`;
|
|
347
|
+
}
|
|
348
|
+
/** Format a date for display */
|
|
349
|
+
FormatDate(date) {
|
|
350
|
+
if (!date)
|
|
351
|
+
return 'N/A';
|
|
352
|
+
return date.toLocaleDateString(undefined, {
|
|
353
|
+
year: 'numeric',
|
|
354
|
+
month: 'short',
|
|
355
|
+
day: 'numeric',
|
|
356
|
+
hour: '2-digit',
|
|
357
|
+
minute: '2-digit',
|
|
358
|
+
});
|
|
359
|
+
}
|
|
360
|
+
/** Format duration in seconds to a readable string */
|
|
361
|
+
FormatDuration(seconds) {
|
|
362
|
+
if (seconds < 60)
|
|
363
|
+
return `${seconds}s`;
|
|
364
|
+
const mins = Math.floor(seconds / 60);
|
|
365
|
+
const secs = seconds % 60;
|
|
366
|
+
return secs > 0 ? `${mins}m ${secs}s` : `${mins}m`;
|
|
367
|
+
}
|
|
368
|
+
/** Get CSS class for a status badge */
|
|
369
|
+
GetStatusClass(status) {
|
|
370
|
+
switch (status.toLowerCase()) {
|
|
371
|
+
case 'success':
|
|
372
|
+
case 'completed':
|
|
373
|
+
return 'status-badge-success';
|
|
374
|
+
case 'failed':
|
|
375
|
+
case 'error':
|
|
376
|
+
return 'status-badge-error';
|
|
377
|
+
case 'running':
|
|
378
|
+
case 'in progress':
|
|
379
|
+
return 'status-badge-info';
|
|
380
|
+
default:
|
|
381
|
+
return 'status-badge-default';
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
/** Load all run data and compute metrics */
|
|
385
|
+
async loadRunData() {
|
|
386
|
+
this.IsLoading = true;
|
|
387
|
+
this.cdr.markForCheck();
|
|
388
|
+
try {
|
|
389
|
+
const runs = await this.fetchRuns();
|
|
390
|
+
this.Runs = runs;
|
|
391
|
+
this.Metrics = this.computeMetrics(runs);
|
|
392
|
+
}
|
|
393
|
+
catch {
|
|
394
|
+
this.Runs = [];
|
|
395
|
+
}
|
|
396
|
+
finally {
|
|
397
|
+
this.IsLoading = false;
|
|
398
|
+
this.markForCheckIfAlive();
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
/** Fetch run records from the server */
|
|
402
|
+
async fetchRuns() {
|
|
403
|
+
const rv = new RunView();
|
|
404
|
+
const result = await rv.RunView({
|
|
405
|
+
EntityName: 'MJ: Archive Runs',
|
|
406
|
+
ExtraFilter: '',
|
|
407
|
+
OrderBy: 'StartedAt DESC',
|
|
408
|
+
ResultType: 'simple',
|
|
409
|
+
});
|
|
410
|
+
if (!result.Success)
|
|
411
|
+
return [];
|
|
412
|
+
return result.Results.map((r) => this.mapRunRecord(r));
|
|
413
|
+
}
|
|
414
|
+
/** Map a raw run record to a typed summary */
|
|
415
|
+
mapRunRecord(r) {
|
|
416
|
+
const startedAt = new Date(r.StartedAt);
|
|
417
|
+
const completedAt = r.CompletedAt ? new Date(r.CompletedAt) : null;
|
|
418
|
+
const durationSeconds = completedAt
|
|
419
|
+
? Math.round((completedAt.getTime() - startedAt.getTime()) / 1000)
|
|
420
|
+
: 0;
|
|
421
|
+
return {
|
|
422
|
+
ID: r.ID,
|
|
423
|
+
ConfigurationName: r.ArchiveConfiguration ?? '',
|
|
424
|
+
StartedAt: startedAt,
|
|
425
|
+
CompletedAt: completedAt,
|
|
426
|
+
Status: r.Status ?? 'Unknown',
|
|
427
|
+
RecordsArchived: Number(r.ArchivedRecords) || 0,
|
|
428
|
+
RecordsFailed: Number(r.FailedRecords) || 0,
|
|
429
|
+
RecordsSkipped: Number(r.SkippedRecords) || 0,
|
|
430
|
+
TotalBytes: Number(r.TotalBytesArchived) || 0,
|
|
431
|
+
DurationSeconds: durationSeconds,
|
|
432
|
+
};
|
|
433
|
+
}
|
|
434
|
+
/** Compute summary metrics from all runs */
|
|
435
|
+
computeMetrics(runs) {
|
|
436
|
+
return {
|
|
437
|
+
TotalRuns: runs.length,
|
|
438
|
+
TotalRecordsArchived: runs.reduce((sum, r) => sum + r.RecordsArchived, 0),
|
|
439
|
+
TotalStorageBytes: runs.reduce((sum, r) => sum + r.TotalBytes, 0),
|
|
440
|
+
LastRunDate: runs.length > 0 ? runs[0].StartedAt : null,
|
|
441
|
+
};
|
|
442
|
+
}
|
|
443
|
+
/** Load detail records for a specific run */
|
|
444
|
+
async loadRunDetails(runId) {
|
|
445
|
+
this.IsDrawerLoading = true;
|
|
446
|
+
this.RunDetails = [];
|
|
447
|
+
this.cdr.markForCheck();
|
|
448
|
+
try {
|
|
449
|
+
const rv = new RunView();
|
|
450
|
+
const escapedId = runId.replace(/'/g, "''");
|
|
451
|
+
const result = await rv.RunView({
|
|
452
|
+
EntityName: 'MJ: Archive Run Details',
|
|
453
|
+
ExtraFilter: `ArchiveRunID='${escapedId}'`,
|
|
454
|
+
OrderBy: '__mj_CreatedAt DESC',
|
|
455
|
+
ResultType: 'simple',
|
|
456
|
+
Fields: ['ID', 'Entity', 'RecordID', 'Status', 'StoragePath', 'BytesArchived', '__mj_CreatedAt'],
|
|
457
|
+
});
|
|
458
|
+
if (result.Success) {
|
|
459
|
+
this.RunDetails = result.Results.map((r) => this.mapDetailRecord(r));
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
catch {
|
|
463
|
+
this.RunDetails = [];
|
|
464
|
+
}
|
|
465
|
+
finally {
|
|
466
|
+
this.IsDrawerLoading = false;
|
|
467
|
+
this.markForCheckIfAlive();
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
/** Map a raw detail record to a typed row */
|
|
471
|
+
mapDetailRecord(r) {
|
|
472
|
+
return {
|
|
473
|
+
ID: r.ID,
|
|
474
|
+
EntityName: r.Entity ?? '',
|
|
475
|
+
RecordID: r.RecordID ?? '',
|
|
476
|
+
Status: r.Status ?? 'Unknown',
|
|
477
|
+
StoragePath: r.StoragePath ?? '',
|
|
478
|
+
Bytes: Number(r.BytesArchived) || 0,
|
|
479
|
+
ArchivedAt: r.__mj_CreatedAt ?? '',
|
|
480
|
+
};
|
|
481
|
+
}
|
|
482
|
+
/** Mark for check only if not destroyed */
|
|
483
|
+
markForCheckIfAlive() {
|
|
484
|
+
if (!this.destroyed) {
|
|
485
|
+
this.cdr.markForCheck();
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
static ɵfac = function ArchiveRunViewerComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || ArchiveRunViewerComponent)(); };
|
|
489
|
+
static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: ArchiveRunViewerComponent, selectors: [["mj-archive-run-viewer"]], standalone: false, decls: 6, vars: 6, consts: [[1, "run-viewer"], [1, "run-viewer-loading"], [1, "archive-drawer-backdrop"], [1, "archive-drawer"], ["text", "Loading archive runs..."], [1, "metrics-row"], [1, "metric-card"], [1, "metric-icon"], [1, "fa-solid", "fa-play-circle"], [1, "metric-content"], [1, "metric-value"], [1, "metric-label"], [1, "metric-icon", "metric-icon-archive"], [1, "fa-solid", "fa-box-archive"], [1, "metric-icon", "metric-icon-storage"], [1, "fa-solid", "fa-database"], [1, "metric-icon", "metric-icon-time"], [1, "fa-solid", "fa-clock"], [1, "run-list"], [1, "run-list-title"], [1, "run-empty"], ["tabindex", "0", "role", "button", 1, "run-card"], [1, "fa-solid", "fa-inbox"], ["tabindex", "0", "role", "button", 1, "run-card", 3, "click", "keydown.enter"], [1, "run-card-header"], [1, "run-card-name"], [1, "status-badge", 3, "ngClass"], [1, "run-card-date"], [1, "fa-solid", "fa-calendar"], [1, "run-card-stats"], [1, "run-stat"], [1, "run-stat", "run-stat-error"], [1, "run-stat", "run-stat-muted"], [1, "fa-solid", "fa-stopwatch"], [1, "fa-solid", "fa-hard-drive"], [1, "fa-solid", "fa-circle-xmark"], [1, "fa-solid", "fa-forward"], [1, "archive-drawer-backdrop", 3, "click"], [1, "drawer-header"], ["aria-label", "Close drawer", 1, "drawer-close-btn", 3, "click"], [1, "fa-solid", "fa-xmark"], [1, "drawer-summary"], [1, "drawer-summary-row"], [1, "drawer-label"], [1, "drawer-value"], [1, "drawer-stats"], [1, "drawer-stat"], [1, "drawer-stat-value"], [1, "drawer-stat-label"], [1, "drawer-stat", "drawer-stat-error"], [1, "drawer-stat", "drawer-stat-muted"], [1, "drawer-details"], ["text", "Loading details...", "size", "small"], [1, "drawer-details-empty"], [1, "drawer-table-wrapper"], [1, "drawer-table"], [1, "cell-entity"], [1, "cell-id", 3, "title"], [1, "status-badge", "status-badge-sm", 3, "ngClass"], [1, "cell-bytes"]], template: function ArchiveRunViewerComponent_Template(rf, ctx) { if (rf & 1) {
|
|
490
|
+
i0.ɵɵelementStart(0, "div", 0);
|
|
491
|
+
i0.ɵɵconditionalCreate(1, ArchiveRunViewerComponent_Conditional_1_Template, 2, 0, "div", 1);
|
|
492
|
+
i0.ɵɵconditionalCreate(2, ArchiveRunViewerComponent_Conditional_2_Template, 39, 5);
|
|
493
|
+
i0.ɵɵconditionalCreate(3, ArchiveRunViewerComponent_Conditional_3_Template, 1, 0, "div", 2);
|
|
494
|
+
i0.ɵɵelementStart(4, "div", 3);
|
|
495
|
+
i0.ɵɵconditionalCreate(5, ArchiveRunViewerComponent_Conditional_5_Template, 48, 11);
|
|
496
|
+
i0.ɵɵelementEnd()();
|
|
497
|
+
} if (rf & 2) {
|
|
498
|
+
i0.ɵɵadvance();
|
|
499
|
+
i0.ɵɵconditional(ctx.IsLoading ? 1 : -1);
|
|
500
|
+
i0.ɵɵadvance();
|
|
501
|
+
i0.ɵɵconditional(!ctx.IsLoading ? 2 : -1);
|
|
502
|
+
i0.ɵɵadvance();
|
|
503
|
+
i0.ɵɵconditional(ctx.DrawerOpen ? 3 : -1);
|
|
504
|
+
i0.ɵɵadvance();
|
|
505
|
+
i0.ɵɵclassProp("open", ctx.DrawerOpen);
|
|
506
|
+
i0.ɵɵadvance();
|
|
507
|
+
i0.ɵɵconditional(ctx.SelectedRun ? 5 : -1);
|
|
508
|
+
} }, dependencies: [i1.NgClass, i2.LoadingComponent], styles: [".run-viewer[_ngcontent-%COMP%] {\n padding: 20px;\n position: relative;\n}\n\n\n\n.run-viewer-loading[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 60px;\n}\n\n\n\n.metrics-row[_ngcontent-%COMP%] {\n display: grid;\n grid-template-columns: repeat(4, 1fr);\n gap: 16px;\n margin-bottom: 24px;\n}\n\n.metric-card[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 14px;\n padding: 18px 20px;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-subtle);\n border-radius: 8px;\n transition: border-color 0.15s ease, box-shadow 0.15s ease;\n\n &:hover {\n border-color: var(--mj-border-default);\n box-shadow: 0 2px 8px color-mix(in srgb, var(--mj-text-primary) 6%, transparent);\n }\n}\n\n.metric-icon[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 42px;\n height: 42px;\n border-radius: 10px;\n font-size: 1.1rem;\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n}\n\n.metric-icon-archive[_ngcontent-%COMP%] {\n background: color-mix(in srgb, var(--mj-status-success) 10%, var(--mj-bg-surface));\n color: var(--mj-status-success);\n}\n\n.metric-icon-storage[_ngcontent-%COMP%] {\n background: color-mix(in srgb, var(--mj-status-warning) 10%, var(--mj-bg-surface));\n color: var(--mj-status-warning);\n}\n\n.metric-icon-time[_ngcontent-%COMP%] {\n background: color-mix(in srgb, var(--mj-status-info) 10%, var(--mj-bg-surface));\n color: var(--mj-status-info);\n}\n\n.metric-content[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n}\n\n.metric-value[_ngcontent-%COMP%] {\n font-size: 1.25rem;\n font-weight: 700;\n color: var(--mj-text-primary);\n line-height: 1.2;\n}\n\n.metric-label[_ngcontent-%COMP%] {\n font-size: 0.78rem;\n color: var(--mj-text-muted);\n margin-top: 2px;\n}\n\n\n\n.run-list-title[_ngcontent-%COMP%] {\n margin: 0 0 14px;\n font-size: 1rem;\n font-weight: 600;\n color: var(--mj-text-primary);\n}\n\n.run-empty[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n align-items: center;\n padding: 40px;\n color: var(--mj-text-muted);\n text-align: center;\n\n i {\n font-size: 2rem;\n margin-bottom: 12px;\n }\n\n p {\n margin: 0;\n font-size: 0.9rem;\n }\n}\n\n\n\n.run-card[_ngcontent-%COMP%] {\n padding: 14px 18px;\n margin-bottom: 10px;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-subtle);\n border-radius: 6px;\n cursor: pointer;\n transition: border-color 0.15s ease, background 0.15s ease;\n\n &:hover {\n border-color: var(--mj-border-default);\n background: var(--mj-bg-surface-hover);\n }\n\n &:focus-visible {\n outline: 2px solid var(--mj-border-focus);\n outline-offset: 2px;\n }\n}\n\n.run-card-header[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: space-between;\n margin-bottom: 6px;\n}\n\n.run-card-name[_ngcontent-%COMP%] {\n font-weight: 600;\n font-size: 0.9rem;\n color: var(--mj-text-primary);\n}\n\n.run-card-date[_ngcontent-%COMP%] {\n font-size: 0.78rem;\n color: var(--mj-text-muted);\n margin-bottom: 8px;\n\n i {\n margin-right: 4px;\n }\n}\n\n.run-card-stats[_ngcontent-%COMP%] {\n display: flex;\n flex-wrap: wrap;\n gap: 14px;\n}\n\n.run-stat[_ngcontent-%COMP%] {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n font-size: 0.78rem;\n color: var(--mj-text-secondary);\n\n i {\n font-size: 0.72rem;\n }\n}\n\n.run-stat-error[_ngcontent-%COMP%] {\n color: var(--mj-status-error-text);\n}\n\n.run-stat-muted[_ngcontent-%COMP%] {\n color: var(--mj-text-muted);\n}\n\n\n\n.status-badge[_ngcontent-%COMP%] {\n display: inline-flex;\n align-items: center;\n padding: 2px 10px;\n font-size: 0.72rem;\n font-weight: 600;\n border-radius: 10px;\n text-transform: uppercase;\n letter-spacing: 0.03em;\n}\n\n.status-badge-sm[_ngcontent-%COMP%] {\n padding: 1px 7px;\n font-size: 0.68rem;\n}\n\n.status-badge-success[_ngcontent-%COMP%] {\n background: var(--mj-status-success-bg);\n color: var(--mj-status-success-text);\n}\n\n.status-badge-error[_ngcontent-%COMP%] {\n background: var(--mj-status-error-bg);\n color: var(--mj-status-error-text);\n}\n\n.status-badge-info[_ngcontent-%COMP%] {\n background: var(--mj-status-info-bg);\n color: var(--mj-status-info-text);\n}\n\n.status-badge-default[_ngcontent-%COMP%] {\n background: var(--mj-bg-surface-sunken);\n color: var(--mj-text-muted);\n}\n\n\n\n.archive-drawer-backdrop[_ngcontent-%COMP%] {\n position: fixed;\n inset: 0;\n background: var(--mj-bg-overlay);\n z-index: 999;\n}\n\n\n\n.archive-drawer[_ngcontent-%COMP%] {\n position: fixed;\n top: 0;\n right: 0;\n width: 400px;\n height: 100vh;\n background: var(--mj-bg-surface);\n border-left: 1px solid var(--mj-border-default);\n box-shadow: -4px 0 24px color-mix(in srgb, var(--mj-text-primary) 10%, transparent);\n z-index: 1000;\n transform: translateX(100%);\n transition: transform 0.3s ease;\n display: flex;\n flex-direction: column;\n overflow: hidden;\n\n &.open {\n transform: translateX(0);\n }\n}\n\n\n\n.drawer-header[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 14px 18px;\n border-bottom: 1px solid var(--mj-border-default);\n background: var(--mj-bg-surface-card);\n\n h3 {\n margin: 0;\n font-size: 1rem;\n font-weight: 600;\n color: var(--mj-text-primary);\n }\n}\n\n.drawer-close-btn[_ngcontent-%COMP%] {\n background: none;\n border: none;\n cursor: pointer;\n padding: 6px;\n border-radius: 4px;\n color: var(--mj-text-muted);\n font-size: 1rem;\n transition: color 0.15s ease, background 0.15s ease;\n\n &:hover {\n color: var(--mj-text-primary);\n background: var(--mj-bg-surface-hover);\n }\n}\n\n\n\n.drawer-summary[_ngcontent-%COMP%] {\n padding: 14px 18px;\n border-bottom: 1px solid var(--mj-border-subtle);\n}\n\n.drawer-summary-row[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 4px 0;\n}\n\n.drawer-label[_ngcontent-%COMP%] {\n font-size: 0.78rem;\n font-weight: 600;\n color: var(--mj-text-secondary);\n text-transform: uppercase;\n letter-spacing: 0.03em;\n}\n\n.drawer-value[_ngcontent-%COMP%] {\n font-size: 0.85rem;\n color: var(--mj-text-primary);\n}\n\n\n\n.drawer-stats[_ngcontent-%COMP%] {\n display: flex;\n gap: 0;\n border-bottom: 1px solid var(--mj-border-subtle);\n}\n\n.drawer-stat[_ngcontent-%COMP%] {\n flex: 1;\n display: flex;\n flex-direction: column;\n align-items: center;\n padding: 14px 10px;\n border-right: 1px solid var(--mj-border-subtle);\n\n &:last-child {\n border-right: none;\n }\n}\n\n.drawer-stat-value[_ngcontent-%COMP%] {\n font-size: 1.2rem;\n font-weight: 700;\n color: var(--mj-text-primary);\n}\n\n.drawer-stat-error[_ngcontent-%COMP%] .drawer-stat-value[_ngcontent-%COMP%] {\n color: var(--mj-status-error);\n}\n\n.drawer-stat-muted[_ngcontent-%COMP%] .drawer-stat-value[_ngcontent-%COMP%] {\n color: var(--mj-text-muted);\n}\n\n.drawer-stat-label[_ngcontent-%COMP%] {\n font-size: 0.72rem;\n color: var(--mj-text-muted);\n margin-top: 2px;\n}\n\n\n\n.drawer-details[_ngcontent-%COMP%] {\n flex: 1;\n padding: 14px 18px;\n overflow-y: auto;\n\n h4 {\n margin: 0 0 12px;\n font-size: 0.88rem;\n font-weight: 600;\n color: var(--mj-text-primary);\n }\n}\n\n.drawer-details-empty[_ngcontent-%COMP%] {\n color: var(--mj-text-muted);\n font-size: 0.85rem;\n text-align: center;\n padding: 20px;\n}\n\n.drawer-table-wrapper[_ngcontent-%COMP%] {\n overflow-x: auto;\n border: 1px solid var(--mj-border-default);\n border-radius: 6px;\n}\n\n.drawer-table[_ngcontent-%COMP%] {\n width: 100%;\n border-collapse: collapse;\n font-size: 0.78rem;\n\n th {\n padding: 7px 10px;\n text-align: left;\n font-weight: 600;\n font-size: 0.72rem;\n text-transform: uppercase;\n letter-spacing: 0.03em;\n color: var(--mj-text-secondary);\n background: var(--mj-bg-surface-card);\n border-bottom: 1px solid var(--mj-border-default);\n }\n\n td {\n padding: 6px 10px;\n border-bottom: 1px solid var(--mj-border-subtle);\n color: var(--mj-text-primary);\n }\n\n tr:last-child td {\n border-bottom: none;\n }\n}\n\n.cell-entity[_ngcontent-%COMP%] {\n font-weight: 500;\n}\n\n.cell-id[_ngcontent-%COMP%] {\n max-width: 100px;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n font-family: monospace;\n font-size: 0.72rem;\n color: var(--mj-text-secondary);\n}\n\n.cell-bytes[_ngcontent-%COMP%] {\n white-space: nowrap;\n}\n\n\n\n@media (max-width: 900px) {\n .metrics-row[_ngcontent-%COMP%] {\n grid-template-columns: repeat(2, 1fr);\n }\n}\n\n@media (max-width: 600px) {\n .metrics-row[_ngcontent-%COMP%] {\n grid-template-columns: 1fr;\n }\n}"], changeDetection: 0 });
|
|
509
|
+
}
|
|
510
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(ArchiveRunViewerComponent, [{
|
|
511
|
+
type: Component,
|
|
512
|
+
args: [{ standalone: false, selector: 'mj-archive-run-viewer', changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"run-viewer\">\n <!-- Loading state -->\n @if (IsLoading) {\n <div class=\"run-viewer-loading\">\n <mj-loading text=\"Loading archive runs...\"></mj-loading>\n </div>\n }\n\n @if (!IsLoading) {\n <!-- Summary metric cards -->\n <div class=\"metrics-row\">\n <div class=\"metric-card\">\n <div class=\"metric-icon\">\n <i class=\"fa-solid fa-play-circle\"></i>\n </div>\n <div class=\"metric-content\">\n <span class=\"metric-value\">{{ Metrics.TotalRuns }}</span>\n <span class=\"metric-label\">Total Runs</span>\n </div>\n </div>\n\n <div class=\"metric-card\">\n <div class=\"metric-icon metric-icon-archive\">\n <i class=\"fa-solid fa-box-archive\"></i>\n </div>\n <div class=\"metric-content\">\n <span class=\"metric-value\">{{ Metrics.TotalRecordsArchived }}</span>\n <span class=\"metric-label\">Records Archived</span>\n </div>\n </div>\n\n <div class=\"metric-card\">\n <div class=\"metric-icon metric-icon-storage\">\n <i class=\"fa-solid fa-database\"></i>\n </div>\n <div class=\"metric-content\">\n <span class=\"metric-value\">{{ FormatBytes(Metrics.TotalStorageBytes) }}</span>\n <span class=\"metric-label\">Storage Used</span>\n </div>\n </div>\n\n <div class=\"metric-card\">\n <div class=\"metric-icon metric-icon-time\">\n <i class=\"fa-solid fa-clock\"></i>\n </div>\n <div class=\"metric-content\">\n <span class=\"metric-value\">{{ FormatDate(Metrics.LastRunDate) }}</span>\n <span class=\"metric-label\">Last Run</span>\n </div>\n </div>\n </div>\n\n <!-- Run list -->\n <div class=\"run-list\">\n <h3 class=\"run-list-title\">Run History</h3>\n\n @if (Runs.length === 0) {\n <div class=\"run-empty\">\n <i class=\"fa-solid fa-inbox\"></i>\n <p>No archive runs found.</p>\n </div>\n }\n\n @for (run of Runs; track run.ID) {\n <div\n class=\"run-card\"\n (click)=\"OpenRunDrawer(run)\"\n (keydown.enter)=\"OpenRunDrawer(run)\"\n tabindex=\"0\"\n role=\"button\"\n [attr.aria-label]=\"'View details for run on ' + FormatDate(run.StartedAt)\">\n <div class=\"run-card-header\">\n <span class=\"run-card-name\">{{ run.ConfigurationName }}</span>\n <span class=\"status-badge\" [ngClass]=\"GetStatusClass(run.Status)\">{{ run.Status }}</span>\n </div>\n <div class=\"run-card-date\">\n <i class=\"fa-solid fa-calendar\"></i>\n {{ FormatDate(run.StartedAt) }}\n </div>\n <div class=\"run-card-stats\">\n <span class=\"run-stat\">\n <i class=\"fa-solid fa-box-archive\"></i>\n {{ run.RecordsArchived }} archived\n </span>\n @if (run.RecordsFailed > 0) {\n <span class=\"run-stat run-stat-error\">\n <i class=\"fa-solid fa-circle-xmark\"></i>\n {{ run.RecordsFailed }} failed\n </span>\n }\n @if (run.RecordsSkipped > 0) {\n <span class=\"run-stat run-stat-muted\">\n <i class=\"fa-solid fa-forward\"></i>\n {{ run.RecordsSkipped }} skipped\n </span>\n }\n <span class=\"run-stat\">\n <i class=\"fa-solid fa-stopwatch\"></i>\n {{ FormatDuration(run.DurationSeconds) }}\n </span>\n <span class=\"run-stat\">\n <i class=\"fa-solid fa-hard-drive\"></i>\n {{ FormatBytes(run.TotalBytes) }}\n </span>\n </div>\n </div>\n }\n </div>\n }\n\n <!-- Drawer backdrop -->\n @if (DrawerOpen) {\n <div class=\"archive-drawer-backdrop\" (click)=\"CloseDrawer()\"></div>\n }\n\n <!-- Slide-in drawer -->\n <div class=\"archive-drawer\" [class.open]=\"DrawerOpen\">\n @if (SelectedRun) {\n <!-- Drawer header -->\n <div class=\"drawer-header\">\n <h3>Run Details</h3>\n <button class=\"drawer-close-btn\" (click)=\"CloseDrawer()\" aria-label=\"Close drawer\">\n <i class=\"fa-solid fa-xmark\"></i>\n </button>\n </div>\n\n <!-- Run summary -->\n <div class=\"drawer-summary\">\n <div class=\"drawer-summary-row\">\n <span class=\"drawer-label\">Configuration</span>\n <span class=\"drawer-value\">{{ SelectedRun.ConfigurationName }}</span>\n </div>\n <div class=\"drawer-summary-row\">\n <span class=\"drawer-label\">Status</span>\n <span class=\"status-badge\" [ngClass]=\"GetStatusClass(SelectedRun.Status)\">{{ SelectedRun.Status }}</span>\n </div>\n <div class=\"drawer-summary-row\">\n <span class=\"drawer-label\">Started</span>\n <span class=\"drawer-value\">{{ FormatDate(SelectedRun.StartedAt) }}</span>\n </div>\n <div class=\"drawer-summary-row\">\n <span class=\"drawer-label\">Duration</span>\n <span class=\"drawer-value\">{{ FormatDuration(SelectedRun.DurationSeconds) }}</span>\n </div>\n </div>\n\n <!-- Stats row -->\n <div class=\"drawer-stats\">\n <div class=\"drawer-stat\">\n <span class=\"drawer-stat-value\">{{ SelectedRun.RecordsArchived }}</span>\n <span class=\"drawer-stat-label\">Archived</span>\n </div>\n <div class=\"drawer-stat drawer-stat-error\">\n <span class=\"drawer-stat-value\">{{ SelectedRun.RecordsFailed }}</span>\n <span class=\"drawer-stat-label\">Failed</span>\n </div>\n <div class=\"drawer-stat drawer-stat-muted\">\n <span class=\"drawer-stat-value\">{{ SelectedRun.RecordsSkipped }}</span>\n <span class=\"drawer-stat-label\">Skipped</span>\n </div>\n </div>\n\n <!-- Detail table -->\n <div class=\"drawer-details\">\n <h4>Per-Record Results</h4>\n\n @if (IsDrawerLoading) {\n <mj-loading text=\"Loading details...\" size=\"small\"></mj-loading>\n }\n\n @if (!IsDrawerLoading && RunDetails.length === 0) {\n <p class=\"drawer-details-empty\">No detail records found.</p>\n }\n\n @if (!IsDrawerLoading && RunDetails.length > 0) {\n <div class=\"drawer-table-wrapper\">\n <table class=\"drawer-table\">\n <thead>\n <tr>\n <th>Entity</th>\n <th>Record ID</th>\n <th>Status</th>\n <th>Bytes</th>\n </tr>\n </thead>\n <tbody>\n @for (detail of RunDetails; track detail.ID) {\n <tr>\n <td class=\"cell-entity\">{{ detail.EntityName }}</td>\n <td class=\"cell-id\" [title]=\"detail.RecordID\">{{ detail.RecordID }}</td>\n <td>\n <span class=\"status-badge status-badge-sm\" [ngClass]=\"GetStatusClass(detail.Status)\">\n {{ detail.Status }}\n </span>\n </td>\n <td class=\"cell-bytes\">{{ FormatBytes(detail.Bytes) }}</td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n }\n </div>\n }\n </div>\n</div>\n", styles: [".run-viewer {\n padding: 20px;\n position: relative;\n}\n\n/* Loading */\n.run-viewer-loading {\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 60px;\n}\n\n/* Metric cards row */\n.metrics-row {\n display: grid;\n grid-template-columns: repeat(4, 1fr);\n gap: 16px;\n margin-bottom: 24px;\n}\n\n.metric-card {\n display: flex;\n align-items: center;\n gap: 14px;\n padding: 18px 20px;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-subtle);\n border-radius: 8px;\n transition: border-color 0.15s ease, box-shadow 0.15s ease;\n\n &:hover {\n border-color: var(--mj-border-default);\n box-shadow: 0 2px 8px color-mix(in srgb, var(--mj-text-primary) 6%, transparent);\n }\n}\n\n.metric-icon {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 42px;\n height: 42px;\n border-radius: 10px;\n font-size: 1.1rem;\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n}\n\n.metric-icon-archive {\n background: color-mix(in srgb, var(--mj-status-success) 10%, var(--mj-bg-surface));\n color: var(--mj-status-success);\n}\n\n.metric-icon-storage {\n background: color-mix(in srgb, var(--mj-status-warning) 10%, var(--mj-bg-surface));\n color: var(--mj-status-warning);\n}\n\n.metric-icon-time {\n background: color-mix(in srgb, var(--mj-status-info) 10%, var(--mj-bg-surface));\n color: var(--mj-status-info);\n}\n\n.metric-content {\n display: flex;\n flex-direction: column;\n}\n\n.metric-value {\n font-size: 1.25rem;\n font-weight: 700;\n color: var(--mj-text-primary);\n line-height: 1.2;\n}\n\n.metric-label {\n font-size: 0.78rem;\n color: var(--mj-text-muted);\n margin-top: 2px;\n}\n\n/* Run list */\n.run-list-title {\n margin: 0 0 14px;\n font-size: 1rem;\n font-weight: 600;\n color: var(--mj-text-primary);\n}\n\n.run-empty {\n display: flex;\n flex-direction: column;\n align-items: center;\n padding: 40px;\n color: var(--mj-text-muted);\n text-align: center;\n\n i {\n font-size: 2rem;\n margin-bottom: 12px;\n }\n\n p {\n margin: 0;\n font-size: 0.9rem;\n }\n}\n\n/* Run card */\n.run-card {\n padding: 14px 18px;\n margin-bottom: 10px;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-subtle);\n border-radius: 6px;\n cursor: pointer;\n transition: border-color 0.15s ease, background 0.15s ease;\n\n &:hover {\n border-color: var(--mj-border-default);\n background: var(--mj-bg-surface-hover);\n }\n\n &:focus-visible {\n outline: 2px solid var(--mj-border-focus);\n outline-offset: 2px;\n }\n}\n\n.run-card-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n margin-bottom: 6px;\n}\n\n.run-card-name {\n font-weight: 600;\n font-size: 0.9rem;\n color: var(--mj-text-primary);\n}\n\n.run-card-date {\n font-size: 0.78rem;\n color: var(--mj-text-muted);\n margin-bottom: 8px;\n\n i {\n margin-right: 4px;\n }\n}\n\n.run-card-stats {\n display: flex;\n flex-wrap: wrap;\n gap: 14px;\n}\n\n.run-stat {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n font-size: 0.78rem;\n color: var(--mj-text-secondary);\n\n i {\n font-size: 0.72rem;\n }\n}\n\n.run-stat-error {\n color: var(--mj-status-error-text);\n}\n\n.run-stat-muted {\n color: var(--mj-text-muted);\n}\n\n/* Status badges */\n.status-badge {\n display: inline-flex;\n align-items: center;\n padding: 2px 10px;\n font-size: 0.72rem;\n font-weight: 600;\n border-radius: 10px;\n text-transform: uppercase;\n letter-spacing: 0.03em;\n}\n\n.status-badge-sm {\n padding: 1px 7px;\n font-size: 0.68rem;\n}\n\n.status-badge-success {\n background: var(--mj-status-success-bg);\n color: var(--mj-status-success-text);\n}\n\n.status-badge-error {\n background: var(--mj-status-error-bg);\n color: var(--mj-status-error-text);\n}\n\n.status-badge-info {\n background: var(--mj-status-info-bg);\n color: var(--mj-status-info-text);\n}\n\n.status-badge-default {\n background: var(--mj-bg-surface-sunken);\n color: var(--mj-text-muted);\n}\n\n/* Drawer backdrop */\n.archive-drawer-backdrop {\n position: fixed;\n inset: 0;\n background: var(--mj-bg-overlay);\n z-index: 999;\n}\n\n/* Slide-in drawer */\n.archive-drawer {\n position: fixed;\n top: 0;\n right: 0;\n width: 400px;\n height: 100vh;\n background: var(--mj-bg-surface);\n border-left: 1px solid var(--mj-border-default);\n box-shadow: -4px 0 24px color-mix(in srgb, var(--mj-text-primary) 10%, transparent);\n z-index: 1000;\n transform: translateX(100%);\n transition: transform 0.3s ease;\n display: flex;\n flex-direction: column;\n overflow: hidden;\n\n &.open {\n transform: translateX(0);\n }\n}\n\n/* Drawer header */\n.drawer-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 14px 18px;\n border-bottom: 1px solid var(--mj-border-default);\n background: var(--mj-bg-surface-card);\n\n h3 {\n margin: 0;\n font-size: 1rem;\n font-weight: 600;\n color: var(--mj-text-primary);\n }\n}\n\n.drawer-close-btn {\n background: none;\n border: none;\n cursor: pointer;\n padding: 6px;\n border-radius: 4px;\n color: var(--mj-text-muted);\n font-size: 1rem;\n transition: color 0.15s ease, background 0.15s ease;\n\n &:hover {\n color: var(--mj-text-primary);\n background: var(--mj-bg-surface-hover);\n }\n}\n\n/* Drawer summary */\n.drawer-summary {\n padding: 14px 18px;\n border-bottom: 1px solid var(--mj-border-subtle);\n}\n\n.drawer-summary-row {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 4px 0;\n}\n\n.drawer-label {\n font-size: 0.78rem;\n font-weight: 600;\n color: var(--mj-text-secondary);\n text-transform: uppercase;\n letter-spacing: 0.03em;\n}\n\n.drawer-value {\n font-size: 0.85rem;\n color: var(--mj-text-primary);\n}\n\n/* Drawer stats */\n.drawer-stats {\n display: flex;\n gap: 0;\n border-bottom: 1px solid var(--mj-border-subtle);\n}\n\n.drawer-stat {\n flex: 1;\n display: flex;\n flex-direction: column;\n align-items: center;\n padding: 14px 10px;\n border-right: 1px solid var(--mj-border-subtle);\n\n &:last-child {\n border-right: none;\n }\n}\n\n.drawer-stat-value {\n font-size: 1.2rem;\n font-weight: 700;\n color: var(--mj-text-primary);\n}\n\n.drawer-stat-error .drawer-stat-value {\n color: var(--mj-status-error);\n}\n\n.drawer-stat-muted .drawer-stat-value {\n color: var(--mj-text-muted);\n}\n\n.drawer-stat-label {\n font-size: 0.72rem;\n color: var(--mj-text-muted);\n margin-top: 2px;\n}\n\n/* Drawer detail table */\n.drawer-details {\n flex: 1;\n padding: 14px 18px;\n overflow-y: auto;\n\n h4 {\n margin: 0 0 12px;\n font-size: 0.88rem;\n font-weight: 600;\n color: var(--mj-text-primary);\n }\n}\n\n.drawer-details-empty {\n color: var(--mj-text-muted);\n font-size: 0.85rem;\n text-align: center;\n padding: 20px;\n}\n\n.drawer-table-wrapper {\n overflow-x: auto;\n border: 1px solid var(--mj-border-default);\n border-radius: 6px;\n}\n\n.drawer-table {\n width: 100%;\n border-collapse: collapse;\n font-size: 0.78rem;\n\n th {\n padding: 7px 10px;\n text-align: left;\n font-weight: 600;\n font-size: 0.72rem;\n text-transform: uppercase;\n letter-spacing: 0.03em;\n color: var(--mj-text-secondary);\n background: var(--mj-bg-surface-card);\n border-bottom: 1px solid var(--mj-border-default);\n }\n\n td {\n padding: 6px 10px;\n border-bottom: 1px solid var(--mj-border-subtle);\n color: var(--mj-text-primary);\n }\n\n tr:last-child td {\n border-bottom: none;\n }\n}\n\n.cell-entity {\n font-weight: 500;\n}\n\n.cell-id {\n max-width: 100px;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n font-family: monospace;\n font-size: 0.72rem;\n color: var(--mj-text-secondary);\n}\n\n.cell-bytes {\n white-space: nowrap;\n}\n\n/* Responsive: stack metric cards on small screens */\n@media (max-width: 900px) {\n .metrics-row {\n grid-template-columns: repeat(2, 1fr);\n }\n}\n\n@media (max-width: 600px) {\n .metrics-row {\n grid-template-columns: 1fr;\n }\n}\n"] }]
|
|
513
|
+
}], null, null); })();
|
|
514
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(ArchiveRunViewerComponent, { className: "ArchiveRunViewerComponent", filePath: "src/lib/archive-run-viewer/archive-run-viewer.component.ts", lineNumber: 51 }); })();
|
|
515
|
+
//# sourceMappingURL=archive-run-viewer.component.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"archive-run-viewer.component.js","sourceRoot":"","sources":["../../../src/lib/archive-run-viewer/archive-run-viewer.component.ts","../../../src/lib/archive-run-viewer/archive-run-viewer.component.html"],"names":[],"mappings":"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","sourcesContent":["import {\n Component,\n ChangeDetectionStrategy,\n ChangeDetectorRef,\n OnInit,\n OnDestroy,\n inject,\n} from '@angular/core';\nimport { RunView } from '@memberjunction/core';\n\n/** Summary metrics for the dashboard cards */\nexport interface ArchiveRunMetrics {\n TotalRuns: number;\n TotalRecordsArchived: number;\n TotalStorageBytes: number;\n LastRunDate: Date | null;\n}\n\n/** Represents a single archive run */\nexport interface ArchiveRunSummary {\n ID: string;\n ConfigurationName: string;\n StartedAt: Date;\n CompletedAt: Date | null;\n Status: string;\n RecordsArchived: number;\n RecordsFailed: number;\n RecordsSkipped: number;\n TotalBytes: number;\n DurationSeconds: number;\n}\n\n/** Detail record for a single archived item within a run */\nexport interface ArchiveRunDetailRow {\n ID: string;\n EntityName: string;\n RecordID: string;\n Status: string;\n StoragePath: string;\n Bytes: number;\n ArchivedAt: string;\n}\n\n@Component({\n standalone: false,\n selector: 'mj-archive-run-viewer',\n templateUrl: './archive-run-viewer.component.html',\n styleUrls: ['./archive-run-viewer.component.scss'],\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class ArchiveRunViewerComponent implements OnInit, OnDestroy {\n private cdr = inject(ChangeDetectorRef);\n\n /** Summary metrics */\n Metrics: ArchiveRunMetrics = {\n TotalRuns: 0,\n TotalRecordsArchived: 0,\n TotalStorageBytes: 0,\n LastRunDate: null,\n };\n\n /** All archive runs */\n Runs: ArchiveRunSummary[] = [];\n\n /** Currently selected run for the drawer */\n SelectedRun: ArchiveRunSummary | null = null;\n\n /** Detail records for the selected run */\n RunDetails: ArchiveRunDetailRow[] = [];\n\n /** Whether the drawer is open */\n DrawerOpen = false;\n\n /** Whether the main data is loading */\n IsLoading = true;\n\n /** Whether drawer details are loading */\n IsDrawerLoading = false;\n\n private destroyed = false;\n\n ngOnInit(): void {\n this.loadRunData();\n }\n\n ngOnDestroy(): void {\n this.destroyed = true;\n }\n\n /** Open the drawer for a specific run */\n async OpenRunDrawer(run: ArchiveRunSummary): Promise<void> {\n this.SelectedRun = run;\n this.DrawerOpen = true;\n this.cdr.markForCheck();\n await this.loadRunDetails(run.ID);\n }\n\n /** Close the drawer */\n CloseDrawer(): void {\n this.DrawerOpen = false;\n this.SelectedRun = null;\n this.RunDetails = [];\n this.cdr.markForCheck();\n }\n\n /** Format bytes into a readable string */\n FormatBytes(bytes: number | string | null | undefined): string {\n const numBytes = Number(bytes);\n if (!numBytes || numBytes <= 0) return '0 B';\n const units = ['B', 'KB', 'MB', 'GB', 'TB'];\n const k = 1024;\n const i = Math.floor(Math.log(numBytes) / Math.log(k));\n const value = numBytes / Math.pow(k, i);\n return `${value.toFixed(i > 0 ? 1 : 0)} ${units[i]}`;\n }\n\n /** Format a date for display */\n FormatDate(date: Date | null): string {\n if (!date) return 'N/A';\n return date.toLocaleDateString(undefined, {\n year: 'numeric',\n month: 'short',\n day: 'numeric',\n hour: '2-digit',\n minute: '2-digit',\n });\n }\n\n /** Format duration in seconds to a readable string */\n FormatDuration(seconds: number): string {\n if (seconds < 60) return `${seconds}s`;\n const mins = Math.floor(seconds / 60);\n const secs = seconds % 60;\n return secs > 0 ? `${mins}m ${secs}s` : `${mins}m`;\n }\n\n /** Get CSS class for a status badge */\n GetStatusClass(status: string): string {\n switch (status.toLowerCase()) {\n case 'success':\n case 'completed':\n return 'status-badge-success';\n case 'failed':\n case 'error':\n return 'status-badge-error';\n case 'running':\n case 'in progress':\n return 'status-badge-info';\n default:\n return 'status-badge-default';\n }\n }\n\n /** Load all run data and compute metrics */\n private async loadRunData(): Promise<void> {\n this.IsLoading = true;\n this.cdr.markForCheck();\n\n try {\n const runs = await this.fetchRuns();\n this.Runs = runs;\n this.Metrics = this.computeMetrics(runs);\n } catch {\n this.Runs = [];\n } finally {\n this.IsLoading = false;\n this.markForCheckIfAlive();\n }\n }\n\n /** Fetch run records from the server */\n private async fetchRuns(): Promise<ArchiveRunSummary[]> {\n const rv = new RunView();\n const result = await rv.RunView<RunRecord>({\n EntityName: 'MJ: Archive Runs',\n ExtraFilter: '',\n OrderBy: 'StartedAt DESC',\n ResultType: 'simple',\n });\n\n if (!result.Success) return [];\n return result.Results.map((r) => this.mapRunRecord(r));\n }\n\n /** Map a raw run record to a typed summary */\n private mapRunRecord(r: RunRecord): ArchiveRunSummary {\n const startedAt = new Date(r.StartedAt);\n const completedAt = r.CompletedAt ? new Date(r.CompletedAt) : null;\n const durationSeconds = completedAt\n ? Math.round((completedAt.getTime() - startedAt.getTime()) / 1000)\n : 0;\n\n return {\n ID: r.ID,\n ConfigurationName: r.ArchiveConfiguration ?? '',\n StartedAt: startedAt,\n CompletedAt: completedAt,\n Status: r.Status ?? 'Unknown',\n RecordsArchived: Number(r.ArchivedRecords) || 0,\n RecordsFailed: Number(r.FailedRecords) || 0,\n RecordsSkipped: Number(r.SkippedRecords) || 0,\n TotalBytes: Number(r.TotalBytesArchived) || 0,\n DurationSeconds: durationSeconds,\n };\n }\n\n /** Compute summary metrics from all runs */\n private computeMetrics(runs: ArchiveRunSummary[]): ArchiveRunMetrics {\n return {\n TotalRuns: runs.length,\n TotalRecordsArchived: runs.reduce((sum, r) => sum + r.RecordsArchived, 0),\n TotalStorageBytes: runs.reduce((sum, r) => sum + r.TotalBytes, 0),\n LastRunDate: runs.length > 0 ? runs[0].StartedAt : null,\n };\n }\n\n /** Load detail records for a specific run */\n private async loadRunDetails(runId: string): Promise<void> {\n this.IsDrawerLoading = true;\n this.RunDetails = [];\n this.cdr.markForCheck();\n\n try {\n const rv = new RunView();\n const escapedId = runId.replace(/'/g, \"''\");\n const result = await rv.RunView<DetailRecord>({\n EntityName: 'MJ: Archive Run Details',\n ExtraFilter: `ArchiveRunID='${escapedId}'`,\n OrderBy: '__mj_CreatedAt DESC',\n ResultType: 'simple',\n Fields: ['ID', 'Entity', 'RecordID', 'Status', 'StoragePath', 'BytesArchived', '__mj_CreatedAt'],\n });\n\n if (result.Success) {\n this.RunDetails = result.Results.map((r) => this.mapDetailRecord(r));\n }\n } catch {\n this.RunDetails = [];\n } finally {\n this.IsDrawerLoading = false;\n this.markForCheckIfAlive();\n }\n }\n\n /** Map a raw detail record to a typed row */\n private mapDetailRecord(r: DetailRecord): ArchiveRunDetailRow {\n return {\n ID: r.ID,\n EntityName: r.Entity ?? '',\n RecordID: r.RecordID ?? '',\n Status: r.Status ?? 'Unknown',\n StoragePath: r.StoragePath ?? '',\n Bytes: Number(r.BytesArchived) || 0,\n ArchivedAt: r.__mj_CreatedAt ?? '',\n };\n }\n\n /** Mark for check only if not destroyed */\n private markForCheckIfAlive(): void {\n if (!this.destroyed) {\n this.cdr.markForCheck();\n }\n }\n}\n\n/** Shape of a run record from the simple RunView query */\ninterface RunRecord {\n ID: string;\n ArchiveConfiguration: string;\n StartedAt: string;\n CompletedAt: string;\n Status: string;\n ArchivedRecords: number;\n FailedRecords: number;\n SkippedRecords: number;\n TotalBytesArchived: number;\n}\n\n/** Shape of a detail record from the simple RunView query */\ninterface DetailRecord {\n ID: string;\n Entity: string;\n RecordID: string;\n Status: string;\n StoragePath: string;\n BytesArchived: number;\n __mj_CreatedAt: string;\n}\n","<div class=\"run-viewer\">\n <!-- Loading state -->\n @if (IsLoading) {\n <div class=\"run-viewer-loading\">\n <mj-loading text=\"Loading archive runs...\"></mj-loading>\n </div>\n }\n\n @if (!IsLoading) {\n <!-- Summary metric cards -->\n <div class=\"metrics-row\">\n <div class=\"metric-card\">\n <div class=\"metric-icon\">\n <i class=\"fa-solid fa-play-circle\"></i>\n </div>\n <div class=\"metric-content\">\n <span class=\"metric-value\">{{ Metrics.TotalRuns }}</span>\n <span class=\"metric-label\">Total Runs</span>\n </div>\n </div>\n\n <div class=\"metric-card\">\n <div class=\"metric-icon metric-icon-archive\">\n <i class=\"fa-solid fa-box-archive\"></i>\n </div>\n <div class=\"metric-content\">\n <span class=\"metric-value\">{{ Metrics.TotalRecordsArchived }}</span>\n <span class=\"metric-label\">Records Archived</span>\n </div>\n </div>\n\n <div class=\"metric-card\">\n <div class=\"metric-icon metric-icon-storage\">\n <i class=\"fa-solid fa-database\"></i>\n </div>\n <div class=\"metric-content\">\n <span class=\"metric-value\">{{ FormatBytes(Metrics.TotalStorageBytes) }}</span>\n <span class=\"metric-label\">Storage Used</span>\n </div>\n </div>\n\n <div class=\"metric-card\">\n <div class=\"metric-icon metric-icon-time\">\n <i class=\"fa-solid fa-clock\"></i>\n </div>\n <div class=\"metric-content\">\n <span class=\"metric-value\">{{ FormatDate(Metrics.LastRunDate) }}</span>\n <span class=\"metric-label\">Last Run</span>\n </div>\n </div>\n </div>\n\n <!-- Run list -->\n <div class=\"run-list\">\n <h3 class=\"run-list-title\">Run History</h3>\n\n @if (Runs.length === 0) {\n <div class=\"run-empty\">\n <i class=\"fa-solid fa-inbox\"></i>\n <p>No archive runs found.</p>\n </div>\n }\n\n @for (run of Runs; track run.ID) {\n <div\n class=\"run-card\"\n (click)=\"OpenRunDrawer(run)\"\n (keydown.enter)=\"OpenRunDrawer(run)\"\n tabindex=\"0\"\n role=\"button\"\n [attr.aria-label]=\"'View details for run on ' + FormatDate(run.StartedAt)\">\n <div class=\"run-card-header\">\n <span class=\"run-card-name\">{{ run.ConfigurationName }}</span>\n <span class=\"status-badge\" [ngClass]=\"GetStatusClass(run.Status)\">{{ run.Status }}</span>\n </div>\n <div class=\"run-card-date\">\n <i class=\"fa-solid fa-calendar\"></i>\n {{ FormatDate(run.StartedAt) }}\n </div>\n <div class=\"run-card-stats\">\n <span class=\"run-stat\">\n <i class=\"fa-solid fa-box-archive\"></i>\n {{ run.RecordsArchived }} archived\n </span>\n @if (run.RecordsFailed > 0) {\n <span class=\"run-stat run-stat-error\">\n <i class=\"fa-solid fa-circle-xmark\"></i>\n {{ run.RecordsFailed }} failed\n </span>\n }\n @if (run.RecordsSkipped > 0) {\n <span class=\"run-stat run-stat-muted\">\n <i class=\"fa-solid fa-forward\"></i>\n {{ run.RecordsSkipped }} skipped\n </span>\n }\n <span class=\"run-stat\">\n <i class=\"fa-solid fa-stopwatch\"></i>\n {{ FormatDuration(run.DurationSeconds) }}\n </span>\n <span class=\"run-stat\">\n <i class=\"fa-solid fa-hard-drive\"></i>\n {{ FormatBytes(run.TotalBytes) }}\n </span>\n </div>\n </div>\n }\n </div>\n }\n\n <!-- Drawer backdrop -->\n @if (DrawerOpen) {\n <div class=\"archive-drawer-backdrop\" (click)=\"CloseDrawer()\"></div>\n }\n\n <!-- Slide-in drawer -->\n <div class=\"archive-drawer\" [class.open]=\"DrawerOpen\">\n @if (SelectedRun) {\n <!-- Drawer header -->\n <div class=\"drawer-header\">\n <h3>Run Details</h3>\n <button class=\"drawer-close-btn\" (click)=\"CloseDrawer()\" aria-label=\"Close drawer\">\n <i class=\"fa-solid fa-xmark\"></i>\n </button>\n </div>\n\n <!-- Run summary -->\n <div class=\"drawer-summary\">\n <div class=\"drawer-summary-row\">\n <span class=\"drawer-label\">Configuration</span>\n <span class=\"drawer-value\">{{ SelectedRun.ConfigurationName }}</span>\n </div>\n <div class=\"drawer-summary-row\">\n <span class=\"drawer-label\">Status</span>\n <span class=\"status-badge\" [ngClass]=\"GetStatusClass(SelectedRun.Status)\">{{ SelectedRun.Status }}</span>\n </div>\n <div class=\"drawer-summary-row\">\n <span class=\"drawer-label\">Started</span>\n <span class=\"drawer-value\">{{ FormatDate(SelectedRun.StartedAt) }}</span>\n </div>\n <div class=\"drawer-summary-row\">\n <span class=\"drawer-label\">Duration</span>\n <span class=\"drawer-value\">{{ FormatDuration(SelectedRun.DurationSeconds) }}</span>\n </div>\n </div>\n\n <!-- Stats row -->\n <div class=\"drawer-stats\">\n <div class=\"drawer-stat\">\n <span class=\"drawer-stat-value\">{{ SelectedRun.RecordsArchived }}</span>\n <span class=\"drawer-stat-label\">Archived</span>\n </div>\n <div class=\"drawer-stat drawer-stat-error\">\n <span class=\"drawer-stat-value\">{{ SelectedRun.RecordsFailed }}</span>\n <span class=\"drawer-stat-label\">Failed</span>\n </div>\n <div class=\"drawer-stat drawer-stat-muted\">\n <span class=\"drawer-stat-value\">{{ SelectedRun.RecordsSkipped }}</span>\n <span class=\"drawer-stat-label\">Skipped</span>\n </div>\n </div>\n\n <!-- Detail table -->\n <div class=\"drawer-details\">\n <h4>Per-Record Results</h4>\n\n @if (IsDrawerLoading) {\n <mj-loading text=\"Loading details...\" size=\"small\"></mj-loading>\n }\n\n @if (!IsDrawerLoading && RunDetails.length === 0) {\n <p class=\"drawer-details-empty\">No detail records found.</p>\n }\n\n @if (!IsDrawerLoading && RunDetails.length > 0) {\n <div class=\"drawer-table-wrapper\">\n <table class=\"drawer-table\">\n <thead>\n <tr>\n <th>Entity</th>\n <th>Record ID</th>\n <th>Status</th>\n <th>Bytes</th>\n </tr>\n </thead>\n <tbody>\n @for (detail of RunDetails; track detail.ID) {\n <tr>\n <td class=\"cell-entity\">{{ detail.EntityName }}</td>\n <td class=\"cell-id\" [title]=\"detail.RecordID\">{{ detail.RecordID }}</td>\n <td>\n <span class=\"status-badge status-badge-sm\" [ngClass]=\"GetStatusClass(detail.Status)\">\n {{ detail.Status }}\n </span>\n </td>\n <td class=\"cell-bytes\">{{ FormatBytes(detail.Bytes) }}</td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n }\n </div>\n }\n </div>\n</div>\n"]}
|