@memberjunction/ng-file-storage 6.1.0-edge.2 → 6.1.0-edge.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/LICENSE +180 -4
  2. package/README.md +60 -15
  3. package/dist/lib/admin/storage-admin-dialog.component.d.ts +63 -0
  4. package/dist/lib/admin/storage-admin-dialog.component.d.ts.map +1 -0
  5. package/dist/lib/admin/storage-admin-dialog.component.js +917 -0
  6. package/dist/lib/admin/storage-admin-dialog.component.js.map +1 -0
  7. package/dist/lib/file-browser/file-browser-resource.component.d.ts.map +1 -1
  8. package/dist/lib/file-browser/file-browser-resource.component.js +11 -21
  9. package/dist/lib/file-browser/file-browser-resource.component.js.map +1 -1
  10. package/dist/lib/file-browser/file-browser.component.js +2 -2
  11. package/dist/lib/file-browser/file-grid.component.d.ts +63 -3
  12. package/dist/lib/file-browser/file-grid.component.d.ts.map +1 -1
  13. package/dist/lib/file-browser/file-grid.component.js +1088 -735
  14. package/dist/lib/file-browser/file-grid.component.js.map +1 -1
  15. package/dist/lib/file-browser/folder-tree.component.js +32 -30
  16. package/dist/lib/file-browser/folder-tree.component.js.map +1 -1
  17. package/dist/lib/file-browser/storage-providers-list.component.d.ts +37 -3
  18. package/dist/lib/file-browser/storage-providers-list.component.d.ts.map +1 -1
  19. package/dist/lib/file-browser/storage-providers-list.component.js +191 -65
  20. package/dist/lib/file-browser/storage-providers-list.component.js.map +1 -1
  21. package/dist/lib/module.d.ts +10 -7
  22. package/dist/lib/module.d.ts.map +1 -1
  23. package/dist/lib/module.js +27 -7
  24. package/dist/lib/module.js.map +1 -1
  25. package/dist/lib/record-attachments/record-attachments.component.d.ts +203 -0
  26. package/dist/lib/record-attachments/record-attachments.component.d.ts.map +1 -0
  27. package/dist/lib/record-attachments/record-attachments.component.js +2270 -0
  28. package/dist/lib/record-attachments/record-attachments.component.js.map +1 -0
  29. package/dist/lib/record-attachments/record-attachments.events.d.ts +124 -0
  30. package/dist/lib/record-attachments/record-attachments.events.d.ts.map +1 -0
  31. package/dist/lib/record-attachments/record-attachments.events.js +178 -0
  32. package/dist/lib/record-attachments/record-attachments.events.js.map +1 -0
  33. package/dist/lib/record-attachments/record-attachments.types.d.ts +90 -0
  34. package/dist/lib/record-attachments/record-attachments.types.d.ts.map +1 -0
  35. package/dist/lib/record-attachments/record-attachments.types.js +57 -0
  36. package/dist/lib/record-attachments/record-attachments.types.js.map +1 -0
  37. package/dist/public-api.d.ts +4 -0
  38. package/dist/public-api.d.ts.map +1 -1
  39. package/dist/public-api.js +4 -0
  40. package/dist/public-api.js.map +1 -1
  41. package/package.json +14 -13
@@ -0,0 +1,917 @@
1
+ import { Component, EventEmitter, Input, Output, ViewContainerRef, inject, ChangeDetectorRef, } from '@angular/core';
2
+ import { Metadata, RunView, } from '@memberjunction/core';
3
+ import { FileStorageEngineBase, } from '@memberjunction/core-entities';
4
+ import { UUIDsEqual } from '@memberjunction/global';
5
+ import { MJNotificationService } from '@memberjunction/ng-notifications';
6
+ import { CredentialDialogService } from '@memberjunction/ng-credentials';
7
+ import * as i0 from "@angular/core";
8
+ import * as i1 from "@angular/forms";
9
+ import * as i2 from "@memberjunction/ng-shared-generic";
10
+ import * as i3 from "@memberjunction/ng-ui-components";
11
+ const _forTrack0 = ($index, $item) => $item.account.ID;
12
+ const _forTrack1 = ($index, $item) => $item.ID;
13
+ function StorageAdminDialogComponent_Conditional_15_Conditional_5_Template(rf, ctx) { if (rf & 1) {
14
+ const _r3 = i0.ɵɵgetCurrentView();
15
+ i0.ɵɵelementStart(0, "button", 21);
16
+ i0.ɵɵlistener("click", function StorageAdminDialogComponent_Conditional_15_Conditional_5_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r3); const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.SearchTerm = ""); });
17
+ i0.ɵɵelement(1, "i", 22);
18
+ i0.ɵɵelementEnd();
19
+ } }
20
+ function StorageAdminDialogComponent_Conditional_15_Conditional_10_Template(rf, ctx) { if (rf & 1) {
21
+ i0.ɵɵelementStart(0, "div", 18);
22
+ i0.ɵɵelement(1, "mj-loading", 23);
23
+ i0.ɵɵelementEnd();
24
+ } }
25
+ function StorageAdminDialogComponent_Conditional_15_Conditional_11_Conditional_1_Template(rf, ctx) { if (rf & 1) {
26
+ const _r4 = i0.ɵɵgetCurrentView();
27
+ i0.ɵɵelementStart(0, "button", 25);
28
+ i0.ɵɵlistener("click", function StorageAdminDialogComponent_Conditional_15_Conditional_11_Conditional_1_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r4); const ctx_r1 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r1.StartCreateAccount()); });
29
+ i0.ɵɵelement(1, "i", 17);
30
+ i0.ɵɵelementStart(2, "span");
31
+ i0.ɵɵtext(3, "Configure First Account");
32
+ i0.ɵɵelementEnd()();
33
+ } }
34
+ function StorageAdminDialogComponent_Conditional_15_Conditional_11_Template(rf, ctx) { if (rf & 1) {
35
+ i0.ɵɵelementStart(0, "mj-empty-state", 19);
36
+ i0.ɵɵconditionalCreate(1, StorageAdminDialogComponent_Conditional_15_Conditional_11_Conditional_1_Template, 4, 0, "button", 24);
37
+ i0.ɵɵelementEnd();
38
+ } if (rf & 2) {
39
+ const ctx_r1 = i0.ɵɵnextContext(2);
40
+ i0.ɵɵproperty("Title", ctx_r1.SearchTerm ? "No matching storage accounts" : "No storage accounts configured")("Message", ctx_r1.SearchTerm ? "Try adjusting your search query." : "Configure storage accounts to connect MemberJunction to cloud storage services like AWS S3, Azure Blob Storage, Box, Dropbox, or Google Drive.");
41
+ i0.ɵɵadvance();
42
+ i0.ɵɵconditional(!ctx_r1.SearchTerm ? 1 : -1);
43
+ } }
44
+ function StorageAdminDialogComponent_Conditional_15_Conditional_12_For_18_Conditional_5_Template(rf, ctx) { if (rf & 1) {
45
+ i0.ɵɵelementStart(0, "span", 30);
46
+ i0.ɵɵtext(1);
47
+ i0.ɵɵelementEnd();
48
+ } if (rf & 2) {
49
+ const row_r6 = i0.ɵɵnextContext().$implicit;
50
+ i0.ɵɵadvance();
51
+ i0.ɵɵtextInterpolate(row_r6.account.Description);
52
+ } }
53
+ function StorageAdminDialogComponent_Conditional_15_Conditional_12_For_18_Conditional_12_Template(rf, ctx) { if (rf & 1) {
54
+ i0.ɵɵelementStart(0, "span", 32);
55
+ i0.ɵɵelement(1, "i", 43);
56
+ i0.ɵɵelementStart(2, "span");
57
+ i0.ɵɵtext(3);
58
+ i0.ɵɵelementEnd()();
59
+ } if (rf & 2) {
60
+ const row_r6 = i0.ɵɵnextContext().$implicit;
61
+ i0.ɵɵproperty("title", "Credential: " + row_r6.credentialName);
62
+ i0.ɵɵadvance(3);
63
+ i0.ɵɵtextInterpolate(row_r6.credentialName);
64
+ } }
65
+ function StorageAdminDialogComponent_Conditional_15_Conditional_12_For_18_Conditional_13_Template(rf, ctx) { if (rf & 1) {
66
+ i0.ɵɵelementStart(0, "span", 33);
67
+ i0.ɵɵelement(1, "i", 44);
68
+ i0.ɵɵelementStart(2, "span");
69
+ i0.ɵɵtext(3, "No Credential");
70
+ i0.ɵɵelementEnd()();
71
+ } }
72
+ function StorageAdminDialogComponent_Conditional_15_Conditional_12_For_18_Conditional_15_Template(rf, ctx) { if (rf & 1) {
73
+ i0.ɵɵelementStart(0, "span", 34);
74
+ i0.ɵɵelement(1, "i", 45);
75
+ i0.ɵɵelementStart(2, "span");
76
+ i0.ɵɵtext(3, "Enabled");
77
+ i0.ɵɵelementEnd()();
78
+ } }
79
+ function StorageAdminDialogComponent_Conditional_15_Conditional_12_For_18_Conditional_16_Template(rf, ctx) { if (rf & 1) {
80
+ i0.ɵɵelementStart(0, "span", 35)(1, "span");
81
+ i0.ɵɵtext(2, "Disabled");
82
+ i0.ɵɵelementEnd()();
83
+ } }
84
+ function StorageAdminDialogComponent_Conditional_15_Conditional_12_For_18_Template(rf, ctx) { if (rf & 1) {
85
+ const _r5 = i0.ɵɵgetCurrentView();
86
+ i0.ɵɵelementStart(0, "tr")(1, "td")(2, "div", 28)(3, "span", 29);
87
+ i0.ɵɵtext(4);
88
+ i0.ɵɵelementEnd();
89
+ i0.ɵɵconditionalCreate(5, StorageAdminDialogComponent_Conditional_15_Conditional_12_For_18_Conditional_5_Template, 2, 1, "span", 30);
90
+ i0.ɵɵelementEnd()();
91
+ i0.ɵɵelementStart(6, "td")(7, "span", 31);
92
+ i0.ɵɵelement(8, "i");
93
+ i0.ɵɵelementStart(9, "span");
94
+ i0.ɵɵtext(10);
95
+ i0.ɵɵelementEnd()()();
96
+ i0.ɵɵelementStart(11, "td");
97
+ i0.ɵɵconditionalCreate(12, StorageAdminDialogComponent_Conditional_15_Conditional_12_For_18_Conditional_12_Template, 4, 2, "span", 32)(13, StorageAdminDialogComponent_Conditional_15_Conditional_12_For_18_Conditional_13_Template, 4, 0, "span", 33);
98
+ i0.ɵɵelementEnd();
99
+ i0.ɵɵelementStart(14, "td");
100
+ i0.ɵɵconditionalCreate(15, StorageAdminDialogComponent_Conditional_15_Conditional_12_For_18_Conditional_15_Template, 4, 0, "span", 34)(16, StorageAdminDialogComponent_Conditional_15_Conditional_12_For_18_Conditional_16_Template, 3, 0, "span", 35);
101
+ i0.ɵɵelementEnd();
102
+ i0.ɵɵelementStart(17, "td")(18, "span", 36);
103
+ i0.ɵɵelement(19, "i", 37);
104
+ i0.ɵɵelementStart(20, "span");
105
+ i0.ɵɵtext(21);
106
+ i0.ɵɵelementEnd()()();
107
+ i0.ɵɵelementStart(22, "td", 27)(23, "div", 38)(24, "button", 39);
108
+ i0.ɵɵlistener("click", function StorageAdminDialogComponent_Conditional_15_Conditional_12_For_18_Template_button_click_24_listener() { const row_r6 = i0.ɵɵrestoreView(_r5).$implicit; const ctx_r1 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r1.StartEditAccount(row_r6.account)); });
109
+ i0.ɵɵelement(25, "i", 40);
110
+ i0.ɵɵelementEnd();
111
+ i0.ɵɵelementStart(26, "button", 41);
112
+ i0.ɵɵlistener("click", function StorageAdminDialogComponent_Conditional_15_Conditional_12_For_18_Template_button_click_26_listener() { const row_r6 = i0.ɵɵrestoreView(_r5).$implicit; const ctx_r1 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r1.ConfirmDeleteAccount(row_r6.account)); });
113
+ i0.ɵɵelement(27, "i", 42);
114
+ i0.ɵɵelementEnd()()()();
115
+ } if (rf & 2) {
116
+ const row_r6 = ctx.$implicit;
117
+ const ctx_r1 = i0.ɵɵnextContext(3);
118
+ i0.ɵɵadvance(4);
119
+ i0.ɵɵtextInterpolate(row_r6.account.Name);
120
+ i0.ɵɵadvance();
121
+ i0.ɵɵconditional(row_r6.account.Description ? 5 : -1);
122
+ i0.ɵɵadvance(3);
123
+ i0.ɵɵclassMap(ctx_r1.GetProviderIcon((row_r6.provider == null ? null : row_r6.provider.Name) || row_r6.account.Provider));
124
+ i0.ɵɵadvance(2);
125
+ i0.ɵɵtextInterpolate((row_r6.provider == null ? null : row_r6.provider.Name) || row_r6.account.Provider);
126
+ i0.ɵɵadvance(2);
127
+ i0.ɵɵconditional(row_r6.credentialName ? 12 : 13);
128
+ i0.ɵɵadvance(3);
129
+ i0.ɵɵconditional(row_r6.account.IncludeInGlobalSearch ? 15 : 16);
130
+ i0.ɵɵadvance(6);
131
+ i0.ɵɵtextInterpolate(row_r6.roleCount === 0 ? "All Users" : row_r6.roleCount + " Role(s)");
132
+ } }
133
+ function StorageAdminDialogComponent_Conditional_15_Conditional_12_Template(rf, ctx) { if (rf & 1) {
134
+ i0.ɵɵelementStart(0, "div", 20)(1, "table", 26)(2, "thead")(3, "tr")(4, "th");
135
+ i0.ɵɵtext(5, "Account Name");
136
+ i0.ɵɵelementEnd();
137
+ i0.ɵɵelementStart(6, "th");
138
+ i0.ɵɵtext(7, "Provider");
139
+ i0.ɵɵelementEnd();
140
+ i0.ɵɵelementStart(8, "th");
141
+ i0.ɵɵtext(9, "Credential");
142
+ i0.ɵɵelementEnd();
143
+ i0.ɵɵelementStart(10, "th");
144
+ i0.ɵɵtext(11, "Global Search");
145
+ i0.ɵɵelementEnd();
146
+ i0.ɵɵelementStart(12, "th");
147
+ i0.ɵɵtext(13, "Role Access");
148
+ i0.ɵɵelementEnd();
149
+ i0.ɵɵelementStart(14, "th", 27);
150
+ i0.ɵɵtext(15, "Actions");
151
+ i0.ɵɵelementEnd()()();
152
+ i0.ɵɵelementStart(16, "tbody");
153
+ i0.ɵɵrepeaterCreate(17, StorageAdminDialogComponent_Conditional_15_Conditional_12_For_18_Template, 28, 8, "tr", null, _forTrack0);
154
+ i0.ɵɵelementEnd()()();
155
+ } if (rf & 2) {
156
+ const ctx_r1 = i0.ɵɵnextContext(2);
157
+ i0.ɵɵadvance(17);
158
+ i0.ɵɵrepeater(ctx_r1.FilteredAccountRows);
159
+ } }
160
+ function StorageAdminDialogComponent_Conditional_15_Template(rf, ctx) { if (rf & 1) {
161
+ const _r1 = i0.ɵɵgetCurrentView();
162
+ i0.ɵɵelementStart(0, "div", 7)(1, "div", 11)(2, "div", 12);
163
+ i0.ɵɵelement(3, "i", 13);
164
+ i0.ɵɵelementStart(4, "input", 14);
165
+ i0.ɵɵtwoWayListener("ngModelChange", function StorageAdminDialogComponent_Conditional_15_Template_input_ngModelChange_4_listener($event) { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); i0.ɵɵtwoWayBindingSet(ctx_r1.SearchTerm, $event) || (ctx_r1.SearchTerm = $event); return i0.ɵɵresetView($event); });
166
+ i0.ɵɵelementEnd();
167
+ i0.ɵɵconditionalCreate(5, StorageAdminDialogComponent_Conditional_15_Conditional_5_Template, 2, 0, "button", 15);
168
+ i0.ɵɵelementEnd();
169
+ i0.ɵɵelementStart(6, "button", 16);
170
+ i0.ɵɵlistener("click", function StorageAdminDialogComponent_Conditional_15_Template_button_click_6_listener() { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.StartCreateAccount()); });
171
+ i0.ɵɵelement(7, "i", 17);
172
+ i0.ɵɵelementStart(8, "span");
173
+ i0.ɵɵtext(9, "Add Storage Account");
174
+ i0.ɵɵelementEnd()()();
175
+ i0.ɵɵconditionalCreate(10, StorageAdminDialogComponent_Conditional_15_Conditional_10_Template, 2, 0, "div", 18)(11, StorageAdminDialogComponent_Conditional_15_Conditional_11_Template, 2, 3, "mj-empty-state", 19)(12, StorageAdminDialogComponent_Conditional_15_Conditional_12_Template, 19, 0, "div", 20);
176
+ i0.ɵɵelementEnd();
177
+ } if (rf & 2) {
178
+ const ctx_r1 = i0.ɵɵnextContext();
179
+ i0.ɵɵadvance(4);
180
+ i0.ɵɵtwoWayProperty("ngModel", ctx_r1.SearchTerm);
181
+ i0.ɵɵadvance();
182
+ i0.ɵɵconditional(ctx_r1.SearchTerm ? 5 : -1);
183
+ i0.ɵɵadvance(5);
184
+ i0.ɵɵconditional(ctx_r1.IsLoading ? 10 : ctx_r1.FilteredAccountRows.length === 0 ? 11 : 12);
185
+ } }
186
+ function StorageAdminDialogComponent_Conditional_16_For_23_Conditional_12_Template(rf, ctx) { if (rf & 1) {
187
+ i0.ɵɵelementStart(0, "div", 56);
188
+ i0.ɵɵtext(1);
189
+ i0.ɵɵelementEnd();
190
+ } if (rf & 2) {
191
+ const prov_r8 = i0.ɵɵnextContext().$implicit;
192
+ i0.ɵɵadvance();
193
+ i0.ɵɵtextInterpolate(prov_r8.Description);
194
+ } }
195
+ function StorageAdminDialogComponent_Conditional_16_For_23_Conditional_20_Template(rf, ctx) { if (rf & 1) {
196
+ i0.ɵɵelementStart(0, "span", 58);
197
+ i0.ɵɵelement(1, "i", 13);
198
+ i0.ɵɵelementStart(2, "span");
199
+ i0.ɵɵtext(3, "Native Search");
200
+ i0.ɵɵelementEnd()();
201
+ } }
202
+ function StorageAdminDialogComponent_Conditional_16_For_23_Conditional_21_Template(rf, ctx) { if (rf & 1) {
203
+ i0.ɵɵelementStart(0, "span", 35)(1, "span");
204
+ i0.ɵɵtext(2, "Standard");
205
+ i0.ɵɵelementEnd()();
206
+ } }
207
+ function StorageAdminDialogComponent_Conditional_16_For_23_Template(rf, ctx) { if (rf & 1) {
208
+ const _r7 = i0.ɵɵgetCurrentView();
209
+ i0.ɵɵelementStart(0, "tr")(1, "td")(2, "label", 50)(3, "input", 51);
210
+ i0.ɵɵlistener("change", function StorageAdminDialogComponent_Conditional_16_For_23_Template_input_change_3_listener() { const prov_r8 = i0.ɵɵrestoreView(_r7).$implicit; const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.ToggleProviderActive(prov_r8)); });
211
+ i0.ɵɵelementEnd();
212
+ i0.ɵɵelement(4, "span", 52);
213
+ i0.ɵɵelementEnd()();
214
+ i0.ɵɵelementStart(5, "td")(6, "div", 53)(7, "div", 54);
215
+ i0.ɵɵelement(8, "i");
216
+ i0.ɵɵelementEnd();
217
+ i0.ɵɵelementStart(9, "div")(10, "div", 55);
218
+ i0.ɵɵtext(11);
219
+ i0.ɵɵelementEnd();
220
+ i0.ɵɵconditionalCreate(12, StorageAdminDialogComponent_Conditional_16_For_23_Conditional_12_Template, 2, 1, "div", 56);
221
+ i0.ɵɵelementEnd()()();
222
+ i0.ɵɵelementStart(13, "td")(14, "code", 57);
223
+ i0.ɵɵtext(15);
224
+ i0.ɵɵelementEnd()();
225
+ i0.ɵɵelementStart(16, "td")(17, "code", 57);
226
+ i0.ɵɵtext(18);
227
+ i0.ɵɵelementEnd()();
228
+ i0.ɵɵelementStart(19, "td");
229
+ i0.ɵɵconditionalCreate(20, StorageAdminDialogComponent_Conditional_16_For_23_Conditional_20_Template, 4, 0, "span", 58)(21, StorageAdminDialogComponent_Conditional_16_For_23_Conditional_21_Template, 3, 0, "span", 35);
230
+ i0.ɵɵelementEnd();
231
+ i0.ɵɵelementStart(22, "td", 48)(23, "span", 59);
232
+ i0.ɵɵtext(24);
233
+ i0.ɵɵelementEnd()()();
234
+ } if (rf & 2) {
235
+ const prov_r8 = ctx.$implicit;
236
+ const ctx_r1 = i0.ɵɵnextContext(2);
237
+ i0.ɵɵclassProp("mj-row-inactive", !prov_r8.IsActive);
238
+ i0.ɵɵadvance(2);
239
+ i0.ɵɵproperty("title", prov_r8.IsActive ? "Deactivate provider" : "Activate provider");
240
+ i0.ɵɵadvance();
241
+ i0.ɵɵproperty("checked", prov_r8.IsActive);
242
+ i0.ɵɵadvance(5);
243
+ i0.ɵɵclassMap(ctx_r1.GetProviderIcon(prov_r8.Name));
244
+ i0.ɵɵadvance(3);
245
+ i0.ɵɵtextInterpolate(prov_r8.Name);
246
+ i0.ɵɵadvance();
247
+ i0.ɵɵconditional(prov_r8.Description ? 12 : -1);
248
+ i0.ɵɵadvance(3);
249
+ i0.ɵɵtextInterpolate(prov_r8.ServerDriverKey || "\u2014");
250
+ i0.ɵɵadvance(3);
251
+ i0.ɵɵtextInterpolate(prov_r8.ClientDriverKey || "\u2014");
252
+ i0.ɵɵadvance(2);
253
+ i0.ɵɵconditional(prov_r8.SupportsSearch ? 20 : 21);
254
+ i0.ɵɵadvance(4);
255
+ i0.ɵɵtextInterpolate(prov_r8.Priority);
256
+ } }
257
+ function StorageAdminDialogComponent_Conditional_16_Template(rf, ctx) { if (rf & 1) {
258
+ i0.ɵɵelementStart(0, "div", 7)(1, "div", 46);
259
+ i0.ɵɵelement(2, "i", 47);
260
+ i0.ɵɵelementStart(3, "span");
261
+ i0.ɵɵtext(4, "Enable or disable storage provider integrations for your organization. Only active providers are available when configuring accounts.");
262
+ i0.ɵɵelementEnd()();
263
+ i0.ɵɵelementStart(5, "div", 20)(6, "table", 26)(7, "thead")(8, "tr")(9, "th");
264
+ i0.ɵɵtext(10, "Status");
265
+ i0.ɵɵelementEnd();
266
+ i0.ɵɵelementStart(11, "th");
267
+ i0.ɵɵtext(12, "Provider Driver");
268
+ i0.ɵɵelementEnd();
269
+ i0.ɵɵelementStart(13, "th");
270
+ i0.ɵɵtext(14, "Server Driver Key");
271
+ i0.ɵɵelementEnd();
272
+ i0.ɵɵelementStart(15, "th");
273
+ i0.ɵɵtext(16, "Client Driver Key");
274
+ i0.ɵɵelementEnd();
275
+ i0.ɵɵelementStart(17, "th");
276
+ i0.ɵɵtext(18, "Search Support");
277
+ i0.ɵɵelementEnd();
278
+ i0.ɵɵelementStart(19, "th", 48);
279
+ i0.ɵɵtext(20, "Priority");
280
+ i0.ɵɵelementEnd()()();
281
+ i0.ɵɵelementStart(21, "tbody");
282
+ i0.ɵɵrepeaterCreate(22, StorageAdminDialogComponent_Conditional_16_For_23_Template, 25, 12, "tr", 49, _forTrack1);
283
+ i0.ɵɵelementEnd()()()();
284
+ } if (rf & 2) {
285
+ const ctx_r1 = i0.ɵɵnextContext();
286
+ i0.ɵɵadvance(22);
287
+ i0.ɵɵrepeater(ctx_r1.Providers);
288
+ } }
289
+ function StorageAdminDialogComponent_Conditional_20_For_9_Template(rf, ctx) { if (rf & 1) {
290
+ i0.ɵɵelementStart(0, "option", 66);
291
+ i0.ɵɵtext(1);
292
+ i0.ɵɵelementEnd();
293
+ } if (rf & 2) {
294
+ const prov_r10 = ctx.$implicit;
295
+ i0.ɵɵproperty("value", prov_r10.ID);
296
+ i0.ɵɵadvance();
297
+ i0.ɵɵtextInterpolate(prov_r10.Name);
298
+ } }
299
+ function StorageAdminDialogComponent_Conditional_20_For_35_Template(rf, ctx) { if (rf & 1) {
300
+ i0.ɵɵelementStart(0, "option", 66);
301
+ i0.ɵɵtext(1);
302
+ i0.ɵɵelementEnd();
303
+ } if (rf & 2) {
304
+ const cred_r11 = ctx.$implicit;
305
+ i0.ɵɵproperty("value", cred_r11.ID);
306
+ i0.ɵɵadvance();
307
+ i0.ɵɵtextInterpolate(cred_r11.Name);
308
+ } }
309
+ function StorageAdminDialogComponent_Conditional_20_For_52_Template(rf, ctx) { if (rf & 1) {
310
+ const _r12 = i0.ɵɵgetCurrentView();
311
+ i0.ɵɵelementStart(0, "label", 80)(1, "input", 51);
312
+ i0.ɵɵlistener("change", function StorageAdminDialogComponent_Conditional_20_For_52_Template_input_change_1_listener() { const role_r13 = i0.ɵɵrestoreView(_r12).$implicit; const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.ToggleRoleSelection(role_r13.ID)); });
313
+ i0.ɵɵelementEnd();
314
+ i0.ɵɵelementStart(2, "span");
315
+ i0.ɵɵtext(3);
316
+ i0.ɵɵelementEnd()();
317
+ } if (rf & 2) {
318
+ const role_r13 = ctx.$implicit;
319
+ const ctx_r1 = i0.ɵɵnextContext(2);
320
+ i0.ɵɵadvance();
321
+ i0.ɵɵproperty("checked", ctx_r1.IsRoleSelected(role_r13.ID));
322
+ i0.ɵɵadvance(2);
323
+ i0.ɵɵtextInterpolate(role_r13.Name);
324
+ } }
325
+ function StorageAdminDialogComponent_Conditional_20_Conditional_57_Template(rf, ctx) { if (rf & 1) {
326
+ i0.ɵɵelement(0, "i", 83);
327
+ i0.ɵɵelementStart(1, "span");
328
+ i0.ɵɵtext(2, "Saving...");
329
+ i0.ɵɵelementEnd();
330
+ } }
331
+ function StorageAdminDialogComponent_Conditional_20_Conditional_58_Template(rf, ctx) { if (rf & 1) {
332
+ i0.ɵɵelement(0, "i", 45);
333
+ i0.ɵɵelementStart(1, "span");
334
+ i0.ɵɵtext(2);
335
+ i0.ɵɵelementEnd();
336
+ } if (rf & 2) {
337
+ const ctx_r1 = i0.ɵɵnextContext(2);
338
+ i0.ɵɵadvance(2);
339
+ i0.ɵɵtextInterpolate(ctx_r1.EditingAccount ? "Save Changes" : "Create Account");
340
+ } }
341
+ function StorageAdminDialogComponent_Conditional_20_Template(rf, ctx) { if (rf & 1) {
342
+ const _r9 = i0.ɵɵgetCurrentView();
343
+ i0.ɵɵelementStart(0, "mj-dialog", 60);
344
+ i0.ɵɵlistener("Close", function StorageAdminDialogComponent_Conditional_20_Template_mj_dialog_Close_0_listener() { i0.ɵɵrestoreView(_r9); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.CancelEditAccount()); });
345
+ i0.ɵɵelementStart(1, "div", 61)(2, "div", 62)(3, "label", 63);
346
+ i0.ɵɵtext(4, "Storage Provider ");
347
+ i0.ɵɵelementStart(5, "span", 64);
348
+ i0.ɵɵtext(6, "*");
349
+ i0.ɵɵelementEnd()();
350
+ i0.ɵɵelementStart(7, "select", 65);
351
+ i0.ɵɵtwoWayListener("ngModelChange", function StorageAdminDialogComponent_Conditional_20_Template_select_ngModelChange_7_listener($event) { i0.ɵɵrestoreView(_r9); const ctx_r1 = i0.ɵɵnextContext(); i0.ɵɵtwoWayBindingSet(ctx_r1.EditingAccountProviderID, $event) || (ctx_r1.EditingAccountProviderID = $event); return i0.ɵɵresetView($event); });
352
+ i0.ɵɵrepeaterCreate(8, StorageAdminDialogComponent_Conditional_20_For_9_Template, 2, 2, "option", 66, _forTrack1);
353
+ i0.ɵɵelementEnd();
354
+ i0.ɵɵelementStart(10, "div", 67);
355
+ i0.ɵɵtext(11, "Select the cloud storage engine driver for this account.");
356
+ i0.ɵɵelementEnd()();
357
+ i0.ɵɵelementStart(12, "div", 62)(13, "label", 63);
358
+ i0.ɵɵtext(14, "Account Name ");
359
+ i0.ɵɵelementStart(15, "span", 64);
360
+ i0.ɵɵtext(16, "*");
361
+ i0.ɵɵelementEnd()();
362
+ i0.ɵɵelementStart(17, "input", 68);
363
+ i0.ɵɵtwoWayListener("ngModelChange", function StorageAdminDialogComponent_Conditional_20_Template_input_ngModelChange_17_listener($event) { i0.ɵɵrestoreView(_r9); const ctx_r1 = i0.ɵɵnextContext(); i0.ɵɵtwoWayBindingSet(ctx_r1.EditingAccountName, $event) || (ctx_r1.EditingAccountName = $event); return i0.ɵɵresetView($event); });
364
+ i0.ɵɵelementEnd()();
365
+ i0.ɵɵelementStart(18, "div", 62)(19, "label", 63);
366
+ i0.ɵɵtext(20, "Description");
367
+ i0.ɵɵelementEnd();
368
+ i0.ɵɵelementStart(21, "textarea", 69);
369
+ i0.ɵɵtwoWayListener("ngModelChange", function StorageAdminDialogComponent_Conditional_20_Template_textarea_ngModelChange_21_listener($event) { i0.ɵɵrestoreView(_r9); const ctx_r1 = i0.ɵɵnextContext(); i0.ɵɵtwoWayBindingSet(ctx_r1.EditingAccountDescription, $event) || (ctx_r1.EditingAccountDescription = $event); return i0.ɵɵresetView($event); });
370
+ i0.ɵɵelementEnd()();
371
+ i0.ɵɵelementStart(22, "div", 62)(23, "div", 70)(24, "label", 63);
372
+ i0.ɵɵtext(25, "Storage Credentials");
373
+ i0.ɵɵelementEnd();
374
+ i0.ɵɵelementStart(26, "button", 71);
375
+ i0.ɵɵlistener("click", function StorageAdminDialogComponent_Conditional_20_Template_button_click_26_listener() { i0.ɵɵrestoreView(_r9); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.OpenCreateCredentialDialog()); });
376
+ i0.ɵɵelement(27, "i", 17);
377
+ i0.ɵɵelementStart(28, "span");
378
+ i0.ɵɵtext(29, "New Credential");
379
+ i0.ɵɵelementEnd()()();
380
+ i0.ɵɵelementStart(30, "div", 72)(31, "select", 73);
381
+ i0.ɵɵtwoWayListener("ngModelChange", function StorageAdminDialogComponent_Conditional_20_Template_select_ngModelChange_31_listener($event) { i0.ɵɵrestoreView(_r9); const ctx_r1 = i0.ɵɵnextContext(); i0.ɵɵtwoWayBindingSet(ctx_r1.EditingAccountCredentialID, $event) || (ctx_r1.EditingAccountCredentialID = $event); return i0.ɵɵresetView($event); });
382
+ i0.ɵɵelementStart(32, "option", 74);
383
+ i0.ɵɵtext(33, "-- No Credential Assigned --");
384
+ i0.ɵɵelementEnd();
385
+ i0.ɵɵrepeaterCreate(34, StorageAdminDialogComponent_Conditional_20_For_35_Template, 2, 2, "option", 66, _forTrack1);
386
+ i0.ɵɵelementEnd()();
387
+ i0.ɵɵelementStart(36, "div", 67);
388
+ i0.ɵɵtext(37, " Credentials manage authentication tokens, connection strings, or API keys securely via the Credential Engine. ");
389
+ i0.ɵɵelementEnd()();
390
+ i0.ɵɵelementStart(38, "div", 75)(39, "label", 76)(40, "input", 77);
391
+ i0.ɵɵtwoWayListener("ngModelChange", function StorageAdminDialogComponent_Conditional_20_Template_input_ngModelChange_40_listener($event) { i0.ɵɵrestoreView(_r9); const ctx_r1 = i0.ɵɵnextContext(); i0.ɵɵtwoWayBindingSet(ctx_r1.EditingAccountIncludeInGlobalSearch, $event) || (ctx_r1.EditingAccountIncludeInGlobalSearch = $event); return i0.ɵɵresetView($event); });
392
+ i0.ɵɵelementEnd();
393
+ i0.ɵɵelementStart(41, "span");
394
+ i0.ɵɵtext(42, "Include in Global Search");
395
+ i0.ɵɵelementEnd()();
396
+ i0.ɵɵelementStart(43, "div", 78);
397
+ i0.ɵɵtext(44, " When enabled, file contents and metadata in this account will be indexed for universal MemberJunction search. ");
398
+ i0.ɵɵelementEnd()();
399
+ i0.ɵɵelementStart(45, "div", 62)(46, "label", 63);
400
+ i0.ɵɵtext(47, "Role Access Permissions");
401
+ i0.ɵɵelementEnd();
402
+ i0.ɵɵelementStart(48, "div", 67);
403
+ i0.ɵɵtext(49, " Select specific roles permitted to access this storage account. If no roles are selected, all authorized users have access. ");
404
+ i0.ɵɵelementEnd();
405
+ i0.ɵɵelementStart(50, "div", 79);
406
+ i0.ɵɵrepeaterCreate(51, StorageAdminDialogComponent_Conditional_20_For_52_Template, 4, 2, "label", 80, _forTrack1);
407
+ i0.ɵɵelementEnd()()();
408
+ i0.ɵɵelementStart(53, "mj-dialog-actions")(54, "button", 81);
409
+ i0.ɵɵlistener("click", function StorageAdminDialogComponent_Conditional_20_Template_button_click_54_listener() { i0.ɵɵrestoreView(_r9); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.CancelEditAccount()); });
410
+ i0.ɵɵtext(55, " Cancel ");
411
+ i0.ɵɵelementEnd();
412
+ i0.ɵɵelementStart(56, "button", 82);
413
+ i0.ɵɵlistener("click", function StorageAdminDialogComponent_Conditional_20_Template_button_click_56_listener() { i0.ɵɵrestoreView(_r9); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.SaveAccount()); });
414
+ i0.ɵɵconditionalCreate(57, StorageAdminDialogComponent_Conditional_20_Conditional_57_Template, 3, 0)(58, StorageAdminDialogComponent_Conditional_20_Conditional_58_Template, 3, 1);
415
+ i0.ɵɵelementEnd()()();
416
+ } if (rf & 2) {
417
+ const ctx_r1 = i0.ɵɵnextContext();
418
+ i0.ɵɵproperty("Visible", ctx_r1.IsEditingAccount)("Title", ctx_r1.EditingAccount ? "Edit Storage Account: " + ctx_r1.EditingAccount.Name : "Add Storage Account")("Width", 640);
419
+ i0.ɵɵadvance(7);
420
+ i0.ɵɵtwoWayProperty("ngModel", ctx_r1.EditingAccountProviderID);
421
+ i0.ɵɵproperty("disabled", !!ctx_r1.EditingAccount);
422
+ i0.ɵɵadvance();
423
+ i0.ɵɵrepeater(ctx_r1.ActiveProviders);
424
+ i0.ɵɵadvance(9);
425
+ i0.ɵɵtwoWayProperty("ngModel", ctx_r1.EditingAccountName);
426
+ i0.ɵɵadvance(4);
427
+ i0.ɵɵtwoWayProperty("ngModel", ctx_r1.EditingAccountDescription);
428
+ i0.ɵɵadvance(10);
429
+ i0.ɵɵtwoWayProperty("ngModel", ctx_r1.EditingAccountCredentialID);
430
+ i0.ɵɵadvance(3);
431
+ i0.ɵɵrepeater(ctx_r1.Credentials);
432
+ i0.ɵɵadvance(6);
433
+ i0.ɵɵtwoWayProperty("ngModel", ctx_r1.EditingAccountIncludeInGlobalSearch);
434
+ i0.ɵɵadvance(11);
435
+ i0.ɵɵrepeater(ctx_r1.Roles);
436
+ i0.ɵɵadvance(3);
437
+ i0.ɵɵproperty("disabled", ctx_r1.IsSaving);
438
+ i0.ɵɵadvance(2);
439
+ i0.ɵɵproperty("disabled", ctx_r1.IsSaving || !ctx_r1.EditingAccountName.trim() || !ctx_r1.EditingAccountProviderID);
440
+ i0.ɵɵadvance();
441
+ i0.ɵɵconditional(ctx_r1.IsSaving ? 57 : 58);
442
+ } }
443
+ function StorageAdminDialogComponent_Conditional_21_Conditional_16_Template(rf, ctx) { if (rf & 1) {
444
+ i0.ɵɵelement(0, "i", 83);
445
+ i0.ɵɵelementStart(1, "span");
446
+ i0.ɵɵtext(2, "Deleting...");
447
+ i0.ɵɵelementEnd();
448
+ } }
449
+ function StorageAdminDialogComponent_Conditional_21_Conditional_17_Template(rf, ctx) { if (rf & 1) {
450
+ i0.ɵɵelement(0, "i", 42);
451
+ i0.ɵɵelementStart(1, "span");
452
+ i0.ɵɵtext(2, "Delete Account");
453
+ i0.ɵɵelementEnd();
454
+ } }
455
+ function StorageAdminDialogComponent_Conditional_21_Template(rf, ctx) { if (rf & 1) {
456
+ const _r14 = i0.ɵɵgetCurrentView();
457
+ i0.ɵɵelementStart(0, "mj-dialog", 84);
458
+ i0.ɵɵlistener("Close", function StorageAdminDialogComponent_Conditional_21_Template_mj_dialog_Close_0_listener() { i0.ɵɵrestoreView(_r14); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.CancelDeleteAccount()); });
459
+ i0.ɵɵelementStart(1, "div", 85)(2, "div", 86);
460
+ i0.ɵɵelement(3, "i", 44);
461
+ i0.ɵɵelementEnd();
462
+ i0.ɵɵelementStart(4, "div", 87)(5, "p", 88);
463
+ i0.ɵɵtext(6, " Are you sure you want to delete ");
464
+ i0.ɵɵelementStart(7, "strong");
465
+ i0.ɵɵtext(8);
466
+ i0.ɵɵelementEnd();
467
+ i0.ɵɵtext(9, "? ");
468
+ i0.ɵɵelementEnd();
469
+ i0.ɵɵelementStart(10, "p", 89);
470
+ i0.ɵɵtext(11, " This will remove the storage account connection and role permissions. Existing files in the remote storage service will not be deleted, but will no longer be accessible via this account in MemberJunction. ");
471
+ i0.ɵɵelementEnd()()();
472
+ i0.ɵɵelementStart(12, "mj-dialog-actions")(13, "button", 81);
473
+ i0.ɵɵlistener("click", function StorageAdminDialogComponent_Conditional_21_Template_button_click_13_listener() { i0.ɵɵrestoreView(_r14); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.CancelDeleteAccount()); });
474
+ i0.ɵɵtext(14, " Cancel ");
475
+ i0.ɵɵelementEnd();
476
+ i0.ɵɵelementStart(15, "button", 90);
477
+ i0.ɵɵlistener("click", function StorageAdminDialogComponent_Conditional_21_Template_button_click_15_listener() { i0.ɵɵrestoreView(_r14); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.ExecuteDeleteAccount()); });
478
+ i0.ɵɵconditionalCreate(16, StorageAdminDialogComponent_Conditional_21_Conditional_16_Template, 3, 0)(17, StorageAdminDialogComponent_Conditional_21_Conditional_17_Template, 3, 0);
479
+ i0.ɵɵelementEnd()()();
480
+ } if (rf & 2) {
481
+ const ctx_r1 = i0.ɵɵnextContext();
482
+ i0.ɵɵproperty("Visible", !!ctx_r1.AccountToDelete)("Width", 460);
483
+ i0.ɵɵadvance(8);
484
+ i0.ɵɵtextInterpolate(ctx_r1.AccountToDelete.Name);
485
+ i0.ɵɵadvance(5);
486
+ i0.ɵɵproperty("disabled", ctx_r1.IsDeleting);
487
+ i0.ɵɵadvance(2);
488
+ i0.ɵɵproperty("disabled", ctx_r1.IsDeleting);
489
+ i0.ɵɵadvance();
490
+ i0.ɵɵconditional(ctx_r1.IsDeleting ? 16 : 17);
491
+ } }
492
+ export class StorageAdminDialogComponent {
493
+ cdr = inject(ChangeDetectorRef);
494
+ notifications = inject(MJNotificationService);
495
+ viewContainerRef = inject(ViewContainerRef);
496
+ credentialDialog = inject(CredentialDialogService);
497
+ Visible = false;
498
+ ActiveTab = 'accounts';
499
+ Provider = null;
500
+ Close = new EventEmitter();
501
+ AccountsChanged = new EventEmitter();
502
+ get ProviderToUse() {
503
+ return this.Provider ?? Metadata.Provider;
504
+ }
505
+ // ── State ───────────────────────────────────────────────────────────
506
+ IsLoading = false;
507
+ IsSaving = false;
508
+ SearchTerm = '';
509
+ Providers = [];
510
+ Accounts = [];
511
+ Credentials = [];
512
+ Roles = [];
513
+ AccountPermissions = [];
514
+ // ── Account Editor Sub-View / Modal ─────────────────────────────────
515
+ IsEditingAccount = false;
516
+ EditingAccount = null;
517
+ EditingAccountName = '';
518
+ EditingAccountDescription = '';
519
+ EditingAccountProviderID = '';
520
+ EditingAccountCredentialID = '';
521
+ EditingAccountIncludeInGlobalSearch = false;
522
+ EditingAccountSelectedRoleIDs = [];
523
+ // ── Delete Confirmation ─────────────────────────────────────────────
524
+ AccountToDelete = null;
525
+ IsDeleting = false;
526
+ async ngOnInit() {
527
+ if (this.Visible) {
528
+ await this.LoadAllData();
529
+ }
530
+ }
531
+ async Open(tab = 'accounts', accountToEdit) {
532
+ this.ActiveTab = tab;
533
+ this.Visible = true;
534
+ this.IsEditingAccount = false;
535
+ this.AccountToDelete = null;
536
+ await this.LoadAllData();
537
+ if (accountToEdit) {
538
+ this.StartEditAccount(accountToEdit);
539
+ }
540
+ this.cdr.markForCheck();
541
+ }
542
+ async LoadAllData() {
543
+ this.IsLoading = true;
544
+ this.cdr.markForCheck();
545
+ try {
546
+ const md = this.ProviderToUse;
547
+ const rv = RunView.FromMetadataProvider(md);
548
+ // Load providers and accounts via FileStorageEngineBase + RunView
549
+ await FileStorageEngineBase.Instance.Config(true);
550
+ this.Providers = [...FileStorageEngineBase.Instance.Providers].sort((a, b) => (a.Priority ?? 0) - (b.Priority ?? 0) || a.Name.localeCompare(b.Name));
551
+ this.Accounts = [...FileStorageEngineBase.Instance.Accounts].sort((a, b) => a.Name.localeCompare(b.Name));
552
+ // Load Credentials
553
+ const credResult = await rv.RunView({
554
+ EntityName: 'MJ: Credentials',
555
+ OrderBy: 'Name',
556
+ ResultType: 'entity_object',
557
+ });
558
+ if (credResult.Success) {
559
+ this.Credentials = credResult.Results;
560
+ }
561
+ // Load Roles
562
+ const roleResult = await rv.RunView({
563
+ EntityName: 'MJ: Roles',
564
+ OrderBy: 'Name',
565
+ ResultType: 'entity_object',
566
+ });
567
+ if (roleResult.Success) {
568
+ this.Roles = roleResult.Results;
569
+ }
570
+ // Load Account Permissions
571
+ const permResult = await rv.RunView({
572
+ EntityName: 'MJ: File Storage Account Permissions',
573
+ ResultType: 'entity_object',
574
+ });
575
+ if (permResult.Success) {
576
+ this.AccountPermissions = permResult.Results;
577
+ }
578
+ }
579
+ catch (err) {
580
+ console.error('[StorageAdminDialogComponent] Error loading storage data:', err);
581
+ this.notifications.CreateSimpleNotification('Failed to load storage configuration.', 'error');
582
+ }
583
+ finally {
584
+ this.IsLoading = false;
585
+ this.cdr.markForCheck();
586
+ }
587
+ }
588
+ // ── Computed Rows & Filters ─────────────────────────────────────────
589
+ get FilteredAccountRows() {
590
+ const term = (this.SearchTerm || '').toLowerCase().trim();
591
+ return this.Accounts.filter((a) => {
592
+ if (!term)
593
+ return true;
594
+ const prov = this.Providers.find((p) => UUIDsEqual(p.ID, a.ProviderID));
595
+ const cred = this.Credentials.find((c) => UUIDsEqual(c.ID, a.CredentialID));
596
+ const credName = cred?.Name || '';
597
+ return (a.Name.toLowerCase().includes(term) ||
598
+ (a.Description || '').toLowerCase().includes(term) ||
599
+ (prov?.Name || '').toLowerCase().includes(term) ||
600
+ credName.toLowerCase().includes(term));
601
+ }).map((account) => {
602
+ const provider = this.Providers.find((p) => UUIDsEqual(p.ID, account.ProviderID));
603
+ const credential = this.Credentials.find((c) => UUIDsEqual(c.ID, account.CredentialID));
604
+ const credentialName = credential?.Name;
605
+ const roleCount = this.AccountPermissions.filter((p) => UUIDsEqual(p.FileStorageAccountID, account.ID)).length;
606
+ return { account, provider, credentialName, roleCount };
607
+ });
608
+ }
609
+ get ActiveProviders() {
610
+ return this.Providers.filter((p) => p.IsActive);
611
+ }
612
+ // ── Account Editing ─────────────────────────────────────────────────
613
+ StartCreateAccount() {
614
+ this.EditingAccount = null;
615
+ this.EditingAccountName = '';
616
+ this.EditingAccountDescription = '';
617
+ this.EditingAccountProviderID = this.ActiveProviders[0]?.ID || (this.Providers[0]?.ID ?? '');
618
+ this.EditingAccountCredentialID = '';
619
+ this.EditingAccountIncludeInGlobalSearch = false;
620
+ this.EditingAccountSelectedRoleIDs = [];
621
+ this.IsEditingAccount = true;
622
+ this.cdr.markForCheck();
623
+ }
624
+ StartEditAccount(account) {
625
+ this.EditingAccount = account;
626
+ this.EditingAccountName = account.Name || '';
627
+ this.EditingAccountDescription = account.Description || '';
628
+ this.EditingAccountProviderID = account.ProviderID || '';
629
+ this.EditingAccountCredentialID = account.CredentialID || '';
630
+ this.EditingAccountIncludeInGlobalSearch = !!account.IncludeInGlobalSearch;
631
+ const accountPerms = this.AccountPermissions.filter((p) => UUIDsEqual(p.FileStorageAccountID, account.ID));
632
+ this.EditingAccountSelectedRoleIDs = accountPerms
633
+ .map((p) => p.RoleID)
634
+ .filter((roleId) => roleId !== null && roleId !== undefined);
635
+ this.IsEditingAccount = true;
636
+ this.cdr.markForCheck();
637
+ }
638
+ CancelEditAccount() {
639
+ this.IsEditingAccount = false;
640
+ this.EditingAccount = null;
641
+ this.cdr.markForCheck();
642
+ }
643
+ ToggleRoleSelection(roleId) {
644
+ const idx = this.EditingAccountSelectedRoleIDs.findIndex((id) => UUIDsEqual(id, roleId));
645
+ if (idx >= 0) {
646
+ this.EditingAccountSelectedRoleIDs.splice(idx, 1);
647
+ }
648
+ else {
649
+ this.EditingAccountSelectedRoleIDs.push(roleId);
650
+ }
651
+ }
652
+ IsRoleSelected(roleId) {
653
+ return this.EditingAccountSelectedRoleIDs.some((id) => UUIDsEqual(id, roleId));
654
+ }
655
+ async OpenCreateCredentialDialog() {
656
+ try {
657
+ const result = await this.credentialDialog.openDialog(this.viewContainerRef, {
658
+ title: 'Create Credential for Storage Account',
659
+ });
660
+ if (result.success && result.credential) {
661
+ // Refresh credentials list
662
+ const md = this.ProviderToUse;
663
+ const rv = RunView.FromMetadataProvider(md);
664
+ const credResult = await rv.RunView({
665
+ EntityName: 'MJ: Credentials',
666
+ OrderBy: 'Name',
667
+ ResultType: 'entity_object',
668
+ });
669
+ if (credResult.Success) {
670
+ this.Credentials = credResult.Results;
671
+ }
672
+ this.EditingAccountCredentialID = result.credential.ID;
673
+ this.notifications.CreateSimpleNotification(`Credential "${result.credential.Name}" created.`, 'success');
674
+ this.cdr.markForCheck();
675
+ }
676
+ }
677
+ catch (err) {
678
+ console.error('[StorageAdminDialogComponent] Error creating credential:', err);
679
+ }
680
+ }
681
+ async SaveAccount() {
682
+ const name = this.EditingAccountName.trim();
683
+ if (!name) {
684
+ this.notifications.CreateSimpleNotification('Account name is required.', 'warning');
685
+ return;
686
+ }
687
+ if (!this.EditingAccountProviderID) {
688
+ this.notifications.CreateSimpleNotification('Storage provider is required.', 'warning');
689
+ return;
690
+ }
691
+ this.IsSaving = true;
692
+ this.cdr.markForCheck();
693
+ try {
694
+ const md = this.ProviderToUse;
695
+ const user = md.CurrentUser;
696
+ let accountEntity;
697
+ if (this.EditingAccount) {
698
+ accountEntity = this.EditingAccount;
699
+ }
700
+ else {
701
+ accountEntity = await md.GetEntityObject('MJ: File Storage Accounts', user);
702
+ }
703
+ accountEntity.Name = name;
704
+ accountEntity.Description = this.EditingAccountDescription.trim() || null;
705
+ accountEntity.ProviderID = this.EditingAccountProviderID;
706
+ accountEntity.CredentialID = this.EditingAccountCredentialID || '';
707
+ accountEntity.IncludeInGlobalSearch = this.EditingAccountIncludeInGlobalSearch;
708
+ const saved = await accountEntity.Save();
709
+ if (!saved) {
710
+ throw new Error(accountEntity.LatestResult?.Message || 'Failed to save storage account.');
711
+ }
712
+ // Sync Role Permissions
713
+ await this.SyncAccountPermissions(accountEntity.ID, this.EditingAccountSelectedRoleIDs);
714
+ // Force engine refresh
715
+ await FileStorageEngineBase.Instance.Config(true);
716
+ await this.LoadAllData();
717
+ this.IsEditingAccount = false;
718
+ this.EditingAccount = null;
719
+ this.AccountsChanged.emit();
720
+ this.notifications.CreateSimpleNotification(`Storage account "${name}" saved successfully.`, 'success');
721
+ }
722
+ catch (err) {
723
+ console.error('[StorageAdminDialogComponent] Error saving account:', err);
724
+ const msg = err instanceof Error ? err.message : 'Failed to save storage account.';
725
+ this.notifications.CreateSimpleNotification(msg, 'error');
726
+ }
727
+ finally {
728
+ this.IsSaving = false;
729
+ this.cdr.markForCheck();
730
+ }
731
+ }
732
+ async SyncAccountPermissions(accountId, desiredRoleIds) {
733
+ const md = this.ProviderToUse;
734
+ const user = md.CurrentUser;
735
+ const existingPerms = this.AccountPermissions.filter((p) => UUIDsEqual(p.FileStorageAccountID, accountId));
736
+ const existingRoleMap = new Map();
737
+ for (const p of existingPerms) {
738
+ if (p.RoleID) {
739
+ existingRoleMap.set(p.RoleID, p);
740
+ }
741
+ }
742
+ // Delete permissions for unselected roles
743
+ for (const p of existingPerms) {
744
+ if (!p.RoleID || !desiredRoleIds.some((rId) => UUIDsEqual(rId, p.RoleID))) {
745
+ await p.Delete();
746
+ }
747
+ }
748
+ // Add permissions for newly selected roles
749
+ for (const roleId of desiredRoleIds) {
750
+ if (!existingRoleMap.has(roleId)) {
751
+ const newPerm = await md.GetEntityObject('MJ: File Storage Account Permissions', user);
752
+ newPerm.FileStorageAccountID = accountId;
753
+ newPerm.Type = 'Role';
754
+ newPerm.RoleID = roleId;
755
+ newPerm.CanRead = true;
756
+ newPerm.CanWrite = true;
757
+ await newPerm.Save();
758
+ }
759
+ }
760
+ }
761
+ // ── Account Deletion ────────────────────────────────────────────────
762
+ ConfirmDeleteAccount(account) {
763
+ this.AccountToDelete = account;
764
+ this.cdr.markForCheck();
765
+ }
766
+ CancelDeleteAccount() {
767
+ this.AccountToDelete = null;
768
+ this.cdr.markForCheck();
769
+ }
770
+ async ExecuteDeleteAccount() {
771
+ if (!this.AccountToDelete)
772
+ return;
773
+ this.IsDeleting = true;
774
+ this.cdr.markForCheck();
775
+ try {
776
+ const accountName = this.AccountToDelete.Name;
777
+ const accountId = this.AccountToDelete.ID;
778
+ // Delete associated permissions first
779
+ const perms = this.AccountPermissions.filter((p) => UUIDsEqual(p.FileStorageAccountID, accountId));
780
+ for (const p of perms) {
781
+ await p.Delete();
782
+ }
783
+ const deleted = await this.AccountToDelete.Delete();
784
+ if (!deleted) {
785
+ throw new Error('Failed to delete storage account.');
786
+ }
787
+ await FileStorageEngineBase.Instance.Config(true);
788
+ await this.LoadAllData();
789
+ this.AccountToDelete = null;
790
+ this.AccountsChanged.emit();
791
+ this.notifications.CreateSimpleNotification(`Storage account "${accountName}" removed.`, 'info');
792
+ }
793
+ catch (err) {
794
+ console.error('[StorageAdminDialogComponent] Delete error:', err);
795
+ const msg = err instanceof Error ? err.message : 'Failed to delete storage account.';
796
+ this.notifications.CreateSimpleNotification(msg, 'error');
797
+ }
798
+ finally {
799
+ this.IsDeleting = false;
800
+ this.cdr.markForCheck();
801
+ }
802
+ }
803
+ // ── Provider Management ─────────────────────────────────────────────
804
+ async ToggleProviderActive(provider) {
805
+ const newState = !provider.IsActive;
806
+ provider.IsActive = newState;
807
+ this.cdr.markForCheck();
808
+ try {
809
+ const saved = await provider.Save();
810
+ if (!saved) {
811
+ provider.IsActive = !newState; // rollback
812
+ throw new Error(provider.LatestResult?.Message || 'Failed to update provider status.');
813
+ }
814
+ await FileStorageEngineBase.Instance.Config(true);
815
+ this.AccountsChanged.emit();
816
+ this.notifications.CreateSimpleNotification(`Provider "${provider.Name}" is now ${newState ? 'active' : 'inactive'}.`, 'info');
817
+ }
818
+ catch (err) {
819
+ console.error('[StorageAdminDialogComponent] Provider toggle error:', err);
820
+ this.notifications.CreateSimpleNotification('Failed to update provider status.', 'error');
821
+ }
822
+ finally {
823
+ this.cdr.markForCheck();
824
+ }
825
+ }
826
+ // ── UI Helpers ──────────────────────────────────────────────────────
827
+ GetProviderIcon(providerName) {
828
+ const name = (providerName || '').toLowerCase();
829
+ if (name.includes('aws') || name.includes('s3'))
830
+ return 'fa-brands fa-aws';
831
+ if (name.includes('azure'))
832
+ return 'fa-brands fa-microsoft';
833
+ if (name.includes('google drive'))
834
+ return 'fa-brands fa-google-drive';
835
+ if (name.includes('google cloud'))
836
+ return 'fa-brands fa-google';
837
+ if (name.includes('dropbox'))
838
+ return 'fa-brands fa-dropbox';
839
+ if (name.includes('box'))
840
+ return 'fa-solid fa-box';
841
+ if (name.includes('sharepoint') || name.includes('onedrive'))
842
+ return 'fa-brands fa-microsoft';
843
+ return 'fa-solid fa-cloud';
844
+ }
845
+ OnDialogClose() {
846
+ this.Visible = false;
847
+ this.IsEditingAccount = false;
848
+ this.AccountToDelete = null;
849
+ this.Close.emit();
850
+ }
851
+ static ɵfac = function StorageAdminDialogComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || StorageAdminDialogComponent)(); };
852
+ static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: StorageAdminDialogComponent, selectors: [["mj-storage-admin-dialog"]], inputs: { Visible: "Visible", ActiveTab: "ActiveTab", Provider: "Provider" }, outputs: { Close: "Close", AccountsChanged: "AccountsChanged" }, standalone: false, decls: 22, vars: 13, consts: [["Title", "Manage File Storage", 3, "Close", "Visible", "Width", "MinWidth"], [1, "mj-storage-admin-container"], [1, "mj-storage-admin-tabs"], ["type", "button", 1, "mj-tab-btn", 3, "click"], [1, "fa-solid", "fa-server"], [1, "mj-tab-count"], [1, "fa-solid", "fa-cubes"], [1, "mj-tab-pane"], ["type", "button", 1, "mj-admin-btn", "mj-admin-btn-secondary", 3, "click"], [3, "Visible", "Title", "Width"], ["Title", "Delete Storage Account", 3, "Visible", "Width"], [1, "mj-admin-pane-toolbar"], [1, "mj-admin-search-wrapper"], [1, "fa-solid", "fa-magnifying-glass"], ["type", "text", "placeholder", "Search storage accounts by name, provider, or credential...", 3, "ngModelChange", "ngModel"], ["type", "button", 1, "mj-clear-btn"], ["type", "button", 1, "mj-admin-btn", "mj-admin-btn-primary", 3, "click"], [1, "fa-solid", "fa-plus"], [1, "mj-admin-loading"], ["Icon", "fa-solid fa-folder-plus", 3, "Title", "Message"], [1, "mj-admin-table-container"], ["type", "button", 1, "mj-clear-btn", 3, "click"], [1, "fa-solid", "fa-xmark"], ["text", "Loading storage accounts..."], ["type", "button", 1, "mj-admin-btn", "mj-admin-btn-primary", "mj-empty-cta"], ["type", "button", 1, "mj-admin-btn", "mj-admin-btn-primary", "mj-empty-cta", 3, "click"], [1, "mj-admin-table"], [1, "mj-text-right"], [1, "mj-account-name-cell"], [1, "mj-account-title"], [1, "mj-account-desc"], [1, "mj-provider-badge"], [1, "mj-status-badge", "mj-status-badge--success", 3, "title"], ["title", "No credential configured", 1, "mj-status-badge", "mj-status-badge--warning"], [1, "mj-status-badge", "mj-status-badge--info"], [1, "mj-status-badge", "mj-status-badge--muted"], [1, "mj-role-count-badge"], [1, "fa-solid", "fa-user-shield"], [1, "mj-action-button-group"], ["type", "button", "title", "Edit Account", 1, "mj-action-icon-btn", 3, "click"], [1, "fa-solid", "fa-pen-to-square"], ["type", "button", "title", "Delete Account", 1, "mj-action-icon-btn", "mj-action-icon-btn--danger", 3, "click"], [1, "fa-solid", "fa-trash-can"], [1, "fa-solid", "fa-key"], [1, "fa-solid", "fa-triangle-exclamation"], [1, "fa-solid", "fa-check"], [1, "mj-admin-pane-desc"], [1, "fa-solid", "fa-circle-info"], [1, "mj-text-center"], [3, "mj-row-inactive"], [1, "mj-toggle-switch", 3, "title"], ["type", "checkbox", 3, "change", "checked"], [1, "mj-toggle-slider"], [1, "mj-provider-cell"], [1, "mj-provider-icon-circle"], [1, "mj-provider-name"], [1, "mj-provider-desc"], [1, "mj-code-pill"], ["title", "Provider supports full-text file search", 1, "mj-status-badge", "mj-status-badge--success"], [1, "mj-priority-pill"], [3, "Close", "Visible", "Title", "Width"], [1, "mj-account-editor-form"], [1, "mj-form-group"], [1, "mj-form-label"], [1, "mj-required"], [1, "mj-form-select", 3, "ngModelChange", "ngModel", "disabled"], [3, "value"], [1, "mj-form-hint"], ["type", "text", "placeholder", "e.g., Corporate Azure Blob Storage, Marketing S3 Bucket", 1, "mj-form-input", 3, "ngModelChange", "ngModel"], ["rows", "2", "placeholder", "Optional notes or details about the intended purpose of this storage account...", 1, "mj-form-textarea", 3, "ngModelChange", "ngModel"], [1, "mj-form-label-row"], ["type", "button", 1, "mj-admin-btn-link", 3, "click"], [1, "mj-credential-input-row"], [1, "mj-form-select", 3, "ngModelChange", "ngModel"], ["value", ""], [1, "mj-form-checkbox-group"], [1, "mj-checkbox-label"], ["type", "checkbox", 3, "ngModelChange", "ngModel"], [1, "mj-form-hint", "mj-checkbox-hint"], [1, "mj-roles-checklist"], [1, "mj-role-check-item"], ["type", "button", 1, "mj-admin-btn", "mj-admin-btn-secondary", 3, "click", "disabled"], ["type", "button", 1, "mj-admin-btn", "mj-admin-btn-primary", 3, "click", "disabled"], [1, "fa-solid", "fa-spinner", "fa-spin"], ["Title", "Delete Storage Account", 3, "Close", "Visible", "Width"], [1, "mj-delete-confirm-body"], [1, "mj-delete-confirm-icon"], [1, "mj-delete-confirm-text"], [1, "mj-delete-confirm-title"], [1, "mj-delete-confirm-subtitle"], ["type", "button", 1, "mj-admin-btn", "mj-admin-btn-danger", 3, "click", "disabled"]], template: function StorageAdminDialogComponent_Template(rf, ctx) { if (rf & 1) {
853
+ i0.ɵɵelementStart(0, "mj-dialog", 0);
854
+ i0.ɵɵlistener("Close", function StorageAdminDialogComponent_Template_mj_dialog_Close_0_listener() { return ctx.OnDialogClose(); });
855
+ i0.ɵɵelementStart(1, "div", 1)(2, "div", 2)(3, "button", 3);
856
+ i0.ɵɵlistener("click", function StorageAdminDialogComponent_Template_button_click_3_listener() { return ctx.ActiveTab = "accounts"; });
857
+ i0.ɵɵelement(4, "i", 4);
858
+ i0.ɵɵelementStart(5, "span");
859
+ i0.ɵɵtext(6, "Storage Accounts");
860
+ i0.ɵɵelementEnd();
861
+ i0.ɵɵelementStart(7, "span", 5);
862
+ i0.ɵɵtext(8);
863
+ i0.ɵɵelementEnd()();
864
+ i0.ɵɵelementStart(9, "button", 3);
865
+ i0.ɵɵlistener("click", function StorageAdminDialogComponent_Template_button_click_9_listener() { return ctx.ActiveTab = "providers"; });
866
+ i0.ɵɵelement(10, "i", 6);
867
+ i0.ɵɵelementStart(11, "span");
868
+ i0.ɵɵtext(12, "Storage Providers");
869
+ i0.ɵɵelementEnd();
870
+ i0.ɵɵelementStart(13, "span", 5);
871
+ i0.ɵɵtext(14);
872
+ i0.ɵɵelementEnd()()();
873
+ i0.ɵɵconditionalCreate(15, StorageAdminDialogComponent_Conditional_15_Template, 13, 3, "div", 7);
874
+ i0.ɵɵconditionalCreate(16, StorageAdminDialogComponent_Conditional_16_Template, 24, 0, "div", 7);
875
+ i0.ɵɵelementEnd();
876
+ i0.ɵɵelementStart(17, "mj-dialog-actions")(18, "button", 8);
877
+ i0.ɵɵlistener("click", function StorageAdminDialogComponent_Template_button_click_18_listener() { return ctx.OnDialogClose(); });
878
+ i0.ɵɵtext(19, " Close ");
879
+ i0.ɵɵelementEnd()()();
880
+ i0.ɵɵconditionalCreate(20, StorageAdminDialogComponent_Conditional_20_Template, 59, 12, "mj-dialog", 9);
881
+ i0.ɵɵconditionalCreate(21, StorageAdminDialogComponent_Conditional_21_Template, 18, 6, "mj-dialog", 10);
882
+ } if (rf & 2) {
883
+ i0.ɵɵproperty("Visible", ctx.Visible)("Width", 880)("MinWidth", 600);
884
+ i0.ɵɵadvance(3);
885
+ i0.ɵɵclassProp("active", ctx.ActiveTab === "accounts");
886
+ i0.ɵɵadvance(5);
887
+ i0.ɵɵtextInterpolate(ctx.Accounts.length);
888
+ i0.ɵɵadvance();
889
+ i0.ɵɵclassProp("active", ctx.ActiveTab === "providers");
890
+ i0.ɵɵadvance(5);
891
+ i0.ɵɵtextInterpolate(ctx.Providers.length);
892
+ i0.ɵɵadvance();
893
+ i0.ɵɵconditional(ctx.ActiveTab === "accounts" ? 15 : -1);
894
+ i0.ɵɵadvance();
895
+ i0.ɵɵconditional(ctx.ActiveTab === "providers" ? 16 : -1);
896
+ i0.ɵɵadvance(4);
897
+ i0.ɵɵconditional(ctx.IsEditingAccount ? 20 : -1);
898
+ i0.ɵɵadvance();
899
+ i0.ɵɵconditional(ctx.AccountToDelete ? 21 : -1);
900
+ } }, dependencies: [i1.NgSelectOption, i1.ɵNgSelectMultipleOption, i1.DefaultValueAccessor, i1.CheckboxControlValueAccessor, i1.SelectControlValueAccessor, i1.NgControlStatus, i1.NgModel, i2.LoadingComponent, i3.MJDialogComponent, i3.MJDialogActionsComponent, i3.MJEmptyStateComponent], styles: ["[_nghost-%COMP%] {\n display: block;\n}\n\n.mj-storage-admin-container[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 16px;\n min-height: 460px;\n color: var(--mj-text-primary);\n}\n\n\n\n.mj-storage-admin-tabs[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 8px;\n border-bottom: 1px solid var(--mj-border-default);\n padding-bottom: 8px;\n}\n\n.mj-tab-btn[_ngcontent-%COMP%] {\n background: transparent;\n border: none;\n border-radius: var(--mj-radius-md, 8px);\n padding: 8px 14px;\n font-size: 0.86rem;\n font-weight: 500;\n color: var(--mj-text-secondary);\n cursor: pointer;\n display: inline-flex;\n align-items: center;\n gap: 8px;\n transition: all 0.15s ease;\n}\n\n.mj-tab-btn[_ngcontent-%COMP%]:hover {\n background: color-mix(in srgb, var(--mj-text-primary) 6%, var(--mj-bg-surface));\n color: var(--mj-text-primary);\n}\n\n.mj-tab-btn.active[_ngcontent-%COMP%] {\n background: color-mix(in srgb, var(--mj-brand-primary) 12%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n font-weight: 600;\n}\n\n.mj-tab-count[_ngcontent-%COMP%] {\n font-size: 0.72rem;\n font-weight: 600;\n padding: 2px 7px;\n border-radius: var(--mj-radius-full, 9999px);\n background: color-mix(in srgb, var(--mj-text-primary) 8%, var(--mj-bg-surface));\n color: var(--mj-text-secondary);\n}\n\n.mj-tab-btn.active[_ngcontent-%COMP%] .mj-tab-count[_ngcontent-%COMP%] {\n background: var(--mj-brand-primary);\n color: var(--mj-text-inverse, #ffffff);\n}\n\n\n\n.mj-tab-pane[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 14px;\n}\n\n.mj-admin-pane-desc[_ngcontent-%COMP%] {\n font-size: 0.82rem;\n color: var(--mj-text-secondary);\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 10px 14px;\n border-radius: var(--mj-radius-md, 8px);\n background: var(--mj-bg-surface-sunken, color-mix(in srgb, var(--mj-text-primary) 3%, var(--mj-bg-surface)));\n border: 1px solid var(--mj-border-default);\n}\n\n.mj-admin-pane-toolbar[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n}\n\n.mj-admin-search-wrapper[_ngcontent-%COMP%] {\n position: relative;\n flex: 1;\n max-width: 440px;\n display: flex;\n align-items: center;\n}\n\n.mj-admin-search-wrapper[_ngcontent-%COMP%] > i[_ngcontent-%COMP%] {\n position: absolute;\n left: 10px;\n font-size: 0.8rem;\n color: var(--mj-text-muted);\n}\n\n.mj-admin-search-wrapper[_ngcontent-%COMP%] input[_ngcontent-%COMP%] {\n width: 100%;\n padding: 7px 32px 7px 32px;\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-sm, 6px);\n background: var(--mj-bg-surface-sunken, color-mix(in srgb, var(--mj-text-primary) 3%, var(--mj-bg-surface)));\n color: var(--mj-text-primary);\n font-size: 0.82rem;\n outline: none;\n transition: all 0.12s ease;\n}\n\n.mj-admin-search-wrapper[_ngcontent-%COMP%] input[_ngcontent-%COMP%]:focus {\n border-color: var(--mj-brand-primary);\n background: var(--mj-bg-surface);\n}\n\n.mj-clear-btn[_ngcontent-%COMP%] {\n position: absolute;\n right: 8px;\n background: none;\n border: none;\n color: var(--mj-text-muted);\n cursor: pointer;\n padding: 2px;\n}\n\n.mj-admin-loading[_ngcontent-%COMP%] {\n padding: 40px;\n display: flex;\n justify-content: center;\n}\n\n.mj-empty-cta[_ngcontent-%COMP%] {\n margin-top: 14px;\n}\n\n\n\n.mj-admin-table-container[_ngcontent-%COMP%] {\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-md, 8px);\n overflow: hidden;\n background: var(--mj-bg-surface);\n}\n\n.mj-admin-table[_ngcontent-%COMP%] {\n width: 100%;\n border-collapse: collapse;\n font-size: 0.82rem;\n text-align: left;\n}\n\n.mj-admin-table[_ngcontent-%COMP%] th[_ngcontent-%COMP%] {\n padding: 10px 14px;\n background: var(--mj-bg-surface-sunken, color-mix(in srgb, var(--mj-text-primary) 3%, var(--mj-bg-surface)));\n color: var(--mj-text-secondary);\n font-weight: 600;\n border-bottom: 1px solid var(--mj-border-default);\n}\n\n.mj-admin-table[_ngcontent-%COMP%] td[_ngcontent-%COMP%] {\n padding: 10px 14px;\n border-bottom: 1px solid var(--mj-border-default);\n vertical-align: middle;\n}\n\n.mj-admin-table[_ngcontent-%COMP%] tr[_ngcontent-%COMP%]:last-child td[_ngcontent-%COMP%] {\n border-bottom: none;\n}\n\n.mj-admin-table[_ngcontent-%COMP%] tr[_ngcontent-%COMP%]:hover td[_ngcontent-%COMP%] {\n background: color-mix(in srgb, var(--mj-text-primary) 2%, var(--mj-bg-surface));\n}\n\n.mj-row-inactive[_ngcontent-%COMP%] {\n opacity: 0.6;\n}\n\n\n\n.mj-account-name-cell[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 2px;\n}\n\n.mj-account-title[_ngcontent-%COMP%] {\n font-weight: 600;\n color: var(--mj-text-primary);\n}\n\n.mj-account-desc[_ngcontent-%COMP%] {\n font-size: 0.74rem;\n color: var(--mj-text-muted);\n}\n\n.mj-provider-badge[_ngcontent-%COMP%] {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 3px 8px;\n border-radius: 4px;\n background: color-mix(in srgb, var(--mj-brand-primary) 8%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n font-weight: 500;\n}\n\n.mj-provider-cell[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 10px;\n}\n\n.mj-provider-icon-circle[_ngcontent-%COMP%] {\n width: 32px;\n height: 32px;\n border-radius: 50%;\n background: color-mix(in srgb, var(--mj-brand-primary) 12%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 1rem;\n flex-shrink: 0;\n}\n\n.mj-provider-name[_ngcontent-%COMP%] {\n font-weight: 600;\n color: var(--mj-text-primary);\n}\n\n.mj-provider-desc[_ngcontent-%COMP%] {\n font-size: 0.74rem;\n color: var(--mj-text-muted);\n}\n\n.mj-status-badge[_ngcontent-%COMP%] {\n display: inline-flex;\n align-items: center;\n gap: 5px;\n padding: 3px 8px;\n border-radius: var(--mj-radius-full, 9999px);\n font-size: 0.72rem;\n font-weight: 600;\n}\n\n.mj-status-badge--success[_ngcontent-%COMP%] {\n background: color-mix(in srgb, var(--mj-status-success, #10b981) 14%, var(--mj-bg-surface));\n color: var(--mj-status-success, #10b981);\n}\n\n.mj-status-badge--warning[_ngcontent-%COMP%] {\n background: color-mix(in srgb, var(--mj-status-warning, #f59e0b) 14%, var(--mj-bg-surface));\n color: var(--mj-status-warning, #f59e0b);\n}\n\n.mj-status-badge--info[_ngcontent-%COMP%] {\n background: color-mix(in srgb, var(--mj-brand-primary) 12%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n}\n\n.mj-status-badge--muted[_ngcontent-%COMP%] {\n background: color-mix(in srgb, var(--mj-text-primary) 8%, var(--mj-bg-surface));\n color: var(--mj-text-muted);\n}\n\n.mj-role-count-badge[_ngcontent-%COMP%] {\n font-size: 0.74rem;\n color: var(--mj-text-secondary);\n display: inline-flex;\n align-items: center;\n gap: 5px;\n}\n\n.mj-code-pill[_ngcontent-%COMP%] {\n font-family: var(--mj-font-mono, monospace);\n font-size: 0.74rem;\n background: var(--mj-bg-surface-sunken, color-mix(in srgb, var(--mj-text-primary) 5%, var(--mj-bg-surface)));\n border: 1px solid var(--mj-border-default);\n padding: 2px 6px;\n border-radius: 4px;\n color: var(--mj-text-secondary);\n}\n\n.mj-priority-pill[_ngcontent-%COMP%] {\n font-size: 0.74rem;\n font-weight: 600;\n padding: 2px 8px;\n border-radius: 4px;\n background: color-mix(in srgb, var(--mj-text-primary) 6%, var(--mj-bg-surface));\n color: var(--mj-text-secondary);\n}\n\n.mj-action-button-group[_ngcontent-%COMP%] {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n}\n\n.mj-action-icon-btn[_ngcontent-%COMP%] {\n background: transparent;\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-sm, 6px);\n width: 28px;\n height: 28px;\n font-size: 0.78rem;\n color: var(--mj-text-secondary);\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: all 0.12s;\n}\n\n.mj-action-icon-btn[_ngcontent-%COMP%]:hover {\n background: color-mix(in srgb, var(--mj-text-primary) 8%, var(--mj-bg-surface));\n color: var(--mj-text-primary);\n border-color: var(--mj-brand-primary);\n}\n\n.mj-action-icon-btn--danger[_ngcontent-%COMP%]:hover {\n background: color-mix(in srgb, var(--mj-status-error, #ef4444) 15%, var(--mj-bg-surface));\n color: var(--mj-status-error, #ef4444);\n border-color: var(--mj-status-error, #ef4444);\n}\n\n.mj-text-right[_ngcontent-%COMP%] {\n text-align: right;\n}\n\n.mj-text-center[_ngcontent-%COMP%] {\n text-align: center;\n}\n\n\n\n.mj-toggle-switch[_ngcontent-%COMP%] {\n position: relative;\n display: inline-block;\n width: 36px;\n height: 20px;\n cursor: pointer;\n}\n\n.mj-toggle-switch[_ngcontent-%COMP%] input[_ngcontent-%COMP%] {\n opacity: 0;\n width: 0;\n height: 0;\n}\n\n.mj-toggle-slider[_ngcontent-%COMP%] {\n position: absolute;\n inset: 0;\n background-color: color-mix(in srgb, var(--mj-text-primary) 20%, var(--mj-bg-surface));\n border-radius: 20px;\n transition: 0.2s ease;\n}\n\n.mj-toggle-slider[_ngcontent-%COMP%]:before {\n position: absolute;\n content: \"\";\n height: 14px;\n width: 14px;\n left: 3px;\n bottom: 3px;\n background-color: var(--mj-bg-surface);\n border-radius: 50%;\n transition: 0.2s ease;\n}\n\n.mj-toggle-switch[_ngcontent-%COMP%] input[_ngcontent-%COMP%]:checked + .mj-toggle-slider[_ngcontent-%COMP%] {\n background-color: var(--mj-brand-primary);\n}\n\n.mj-toggle-switch[_ngcontent-%COMP%] input[_ngcontent-%COMP%]:checked + .mj-toggle-slider[_ngcontent-%COMP%]:before {\n transform: translateX(16px);\n}\n\n\n\n.mj-account-editor-form[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 14px;\n padding: 4px 0;\n}\n\n.mj-form-group[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 5px;\n}\n\n.mj-form-label-row[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: space-between;\n}\n\n.mj-form-label[_ngcontent-%COMP%] {\n font-size: 0.8rem;\n font-weight: 600;\n color: var(--mj-text-secondary);\n}\n\n.mj-required[_ngcontent-%COMP%] {\n color: var(--mj-status-error, #ef4444);\n}\n\n.mj-form-input[_ngcontent-%COMP%], .mj-form-select[_ngcontent-%COMP%], .mj-form-textarea[_ngcontent-%COMP%] {\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-sm, 6px);\n padding: 8px 10px;\n font-size: 0.84rem;\n color: var(--mj-text-primary);\n background: var(--mj-bg-surface);\n outline: none;\n font-family: inherit;\n transition: border-color 0.12s;\n}\n\n.mj-form-input[_ngcontent-%COMP%]:focus, .mj-form-select[_ngcontent-%COMP%]:focus, .mj-form-textarea[_ngcontent-%COMP%]:focus {\n border-color: var(--mj-brand-primary);\n}\n\n.mj-form-hint[_ngcontent-%COMP%] {\n font-size: 0.72rem;\n color: var(--mj-text-muted);\n}\n\n.mj-credential-input-row[_ngcontent-%COMP%] {\n display: flex;\n gap: 8px;\n}\n\n.mj-credential-input-row[_ngcontent-%COMP%] .mj-form-select[_ngcontent-%COMP%] {\n flex: 1;\n}\n\n.mj-form-checkbox-group[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 4px;\n padding: 6px 0;\n}\n\n.mj-checkbox-label[_ngcontent-%COMP%] {\n display: inline-flex;\n align-items: center;\n gap: 8px;\n font-size: 0.82rem;\n font-weight: 500;\n cursor: pointer;\n}\n\n.mj-checkbox-hint[_ngcontent-%COMP%] {\n margin-left: 22px;\n}\n\n.mj-roles-checklist[_ngcontent-%COMP%] {\n display: grid;\n grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));\n gap: 8px;\n max-height: 140px;\n overflow-y: auto;\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-sm, 6px);\n padding: 10px;\n background: var(--mj-bg-surface-sunken, color-mix(in srgb, var(--mj-text-primary) 3%, var(--mj-bg-surface)));\n}\n\n.mj-role-check-item[_ngcontent-%COMP%] {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n font-size: 0.78rem;\n color: var(--mj-text-primary);\n cursor: pointer;\n}\n\n\n\n.mj-delete-confirm-body[_ngcontent-%COMP%] {\n display: flex;\n gap: 16px;\n padding: 10px 0;\n}\n\n.mj-delete-confirm-icon[_ngcontent-%COMP%] {\n font-size: 2.2rem;\n color: var(--mj-status-error, #ef4444);\n flex-shrink: 0;\n}\n\n.mj-delete-confirm-title[_ngcontent-%COMP%] {\n font-size: 0.92rem;\n font-weight: 600;\n color: var(--mj-text-primary);\n margin: 0 0 6px 0;\n}\n\n.mj-delete-confirm-subtitle[_ngcontent-%COMP%] {\n font-size: 0.8rem;\n color: var(--mj-text-secondary);\n line-height: 1.4;\n margin: 0;\n}\n\n\n\n.mj-admin-btn[_ngcontent-%COMP%] {\n padding: 7px 14px;\n border-radius: var(--mj-radius-sm, 6px);\n font-size: 0.82rem;\n font-weight: 500;\n cursor: pointer;\n display: inline-flex;\n align-items: center;\n gap: 6px;\n border: 1px solid transparent;\n transition: all 0.12s;\n}\n\n.mj-admin-btn-primary[_ngcontent-%COMP%] {\n background: var(--mj-brand-primary);\n color: var(--mj-text-inverse, #ffffff);\n}\n\n.mj-admin-btn-primary[_ngcontent-%COMP%]:hover {\n background: var(--mj-brand-primary-hover);\n}\n\n.mj-admin-btn-secondary[_ngcontent-%COMP%] {\n background: var(--mj-bg-surface);\n border-color: var(--mj-border-default);\n color: var(--mj-text-secondary);\n}\n\n.mj-admin-btn-secondary[_ngcontent-%COMP%]:hover {\n background: color-mix(in srgb, var(--mj-text-primary) 8%, var(--mj-bg-surface));\n color: var(--mj-text-primary);\n}\n\n.mj-admin-btn-danger[_ngcontent-%COMP%] {\n background: var(--mj-status-error, #ef4444);\n color: var(--mj-text-inverse, #ffffff);\n}\n\n.mj-admin-btn-danger[_ngcontent-%COMP%]:hover {\n background: color-mix(in srgb, var(--mj-text-primary, #000000) 15%, var(--mj-status-error, #ef4444));\n}\n\n.mj-admin-btn-link[_ngcontent-%COMP%] {\n background: none;\n border: none;\n color: var(--mj-brand-primary);\n font-size: 0.78rem;\n font-weight: 600;\n cursor: pointer;\n display: inline-flex;\n align-items: center;\n gap: 4px;\n padding: 0;\n}\n\n.mj-admin-btn-link[_ngcontent-%COMP%]:hover {\n text-decoration: underline;\n}"] });
901
+ }
902
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(StorageAdminDialogComponent, [{
903
+ type: Component,
904
+ args: [{ standalone: false, selector: 'mj-storage-admin-dialog', template: "<mj-dialog\n [Visible]=\"Visible\"\n Title=\"Manage File Storage\"\n [Width]=\"880\"\n [MinWidth]=\"600\"\n (Close)=\"OnDialogClose()\">\n\n <div class=\"mj-storage-admin-container\">\n <!-- Tab Navigation -->\n <div class=\"mj-storage-admin-tabs\">\n <button\n type=\"button\"\n class=\"mj-tab-btn\"\n [class.active]=\"ActiveTab === 'accounts'\"\n (click)=\"ActiveTab = 'accounts'\">\n <i class=\"fa-solid fa-server\"></i>\n <span>Storage Accounts</span>\n <span class=\"mj-tab-count\">{{ Accounts.length }}</span>\n </button>\n\n <button\n type=\"button\"\n class=\"mj-tab-btn\"\n [class.active]=\"ActiveTab === 'providers'\"\n (click)=\"ActiveTab = 'providers'\">\n <i class=\"fa-solid fa-cubes\"></i>\n <span>Storage Providers</span>\n <span class=\"mj-tab-count\">{{ Providers.length }}</span>\n </button>\n </div>\n\n <!-- \u2500\u2500 TAB 1: STORAGE ACCOUNTS \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n @if (ActiveTab === 'accounts') {\n <div class=\"mj-tab-pane\">\n <!-- Toolbar -->\n <div class=\"mj-admin-pane-toolbar\">\n <div class=\"mj-admin-search-wrapper\">\n <i class=\"fa-solid fa-magnifying-glass\"></i>\n <input\n type=\"text\"\n [(ngModel)]=\"SearchTerm\"\n placeholder=\"Search storage accounts by name, provider, or credential...\" />\n @if (SearchTerm) {\n <button type=\"button\" class=\"mj-clear-btn\" (click)=\"SearchTerm = ''\">\n <i class=\"fa-solid fa-xmark\"></i>\n </button>\n }\n </div>\n\n <button\n type=\"button\"\n class=\"mj-admin-btn mj-admin-btn-primary\"\n (click)=\"StartCreateAccount()\">\n <i class=\"fa-solid fa-plus\"></i>\n <span>Add Storage Account</span>\n </button>\n </div>\n\n <!-- Loading -->\n @if (IsLoading) {\n <div class=\"mj-admin-loading\">\n <mj-loading text=\"Loading storage accounts...\"></mj-loading>\n </div>\n } @else if (FilteredAccountRows.length === 0) {\n <mj-empty-state\n Icon=\"fa-solid fa-folder-plus\"\n [Title]=\"SearchTerm ? 'No matching storage accounts' : 'No storage accounts configured'\"\n [Message]=\"SearchTerm ? 'Try adjusting your search query.' : 'Configure storage accounts to connect MemberJunction to cloud storage services like AWS S3, Azure Blob Storage, Box, Dropbox, or Google Drive.'\">\n @if (!SearchTerm) {\n <button\n type=\"button\"\n class=\"mj-admin-btn mj-admin-btn-primary mj-empty-cta\"\n (click)=\"StartCreateAccount()\">\n <i class=\"fa-solid fa-plus\"></i>\n <span>Configure First Account</span>\n </button>\n }\n </mj-empty-state>\n } @else {\n <!-- Accounts List -->\n <div class=\"mj-admin-table-container\">\n <table class=\"mj-admin-table\">\n <thead>\n <tr>\n <th>Account Name</th>\n <th>Provider</th>\n <th>Credential</th>\n <th>Global Search</th>\n <th>Role Access</th>\n <th class=\"mj-text-right\">Actions</th>\n </tr>\n </thead>\n <tbody>\n @for (row of FilteredAccountRows; track row.account.ID) {\n <tr>\n <td>\n <div class=\"mj-account-name-cell\">\n <span class=\"mj-account-title\">{{ row.account.Name }}</span>\n @if (row.account.Description) {\n <span class=\"mj-account-desc\">{{ row.account.Description }}</span>\n }\n </div>\n </td>\n <td>\n <span class=\"mj-provider-badge\">\n <i [class]=\"GetProviderIcon(row.provider?.Name || row.account.Provider)\"></i>\n <span>{{ row.provider?.Name || row.account.Provider }}</span>\n </span>\n </td>\n <td>\n @if (row.credentialName) {\n <span class=\"mj-status-badge mj-status-badge--success\" [title]=\"'Credential: ' + row.credentialName\">\n <i class=\"fa-solid fa-key\"></i>\n <span>{{ row.credentialName }}</span>\n </span>\n } @else {\n <span class=\"mj-status-badge mj-status-badge--warning\" title=\"No credential configured\">\n <i class=\"fa-solid fa-triangle-exclamation\"></i>\n <span>No Credential</span>\n </span>\n }\n </td>\n <td>\n @if (row.account.IncludeInGlobalSearch) {\n <span class=\"mj-status-badge mj-status-badge--info\">\n <i class=\"fa-solid fa-check\"></i>\n <span>Enabled</span>\n </span>\n } @else {\n <span class=\"mj-status-badge mj-status-badge--muted\">\n <span>Disabled</span>\n </span>\n }\n </td>\n <td>\n <span class=\"mj-role-count-badge\">\n <i class=\"fa-solid fa-user-shield\"></i>\n <span>{{ row.roleCount === 0 ? 'All Users' : row.roleCount + ' Role(s)' }}</span>\n </span>\n </td>\n <td class=\"mj-text-right\">\n <div class=\"mj-action-button-group\">\n <button\n type=\"button\"\n class=\"mj-action-icon-btn\"\n title=\"Edit Account\"\n (click)=\"StartEditAccount(row.account)\">\n <i class=\"fa-solid fa-pen-to-square\"></i>\n </button>\n <button\n type=\"button\"\n class=\"mj-action-icon-btn mj-action-icon-btn--danger\"\n title=\"Delete Account\"\n (click)=\"ConfirmDeleteAccount(row.account)\">\n <i class=\"fa-solid fa-trash-can\"></i>\n </button>\n </div>\n </td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n }\n </div>\n }\n\n <!-- \u2500\u2500 TAB 2: STORAGE PROVIDERS \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n @if (ActiveTab === 'providers') {\n <div class=\"mj-tab-pane\">\n <div class=\"mj-admin-pane-desc\">\n <i class=\"fa-solid fa-circle-info\"></i>\n <span>Enable or disable storage provider integrations for your organization. Only active providers are available when configuring accounts.</span>\n </div>\n\n <div class=\"mj-admin-table-container\">\n <table class=\"mj-admin-table\">\n <thead>\n <tr>\n <th>Status</th>\n <th>Provider Driver</th>\n <th>Server Driver Key</th>\n <th>Client Driver Key</th>\n <th>Search Support</th>\n <th class=\"mj-text-center\">Priority</th>\n </tr>\n </thead>\n <tbody>\n @for (prov of Providers; track prov.ID) {\n <tr [class.mj-row-inactive]=\"!prov.IsActive\">\n <td>\n <label class=\"mj-toggle-switch\" [title]=\"prov.IsActive ? 'Deactivate provider' : 'Activate provider'\">\n <input\n type=\"checkbox\"\n [checked]=\"prov.IsActive\"\n (change)=\"ToggleProviderActive(prov)\" />\n <span class=\"mj-toggle-slider\"></span>\n </label>\n </td>\n <td>\n <div class=\"mj-provider-cell\">\n <div class=\"mj-provider-icon-circle\">\n <i [class]=\"GetProviderIcon(prov.Name)\"></i>\n </div>\n <div>\n <div class=\"mj-provider-name\">{{ prov.Name }}</div>\n @if (prov.Description) {\n <div class=\"mj-provider-desc\">{{ prov.Description }}</div>\n }\n </div>\n </div>\n </td>\n <td>\n <code class=\"mj-code-pill\">{{ prov.ServerDriverKey || '\u2014' }}</code>\n </td>\n <td>\n <code class=\"mj-code-pill\">{{ prov.ClientDriverKey || '\u2014' }}</code>\n </td>\n <td>\n @if (prov.SupportsSearch) {\n <span class=\"mj-status-badge mj-status-badge--success\" title=\"Provider supports full-text file search\">\n <i class=\"fa-solid fa-magnifying-glass\"></i>\n <span>Native Search</span>\n </span>\n } @else {\n <span class=\"mj-status-badge mj-status-badge--muted\">\n <span>Standard</span>\n </span>\n }\n </td>\n <td class=\"mj-text-center\">\n <span class=\"mj-priority-pill\">{{ prov.Priority }}</span>\n </td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n </div>\n }\n </div>\n\n <mj-dialog-actions>\n <button type=\"button\" class=\"mj-admin-btn mj-admin-btn-secondary\" (click)=\"OnDialogClose()\">\n Close\n </button>\n </mj-dialog-actions>\n</mj-dialog>\n\n<!-- \u2500\u2500 ACCOUNT CREATE / EDIT SUB-DIALOG \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n@if (IsEditingAccount) {\n <mj-dialog\n [Visible]=\"IsEditingAccount\"\n [Title]=\"EditingAccount ? 'Edit Storage Account: ' + EditingAccount.Name : 'Add Storage Account'\"\n [Width]=\"640\"\n (Close)=\"CancelEditAccount()\">\n\n <div class=\"mj-account-editor-form\">\n <!-- Provider Selection -->\n <div class=\"mj-form-group\">\n <label class=\"mj-form-label\">Storage Provider <span class=\"mj-required\">*</span></label>\n <select\n class=\"mj-form-select\"\n [(ngModel)]=\"EditingAccountProviderID\"\n [disabled]=\"!!EditingAccount\">\n @for (prov of ActiveProviders; track prov.ID) {\n <option [value]=\"prov.ID\">{{ prov.Name }}</option>\n }\n </select>\n <div class=\"mj-form-hint\">Select the cloud storage engine driver for this account.</div>\n </div>\n\n <!-- Account Name -->\n <div class=\"mj-form-group\">\n <label class=\"mj-form-label\">Account Name <span class=\"mj-required\">*</span></label>\n <input\n type=\"text\"\n class=\"mj-form-input\"\n [(ngModel)]=\"EditingAccountName\"\n placeholder=\"e.g., Corporate Azure Blob Storage, Marketing S3 Bucket\" />\n </div>\n\n <!-- Account Description -->\n <div class=\"mj-form-group\">\n <label class=\"mj-form-label\">Description</label>\n <textarea\n class=\"mj-form-textarea\"\n rows=\"2\"\n [(ngModel)]=\"EditingAccountDescription\"\n placeholder=\"Optional notes or details about the intended purpose of this storage account...\"></textarea>\n </div>\n\n <!-- Credential Picker with Inline Create -->\n <div class=\"mj-form-group\">\n <div class=\"mj-form-label-row\">\n <label class=\"mj-form-label\">Storage Credentials</label>\n <button\n type=\"button\"\n class=\"mj-admin-btn-link\"\n (click)=\"OpenCreateCredentialDialog()\">\n <i class=\"fa-solid fa-plus\"></i>\n <span>New Credential</span>\n </button>\n </div>\n <div class=\"mj-credential-input-row\">\n <select class=\"mj-form-select\" [(ngModel)]=\"EditingAccountCredentialID\">\n <option value=\"\">-- No Credential Assigned --</option>\n @for (cred of Credentials; track cred.ID) {\n <option [value]=\"cred.ID\">{{ cred.Name }}</option>\n }\n </select>\n </div>\n <div class=\"mj-form-hint\">\n Credentials manage authentication tokens, connection strings, or API keys securely via the Credential Engine.\n </div>\n </div>\n\n <!-- Global Search Toggle -->\n <div class=\"mj-form-checkbox-group\">\n <label class=\"mj-checkbox-label\">\n <input\n type=\"checkbox\"\n [(ngModel)]=\"EditingAccountIncludeInGlobalSearch\" />\n <span>Include in Global Search</span>\n </label>\n <div class=\"mj-form-hint mj-checkbox-hint\">\n When enabled, file contents and metadata in this account will be indexed for universal MemberJunction search.\n </div>\n </div>\n\n <!-- Role Permissions -->\n <div class=\"mj-form-group\">\n <label class=\"mj-form-label\">Role Access Permissions</label>\n <div class=\"mj-form-hint\">\n Select specific roles permitted to access this storage account. If no roles are selected, all authorized users have access.\n </div>\n <div class=\"mj-roles-checklist\">\n @for (role of Roles; track role.ID) {\n <label class=\"mj-role-check-item\">\n <input\n type=\"checkbox\"\n [checked]=\"IsRoleSelected(role.ID)\"\n (change)=\"ToggleRoleSelection(role.ID)\" />\n <span>{{ role.Name }}</span>\n </label>\n }\n </div>\n </div>\n </div>\n\n <mj-dialog-actions>\n <button\n type=\"button\"\n class=\"mj-admin-btn mj-admin-btn-secondary\"\n [disabled]=\"IsSaving\"\n (click)=\"CancelEditAccount()\">\n Cancel\n </button>\n <button\n type=\"button\"\n class=\"mj-admin-btn mj-admin-btn-primary\"\n [disabled]=\"IsSaving || !EditingAccountName.trim() || !EditingAccountProviderID\"\n (click)=\"SaveAccount()\">\n @if (IsSaving) {\n <i class=\"fa-solid fa-spinner fa-spin\"></i>\n <span>Saving...</span>\n } @else {\n <i class=\"fa-solid fa-check\"></i>\n <span>{{ EditingAccount ? 'Save Changes' : 'Create Account' }}</span>\n }\n </button>\n </mj-dialog-actions>\n </mj-dialog>\n}\n\n<!-- \u2500\u2500 DELETE CONFIRMATION DIALOG \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n@if (AccountToDelete) {\n <mj-dialog\n [Visible]=\"!!AccountToDelete\"\n Title=\"Delete Storage Account\"\n [Width]=\"460\"\n (Close)=\"CancelDeleteAccount()\">\n\n <div class=\"mj-delete-confirm-body\">\n <div class=\"mj-delete-confirm-icon\">\n <i class=\"fa-solid fa-triangle-exclamation\"></i>\n </div>\n <div class=\"mj-delete-confirm-text\">\n <p class=\"mj-delete-confirm-title\">\n Are you sure you want to delete <strong>{{ AccountToDelete.Name }}</strong>?\n </p>\n <p class=\"mj-delete-confirm-subtitle\">\n This will remove the storage account connection and role permissions. Existing files in the remote storage service will not be deleted, but will no longer be accessible via this account in MemberJunction.\n </p>\n </div>\n </div>\n\n <mj-dialog-actions>\n <button\n type=\"button\"\n class=\"mj-admin-btn mj-admin-btn-secondary\"\n [disabled]=\"IsDeleting\"\n (click)=\"CancelDeleteAccount()\">\n Cancel\n </button>\n <button\n type=\"button\"\n class=\"mj-admin-btn mj-admin-btn-danger\"\n [disabled]=\"IsDeleting\"\n (click)=\"ExecuteDeleteAccount()\">\n @if (IsDeleting) {\n <i class=\"fa-solid fa-spinner fa-spin\"></i>\n <span>Deleting...</span>\n } @else {\n <i class=\"fa-solid fa-trash-can\"></i>\n <span>Delete Account</span>\n }\n </button>\n </mj-dialog-actions>\n </mj-dialog>\n}\n", styles: [":host {\n display: block;\n}\n\n.mj-storage-admin-container {\n display: flex;\n flex-direction: column;\n gap: 16px;\n min-height: 460px;\n color: var(--mj-text-primary);\n}\n\n/* Tabs */\n.mj-storage-admin-tabs {\n display: flex;\n align-items: center;\n gap: 8px;\n border-bottom: 1px solid var(--mj-border-default);\n padding-bottom: 8px;\n}\n\n.mj-tab-btn {\n background: transparent;\n border: none;\n border-radius: var(--mj-radius-md, 8px);\n padding: 8px 14px;\n font-size: 0.86rem;\n font-weight: 500;\n color: var(--mj-text-secondary);\n cursor: pointer;\n display: inline-flex;\n align-items: center;\n gap: 8px;\n transition: all 0.15s ease;\n}\n\n.mj-tab-btn:hover {\n background: color-mix(in srgb, var(--mj-text-primary) 6%, var(--mj-bg-surface));\n color: var(--mj-text-primary);\n}\n\n.mj-tab-btn.active {\n background: color-mix(in srgb, var(--mj-brand-primary) 12%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n font-weight: 600;\n}\n\n.mj-tab-count {\n font-size: 0.72rem;\n font-weight: 600;\n padding: 2px 7px;\n border-radius: var(--mj-radius-full, 9999px);\n background: color-mix(in srgb, var(--mj-text-primary) 8%, var(--mj-bg-surface));\n color: var(--mj-text-secondary);\n}\n\n.mj-tab-btn.active .mj-tab-count {\n background: var(--mj-brand-primary);\n color: var(--mj-text-inverse, #ffffff);\n}\n\n/* Pane */\n.mj-tab-pane {\n display: flex;\n flex-direction: column;\n gap: 14px;\n}\n\n.mj-admin-pane-desc {\n font-size: 0.82rem;\n color: var(--mj-text-secondary);\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 10px 14px;\n border-radius: var(--mj-radius-md, 8px);\n background: var(--mj-bg-surface-sunken, color-mix(in srgb, var(--mj-text-primary) 3%, var(--mj-bg-surface)));\n border: 1px solid var(--mj-border-default);\n}\n\n.mj-admin-pane-toolbar {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n}\n\n.mj-admin-search-wrapper {\n position: relative;\n flex: 1;\n max-width: 440px;\n display: flex;\n align-items: center;\n}\n\n.mj-admin-search-wrapper > i {\n position: absolute;\n left: 10px;\n font-size: 0.8rem;\n color: var(--mj-text-muted);\n}\n\n.mj-admin-search-wrapper input {\n width: 100%;\n padding: 7px 32px 7px 32px;\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-sm, 6px);\n background: var(--mj-bg-surface-sunken, color-mix(in srgb, var(--mj-text-primary) 3%, var(--mj-bg-surface)));\n color: var(--mj-text-primary);\n font-size: 0.82rem;\n outline: none;\n transition: all 0.12s ease;\n}\n\n.mj-admin-search-wrapper input:focus {\n border-color: var(--mj-brand-primary);\n background: var(--mj-bg-surface);\n}\n\n.mj-clear-btn {\n position: absolute;\n right: 8px;\n background: none;\n border: none;\n color: var(--mj-text-muted);\n cursor: pointer;\n padding: 2px;\n}\n\n.mj-admin-loading {\n padding: 40px;\n display: flex;\n justify-content: center;\n}\n\n.mj-empty-cta {\n margin-top: 14px;\n}\n\n/* Tables */\n.mj-admin-table-container {\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-md, 8px);\n overflow: hidden;\n background: var(--mj-bg-surface);\n}\n\n.mj-admin-table {\n width: 100%;\n border-collapse: collapse;\n font-size: 0.82rem;\n text-align: left;\n}\n\n.mj-admin-table th {\n padding: 10px 14px;\n background: var(--mj-bg-surface-sunken, color-mix(in srgb, var(--mj-text-primary) 3%, var(--mj-bg-surface)));\n color: var(--mj-text-secondary);\n font-weight: 600;\n border-bottom: 1px solid var(--mj-border-default);\n}\n\n.mj-admin-table td {\n padding: 10px 14px;\n border-bottom: 1px solid var(--mj-border-default);\n vertical-align: middle;\n}\n\n.mj-admin-table tr:last-child td {\n border-bottom: none;\n}\n\n.mj-admin-table tr:hover td {\n background: color-mix(in srgb, var(--mj-text-primary) 2%, var(--mj-bg-surface));\n}\n\n.mj-row-inactive {\n opacity: 0.6;\n}\n\n/* Cells */\n.mj-account-name-cell {\n display: flex;\n flex-direction: column;\n gap: 2px;\n}\n\n.mj-account-title {\n font-weight: 600;\n color: var(--mj-text-primary);\n}\n\n.mj-account-desc {\n font-size: 0.74rem;\n color: var(--mj-text-muted);\n}\n\n.mj-provider-badge {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 3px 8px;\n border-radius: 4px;\n background: color-mix(in srgb, var(--mj-brand-primary) 8%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n font-weight: 500;\n}\n\n.mj-provider-cell {\n display: flex;\n align-items: center;\n gap: 10px;\n}\n\n.mj-provider-icon-circle {\n width: 32px;\n height: 32px;\n border-radius: 50%;\n background: color-mix(in srgb, var(--mj-brand-primary) 12%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 1rem;\n flex-shrink: 0;\n}\n\n.mj-provider-name {\n font-weight: 600;\n color: var(--mj-text-primary);\n}\n\n.mj-provider-desc {\n font-size: 0.74rem;\n color: var(--mj-text-muted);\n}\n\n.mj-status-badge {\n display: inline-flex;\n align-items: center;\n gap: 5px;\n padding: 3px 8px;\n border-radius: var(--mj-radius-full, 9999px);\n font-size: 0.72rem;\n font-weight: 600;\n}\n\n.mj-status-badge--success {\n background: color-mix(in srgb, var(--mj-status-success, #10b981) 14%, var(--mj-bg-surface));\n color: var(--mj-status-success, #10b981);\n}\n\n.mj-status-badge--warning {\n background: color-mix(in srgb, var(--mj-status-warning, #f59e0b) 14%, var(--mj-bg-surface));\n color: var(--mj-status-warning, #f59e0b);\n}\n\n.mj-status-badge--info {\n background: color-mix(in srgb, var(--mj-brand-primary) 12%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n}\n\n.mj-status-badge--muted {\n background: color-mix(in srgb, var(--mj-text-primary) 8%, var(--mj-bg-surface));\n color: var(--mj-text-muted);\n}\n\n.mj-role-count-badge {\n font-size: 0.74rem;\n color: var(--mj-text-secondary);\n display: inline-flex;\n align-items: center;\n gap: 5px;\n}\n\n.mj-code-pill {\n font-family: var(--mj-font-mono, monospace);\n font-size: 0.74rem;\n background: var(--mj-bg-surface-sunken, color-mix(in srgb, var(--mj-text-primary) 5%, var(--mj-bg-surface)));\n border: 1px solid var(--mj-border-default);\n padding: 2px 6px;\n border-radius: 4px;\n color: var(--mj-text-secondary);\n}\n\n.mj-priority-pill {\n font-size: 0.74rem;\n font-weight: 600;\n padding: 2px 8px;\n border-radius: 4px;\n background: color-mix(in srgb, var(--mj-text-primary) 6%, var(--mj-bg-surface));\n color: var(--mj-text-secondary);\n}\n\n.mj-action-button-group {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n}\n\n.mj-action-icon-btn {\n background: transparent;\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-sm, 6px);\n width: 28px;\n height: 28px;\n font-size: 0.78rem;\n color: var(--mj-text-secondary);\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: all 0.12s;\n}\n\n.mj-action-icon-btn:hover {\n background: color-mix(in srgb, var(--mj-text-primary) 8%, var(--mj-bg-surface));\n color: var(--mj-text-primary);\n border-color: var(--mj-brand-primary);\n}\n\n.mj-action-icon-btn--danger:hover {\n background: color-mix(in srgb, var(--mj-status-error, #ef4444) 15%, var(--mj-bg-surface));\n color: var(--mj-status-error, #ef4444);\n border-color: var(--mj-status-error, #ef4444);\n}\n\n.mj-text-right {\n text-align: right;\n}\n\n.mj-text-center {\n text-align: center;\n}\n\n/* Toggle Switch */\n.mj-toggle-switch {\n position: relative;\n display: inline-block;\n width: 36px;\n height: 20px;\n cursor: pointer;\n}\n\n.mj-toggle-switch input {\n opacity: 0;\n width: 0;\n height: 0;\n}\n\n.mj-toggle-slider {\n position: absolute;\n inset: 0;\n background-color: color-mix(in srgb, var(--mj-text-primary) 20%, var(--mj-bg-surface));\n border-radius: 20px;\n transition: 0.2s ease;\n}\n\n.mj-toggle-slider:before {\n position: absolute;\n content: \"\";\n height: 14px;\n width: 14px;\n left: 3px;\n bottom: 3px;\n background-color: var(--mj-bg-surface);\n border-radius: 50%;\n transition: 0.2s ease;\n}\n\n.mj-toggle-switch input:checked + .mj-toggle-slider {\n background-color: var(--mj-brand-primary);\n}\n\n.mj-toggle-switch input:checked + .mj-toggle-slider:before {\n transform: translateX(16px);\n}\n\n/* Account Form */\n.mj-account-editor-form {\n display: flex;\n flex-direction: column;\n gap: 14px;\n padding: 4px 0;\n}\n\n.mj-form-group {\n display: flex;\n flex-direction: column;\n gap: 5px;\n}\n\n.mj-form-label-row {\n display: flex;\n align-items: center;\n justify-content: space-between;\n}\n\n.mj-form-label {\n font-size: 0.8rem;\n font-weight: 600;\n color: var(--mj-text-secondary);\n}\n\n.mj-required {\n color: var(--mj-status-error, #ef4444);\n}\n\n.mj-form-input, .mj-form-select, .mj-form-textarea {\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-sm, 6px);\n padding: 8px 10px;\n font-size: 0.84rem;\n color: var(--mj-text-primary);\n background: var(--mj-bg-surface);\n outline: none;\n font-family: inherit;\n transition: border-color 0.12s;\n}\n\n.mj-form-input:focus, .mj-form-select:focus, .mj-form-textarea:focus {\n border-color: var(--mj-brand-primary);\n}\n\n.mj-form-hint {\n font-size: 0.72rem;\n color: var(--mj-text-muted);\n}\n\n.mj-credential-input-row {\n display: flex;\n gap: 8px;\n}\n\n.mj-credential-input-row .mj-form-select {\n flex: 1;\n}\n\n.mj-form-checkbox-group {\n display: flex;\n flex-direction: column;\n gap: 4px;\n padding: 6px 0;\n}\n\n.mj-checkbox-label {\n display: inline-flex;\n align-items: center;\n gap: 8px;\n font-size: 0.82rem;\n font-weight: 500;\n cursor: pointer;\n}\n\n.mj-checkbox-hint {\n margin-left: 22px;\n}\n\n.mj-roles-checklist {\n display: grid;\n grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));\n gap: 8px;\n max-height: 140px;\n overflow-y: auto;\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-sm, 6px);\n padding: 10px;\n background: var(--mj-bg-surface-sunken, color-mix(in srgb, var(--mj-text-primary) 3%, var(--mj-bg-surface)));\n}\n\n.mj-role-check-item {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n font-size: 0.78rem;\n color: var(--mj-text-primary);\n cursor: pointer;\n}\n\n/* Delete Confirm */\n.mj-delete-confirm-body {\n display: flex;\n gap: 16px;\n padding: 10px 0;\n}\n\n.mj-delete-confirm-icon {\n font-size: 2.2rem;\n color: var(--mj-status-error, #ef4444);\n flex-shrink: 0;\n}\n\n.mj-delete-confirm-title {\n font-size: 0.92rem;\n font-weight: 600;\n color: var(--mj-text-primary);\n margin: 0 0 6px 0;\n}\n\n.mj-delete-confirm-subtitle {\n font-size: 0.8rem;\n color: var(--mj-text-secondary);\n line-height: 1.4;\n margin: 0;\n}\n\n/* Dialog Action Buttons */\n.mj-admin-btn {\n padding: 7px 14px;\n border-radius: var(--mj-radius-sm, 6px);\n font-size: 0.82rem;\n font-weight: 500;\n cursor: pointer;\n display: inline-flex;\n align-items: center;\n gap: 6px;\n border: 1px solid transparent;\n transition: all 0.12s;\n}\n\n.mj-admin-btn-primary {\n background: var(--mj-brand-primary);\n color: var(--mj-text-inverse, #ffffff);\n}\n\n.mj-admin-btn-primary:hover {\n background: var(--mj-brand-primary-hover);\n}\n\n.mj-admin-btn-secondary {\n background: var(--mj-bg-surface);\n border-color: var(--mj-border-default);\n color: var(--mj-text-secondary);\n}\n\n.mj-admin-btn-secondary:hover {\n background: color-mix(in srgb, var(--mj-text-primary) 8%, var(--mj-bg-surface));\n color: var(--mj-text-primary);\n}\n\n.mj-admin-btn-danger {\n background: var(--mj-status-error, #ef4444);\n color: var(--mj-text-inverse, #ffffff);\n}\n\n.mj-admin-btn-danger:hover {\n background: color-mix(in srgb, var(--mj-text-primary, #000000) 15%, var(--mj-status-error, #ef4444));\n}\n\n.mj-admin-btn-link {\n background: none;\n border: none;\n color: var(--mj-brand-primary);\n font-size: 0.78rem;\n font-weight: 600;\n cursor: pointer;\n display: inline-flex;\n align-items: center;\n gap: 4px;\n padding: 0;\n}\n\n.mj-admin-btn-link:hover {\n text-decoration: underline;\n}\n"] }]
905
+ }], null, { Visible: [{
906
+ type: Input
907
+ }], ActiveTab: [{
908
+ type: Input
909
+ }], Provider: [{
910
+ type: Input
911
+ }], Close: [{
912
+ type: Output
913
+ }], AccountsChanged: [{
914
+ type: Output
915
+ }] }); })();
916
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(StorageAdminDialogComponent, { className: "StorageAdminDialogComponent", filePath: "src/lib/admin/storage-admin-dialog.component.ts", lineNumber: 43 }); })();
917
+ //# sourceMappingURL=storage-admin-dialog.component.js.map