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

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
@@ -1,83 +1,133 @@
1
- import { Component, EventEmitter, Output } from '@angular/core';
1
+ import { Component, EventEmitter, Output, ChangeDetectorRef, inject } from '@angular/core';
2
2
  import { FileStorageEngineBase } from '@memberjunction/core-entities';
3
+ import { Metadata } from '@memberjunction/core';
3
4
  import { UUIDsEqual } from '@memberjunction/global';
4
5
  import * as i0 from "@angular/core";
5
6
  import * as i1 from "@memberjunction/ng-shared-generic";
6
7
  import * as i2 from "@memberjunction/ng-ui-components";
8
+ import * as i3 from "../admin/storage-admin-dialog.component";
9
+ const _forTrack0 = ($index, $item) => $item.account.ID;
7
10
  function StorageProvidersListComponent_Conditional_4_Template(rf, ctx) { if (rf & 1) {
8
- i0.ɵɵelementStart(0, "div", 3);
9
- i0.ɵɵelement(1, "mj-loading", 7);
11
+ const _r1 = i0.ɵɵgetCurrentView();
12
+ i0.ɵɵelementStart(0, "div", 3)(1, "button", 9);
13
+ i0.ɵɵlistener("click", function StorageProvidersListComponent_Conditional_4_Template_button_click_1_listener() { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.openAdminDialog("accounts")); });
14
+ i0.ɵɵelement(2, "i", 10);
10
15
  i0.ɵɵelementEnd();
16
+ i0.ɵɵelementStart(3, "button", 11);
17
+ i0.ɵɵlistener("click", function StorageProvidersListComponent_Conditional_4_Template_button_click_3_listener() { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.openAdminDialog("accounts")); });
18
+ i0.ɵɵelement(4, "i", 12);
19
+ i0.ɵɵelementEnd()();
11
20
  } }
12
21
  function StorageProvidersListComponent_Conditional_5_Template(rf, ctx) { if (rf & 1) {
13
- i0.ɵɵelement(0, "mj-empty-state", 4);
14
- } if (rf & 2) {
15
- const ctx_r0 = i0.ɵɵnextContext();
16
- i0.ɵɵproperty("Title", ctx_r0.errorMessage);
22
+ i0.ɵɵelementStart(0, "div", 4);
23
+ i0.ɵɵelement(1, "mj-loading", 13);
24
+ i0.ɵɵelementEnd();
17
25
  } }
18
26
  function StorageProvidersListComponent_Conditional_6_Template(rf, ctx) { if (rf & 1) {
19
27
  i0.ɵɵelement(0, "mj-empty-state", 5);
28
+ } if (rf & 2) {
29
+ const ctx_r1 = i0.ɵɵnextContext();
30
+ i0.ɵɵproperty("Title", ctx_r1.errorMessage);
31
+ } }
32
+ function StorageProvidersListComponent_Conditional_7_Conditional_1_Template(rf, ctx) { if (rf & 1) {
33
+ const _r3 = i0.ɵɵgetCurrentView();
34
+ i0.ɵɵelementStart(0, "button", 15);
35
+ i0.ɵɵlistener("click", function StorageProvidersListComponent_Conditional_7_Conditional_1_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r3); const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.openAdminDialog("accounts")); });
36
+ i0.ɵɵelement(1, "i", 10);
37
+ i0.ɵɵelementStart(2, "span");
38
+ i0.ɵɵtext(3, "Configure Account");
39
+ i0.ɵɵelementEnd()();
40
+ } }
41
+ function StorageProvidersListComponent_Conditional_7_Template(rf, ctx) { if (rf & 1) {
42
+ i0.ɵɵelementStart(0, "mj-empty-state", 6);
43
+ i0.ɵɵconditionalCreate(1, StorageProvidersListComponent_Conditional_7_Conditional_1_Template, 4, 0, "button", 14);
44
+ i0.ɵɵelementEnd();
45
+ } if (rf & 2) {
46
+ const ctx_r1 = i0.ɵɵnextContext();
47
+ i0.ɵɵproperty("Title", ctx_r1.userCanManage ? "No storage accounts configured" : "No storage accounts available")("Message", ctx_r1.userCanManage ? "Configure storage accounts to connect MemberJunction to cloud storage providers." : "Contact your administrator to set up file storage accounts.");
48
+ i0.ɵɵadvance();
49
+ i0.ɵɵconditional(ctx_r1.userCanManage ? 1 : -1);
20
50
  } }
21
- function StorageProvidersListComponent_Conditional_7_For_2_Conditional_9_Template(rf, ctx) { if (rf & 1) {
22
- i0.ɵɵelementStart(0, "span", 15);
23
- i0.ɵɵelement(1, "i", 18);
51
+ function StorageProvidersListComponent_Conditional_8_For_2_Conditional_9_Template(rf, ctx) { if (rf & 1) {
52
+ i0.ɵɵelementStart(0, "span", 23);
53
+ i0.ɵɵelement(1, "i", 27);
24
54
  i0.ɵɵelementEnd();
25
55
  } }
26
- function StorageProvidersListComponent_Conditional_7_For_2_Conditional_10_Template(rf, ctx) { if (rf & 1) {
27
- i0.ɵɵelementStart(0, "span", 16);
28
- i0.ɵɵelement(1, "i", 19);
56
+ function StorageProvidersListComponent_Conditional_8_For_2_Conditional_10_Template(rf, ctx) { if (rf & 1) {
57
+ i0.ɵɵelementStart(0, "span", 24);
58
+ i0.ɵɵelement(1, "i", 28);
29
59
  i0.ɵɵelementEnd();
30
60
  } }
31
- function StorageProvidersListComponent_Conditional_7_For_2_Conditional_11_Template(rf, ctx) { if (rf & 1) {
32
- i0.ɵɵelementStart(0, "span", 17);
33
- i0.ɵɵelement(1, "i", 20);
61
+ function StorageProvidersListComponent_Conditional_8_For_2_Conditional_11_Template(rf, ctx) { if (rf & 1) {
62
+ i0.ɵɵelementStart(0, "span", 25);
63
+ i0.ɵɵelement(1, "i", 29);
34
64
  i0.ɵɵelementEnd();
35
65
  } }
36
- function StorageProvidersListComponent_Conditional_7_For_2_Template(rf, ctx) { if (rf & 1) {
37
- const _r2 = i0.ɵɵgetCurrentView();
38
- i0.ɵɵelementStart(0, "div", 9);
39
- i0.ɵɵlistener("click", function StorageProvidersListComponent_Conditional_7_For_2_Template_div_click_0_listener() { const item_r3 = i0.ɵɵrestoreView(_r2).$implicit; const ctx_r0 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r0.selectAccount(item_r3)); });
40
- i0.ɵɵelementStart(1, "div", 10);
66
+ function StorageProvidersListComponent_Conditional_8_For_2_Conditional_12_Template(rf, ctx) { if (rf & 1) {
67
+ const _r6 = i0.ɵɵgetCurrentView();
68
+ i0.ɵɵelementStart(0, "button", 30);
69
+ i0.ɵɵlistener("click", function StorageProvidersListComponent_Conditional_8_For_2_Conditional_12_Template_button_click_0_listener($event) { i0.ɵɵrestoreView(_r6); const item_r5 = i0.ɵɵnextContext().$implicit; const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.onEditAccountClick(item_r5, $event)); });
70
+ i0.ɵɵelement(1, "i", 31);
71
+ i0.ɵɵelementEnd();
72
+ } }
73
+ function StorageProvidersListComponent_Conditional_8_For_2_Template(rf, ctx) { if (rf & 1) {
74
+ const _r4 = i0.ɵɵgetCurrentView();
75
+ i0.ɵɵelementStart(0, "div", 17);
76
+ i0.ɵɵlistener("click", function StorageProvidersListComponent_Conditional_8_For_2_Template_div_click_0_listener() { const item_r5 = i0.ɵɵrestoreView(_r4).$implicit; const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.selectAccount(item_r5)); });
77
+ i0.ɵɵelementStart(1, "div", 18);
41
78
  i0.ɵɵelement(2, "i");
42
79
  i0.ɵɵelementEnd();
43
- i0.ɵɵelementStart(3, "div", 11)(4, "div", 12);
80
+ i0.ɵɵelementStart(3, "div", 19)(4, "div", 20);
44
81
  i0.ɵɵtext(5);
45
82
  i0.ɵɵelementEnd();
46
- i0.ɵɵelementStart(6, "div", 13)(7, "span", 14);
83
+ i0.ɵɵelementStart(6, "div", 21)(7, "span", 22);
47
84
  i0.ɵɵtext(8);
48
85
  i0.ɵɵelementEnd();
49
- i0.ɵɵconditionalCreate(9, StorageProvidersListComponent_Conditional_7_For_2_Conditional_9_Template, 2, 0, "span", 15);
50
- i0.ɵɵconditionalCreate(10, StorageProvidersListComponent_Conditional_7_For_2_Conditional_10_Template, 2, 0, "span", 16);
51
- i0.ɵɵconditionalCreate(11, StorageProvidersListComponent_Conditional_7_For_2_Conditional_11_Template, 2, 0, "span", 17);
52
- i0.ɵɵelementEnd()()();
86
+ i0.ɵɵconditionalCreate(9, StorageProvidersListComponent_Conditional_8_For_2_Conditional_9_Template, 2, 0, "span", 23);
87
+ i0.ɵɵconditionalCreate(10, StorageProvidersListComponent_Conditional_8_For_2_Conditional_10_Template, 2, 0, "span", 24);
88
+ i0.ɵɵconditionalCreate(11, StorageProvidersListComponent_Conditional_8_For_2_Conditional_11_Template, 2, 0, "span", 25);
89
+ i0.ɵɵelementEnd()();
90
+ i0.ɵɵconditionalCreate(12, StorageProvidersListComponent_Conditional_8_For_2_Conditional_12_Template, 2, 0, "button", 26);
91
+ i0.ɵɵelementEnd();
53
92
  } if (rf & 2) {
54
- const item_r3 = ctx.$implicit;
55
- const ctx_r0 = i0.ɵɵnextContext(2);
56
- i0.ɵɵclassProp("selected", ctx_r0.isSelected(item_r3));
57
- i0.ɵɵproperty("title", item_r3.account.Description || item_r3.account.Name);
93
+ const item_r5 = ctx.$implicit;
94
+ const ctx_r1 = i0.ɵɵnextContext(2);
95
+ i0.ɵɵclassProp("selected", ctx_r1.isSelected(item_r5));
96
+ i0.ɵɵproperty("title", item_r5.account.Description || item_r5.account.Name);
58
97
  i0.ɵɵadvance(2);
59
- i0.ɵɵclassMap(ctx_r0.getProviderIcon(item_r3.provider.Name));
98
+ i0.ɵɵclassMap(ctx_r1.getProviderIcon(item_r5.provider.Name));
60
99
  i0.ɵɵadvance(3);
61
- i0.ɵɵtextInterpolate(item_r3.account.Name);
100
+ i0.ɵɵtextInterpolate(item_r5.account.Name);
62
101
  i0.ɵɵadvance(2);
63
- i0.ɵɵproperty("title", item_r3.provider.Name);
102
+ i0.ɵɵproperty("title", item_r5.provider.Name);
64
103
  i0.ɵɵadvance();
65
- i0.ɵɵtextInterpolate1(" ", item_r3.provider.Name, " ");
104
+ i0.ɵɵtextInterpolate1(" ", item_r5.provider.Name, " ");
66
105
  i0.ɵɵadvance();
67
- i0.ɵɵconditional(item_r3.provider.SupportsSearch ? 9 : -1);
106
+ i0.ɵɵconditional(item_r5.provider.SupportsSearch ? 9 : -1);
68
107
  i0.ɵɵadvance();
69
- i0.ɵɵconditional(item_r3.account.CredentialID ? 10 : -1);
108
+ i0.ɵɵconditional(item_r5.account.CredentialID ? 10 : -1);
70
109
  i0.ɵɵadvance();
71
- i0.ɵɵconditional(!item_r3.account.CredentialID ? 11 : -1);
110
+ i0.ɵɵconditional(!item_r5.account.CredentialID ? 11 : -1);
111
+ i0.ɵɵadvance();
112
+ i0.ɵɵconditional(ctx_r1.userCanManage ? 12 : -1);
72
113
  } }
73
- function StorageProvidersListComponent_Conditional_7_Template(rf, ctx) { if (rf & 1) {
74
- i0.ɵɵelementStart(0, "div", 6);
75
- i0.ɵɵrepeaterCreate(1, StorageProvidersListComponent_Conditional_7_For_2_Template, 12, 11, "div", 8, i0.ɵɵrepeaterTrackByIdentity);
114
+ function StorageProvidersListComponent_Conditional_8_Template(rf, ctx) { if (rf & 1) {
115
+ i0.ɵɵelementStart(0, "div", 7);
116
+ i0.ɵɵrepeaterCreate(1, StorageProvidersListComponent_Conditional_8_For_2_Template, 13, 12, "div", 16, _forTrack0);
76
117
  i0.ɵɵelementEnd();
77
118
  } if (rf & 2) {
78
- const ctx_r0 = i0.ɵɵnextContext();
119
+ const ctx_r1 = i0.ɵɵnextContext();
79
120
  i0.ɵɵadvance();
80
- i0.ɵɵrepeater(ctx_r0.accounts);
121
+ i0.ɵɵrepeater(ctx_r1.accounts);
122
+ } }
123
+ function StorageProvidersListComponent_Conditional_9_Template(rf, ctx) { if (rf & 1) {
124
+ const _r7 = i0.ɵɵgetCurrentView();
125
+ i0.ɵɵelementStart(0, "mj-storage-admin-dialog", 32);
126
+ i0.ɵɵlistener("Close", function StorageProvidersListComponent_Conditional_9_Template_mj_storage_admin_dialog_Close_0_listener() { i0.ɵɵrestoreView(_r7); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.closeAdminDialog()); })("AccountsChanged", function StorageProvidersListComponent_Conditional_9_Template_mj_storage_admin_dialog_AccountsChanged_0_listener() { i0.ɵɵrestoreView(_r7); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.onAdminAccountsChanged()); });
127
+ i0.ɵɵelementEnd();
128
+ } if (rf & 2) {
129
+ const ctx_r1 = i0.ɵɵnextContext();
130
+ i0.ɵɵproperty("Visible", ctx_r1.isManageDialogOpen)("ActiveTab", ctx_r1.adminDialogTab);
81
131
  } }
82
132
  /**
83
133
  * Displays a list of organizational file storage accounts.
@@ -86,7 +136,7 @@ function StorageProvidersListComponent_Conditional_7_Template(rf, ctx) { if (rf
86
136
  * their own OAuth connections - credentials are handled at the org level.
87
137
  */
88
138
  export class StorageProvidersListComponent {
89
- cdr;
139
+ cdr = inject(ChangeDetectorRef);
90
140
  /**
91
141
  * Emits when an account is selected by the user, or null when no accounts are available.
92
142
  * Emits the full account-with-provider object for downstream components to use.
@@ -108,24 +158,63 @@ export class StorageProvidersListComponent {
108
158
  * Error message if loading fails.
109
159
  */
110
160
  errorMessage = null;
111
- constructor(cdr) {
112
- this.cdr = cdr;
161
+ /**
162
+ * Whether current user has admin rights to configure storage accounts or providers.
163
+ */
164
+ userCanManage = false;
165
+ /**
166
+ * Admin dialog state
167
+ */
168
+ isManageDialogOpen = false;
169
+ adminDialogTab = 'accounts';
170
+ accountToEdit = null;
171
+ Provider = null;
172
+ get ProviderToUse() {
173
+ return this.Provider ?? Metadata.Provider;
113
174
  }
175
+ constructor() { }
114
176
  ngOnInit() {
177
+ if (!this.userCanManage) {
178
+ this.checkPermissions();
179
+ }
115
180
  this.loadAccounts();
116
181
  }
182
+ /**
183
+ * Checks if current user has create or update permissions on storage entities.
184
+ */
185
+ checkPermissions() {
186
+ try {
187
+ const md = this.ProviderToUse;
188
+ const user = md.CurrentUser;
189
+ if (!user) {
190
+ return;
191
+ }
192
+ const provEntity = md.Entities.find(e => e.Name === 'MJ: File Storage Providers');
193
+ const acctEntity = md.Entities.find(e => e.Name === 'MJ: File Storage Accounts');
194
+ const provCanManage = provEntity ? provEntity.GetUserPermisions(user).CanCreate || provEntity.GetUserPermisions(user).CanUpdate : false;
195
+ const acctCanManage = acctEntity ? acctEntity.GetUserPermisions(user).CanCreate || acctEntity.GetUserPermisions(user).CanUpdate : false;
196
+ this.userCanManage = !!(provCanManage || acctCanManage);
197
+ }
198
+ catch {
199
+ this.userCanManage = false;
200
+ }
201
+ }
117
202
  /**
118
203
  * Loads all available file storage accounts with their provider details.
119
204
  * Uses FileStorageEngineBase for centralized, cached access.
120
205
  */
121
- async loadAccounts() {
206
+ async loadAccounts(forceRefresh = false) {
122
207
  this.isLoading = true;
123
208
  this.errorMessage = null;
124
209
  try {
125
210
  const engine = FileStorageEngineBase.Instance;
126
- await engine.Config(false); // Use cached data if available
211
+ await engine.Config(forceRefresh);
127
212
  // Only show accounts whose provider is active
128
- this.accounts = engine.AccountsWithProviders.filter(a => a.provider.IsActive);
213
+ this.accounts = engine.AccountsWithProviders.filter(a => a.provider.IsActive !== false);
214
+ if (this.accounts.length === 0 && !forceRefresh) {
215
+ await engine.Config(true);
216
+ this.accounts = engine.AccountsWithProviders.filter(a => a.provider.IsActive !== false);
217
+ }
129
218
  console.log('[StorageAccountsList] Loaded accounts:', this.accounts.map(a => ({
130
219
  name: a.account.Name,
131
220
  provider: a.provider.Name,
@@ -195,34 +284,71 @@ export class StorageProvidersListComponent {
195
284
  * Refreshes the accounts list by forcing a reload from the database.
196
285
  */
197
286
  refresh() {
198
- FileStorageEngineBase.Instance.Config(true).then(() => this.loadAccounts());
287
+ void this.loadAccounts(true);
288
+ }
289
+ /**
290
+ * Opens the storage administration dialog
291
+ */
292
+ openAdminDialog(tab = 'accounts', account) {
293
+ this.adminDialogTab = tab;
294
+ this.accountToEdit = account ?? null;
295
+ this.isManageDialogOpen = true;
296
+ this.cdr.markForCheck();
199
297
  }
200
- static ɵfac = function StorageProvidersListComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || StorageProvidersListComponent)(i0.ɵɵdirectiveInject(i0.ChangeDetectorRef)); };
201
- static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: StorageProvidersListComponent, selectors: [["mj-storage-providers-list"]], outputs: { accountSelected: "accountSelected" }, standalone: false, decls: 8, vars: 4, consts: [[1, "providers-list"], [1, "providers-header"], [1, "header-title"], [1, "loading-state"], ["Variant", "error", "Icon", "fa-solid fa-triangle-exclamation", "Size", "compact", 1, "storage-providers-empty-fill", 3, "Title"], ["Icon", "fa-solid fa-folder-plus", "Title", "No storage accounts configured", "Message", "Contact your administrator to set up file storage accounts", "Size", "compact", 1, "storage-providers-empty-fill"], [1, "providers-container"], ["text", "Loading accounts..."], [1, "provider-item", 3, "selected", "title"], [1, "provider-item", 3, "click", "title"], [1, "provider-icon"], [1, "provider-info"], [1, "provider-name"], [1, "provider-meta"], [1, "provider-type-badge", 3, "title"], ["title", "Supports search", 1, "search-badge"], ["title", "Credentials configured", 1, "credential-badge"], ["title", "No credentials configured", 1, "unconfigured-badge"], [1, "fa-solid", "fa-magnifying-glass"], [1, "fa-solid", "fa-key"], [1, "fa-solid", "fa-exclamation-triangle"]], template: function StorageProvidersListComponent_Template(rf, ctx) { if (rf & 1) {
298
+ /**
299
+ * Closes the storage administration dialog
300
+ */
301
+ closeAdminDialog() {
302
+ this.isManageDialogOpen = false;
303
+ this.accountToEdit = null;
304
+ this.cdr.markForCheck();
305
+ }
306
+ /**
307
+ * Handles storage configuration changes from the admin dialog
308
+ */
309
+ onAdminAccountsChanged() {
310
+ this.refresh();
311
+ }
312
+ /**
313
+ * Handles inline edit click on an account item
314
+ */
315
+ onEditAccountClick(item, event) {
316
+ event.stopPropagation();
317
+ this.openAdminDialog('accounts', item.account);
318
+ }
319
+ static ɵfac = function StorageProvidersListComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || StorageProvidersListComponent)(); };
320
+ static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: StorageProvidersListComponent, selectors: [["mj-storage-providers-list"]], outputs: { accountSelected: "accountSelected" }, standalone: false, decls: 10, vars: 6, consts: [[1, "providers-list"], [1, "providers-header"], [1, "header-title"], [1, "admin-header-actions"], [1, "loading-state"], ["Variant", "error", "Icon", "fa-solid fa-triangle-exclamation", "Size", "compact", 1, "storage-providers-empty-fill", 3, "Title"], ["Icon", "fa-solid fa-folder-plus", "Size", "compact", 1, "storage-providers-empty-fill", 3, "Title", "Message"], [1, "providers-container"], [3, "Visible", "ActiveTab"], ["type", "button", "title", "Add Storage Account", 1, "admin-action-btn", 3, "click"], [1, "fa-solid", "fa-plus"], ["type", "button", "title", "Manage Storage Providers & Accounts", 1, "admin-action-btn", 3, "click"], [1, "fa-solid", "fa-gear"], ["text", "Loading accounts..."], ["type", "button", 1, "mj-empty-action-btn"], ["type", "button", 1, "mj-empty-action-btn", 3, "click"], [1, "provider-item", 3, "selected", "title"], [1, "provider-item", 3, "click", "title"], [1, "provider-icon"], [1, "provider-info"], [1, "provider-name"], [1, "provider-meta"], [1, "provider-type-badge", 3, "title"], ["title", "Supports search", 1, "search-badge"], ["title", "Credentials configured", 1, "credential-badge"], ["title", "No credentials configured", 1, "unconfigured-badge"], ["type", "button", "title", "Edit Storage Account", 1, "provider-edit-btn"], [1, "fa-solid", "fa-magnifying-glass"], [1, "fa-solid", "fa-key"], [1, "fa-solid", "fa-exclamation-triangle"], ["type", "button", "title", "Edit Storage Account", 1, "provider-edit-btn", 3, "click"], [1, "fa-solid", "fa-pen-to-square"], [3, "Close", "AccountsChanged", "Visible", "ActiveTab"]], template: function StorageProvidersListComponent_Template(rf, ctx) { if (rf & 1) {
202
321
  i0.ɵɵelementStart(0, "div", 0)(1, "div", 1)(2, "span", 2);
203
322
  i0.ɵɵtext(3, "Storage Accounts");
204
- i0.ɵɵelementEnd()();
205
- i0.ɵɵconditionalCreate(4, StorageProvidersListComponent_Conditional_4_Template, 2, 0, "div", 3);
206
- i0.ɵɵconditionalCreate(5, StorageProvidersListComponent_Conditional_5_Template, 1, 1, "mj-empty-state", 4);
207
- i0.ɵɵconditionalCreate(6, StorageProvidersListComponent_Conditional_6_Template, 1, 0, "mj-empty-state", 5);
208
- i0.ɵɵconditionalCreate(7, StorageProvidersListComponent_Conditional_7_Template, 3, 0, "div", 6);
209
323
  i0.ɵɵelementEnd();
324
+ i0.ɵɵconditionalCreate(4, StorageProvidersListComponent_Conditional_4_Template, 5, 0, "div", 3);
325
+ i0.ɵɵelementEnd();
326
+ i0.ɵɵconditionalCreate(5, StorageProvidersListComponent_Conditional_5_Template, 2, 0, "div", 4);
327
+ i0.ɵɵconditionalCreate(6, StorageProvidersListComponent_Conditional_6_Template, 1, 1, "mj-empty-state", 5);
328
+ i0.ɵɵconditionalCreate(7, StorageProvidersListComponent_Conditional_7_Template, 2, 3, "mj-empty-state", 6);
329
+ i0.ɵɵconditionalCreate(8, StorageProvidersListComponent_Conditional_8_Template, 3, 0, "div", 7);
330
+ i0.ɵɵelementEnd();
331
+ i0.ɵɵconditionalCreate(9, StorageProvidersListComponent_Conditional_9_Template, 1, 2, "mj-storage-admin-dialog", 8);
210
332
  } if (rf & 2) {
211
333
  i0.ɵɵadvance(4);
212
- i0.ɵɵconditional(ctx.isLoading ? 4 : -1);
334
+ i0.ɵɵconditional(ctx.userCanManage ? 4 : -1);
335
+ i0.ɵɵadvance();
336
+ i0.ɵɵconditional(ctx.isLoading ? 5 : -1);
337
+ i0.ɵɵadvance();
338
+ i0.ɵɵconditional(ctx.errorMessage && !ctx.isLoading ? 6 : -1);
213
339
  i0.ɵɵadvance();
214
- i0.ɵɵconditional(ctx.errorMessage && !ctx.isLoading ? 5 : -1);
340
+ i0.ɵɵconditional(!ctx.isLoading && !ctx.errorMessage && ctx.accounts.length === 0 ? 7 : -1);
215
341
  i0.ɵɵadvance();
216
- i0.ɵɵconditional(!ctx.isLoading && !ctx.errorMessage && ctx.accounts.length === 0 ? 6 : -1);
342
+ i0.ɵɵconditional(!ctx.isLoading && !ctx.errorMessage && ctx.accounts.length > 0 ? 8 : -1);
217
343
  i0.ɵɵadvance();
218
- i0.ɵɵconditional(!ctx.isLoading && !ctx.errorMessage && ctx.accounts.length > 0 ? 7 : -1);
219
- } }, dependencies: [i1.LoadingComponent, i2.MJEmptyStateComponent], styles: ["\n\n\n\n\n.providers-list[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n height: 100%;\n overflow: hidden;\n}\n\n\n\n.providers-header[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 12px 16px;\n border-bottom: 1px solid var(--mj-border-default);\n background-color: var(--mj-bg-surface-card);\n}\n\n.header-title[_ngcontent-%COMP%] {\n font-size: 13px;\n font-weight: 600;\n color: var(--mj-text-primary);\n text-transform: uppercase;\n letter-spacing: 0.5px;\n}\n\n.add-provider-btn[_ngcontent-%COMP%] {\n width: 28px;\n height: 28px;\n border: none;\n border-radius: 50%;\n background-color: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: all 0.2s ease;\n font-size: 14px;\n}\n\n.add-provider-btn[_ngcontent-%COMP%]:hover:not(:disabled) {\n background-color: var(--mj-brand-primary-hover);\n transform: scale(1.05);\n}\n\n.add-provider-btn[_ngcontent-%COMP%]:disabled {\n background-color: var(--mj-border-strong);\n cursor: not-allowed;\n}\n\n\n\n.loading-state[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: 24px;\n text-align: center;\n color: var(--mj-text-muted);\n flex: 1;\n}\n\n\n\n.storage-providers-empty-fill[_ngcontent-%COMP%] {\n flex: 1;\n}\n\n\n\n.providers-container[_ngcontent-%COMP%] {\n flex: 1;\n overflow-y: auto;\n overflow-x: hidden;\n}\n\n\n\n.provider-item[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n padding: 12px 16px;\n cursor: pointer;\n transition: all 0.2s ease;\n border-bottom: 1px solid var(--mj-bg-surface-sunken);\n position: relative;\n}\n\n.provider-item[_ngcontent-%COMP%]:hover {\n background-color: var(--mj-bg-surface-card);\n}\n\n.provider-item.selected[_ngcontent-%COMP%] {\n background-color: color-mix(in srgb, var(--mj-brand-primary) 8%, var(--mj-bg-surface));\n border-left: 3px solid var(--mj-brand-primary);\n}\n\n.provider-item.selected[_ngcontent-%COMP%]::before {\n content: '';\n position: absolute;\n left: 0;\n top: 0;\n bottom: 0;\n width: 3px;\n background-color: var(--mj-brand-primary);\n}\n\n\n\n.provider-icon[_ngcontent-%COMP%] {\n flex-shrink: 0;\n width: 32px;\n height: 32px;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 20px;\n color: var(--mj-text-secondary);\n margin-right: 12px;\n}\n\n.provider-item.selected[_ngcontent-%COMP%] .provider-icon[_ngcontent-%COMP%] {\n color: var(--mj-brand-primary);\n}\n\n\n\n.provider-info[_ngcontent-%COMP%] {\n flex: 1;\n min-width: 0;\n display: flex;\n flex-direction: column;\n gap: 2px;\n}\n\n.provider-name[_ngcontent-%COMP%] {\n font-size: 13px;\n font-weight: 500;\n color: var(--mj-text-primary);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.provider-item.selected[_ngcontent-%COMP%] .provider-name[_ngcontent-%COMP%] {\n color: var(--mj-brand-primary);\n font-weight: 600;\n}\n\n.provider-meta[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 8px;\n font-size: 11px;\n color: var(--mj-text-muted);\n}\n\n\n\n.default-badge[_ngcontent-%COMP%] {\n display: inline-flex;\n align-items: center;\n color: var(--mj-status-warning);\n}\n\n.default-badge[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 10px;\n}\n\n\n\n.provider-type-badge[_ngcontent-%COMP%] {\n display: inline-flex;\n align-items: center;\n padding: 2px 6px;\n background-color: var(--mj-bg-surface-sunken);\n border-radius: 10px;\n font-size: 10px;\n color: var(--mj-text-secondary);\n max-width: 100px;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.provider-item.selected[_ngcontent-%COMP%] .provider-type-badge[_ngcontent-%COMP%] {\n background-color: color-mix(in srgb, var(--mj-brand-primary) 15%, transparent);\n color: var(--mj-brand-primary);\n}\n\n\n\n.search-badge[_ngcontent-%COMP%] {\n display: inline-flex;\n align-items: center;\n padding: 2px 6px;\n background-color: var(--mj-bg-surface-sunken);\n border-radius: 10px;\n font-size: 10px;\n color: var(--mj-text-secondary);\n}\n\n.provider-item.selected[_ngcontent-%COMP%] .search-badge[_ngcontent-%COMP%] {\n background-color: color-mix(in srgb, var(--mj-brand-primary) 15%, transparent);\n color: var(--mj-brand-primary);\n}\n\n.search-badge[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 9px;\n}\n\n\n\n.credential-badge[_ngcontent-%COMP%] {\n display: inline-flex;\n align-items: center;\n padding: 2px 6px;\n background-color: color-mix(in srgb, var(--mj-status-success) 8%, var(--mj-bg-surface));\n border-radius: 10px;\n font-size: 10px;\n color: var(--mj-status-success);\n}\n\n.provider-item.selected[_ngcontent-%COMP%] .credential-badge[_ngcontent-%COMP%] {\n background-color: color-mix(in srgb, var(--mj-status-success) 15%, transparent);\n color: var(--mj-status-success);\n}\n\n.credential-badge[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 9px;\n}\n\n\n\n.unconfigured-badge[_ngcontent-%COMP%] {\n display: inline-flex;\n align-items: center;\n color: var(--mj-status-warning);\n}\n\n.unconfigured-badge[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 10px;\n}\n\n\n\n.oauth-connected-badge[_ngcontent-%COMP%] {\n display: inline-flex;\n align-items: center;\n padding: 2px 6px;\n background-color: color-mix(in srgb, var(--mj-status-success) 8%, var(--mj-bg-surface));\n border-radius: 10px;\n font-size: 10px;\n color: var(--mj-status-success);\n}\n\n.oauth-connected-badge[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 9px;\n}\n\n.oauth-disconnected-badge[_ngcontent-%COMP%] {\n display: inline-flex;\n align-items: center;\n padding: 2px 6px;\n background-color: color-mix(in srgb, var(--mj-status-warning) 8%, var(--mj-bg-surface));\n border-radius: 10px;\n font-size: 10px;\n color: var(--mj-status-warning);\n}\n\n.oauth-disconnected-badge[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 9px;\n}\n\n\n\n.provider-actions[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 4px;\n margin-left: 8px;\n}\n\n.connect-btn[_ngcontent-%COMP%], \n.disconnect-btn[_ngcontent-%COMP%] {\n width: 26px;\n height: 26px;\n border: none;\n border-radius: 50%;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: all 0.2s ease;\n font-size: 12px;\n}\n\n.connect-btn[_ngcontent-%COMP%] {\n background-color: color-mix(in srgb, var(--mj-status-success) 8%, var(--mj-bg-surface));\n color: var(--mj-status-success);\n}\n\n.connect-btn[_ngcontent-%COMP%]:hover:not(:disabled) {\n background-color: color-mix(in srgb, var(--mj-status-success) 15%, var(--mj-bg-surface));\n transform: scale(1.05);\n}\n\n.connect-btn[_ngcontent-%COMP%]:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n\n.disconnect-btn[_ngcontent-%COMP%] {\n background-color: transparent;\n color: var(--mj-text-muted);\n}\n\n.disconnect-btn[_ngcontent-%COMP%]:hover {\n background-color: color-mix(in srgb, var(--mj-status-error) 8%, var(--mj-bg-surface));\n color: var(--mj-status-error);\n}\n\n\n\n.remove-provider-btn[_ngcontent-%COMP%] {\n width: 24px;\n height: 24px;\n border: none;\n border-radius: 50%;\n background-color: transparent;\n color: var(--mj-text-disabled);\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: all 0.2s ease;\n font-size: 12px;\n opacity: 0;\n margin-left: 8px;\n}\n\n.provider-item[_ngcontent-%COMP%]:hover .remove-provider-btn[_ngcontent-%COMP%] {\n opacity: 1;\n}\n\n.remove-provider-btn[_ngcontent-%COMP%]:hover:not(:disabled) {\n background-color: color-mix(in srgb, var(--mj-status-error) 8%, var(--mj-bg-surface));\n color: var(--mj-status-error);\n}\n\n.remove-provider-btn[_ngcontent-%COMP%]:disabled {\n cursor: not-allowed;\n opacity: 0.5;\n}\n\n\n\n.providers-container[_ngcontent-%COMP%]::-webkit-scrollbar {\n width: 6px;\n}\n\n.providers-container[_ngcontent-%COMP%]::-webkit-scrollbar-track {\n background: transparent;\n}\n\n.providers-container[_ngcontent-%COMP%]::-webkit-scrollbar-thumb {\n background: var(--mj-border-strong);\n border-radius: 3px;\n}\n\n.providers-container[_ngcontent-%COMP%]::-webkit-scrollbar-thumb:hover {\n background: var(--mj-text-disabled);\n}\n\n\n\n\n\n\n.add-provider-dialog[_ngcontent-%COMP%] {\n min-height: 150px;\n}\n\n.dialog-loading[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 40px 20px;\n}\n\n.dialog-empty[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: 30px 20px;\n text-align: center;\n}\n\n.dialog-empty-icon[_ngcontent-%COMP%] {\n font-size: 40px;\n color: var(--mj-status-success);\n margin-bottom: 12px;\n}\n\n.dialog-empty-message[_ngcontent-%COMP%] {\n font-size: 16px;\n font-weight: 500;\n color: var(--mj-text-primary);\n margin: 0 0 4px 0;\n}\n\n.dialog-empty-hint[_ngcontent-%COMP%] {\n font-size: 13px;\n color: var(--mj-text-muted);\n margin: 0;\n}\n\n.dialog-instruction[_ngcontent-%COMP%] {\n font-size: 13px;\n color: var(--mj-text-secondary);\n margin: 0 0 12px 0;\n padding: 0 4px;\n}\n\n.available-providers[_ngcontent-%COMP%] {\n max-height: 350px;\n overflow-y: auto;\n}\n\n.available-provider-item[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n padding: 12px;\n border-radius: 8px;\n cursor: pointer;\n transition: all 0.2s ease;\n margin-bottom: 8px;\n border: 1px solid var(--mj-border-default);\n}\n\n.available-provider-item[_ngcontent-%COMP%]:hover:not(.disabled) {\n background-color: var(--mj-bg-surface-card);\n border-color: var(--mj-brand-primary);\n}\n\n.available-provider-item.disabled[_ngcontent-%COMP%] {\n opacity: 0.6;\n cursor: not-allowed;\n background-color: var(--mj-bg-surface-card);\n}\n\n.available-provider-item[_ngcontent-%COMP%] .provider-icon[_ngcontent-%COMP%] {\n margin-right: 12px;\n}\n\n.available-provider-item[_ngcontent-%COMP%] .provider-info[_ngcontent-%COMP%] {\n flex: 1;\n}\n\n.available-provider-item[_ngcontent-%COMP%] .provider-name[_ngcontent-%COMP%] {\n font-size: 14px;\n font-weight: 500;\n color: var(--mj-text-primary);\n}\n\n.available-provider-item[_ngcontent-%COMP%] .provider-description[_ngcontent-%COMP%] {\n font-size: 12px;\n color: var(--mj-text-muted);\n margin-top: 2px;\n line-height: 1.3;\n}\n\n.available-provider-item[_ngcontent-%COMP%] .provider-warning[_ngcontent-%COMP%] {\n font-size: 11px;\n color: var(--mj-status-warning);\n margin-top: 4px;\n display: flex;\n align-items: center;\n gap: 4px;\n}\n\n.available-provider-item[_ngcontent-%COMP%] .provider-warning[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 10px;\n}\n\n\n\n.available-provider-item[_ngcontent-%COMP%] .oauth-hint[_ngcontent-%COMP%] {\n font-size: 11px;\n color: var(--mj-brand-primary);\n margin-top: 4px;\n display: flex;\n align-items: center;\n gap: 4px;\n}\n\n.available-provider-item[_ngcontent-%COMP%] .oauth-hint[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 10px;\n}\n\n.available-provider-item.oauth-provider[_ngcontent-%COMP%] {\n border-color: var(--mj-brand-primary);\n border-style: dashed;\n}\n\n.available-provider-item.oauth-provider[_ngcontent-%COMP%]:hover:not(.disabled) {\n background-color: color-mix(in srgb, var(--mj-brand-primary) 8%, var(--mj-bg-surface));\n border-style: solid;\n}\n\n.add-indicator[_ngcontent-%COMP%] {\n width: 28px;\n height: 28px;\n display: flex;\n align-items: center;\n justify-content: center;\n color: var(--mj-brand-primary);\n font-size: 14px;\n}\n\n\n\n@media screen and (max-width: 768px) {\n .providers-header[_ngcontent-%COMP%] {\n padding: 10px 12px;\n }\n\n .header-title[_ngcontent-%COMP%] {\n font-size: 12px;\n }\n\n .add-provider-btn[_ngcontent-%COMP%] {\n width: 26px;\n height: 26px;\n font-size: 12px;\n }\n\n .provider-item[_ngcontent-%COMP%] {\n padding: 10px 12px;\n }\n\n .provider-icon[_ngcontent-%COMP%] {\n width: 28px;\n height: 28px;\n font-size: 18px;\n margin-right: 10px;\n }\n\n .provider-name[_ngcontent-%COMP%] {\n font-size: 12px;\n }\n\n .provider-status[_ngcontent-%COMP%] {\n font-size: 10px;\n }\n\n .remove-provider-btn[_ngcontent-%COMP%] {\n width: 22px;\n height: 22px;\n font-size: 11px;\n opacity: 1;\n }\n}"] });
344
+ i0.ɵɵconditional(ctx.isManageDialogOpen ? 9 : -1);
345
+ } }, dependencies: [i1.LoadingComponent, i2.MJEmptyStateComponent, i3.StorageAdminDialogComponent], styles: ["\n\n\n\n\n.providers-list[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n height: 100%;\n overflow: hidden;\n}\n\n\n\n.providers-header[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 12px 16px;\n border-bottom: 1px solid var(--mj-border-default);\n background-color: var(--mj-bg-surface-card);\n}\n\n.header-title[_ngcontent-%COMP%] {\n font-size: 13px;\n font-weight: 600;\n color: var(--mj-text-primary);\n text-transform: uppercase;\n letter-spacing: 0.5px;\n}\n\n.admin-header-actions[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 6px;\n}\n\n.admin-action-btn[_ngcontent-%COMP%] {\n width: 26px;\n height: 26px;\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-sm, 6px);\n background-color: var(--mj-bg-surface);\n color: var(--mj-text-secondary);\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: all 0.15s ease;\n font-size: 11px;\n}\n\n.admin-action-btn[_ngcontent-%COMP%]:hover {\n background-color: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n border-color: var(--mj-brand-primary);\n}\n\n.provider-edit-btn[_ngcontent-%COMP%] {\n width: 24px;\n height: 24px;\n border: none;\n border-radius: var(--mj-radius-sm, 4px);\n background-color: transparent;\n color: var(--mj-text-muted);\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: all 0.15s ease;\n font-size: 11px;\n opacity: 0;\n margin-left: 6px;\n flex-shrink: 0;\n}\n\n.provider-item[_ngcontent-%COMP%]:hover .provider-edit-btn[_ngcontent-%COMP%] {\n opacity: 1;\n}\n\n.provider-edit-btn[_ngcontent-%COMP%]:hover {\n background-color: color-mix(in srgb, var(--mj-brand-primary) 12%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n}\n\n.mj-empty-action-btn[_ngcontent-%COMP%] {\n margin-top: 12px;\n padding: 6px 12px;\n border-radius: var(--mj-radius-sm, 6px);\n border: 1px solid transparent;\n background-color: var(--mj-brand-primary);\n color: var(--mj-text-inverse, #ffffff);\n font-size: 12px;\n font-weight: 500;\n cursor: pointer;\n display: inline-flex;\n align-items: center;\n gap: 6px;\n transition: background-color 0.12s;\n}\n\n.mj-empty-action-btn[_ngcontent-%COMP%]:hover {\n background-color: var(--mj-brand-primary-hover);\n}\n\n.add-provider-btn[_ngcontent-%COMP%] {\n width: 28px;\n height: 28px;\n border: none;\n border-radius: 50%;\n background-color: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: all 0.2s ease;\n font-size: 14px;\n}\n\n.add-provider-btn[_ngcontent-%COMP%]:hover:not(:disabled) {\n background-color: var(--mj-brand-primary-hover);\n transform: scale(1.05);\n}\n\n.add-provider-btn[_ngcontent-%COMP%]:disabled {\n background-color: var(--mj-border-strong);\n cursor: not-allowed;\n}\n\n\n\n.loading-state[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: 24px;\n text-align: center;\n color: var(--mj-text-muted);\n flex: 1;\n}\n\n\n\n.storage-providers-empty-fill[_ngcontent-%COMP%] {\n flex: 1;\n}\n\n\n\n.providers-container[_ngcontent-%COMP%] {\n flex: 1;\n overflow-y: auto;\n overflow-x: hidden;\n}\n\n\n\n.provider-item[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n padding: 12px 16px;\n cursor: pointer;\n transition: all 0.2s ease;\n border-bottom: 1px solid var(--mj-bg-surface-sunken);\n position: relative;\n}\n\n.provider-item[_ngcontent-%COMP%]:hover {\n background-color: var(--mj-bg-surface-card);\n}\n\n.provider-item.selected[_ngcontent-%COMP%] {\n background-color: color-mix(in srgb, var(--mj-brand-primary) 8%, var(--mj-bg-surface));\n border-left: 3px solid var(--mj-brand-primary);\n}\n\n.provider-item.selected[_ngcontent-%COMP%]::before {\n content: '';\n position: absolute;\n left: 0;\n top: 0;\n bottom: 0;\n width: 3px;\n background-color: var(--mj-brand-primary);\n}\n\n\n\n.provider-icon[_ngcontent-%COMP%] {\n flex-shrink: 0;\n width: 32px;\n height: 32px;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 20px;\n color: var(--mj-text-secondary);\n margin-right: 12px;\n}\n\n.provider-item.selected[_ngcontent-%COMP%] .provider-icon[_ngcontent-%COMP%] {\n color: var(--mj-brand-primary);\n}\n\n\n\n.provider-info[_ngcontent-%COMP%] {\n flex: 1;\n min-width: 0;\n display: flex;\n flex-direction: column;\n gap: 2px;\n}\n\n.provider-name[_ngcontent-%COMP%] {\n font-size: 13px;\n font-weight: 500;\n color: var(--mj-text-primary);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.provider-item.selected[_ngcontent-%COMP%] .provider-name[_ngcontent-%COMP%] {\n color: var(--mj-brand-primary);\n font-weight: 600;\n}\n\n.provider-meta[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 8px;\n font-size: 11px;\n color: var(--mj-text-muted);\n}\n\n\n\n.default-badge[_ngcontent-%COMP%] {\n display: inline-flex;\n align-items: center;\n color: var(--mj-status-warning);\n}\n\n.default-badge[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 10px;\n}\n\n\n\n.provider-type-badge[_ngcontent-%COMP%] {\n display: inline-flex;\n align-items: center;\n padding: 2px 6px;\n background-color: var(--mj-bg-surface-sunken);\n border-radius: 10px;\n font-size: 10px;\n color: var(--mj-text-secondary);\n max-width: 100px;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.provider-item.selected[_ngcontent-%COMP%] .provider-type-badge[_ngcontent-%COMP%] {\n background-color: color-mix(in srgb, var(--mj-brand-primary) 15%, transparent);\n color: var(--mj-brand-primary);\n}\n\n\n\n.search-badge[_ngcontent-%COMP%] {\n display: inline-flex;\n align-items: center;\n padding: 2px 6px;\n background-color: var(--mj-bg-surface-sunken);\n border-radius: 10px;\n font-size: 10px;\n color: var(--mj-text-secondary);\n}\n\n.provider-item.selected[_ngcontent-%COMP%] .search-badge[_ngcontent-%COMP%] {\n background-color: color-mix(in srgb, var(--mj-brand-primary) 15%, transparent);\n color: var(--mj-brand-primary);\n}\n\n.search-badge[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 9px;\n}\n\n\n\n.credential-badge[_ngcontent-%COMP%] {\n display: inline-flex;\n align-items: center;\n padding: 2px 6px;\n background-color: color-mix(in srgb, var(--mj-status-success) 8%, var(--mj-bg-surface));\n border-radius: 10px;\n font-size: 10px;\n color: var(--mj-status-success);\n}\n\n.provider-item.selected[_ngcontent-%COMP%] .credential-badge[_ngcontent-%COMP%] {\n background-color: color-mix(in srgb, var(--mj-status-success) 15%, transparent);\n color: var(--mj-status-success);\n}\n\n.credential-badge[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 9px;\n}\n\n\n\n.unconfigured-badge[_ngcontent-%COMP%] {\n display: inline-flex;\n align-items: center;\n color: var(--mj-status-warning);\n}\n\n.unconfigured-badge[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 10px;\n}\n\n\n\n.oauth-connected-badge[_ngcontent-%COMP%] {\n display: inline-flex;\n align-items: center;\n padding: 2px 6px;\n background-color: color-mix(in srgb, var(--mj-status-success) 8%, var(--mj-bg-surface));\n border-radius: 10px;\n font-size: 10px;\n color: var(--mj-status-success);\n}\n\n.oauth-connected-badge[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 9px;\n}\n\n.oauth-disconnected-badge[_ngcontent-%COMP%] {\n display: inline-flex;\n align-items: center;\n padding: 2px 6px;\n background-color: color-mix(in srgb, var(--mj-status-warning) 8%, var(--mj-bg-surface));\n border-radius: 10px;\n font-size: 10px;\n color: var(--mj-status-warning);\n}\n\n.oauth-disconnected-badge[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 9px;\n}\n\n\n\n.provider-actions[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 4px;\n margin-left: 8px;\n}\n\n.connect-btn[_ngcontent-%COMP%], \n.disconnect-btn[_ngcontent-%COMP%] {\n width: 26px;\n height: 26px;\n border: none;\n border-radius: 50%;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: all 0.2s ease;\n font-size: 12px;\n}\n\n.connect-btn[_ngcontent-%COMP%] {\n background-color: color-mix(in srgb, var(--mj-status-success) 8%, var(--mj-bg-surface));\n color: var(--mj-status-success);\n}\n\n.connect-btn[_ngcontent-%COMP%]:hover:not(:disabled) {\n background-color: color-mix(in srgb, var(--mj-status-success) 15%, var(--mj-bg-surface));\n transform: scale(1.05);\n}\n\n.connect-btn[_ngcontent-%COMP%]:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n\n.disconnect-btn[_ngcontent-%COMP%] {\n background-color: transparent;\n color: var(--mj-text-muted);\n}\n\n.disconnect-btn[_ngcontent-%COMP%]:hover {\n background-color: color-mix(in srgb, var(--mj-status-error) 8%, var(--mj-bg-surface));\n color: var(--mj-status-error);\n}\n\n\n\n.remove-provider-btn[_ngcontent-%COMP%] {\n width: 24px;\n height: 24px;\n border: none;\n border-radius: 50%;\n background-color: transparent;\n color: var(--mj-text-disabled);\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: all 0.2s ease;\n font-size: 12px;\n opacity: 0;\n margin-left: 8px;\n}\n\n.provider-item[_ngcontent-%COMP%]:hover .remove-provider-btn[_ngcontent-%COMP%] {\n opacity: 1;\n}\n\n.remove-provider-btn[_ngcontent-%COMP%]:hover:not(:disabled) {\n background-color: color-mix(in srgb, var(--mj-status-error) 8%, var(--mj-bg-surface));\n color: var(--mj-status-error);\n}\n\n.remove-provider-btn[_ngcontent-%COMP%]:disabled {\n cursor: not-allowed;\n opacity: 0.5;\n}\n\n\n\n.providers-container[_ngcontent-%COMP%]::-webkit-scrollbar {\n width: 6px;\n}\n\n.providers-container[_ngcontent-%COMP%]::-webkit-scrollbar-track {\n background: transparent;\n}\n\n.providers-container[_ngcontent-%COMP%]::-webkit-scrollbar-thumb {\n background: var(--mj-border-strong);\n border-radius: 3px;\n}\n\n.providers-container[_ngcontent-%COMP%]::-webkit-scrollbar-thumb:hover {\n background: var(--mj-text-disabled);\n}\n\n\n\n\n\n\n.add-provider-dialog[_ngcontent-%COMP%] {\n min-height: 150px;\n}\n\n.dialog-loading[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 40px 20px;\n}\n\n.dialog-empty[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: 30px 20px;\n text-align: center;\n}\n\n.dialog-empty-icon[_ngcontent-%COMP%] {\n font-size: 40px;\n color: var(--mj-status-success);\n margin-bottom: 12px;\n}\n\n.dialog-empty-message[_ngcontent-%COMP%] {\n font-size: 16px;\n font-weight: 500;\n color: var(--mj-text-primary);\n margin: 0 0 4px 0;\n}\n\n.dialog-empty-hint[_ngcontent-%COMP%] {\n font-size: 13px;\n color: var(--mj-text-muted);\n margin: 0;\n}\n\n.dialog-instruction[_ngcontent-%COMP%] {\n font-size: 13px;\n color: var(--mj-text-secondary);\n margin: 0 0 12px 0;\n padding: 0 4px;\n}\n\n.available-providers[_ngcontent-%COMP%] {\n max-height: 350px;\n overflow-y: auto;\n}\n\n.available-provider-item[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n padding: 12px;\n border-radius: 8px;\n cursor: pointer;\n transition: all 0.2s ease;\n margin-bottom: 8px;\n border: 1px solid var(--mj-border-default);\n}\n\n.available-provider-item[_ngcontent-%COMP%]:hover:not(.disabled) {\n background-color: var(--mj-bg-surface-card);\n border-color: var(--mj-brand-primary);\n}\n\n.available-provider-item.disabled[_ngcontent-%COMP%] {\n opacity: 0.6;\n cursor: not-allowed;\n background-color: var(--mj-bg-surface-card);\n}\n\n.available-provider-item[_ngcontent-%COMP%] .provider-icon[_ngcontent-%COMP%] {\n margin-right: 12px;\n}\n\n.available-provider-item[_ngcontent-%COMP%] .provider-info[_ngcontent-%COMP%] {\n flex: 1;\n}\n\n.available-provider-item[_ngcontent-%COMP%] .provider-name[_ngcontent-%COMP%] {\n font-size: 14px;\n font-weight: 500;\n color: var(--mj-text-primary);\n}\n\n.available-provider-item[_ngcontent-%COMP%] .provider-description[_ngcontent-%COMP%] {\n font-size: 12px;\n color: var(--mj-text-muted);\n margin-top: 2px;\n line-height: 1.3;\n}\n\n.available-provider-item[_ngcontent-%COMP%] .provider-warning[_ngcontent-%COMP%] {\n font-size: 11px;\n color: var(--mj-status-warning);\n margin-top: 4px;\n display: flex;\n align-items: center;\n gap: 4px;\n}\n\n.available-provider-item[_ngcontent-%COMP%] .provider-warning[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 10px;\n}\n\n\n\n.available-provider-item[_ngcontent-%COMP%] .oauth-hint[_ngcontent-%COMP%] {\n font-size: 11px;\n color: var(--mj-brand-primary);\n margin-top: 4px;\n display: flex;\n align-items: center;\n gap: 4px;\n}\n\n.available-provider-item[_ngcontent-%COMP%] .oauth-hint[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 10px;\n}\n\n.available-provider-item.oauth-provider[_ngcontent-%COMP%] {\n border-color: var(--mj-brand-primary);\n border-style: dashed;\n}\n\n.available-provider-item.oauth-provider[_ngcontent-%COMP%]:hover:not(.disabled) {\n background-color: color-mix(in srgb, var(--mj-brand-primary) 8%, var(--mj-bg-surface));\n border-style: solid;\n}\n\n.add-indicator[_ngcontent-%COMP%] {\n width: 28px;\n height: 28px;\n display: flex;\n align-items: center;\n justify-content: center;\n color: var(--mj-brand-primary);\n font-size: 14px;\n}\n\n\n\n@media screen and (max-width: 768px) {\n .providers-header[_ngcontent-%COMP%] {\n padding: 10px 12px;\n }\n\n .header-title[_ngcontent-%COMP%] {\n font-size: 12px;\n }\n\n .add-provider-btn[_ngcontent-%COMP%] {\n width: 26px;\n height: 26px;\n font-size: 12px;\n }\n\n .provider-item[_ngcontent-%COMP%] {\n padding: 10px 12px;\n }\n\n .provider-icon[_ngcontent-%COMP%] {\n width: 28px;\n height: 28px;\n font-size: 18px;\n margin-right: 10px;\n }\n\n .provider-name[_ngcontent-%COMP%] {\n font-size: 12px;\n }\n\n .provider-status[_ngcontent-%COMP%] {\n font-size: 10px;\n }\n\n .remove-provider-btn[_ngcontent-%COMP%] {\n width: 22px;\n height: 22px;\n font-size: 11px;\n opacity: 1;\n }\n}"] });
220
346
  }
221
347
  (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(StorageProvidersListComponent, [{
222
348
  type: Component,
223
- args: [{ standalone: false, selector: 'mj-storage-providers-list', template: "<div class=\"providers-list\">\n <!-- Header -->\n <div class=\"providers-header\">\n <span class=\"header-title\">Storage Accounts</span>\n </div>\n\n <!-- Loading State -->\n @if (isLoading) {\n <div class=\"loading-state\">\n <mj-loading text=\"Loading accounts...\"></mj-loading>\n </div>\n }\n\n <!-- Error State -->\n @if (errorMessage && !isLoading) {\n <mj-empty-state\n class=\"storage-providers-empty-fill\"\n Variant=\"error\"\n Icon=\"fa-solid fa-triangle-exclamation\"\n [Title]=\"errorMessage\"\n Size=\"compact\" />\n }\n\n <!-- Empty State -->\n @if (!isLoading && !errorMessage && accounts.length === 0) {\n <mj-empty-state\n class=\"storage-providers-empty-fill\"\n Icon=\"fa-solid fa-folder-plus\"\n Title=\"No storage accounts configured\"\n Message=\"Contact your administrator to set up file storage accounts\"\n Size=\"compact\" />\n }\n\n <!-- Account List -->\n @if (!isLoading && !errorMessage && accounts.length > 0) {\n <div class=\"providers-container\">\n @for (item of accounts; track item) {\n <div\n class=\"provider-item\"\n [class.selected]=\"isSelected(item)\"\n (click)=\"selectAccount(item)\"\n [title]=\"item.account.Description || item.account.Name\"\n >\n <div class=\"provider-icon\">\n <i [class]=\"getProviderIcon(item.provider.Name)\"></i>\n </div>\n <div class=\"provider-info\">\n <div class=\"provider-name\">{{ item.account.Name }}</div>\n <div class=\"provider-meta\">\n <!-- Provider type badge -->\n <span class=\"provider-type-badge\" [title]=\"item.provider.Name\">\n {{ item.provider.Name }}\n </span>\n <!-- Search support badge -->\n @if (item.provider.SupportsSearch) {\n <span class=\"search-badge\" title=\"Supports search\">\n <i class=\"fa-solid fa-magnifying-glass\"></i>\n </span>\n }\n <!-- Credential configured badge -->\n @if (item.account.CredentialID) {\n <span class=\"credential-badge\" title=\"Credentials configured\">\n <i class=\"fa-solid fa-key\"></i>\n </span>\n }\n <!-- No credential warning -->\n @if (!item.account.CredentialID) {\n <span class=\"unconfigured-badge\" title=\"No credentials configured\">\n <i class=\"fa-solid fa-exclamation-triangle\"></i>\n </span>\n }\n </div>\n </div>\n </div>\n }\n </div>\n }\n</div>\n", styles: ["/* ===========================\n Storage Providers List\n =========================== */\n\n.providers-list {\n display: flex;\n flex-direction: column;\n height: 100%;\n overflow: hidden;\n}\n\n/* Header with Add Button */\n.providers-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 12px 16px;\n border-bottom: 1px solid var(--mj-border-default);\n background-color: var(--mj-bg-surface-card);\n}\n\n.header-title {\n font-size: 13px;\n font-weight: 600;\n color: var(--mj-text-primary);\n text-transform: uppercase;\n letter-spacing: 0.5px;\n}\n\n.add-provider-btn {\n width: 28px;\n height: 28px;\n border: none;\n border-radius: 50%;\n background-color: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: all 0.2s ease;\n font-size: 14px;\n}\n\n.add-provider-btn:hover:not(:disabled) {\n background-color: var(--mj-brand-primary-hover);\n transform: scale(1.05);\n}\n\n.add-provider-btn:disabled {\n background-color: var(--mj-border-strong);\n cursor: not-allowed;\n}\n\n/* Loading State */\n.loading-state {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: 24px;\n text-align: center;\n color: var(--mj-text-muted);\n flex: 1;\n}\n\n/* Empty/error states fill the panel vertically (flex-parent gotcha) */\n.storage-providers-empty-fill {\n flex: 1;\n}\n\n/* Provider List Container */\n.providers-container {\n flex: 1;\n overflow-y: auto;\n overflow-x: hidden;\n}\n\n/* Individual Provider Item */\n.provider-item {\n display: flex;\n align-items: center;\n padding: 12px 16px;\n cursor: pointer;\n transition: all 0.2s ease;\n border-bottom: 1px solid var(--mj-bg-surface-sunken);\n position: relative;\n}\n\n.provider-item:hover {\n background-color: var(--mj-bg-surface-card);\n}\n\n.provider-item.selected {\n background-color: color-mix(in srgb, var(--mj-brand-primary) 8%, var(--mj-bg-surface));\n border-left: 3px solid var(--mj-brand-primary);\n}\n\n.provider-item.selected::before {\n content: '';\n position: absolute;\n left: 0;\n top: 0;\n bottom: 0;\n width: 3px;\n background-color: var(--mj-brand-primary);\n}\n\n/* Provider Icon */\n.provider-icon {\n flex-shrink: 0;\n width: 32px;\n height: 32px;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 20px;\n color: var(--mj-text-secondary);\n margin-right: 12px;\n}\n\n.provider-item.selected .provider-icon {\n color: var(--mj-brand-primary);\n}\n\n/* Provider Info */\n.provider-info {\n flex: 1;\n min-width: 0;\n display: flex;\n flex-direction: column;\n gap: 2px;\n}\n\n.provider-name {\n font-size: 13px;\n font-weight: 500;\n color: var(--mj-text-primary);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.provider-item.selected .provider-name {\n color: var(--mj-brand-primary);\n font-weight: 600;\n}\n\n.provider-meta {\n display: flex;\n align-items: center;\n gap: 8px;\n font-size: 11px;\n color: var(--mj-text-muted);\n}\n\n/* Default Badge */\n.default-badge {\n display: inline-flex;\n align-items: center;\n color: var(--mj-status-warning);\n}\n\n.default-badge i {\n font-size: 10px;\n}\n\n/* Provider Type Badge */\n.provider-type-badge {\n display: inline-flex;\n align-items: center;\n padding: 2px 6px;\n background-color: var(--mj-bg-surface-sunken);\n border-radius: 10px;\n font-size: 10px;\n color: var(--mj-text-secondary);\n max-width: 100px;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.provider-item.selected .provider-type-badge {\n background-color: color-mix(in srgb, var(--mj-brand-primary) 15%, transparent);\n color: var(--mj-brand-primary);\n}\n\n/* Search Badge */\n.search-badge {\n display: inline-flex;\n align-items: center;\n padding: 2px 6px;\n background-color: var(--mj-bg-surface-sunken);\n border-radius: 10px;\n font-size: 10px;\n color: var(--mj-text-secondary);\n}\n\n.provider-item.selected .search-badge {\n background-color: color-mix(in srgb, var(--mj-brand-primary) 15%, transparent);\n color: var(--mj-brand-primary);\n}\n\n.search-badge i {\n font-size: 9px;\n}\n\n/* Credential Badge */\n.credential-badge {\n display: inline-flex;\n align-items: center;\n padding: 2px 6px;\n background-color: color-mix(in srgb, var(--mj-status-success) 8%, var(--mj-bg-surface));\n border-radius: 10px;\n font-size: 10px;\n color: var(--mj-status-success);\n}\n\n.provider-item.selected .credential-badge {\n background-color: color-mix(in srgb, var(--mj-status-success) 15%, transparent);\n color: var(--mj-status-success);\n}\n\n.credential-badge i {\n font-size: 9px;\n}\n\n/* Unconfigured Badge */\n.unconfigured-badge {\n display: inline-flex;\n align-items: center;\n color: var(--mj-status-warning);\n}\n\n.unconfigured-badge i {\n font-size: 10px;\n}\n\n/* OAuth Status Badges */\n.oauth-connected-badge {\n display: inline-flex;\n align-items: center;\n padding: 2px 6px;\n background-color: color-mix(in srgb, var(--mj-status-success) 8%, var(--mj-bg-surface));\n border-radius: 10px;\n font-size: 10px;\n color: var(--mj-status-success);\n}\n\n.oauth-connected-badge i {\n font-size: 9px;\n}\n\n.oauth-disconnected-badge {\n display: inline-flex;\n align-items: center;\n padding: 2px 6px;\n background-color: color-mix(in srgb, var(--mj-status-warning) 8%, var(--mj-bg-surface));\n border-radius: 10px;\n font-size: 10px;\n color: var(--mj-status-warning);\n}\n\n.oauth-disconnected-badge i {\n font-size: 9px;\n}\n\n/* Provider Actions (OAuth connect/disconnect buttons) */\n.provider-actions {\n display: flex;\n align-items: center;\n gap: 4px;\n margin-left: 8px;\n}\n\n.connect-btn,\n.disconnect-btn {\n width: 26px;\n height: 26px;\n border: none;\n border-radius: 50%;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: all 0.2s ease;\n font-size: 12px;\n}\n\n.connect-btn {\n background-color: color-mix(in srgb, var(--mj-status-success) 8%, var(--mj-bg-surface));\n color: var(--mj-status-success);\n}\n\n.connect-btn:hover:not(:disabled) {\n background-color: color-mix(in srgb, var(--mj-status-success) 15%, var(--mj-bg-surface));\n transform: scale(1.05);\n}\n\n.connect-btn:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n\n.disconnect-btn {\n background-color: transparent;\n color: var(--mj-text-muted);\n}\n\n.disconnect-btn:hover {\n background-color: color-mix(in srgb, var(--mj-status-error) 8%, var(--mj-bg-surface));\n color: var(--mj-status-error);\n}\n\n/* Remove Provider Button */\n.remove-provider-btn {\n width: 24px;\n height: 24px;\n border: none;\n border-radius: 50%;\n background-color: transparent;\n color: var(--mj-text-disabled);\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: all 0.2s ease;\n font-size: 12px;\n opacity: 0;\n margin-left: 8px;\n}\n\n.provider-item:hover .remove-provider-btn {\n opacity: 1;\n}\n\n.remove-provider-btn:hover:not(:disabled) {\n background-color: color-mix(in srgb, var(--mj-status-error) 8%, var(--mj-bg-surface));\n color: var(--mj-status-error);\n}\n\n.remove-provider-btn:disabled {\n cursor: not-allowed;\n opacity: 0.5;\n}\n\n/* Scrollbar Styling */\n.providers-container::-webkit-scrollbar {\n width: 6px;\n}\n\n.providers-container::-webkit-scrollbar-track {\n background: transparent;\n}\n\n.providers-container::-webkit-scrollbar-thumb {\n background: var(--mj-border-strong);\n border-radius: 3px;\n}\n\n.providers-container::-webkit-scrollbar-thumb:hover {\n background: var(--mj-text-disabled);\n}\n\n/* ===========================\n Add Provider Dialog\n =========================== */\n\n.add-provider-dialog {\n min-height: 150px;\n}\n\n.dialog-loading {\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 40px 20px;\n}\n\n.dialog-empty {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: 30px 20px;\n text-align: center;\n}\n\n.dialog-empty-icon {\n font-size: 40px;\n color: var(--mj-status-success);\n margin-bottom: 12px;\n}\n\n.dialog-empty-message {\n font-size: 16px;\n font-weight: 500;\n color: var(--mj-text-primary);\n margin: 0 0 4px 0;\n}\n\n.dialog-empty-hint {\n font-size: 13px;\n color: var(--mj-text-muted);\n margin: 0;\n}\n\n.dialog-instruction {\n font-size: 13px;\n color: var(--mj-text-secondary);\n margin: 0 0 12px 0;\n padding: 0 4px;\n}\n\n.available-providers {\n max-height: 350px;\n overflow-y: auto;\n}\n\n.available-provider-item {\n display: flex;\n align-items: center;\n padding: 12px;\n border-radius: 8px;\n cursor: pointer;\n transition: all 0.2s ease;\n margin-bottom: 8px;\n border: 1px solid var(--mj-border-default);\n}\n\n.available-provider-item:hover:not(.disabled) {\n background-color: var(--mj-bg-surface-card);\n border-color: var(--mj-brand-primary);\n}\n\n.available-provider-item.disabled {\n opacity: 0.6;\n cursor: not-allowed;\n background-color: var(--mj-bg-surface-card);\n}\n\n.available-provider-item .provider-icon {\n margin-right: 12px;\n}\n\n.available-provider-item .provider-info {\n flex: 1;\n}\n\n.available-provider-item .provider-name {\n font-size: 14px;\n font-weight: 500;\n color: var(--mj-text-primary);\n}\n\n.available-provider-item .provider-description {\n font-size: 12px;\n color: var(--mj-text-muted);\n margin-top: 2px;\n line-height: 1.3;\n}\n\n.available-provider-item .provider-warning {\n font-size: 11px;\n color: var(--mj-status-warning);\n margin-top: 4px;\n display: flex;\n align-items: center;\n gap: 4px;\n}\n\n.available-provider-item .provider-warning i {\n font-size: 10px;\n}\n\n/* OAuth hint in add provider dialog */\n.available-provider-item .oauth-hint {\n font-size: 11px;\n color: var(--mj-brand-primary);\n margin-top: 4px;\n display: flex;\n align-items: center;\n gap: 4px;\n}\n\n.available-provider-item .oauth-hint i {\n font-size: 10px;\n}\n\n.available-provider-item.oauth-provider {\n border-color: var(--mj-brand-primary);\n border-style: dashed;\n}\n\n.available-provider-item.oauth-provider:hover:not(.disabled) {\n background-color: color-mix(in srgb, var(--mj-brand-primary) 8%, var(--mj-bg-surface));\n border-style: solid;\n}\n\n.add-indicator {\n width: 28px;\n height: 28px;\n display: flex;\n align-items: center;\n justify-content: center;\n color: var(--mj-brand-primary);\n font-size: 14px;\n}\n\n/* Responsive Adjustments */\n@media screen and (max-width: 768px) {\n .providers-header {\n padding: 10px 12px;\n }\n\n .header-title {\n font-size: 12px;\n }\n\n .add-provider-btn {\n width: 26px;\n height: 26px;\n font-size: 12px;\n }\n\n .provider-item {\n padding: 10px 12px;\n }\n\n .provider-icon {\n width: 28px;\n height: 28px;\n font-size: 18px;\n margin-right: 10px;\n }\n\n .provider-name {\n font-size: 12px;\n }\n\n .provider-status {\n font-size: 10px;\n }\n\n .remove-provider-btn {\n width: 22px;\n height: 22px;\n font-size: 11px;\n opacity: 1;\n }\n}\n"] }]
224
- }], () => [{ type: i0.ChangeDetectorRef }], { accountSelected: [{
349
+ args: [{ standalone: false, selector: 'mj-storage-providers-list', template: "<div class=\"providers-list\">\n <!-- Header -->\n <div class=\"providers-header\">\n <span class=\"header-title\">Storage Accounts</span>\n @if (userCanManage) {\n <div class=\"admin-header-actions\">\n <button\n type=\"button\"\n class=\"admin-action-btn\"\n title=\"Add Storage Account\"\n (click)=\"openAdminDialog('accounts')\">\n <i class=\"fa-solid fa-plus\"></i>\n </button>\n <button\n type=\"button\"\n class=\"admin-action-btn\"\n title=\"Manage Storage Providers & Accounts\"\n (click)=\"openAdminDialog('accounts')\">\n <i class=\"fa-solid fa-gear\"></i>\n </button>\n </div>\n }\n </div>\n\n <!-- Loading State -->\n @if (isLoading) {\n <div class=\"loading-state\">\n <mj-loading text=\"Loading accounts...\"></mj-loading>\n </div>\n }\n\n <!-- Error State -->\n @if (errorMessage && !isLoading) {\n <mj-empty-state\n class=\"storage-providers-empty-fill\"\n Variant=\"error\"\n Icon=\"fa-solid fa-triangle-exclamation\"\n [Title]=\"errorMessage\"\n Size=\"compact\" />\n }\n\n <!-- Empty State -->\n @if (!isLoading && !errorMessage && accounts.length === 0) {\n <mj-empty-state\n class=\"storage-providers-empty-fill\"\n Icon=\"fa-solid fa-folder-plus\"\n [Title]=\"userCanManage ? 'No storage accounts configured' : 'No storage accounts available'\"\n [Message]=\"userCanManage ? 'Configure storage accounts to connect MemberJunction to cloud storage providers.' : 'Contact your administrator to set up file storage accounts.'\"\n Size=\"compact\">\n @if (userCanManage) {\n <button\n type=\"button\"\n class=\"mj-empty-action-btn\"\n (click)=\"openAdminDialog('accounts')\">\n <i class=\"fa-solid fa-plus\"></i>\n <span>Configure Account</span>\n </button>\n }\n </mj-empty-state>\n }\n\n <!-- Account List -->\n @if (!isLoading && !errorMessage && accounts.length > 0) {\n <div class=\"providers-container\">\n @for (item of accounts; track item.account.ID) {\n <div\n class=\"provider-item\"\n [class.selected]=\"isSelected(item)\"\n (click)=\"selectAccount(item)\"\n [title]=\"item.account.Description || item.account.Name\">\n <div class=\"provider-icon\">\n <i [class]=\"getProviderIcon(item.provider.Name)\"></i>\n </div>\n <div class=\"provider-info\">\n <div class=\"provider-name\">{{ item.account.Name }}</div>\n <div class=\"provider-meta\">\n <!-- Provider type badge -->\n <span class=\"provider-type-badge\" [title]=\"item.provider.Name\">\n {{ item.provider.Name }}\n </span>\n <!-- Search support badge -->\n @if (item.provider.SupportsSearch) {\n <span class=\"search-badge\" title=\"Supports search\">\n <i class=\"fa-solid fa-magnifying-glass\"></i>\n </span>\n }\n <!-- Credential configured badge -->\n @if (item.account.CredentialID) {\n <span class=\"credential-badge\" title=\"Credentials configured\">\n <i class=\"fa-solid fa-key\"></i>\n </span>\n }\n <!-- No credential warning -->\n @if (!item.account.CredentialID) {\n <span class=\"unconfigured-badge\" title=\"No credentials configured\">\n <i class=\"fa-solid fa-exclamation-triangle\"></i>\n </span>\n }\n </div>\n </div>\n @if (userCanManage) {\n <button\n type=\"button\"\n class=\"provider-edit-btn\"\n title=\"Edit Storage Account\"\n (click)=\"onEditAccountClick(item, $event)\">\n <i class=\"fa-solid fa-pen-to-square\"></i>\n </button>\n }\n </div>\n }\n </div>\n }\n</div>\n\n<!-- Storage Administration Dialog -->\n@if (isManageDialogOpen) {\n <mj-storage-admin-dialog\n [Visible]=\"isManageDialogOpen\"\n [ActiveTab]=\"adminDialogTab\"\n (Close)=\"closeAdminDialog()\"\n (AccountsChanged)=\"onAdminAccountsChanged()\">\n </mj-storage-admin-dialog>\n}\n", styles: ["/* ===========================\n Storage Providers List\n =========================== */\n\n.providers-list {\n display: flex;\n flex-direction: column;\n height: 100%;\n overflow: hidden;\n}\n\n/* Header with Add Button */\n.providers-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 12px 16px;\n border-bottom: 1px solid var(--mj-border-default);\n background-color: var(--mj-bg-surface-card);\n}\n\n.header-title {\n font-size: 13px;\n font-weight: 600;\n color: var(--mj-text-primary);\n text-transform: uppercase;\n letter-spacing: 0.5px;\n}\n\n.admin-header-actions {\n display: flex;\n align-items: center;\n gap: 6px;\n}\n\n.admin-action-btn {\n width: 26px;\n height: 26px;\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-sm, 6px);\n background-color: var(--mj-bg-surface);\n color: var(--mj-text-secondary);\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: all 0.15s ease;\n font-size: 11px;\n}\n\n.admin-action-btn:hover {\n background-color: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n border-color: var(--mj-brand-primary);\n}\n\n.provider-edit-btn {\n width: 24px;\n height: 24px;\n border: none;\n border-radius: var(--mj-radius-sm, 4px);\n background-color: transparent;\n color: var(--mj-text-muted);\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: all 0.15s ease;\n font-size: 11px;\n opacity: 0;\n margin-left: 6px;\n flex-shrink: 0;\n}\n\n.provider-item:hover .provider-edit-btn {\n opacity: 1;\n}\n\n.provider-edit-btn:hover {\n background-color: color-mix(in srgb, var(--mj-brand-primary) 12%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n}\n\n.mj-empty-action-btn {\n margin-top: 12px;\n padding: 6px 12px;\n border-radius: var(--mj-radius-sm, 6px);\n border: 1px solid transparent;\n background-color: var(--mj-brand-primary);\n color: var(--mj-text-inverse, #ffffff);\n font-size: 12px;\n font-weight: 500;\n cursor: pointer;\n display: inline-flex;\n align-items: center;\n gap: 6px;\n transition: background-color 0.12s;\n}\n\n.mj-empty-action-btn:hover {\n background-color: var(--mj-brand-primary-hover);\n}\n\n.add-provider-btn {\n width: 28px;\n height: 28px;\n border: none;\n border-radius: 50%;\n background-color: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: all 0.2s ease;\n font-size: 14px;\n}\n\n.add-provider-btn:hover:not(:disabled) {\n background-color: var(--mj-brand-primary-hover);\n transform: scale(1.05);\n}\n\n.add-provider-btn:disabled {\n background-color: var(--mj-border-strong);\n cursor: not-allowed;\n}\n\n/* Loading State */\n.loading-state {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: 24px;\n text-align: center;\n color: var(--mj-text-muted);\n flex: 1;\n}\n\n/* Empty/error states fill the panel vertically (flex-parent gotcha) */\n.storage-providers-empty-fill {\n flex: 1;\n}\n\n/* Provider List Container */\n.providers-container {\n flex: 1;\n overflow-y: auto;\n overflow-x: hidden;\n}\n\n/* Individual Provider Item */\n.provider-item {\n display: flex;\n align-items: center;\n padding: 12px 16px;\n cursor: pointer;\n transition: all 0.2s ease;\n border-bottom: 1px solid var(--mj-bg-surface-sunken);\n position: relative;\n}\n\n.provider-item:hover {\n background-color: var(--mj-bg-surface-card);\n}\n\n.provider-item.selected {\n background-color: color-mix(in srgb, var(--mj-brand-primary) 8%, var(--mj-bg-surface));\n border-left: 3px solid var(--mj-brand-primary);\n}\n\n.provider-item.selected::before {\n content: '';\n position: absolute;\n left: 0;\n top: 0;\n bottom: 0;\n width: 3px;\n background-color: var(--mj-brand-primary);\n}\n\n/* Provider Icon */\n.provider-icon {\n flex-shrink: 0;\n width: 32px;\n height: 32px;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 20px;\n color: var(--mj-text-secondary);\n margin-right: 12px;\n}\n\n.provider-item.selected .provider-icon {\n color: var(--mj-brand-primary);\n}\n\n/* Provider Info */\n.provider-info {\n flex: 1;\n min-width: 0;\n display: flex;\n flex-direction: column;\n gap: 2px;\n}\n\n.provider-name {\n font-size: 13px;\n font-weight: 500;\n color: var(--mj-text-primary);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.provider-item.selected .provider-name {\n color: var(--mj-brand-primary);\n font-weight: 600;\n}\n\n.provider-meta {\n display: flex;\n align-items: center;\n gap: 8px;\n font-size: 11px;\n color: var(--mj-text-muted);\n}\n\n/* Default Badge */\n.default-badge {\n display: inline-flex;\n align-items: center;\n color: var(--mj-status-warning);\n}\n\n.default-badge i {\n font-size: 10px;\n}\n\n/* Provider Type Badge */\n.provider-type-badge {\n display: inline-flex;\n align-items: center;\n padding: 2px 6px;\n background-color: var(--mj-bg-surface-sunken);\n border-radius: 10px;\n font-size: 10px;\n color: var(--mj-text-secondary);\n max-width: 100px;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.provider-item.selected .provider-type-badge {\n background-color: color-mix(in srgb, var(--mj-brand-primary) 15%, transparent);\n color: var(--mj-brand-primary);\n}\n\n/* Search Badge */\n.search-badge {\n display: inline-flex;\n align-items: center;\n padding: 2px 6px;\n background-color: var(--mj-bg-surface-sunken);\n border-radius: 10px;\n font-size: 10px;\n color: var(--mj-text-secondary);\n}\n\n.provider-item.selected .search-badge {\n background-color: color-mix(in srgb, var(--mj-brand-primary) 15%, transparent);\n color: var(--mj-brand-primary);\n}\n\n.search-badge i {\n font-size: 9px;\n}\n\n/* Credential Badge */\n.credential-badge {\n display: inline-flex;\n align-items: center;\n padding: 2px 6px;\n background-color: color-mix(in srgb, var(--mj-status-success) 8%, var(--mj-bg-surface));\n border-radius: 10px;\n font-size: 10px;\n color: var(--mj-status-success);\n}\n\n.provider-item.selected .credential-badge {\n background-color: color-mix(in srgb, var(--mj-status-success) 15%, transparent);\n color: var(--mj-status-success);\n}\n\n.credential-badge i {\n font-size: 9px;\n}\n\n/* Unconfigured Badge */\n.unconfigured-badge {\n display: inline-flex;\n align-items: center;\n color: var(--mj-status-warning);\n}\n\n.unconfigured-badge i {\n font-size: 10px;\n}\n\n/* OAuth Status Badges */\n.oauth-connected-badge {\n display: inline-flex;\n align-items: center;\n padding: 2px 6px;\n background-color: color-mix(in srgb, var(--mj-status-success) 8%, var(--mj-bg-surface));\n border-radius: 10px;\n font-size: 10px;\n color: var(--mj-status-success);\n}\n\n.oauth-connected-badge i {\n font-size: 9px;\n}\n\n.oauth-disconnected-badge {\n display: inline-flex;\n align-items: center;\n padding: 2px 6px;\n background-color: color-mix(in srgb, var(--mj-status-warning) 8%, var(--mj-bg-surface));\n border-radius: 10px;\n font-size: 10px;\n color: var(--mj-status-warning);\n}\n\n.oauth-disconnected-badge i {\n font-size: 9px;\n}\n\n/* Provider Actions (OAuth connect/disconnect buttons) */\n.provider-actions {\n display: flex;\n align-items: center;\n gap: 4px;\n margin-left: 8px;\n}\n\n.connect-btn,\n.disconnect-btn {\n width: 26px;\n height: 26px;\n border: none;\n border-radius: 50%;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: all 0.2s ease;\n font-size: 12px;\n}\n\n.connect-btn {\n background-color: color-mix(in srgb, var(--mj-status-success) 8%, var(--mj-bg-surface));\n color: var(--mj-status-success);\n}\n\n.connect-btn:hover:not(:disabled) {\n background-color: color-mix(in srgb, var(--mj-status-success) 15%, var(--mj-bg-surface));\n transform: scale(1.05);\n}\n\n.connect-btn:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n\n.disconnect-btn {\n background-color: transparent;\n color: var(--mj-text-muted);\n}\n\n.disconnect-btn:hover {\n background-color: color-mix(in srgb, var(--mj-status-error) 8%, var(--mj-bg-surface));\n color: var(--mj-status-error);\n}\n\n/* Remove Provider Button */\n.remove-provider-btn {\n width: 24px;\n height: 24px;\n border: none;\n border-radius: 50%;\n background-color: transparent;\n color: var(--mj-text-disabled);\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: all 0.2s ease;\n font-size: 12px;\n opacity: 0;\n margin-left: 8px;\n}\n\n.provider-item:hover .remove-provider-btn {\n opacity: 1;\n}\n\n.remove-provider-btn:hover:not(:disabled) {\n background-color: color-mix(in srgb, var(--mj-status-error) 8%, var(--mj-bg-surface));\n color: var(--mj-status-error);\n}\n\n.remove-provider-btn:disabled {\n cursor: not-allowed;\n opacity: 0.5;\n}\n\n/* Scrollbar Styling */\n.providers-container::-webkit-scrollbar {\n width: 6px;\n}\n\n.providers-container::-webkit-scrollbar-track {\n background: transparent;\n}\n\n.providers-container::-webkit-scrollbar-thumb {\n background: var(--mj-border-strong);\n border-radius: 3px;\n}\n\n.providers-container::-webkit-scrollbar-thumb:hover {\n background: var(--mj-text-disabled);\n}\n\n/* ===========================\n Add Provider Dialog\n =========================== */\n\n.add-provider-dialog {\n min-height: 150px;\n}\n\n.dialog-loading {\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 40px 20px;\n}\n\n.dialog-empty {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: 30px 20px;\n text-align: center;\n}\n\n.dialog-empty-icon {\n font-size: 40px;\n color: var(--mj-status-success);\n margin-bottom: 12px;\n}\n\n.dialog-empty-message {\n font-size: 16px;\n font-weight: 500;\n color: var(--mj-text-primary);\n margin: 0 0 4px 0;\n}\n\n.dialog-empty-hint {\n font-size: 13px;\n color: var(--mj-text-muted);\n margin: 0;\n}\n\n.dialog-instruction {\n font-size: 13px;\n color: var(--mj-text-secondary);\n margin: 0 0 12px 0;\n padding: 0 4px;\n}\n\n.available-providers {\n max-height: 350px;\n overflow-y: auto;\n}\n\n.available-provider-item {\n display: flex;\n align-items: center;\n padding: 12px;\n border-radius: 8px;\n cursor: pointer;\n transition: all 0.2s ease;\n margin-bottom: 8px;\n border: 1px solid var(--mj-border-default);\n}\n\n.available-provider-item:hover:not(.disabled) {\n background-color: var(--mj-bg-surface-card);\n border-color: var(--mj-brand-primary);\n}\n\n.available-provider-item.disabled {\n opacity: 0.6;\n cursor: not-allowed;\n background-color: var(--mj-bg-surface-card);\n}\n\n.available-provider-item .provider-icon {\n margin-right: 12px;\n}\n\n.available-provider-item .provider-info {\n flex: 1;\n}\n\n.available-provider-item .provider-name {\n font-size: 14px;\n font-weight: 500;\n color: var(--mj-text-primary);\n}\n\n.available-provider-item .provider-description {\n font-size: 12px;\n color: var(--mj-text-muted);\n margin-top: 2px;\n line-height: 1.3;\n}\n\n.available-provider-item .provider-warning {\n font-size: 11px;\n color: var(--mj-status-warning);\n margin-top: 4px;\n display: flex;\n align-items: center;\n gap: 4px;\n}\n\n.available-provider-item .provider-warning i {\n font-size: 10px;\n}\n\n/* OAuth hint in add provider dialog */\n.available-provider-item .oauth-hint {\n font-size: 11px;\n color: var(--mj-brand-primary);\n margin-top: 4px;\n display: flex;\n align-items: center;\n gap: 4px;\n}\n\n.available-provider-item .oauth-hint i {\n font-size: 10px;\n}\n\n.available-provider-item.oauth-provider {\n border-color: var(--mj-brand-primary);\n border-style: dashed;\n}\n\n.available-provider-item.oauth-provider:hover:not(.disabled) {\n background-color: color-mix(in srgb, var(--mj-brand-primary) 8%, var(--mj-bg-surface));\n border-style: solid;\n}\n\n.add-indicator {\n width: 28px;\n height: 28px;\n display: flex;\n align-items: center;\n justify-content: center;\n color: var(--mj-brand-primary);\n font-size: 14px;\n}\n\n/* Responsive Adjustments */\n@media screen and (max-width: 768px) {\n .providers-header {\n padding: 10px 12px;\n }\n\n .header-title {\n font-size: 12px;\n }\n\n .add-provider-btn {\n width: 26px;\n height: 26px;\n font-size: 12px;\n }\n\n .provider-item {\n padding: 10px 12px;\n }\n\n .provider-icon {\n width: 28px;\n height: 28px;\n font-size: 18px;\n margin-right: 10px;\n }\n\n .provider-name {\n font-size: 12px;\n }\n\n .provider-status {\n font-size: 10px;\n }\n\n .remove-provider-btn {\n width: 22px;\n height: 22px;\n font-size: 11px;\n opacity: 1;\n }\n}\n"] }]
350
+ }], () => [], { accountSelected: [{
225
351
  type: Output
226
352
  }] }); })();
227
- (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(StorageProvidersListComponent, { className: "StorageProvidersListComponent", filePath: "src/lib/file-browser/storage-providers-list.component.ts", lineNumber: 17 }); })();
353
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(StorageProvidersListComponent, { className: "StorageProvidersListComponent", filePath: "src/lib/file-browser/storage-providers-list.component.ts", lineNumber: 19 }); })();
228
354
  //# sourceMappingURL=storage-providers-list.component.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"storage-providers-list.component.js","sourceRoot":"","sources":["../../../src/lib/file-browser/storage-providers-list.component.ts","../../../src/lib/file-browser/storage-providers-list.component.html"],"names":[],"mappings":"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","sourcesContent":["import { Component, EventEmitter, OnInit, Output, ChangeDetectorRef } from '@angular/core';\nimport { FileStorageEngineBase, StorageAccountWithProvider } from '@memberjunction/core-entities';\nimport { UUIDsEqual } from '@memberjunction/global';\n\n/**\n * Displays a list of organizational file storage accounts.\n * In the enterprise model, accounts are configured by administrators\n * and available to users based on permissions. Users no longer manage\n * their own OAuth connections - credentials are handled at the org level.\n */\n@Component({\n standalone: false,\n selector: 'mj-storage-providers-list',\n templateUrl: './storage-providers-list.component.html',\n styleUrls: ['./storage-providers-list.component.css']\n})\nexport class StorageProvidersListComponent implements OnInit {\n /**\n * Emits when an account is selected by the user, or null when no accounts are available.\n * Emits the full account-with-provider object for downstream components to use.\n */\n @Output() accountSelected = new EventEmitter<StorageAccountWithProvider | null>();\n\n /**\n * All available storage accounts with their provider details.\n */\n public accounts: StorageAccountWithProvider[] = [];\n\n /**\n * Currently selected account.\n */\n public selectedAccount: StorageAccountWithProvider | null = null;\n\n /**\n * Loading state indicator.\n */\n public isLoading: boolean = false;\n\n /**\n * Error message if loading fails.\n */\n public errorMessage: string | null = null;\n\n constructor(private cdr: ChangeDetectorRef) {}\n\n ngOnInit(): void {\n this.loadAccounts();\n }\n\n /**\n * Loads all available file storage accounts with their provider details.\n * Uses FileStorageEngineBase for centralized, cached access.\n */\n private async loadAccounts(): Promise<void> {\n this.isLoading = true;\n this.errorMessage = null;\n\n try {\n const engine = FileStorageEngineBase.Instance;\n await engine.Config(false); // Use cached data if available\n\n // Only show accounts whose provider is active\n this.accounts = engine.AccountsWithProviders.filter(a => a.provider.IsActive);\n\n console.log('[StorageAccountsList] Loaded accounts:', this.accounts.map(a => ({\n name: a.account.Name,\n provider: a.provider.Name,\n hasCredential: !!a.account.CredentialID\n })));\n\n // Auto-select first account if available\n if (this.accounts.length > 0) {\n this.selectAccount(this.accounts[0]);\n } else {\n this.accountSelected.emit(null);\n }\n\n } catch (error) {\n this.errorMessage = error instanceof Error ? error.message : 'Failed to load storage accounts';\n console.error('[StorageProvidersList] Error loading accounts:', error);\n } finally {\n this.isLoading = false;\n this.cdr.detectChanges();\n }\n }\n\n /**\n * Handles account selection by the user.\n */\n public selectAccount(accountWithProvider: StorageAccountWithProvider): void {\n this.selectedAccount = accountWithProvider;\n this.accountSelected.emit(accountWithProvider);\n }\n\n /**\n * Checks if an account is currently selected.\n */\n public isSelected(accountWithProvider: StorageAccountWithProvider): boolean {\n return UUIDsEqual(this.selectedAccount?.account.ID, accountWithProvider.account.ID);\n }\n\n /**\n * Gets the icon class for a provider based on its name.\n */\n public getProviderIcon(providerName: string): string {\n const name = providerName.toLowerCase();\n\n if (name.includes('aws') || name.includes('s3')) {\n return 'fa-brands fa-aws';\n } else if (name.includes('azure')) {\n return 'fa-brands fa-microsoft';\n } else if (name.includes('google drive')) {\n return 'fa-brands fa-google-drive';\n } else if (name.includes('google cloud')) {\n return 'fa-brands fa-google';\n } else if (name.includes('dropbox')) {\n return 'fa-brands fa-dropbox';\n } else if (name.includes('box')) {\n return 'fa-solid fa-box';\n } else if (name.includes('sharepoint') || name.includes('onedrive')) {\n return 'fa-brands fa-microsoft';\n } else {\n return 'fa-solid fa-cloud';\n }\n }\n\n /**\n * Refreshes the accounts list by forcing a reload from the database.\n */\n public refresh(): void {\n FileStorageEngineBase.Instance.Config(true).then(() => this.loadAccounts());\n }\n}\n","<div class=\"providers-list\">\n <!-- Header -->\n <div class=\"providers-header\">\n <span class=\"header-title\">Storage Accounts</span>\n </div>\n\n <!-- Loading State -->\n @if (isLoading) {\n <div class=\"loading-state\">\n <mj-loading text=\"Loading accounts...\"></mj-loading>\n </div>\n }\n\n <!-- Error State -->\n @if (errorMessage && !isLoading) {\n <mj-empty-state\n class=\"storage-providers-empty-fill\"\n Variant=\"error\"\n Icon=\"fa-solid fa-triangle-exclamation\"\n [Title]=\"errorMessage\"\n Size=\"compact\" />\n }\n\n <!-- Empty State -->\n @if (!isLoading && !errorMessage && accounts.length === 0) {\n <mj-empty-state\n class=\"storage-providers-empty-fill\"\n Icon=\"fa-solid fa-folder-plus\"\n Title=\"No storage accounts configured\"\n Message=\"Contact your administrator to set up file storage accounts\"\n Size=\"compact\" />\n }\n\n <!-- Account List -->\n @if (!isLoading && !errorMessage && accounts.length > 0) {\n <div class=\"providers-container\">\n @for (item of accounts; track item) {\n <div\n class=\"provider-item\"\n [class.selected]=\"isSelected(item)\"\n (click)=\"selectAccount(item)\"\n [title]=\"item.account.Description || item.account.Name\"\n >\n <div class=\"provider-icon\">\n <i [class]=\"getProviderIcon(item.provider.Name)\"></i>\n </div>\n <div class=\"provider-info\">\n <div class=\"provider-name\">{{ item.account.Name }}</div>\n <div class=\"provider-meta\">\n <!-- Provider type badge -->\n <span class=\"provider-type-badge\" [title]=\"item.provider.Name\">\n {{ item.provider.Name }}\n </span>\n <!-- Search support badge -->\n @if (item.provider.SupportsSearch) {\n <span class=\"search-badge\" title=\"Supports search\">\n <i class=\"fa-solid fa-magnifying-glass\"></i>\n </span>\n }\n <!-- Credential configured badge -->\n @if (item.account.CredentialID) {\n <span class=\"credential-badge\" title=\"Credentials configured\">\n <i class=\"fa-solid fa-key\"></i>\n </span>\n }\n <!-- No credential warning -->\n @if (!item.account.CredentialID) {\n <span class=\"unconfigured-badge\" title=\"No credentials configured\">\n <i class=\"fa-solid fa-exclamation-triangle\"></i>\n </span>\n }\n </div>\n </div>\n </div>\n }\n </div>\n }\n</div>\n"]}
1
+ {"version":3,"file":"storage-providers-list.component.js","sourceRoot":"","sources":["../../../src/lib/file-browser/storage-providers-list.component.ts","../../../src/lib/file-browser/storage-providers-list.component.html"],"names":[],"mappings":"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","sourcesContent":["import { Component, EventEmitter, OnInit, Output, ChangeDetectorRef, inject } from '@angular/core';\nimport { FileStorageEngineBase, StorageAccountWithProvider, MJFileStorageAccountEntity } from '@memberjunction/core-entities';\nimport { Metadata, type IMetadataProvider } from '@memberjunction/core';\nimport { UUIDsEqual } from '@memberjunction/global';\nimport { StorageAdminTab } from '../admin/storage-admin-dialog.component';\n\n/**\n * Displays a list of organizational file storage accounts.\n * In the enterprise model, accounts are configured by administrators\n * and available to users based on permissions. Users no longer manage\n * their own OAuth connections - credentials are handled at the org level.\n */\n@Component({\n standalone: false,\n selector: 'mj-storage-providers-list',\n templateUrl: './storage-providers-list.component.html',\n styleUrls: ['./storage-providers-list.component.css']\n})\nexport class StorageProvidersListComponent implements OnInit {\n private cdr = inject(ChangeDetectorRef);\n\n /**\n * Emits when an account is selected by the user, or null when no accounts are available.\n * Emits the full account-with-provider object for downstream components to use.\n */\n @Output() accountSelected = new EventEmitter<StorageAccountWithProvider | null>();\n\n /**\n * All available storage accounts with their provider details.\n */\n public accounts: StorageAccountWithProvider[] = [];\n\n /**\n * Currently selected account.\n */\n public selectedAccount: StorageAccountWithProvider | null = null;\n\n /**\n * Loading state indicator.\n */\n public isLoading: boolean = false;\n\n /**\n * Error message if loading fails.\n */\n public errorMessage: string | null = null;\n\n /**\n * Whether current user has admin rights to configure storage accounts or providers.\n */\n public userCanManage: boolean = false;\n\n /**\n * Admin dialog state\n */\n public isManageDialogOpen: boolean = false;\n public adminDialogTab: StorageAdminTab = 'accounts';\n public accountToEdit: MJFileStorageAccountEntity | null = null;\n\n public Provider: IMetadataProvider | null = null;\n\n public get ProviderToUse(): IMetadataProvider {\n return this.Provider ?? Metadata.Provider;\n }\n\n constructor() {}\n\n ngOnInit(): void {\n if (!this.userCanManage) {\n this.checkPermissions();\n }\n this.loadAccounts();\n }\n\n /**\n * Checks if current user has create or update permissions on storage entities.\n */\n private checkPermissions(): void {\n try {\n const md = this.ProviderToUse;\n const user = md.CurrentUser;\n if (!user) {\n return;\n }\n\n const provEntity = md.Entities.find(e => e.Name === 'MJ: File Storage Providers');\n const acctEntity = md.Entities.find(e => e.Name === 'MJ: File Storage Accounts');\n\n const provCanManage = provEntity ? provEntity.GetUserPermisions(user).CanCreate || provEntity.GetUserPermisions(user).CanUpdate : false;\n const acctCanManage = acctEntity ? acctEntity.GetUserPermisions(user).CanCreate || acctEntity.GetUserPermisions(user).CanUpdate : false;\n\n this.userCanManage = !!(provCanManage || acctCanManage);\n } catch {\n this.userCanManage = false;\n }\n }\n\n /**\n * Loads all available file storage accounts with their provider details.\n * Uses FileStorageEngineBase for centralized, cached access.\n */\n private async loadAccounts(forceRefresh = false): Promise<void> {\n this.isLoading = true;\n this.errorMessage = null;\n\n try {\n const engine = FileStorageEngineBase.Instance;\n await engine.Config(forceRefresh);\n\n // Only show accounts whose provider is active\n this.accounts = engine.AccountsWithProviders.filter(a => a.provider.IsActive !== false);\n\n if (this.accounts.length === 0 && !forceRefresh) {\n await engine.Config(true);\n this.accounts = engine.AccountsWithProviders.filter(a => a.provider.IsActive !== false);\n }\n\n console.log('[StorageAccountsList] Loaded accounts:', this.accounts.map(a => ({\n name: a.account.Name,\n provider: a.provider.Name,\n hasCredential: !!a.account.CredentialID\n })));\n\n // Auto-select first account if available\n if (this.accounts.length > 0) {\n this.selectAccount(this.accounts[0]);\n } else {\n this.accountSelected.emit(null);\n }\n\n } catch (error) {\n this.errorMessage = error instanceof Error ? error.message : 'Failed to load storage accounts';\n console.error('[StorageProvidersList] Error loading accounts:', error);\n } finally {\n this.isLoading = false;\n this.cdr.detectChanges();\n }\n }\n\n /**\n * Handles account selection by the user.\n */\n public selectAccount(accountWithProvider: StorageAccountWithProvider): void {\n this.selectedAccount = accountWithProvider;\n this.accountSelected.emit(accountWithProvider);\n }\n\n /**\n * Checks if an account is currently selected.\n */\n public isSelected(accountWithProvider: StorageAccountWithProvider): boolean {\n return UUIDsEqual(this.selectedAccount?.account.ID, accountWithProvider.account.ID);\n }\n\n /**\n * Gets the icon class for a provider based on its name.\n */\n public getProviderIcon(providerName: string): string {\n const name = providerName.toLowerCase();\n\n if (name.includes('aws') || name.includes('s3')) {\n return 'fa-brands fa-aws';\n } else if (name.includes('azure')) {\n return 'fa-brands fa-microsoft';\n } else if (name.includes('google drive')) {\n return 'fa-brands fa-google-drive';\n } else if (name.includes('google cloud')) {\n return 'fa-brands fa-google';\n } else if (name.includes('dropbox')) {\n return 'fa-brands fa-dropbox';\n } else if (name.includes('box')) {\n return 'fa-solid fa-box';\n } else if (name.includes('sharepoint') || name.includes('onedrive')) {\n return 'fa-brands fa-microsoft';\n } else {\n return 'fa-solid fa-cloud';\n }\n }\n\n /**\n * Refreshes the accounts list by forcing a reload from the database.\n */\n public refresh(): void {\n void this.loadAccounts(true);\n }\n\n /**\n * Opens the storage administration dialog\n */\n public openAdminDialog(tab: StorageAdminTab = 'accounts', account?: MJFileStorageAccountEntity | null): void {\n this.adminDialogTab = tab;\n this.accountToEdit = account ?? null;\n this.isManageDialogOpen = true;\n this.cdr.markForCheck();\n }\n\n /**\n * Closes the storage administration dialog\n */\n public closeAdminDialog(): void {\n this.isManageDialogOpen = false;\n this.accountToEdit = null;\n this.cdr.markForCheck();\n }\n\n /**\n * Handles storage configuration changes from the admin dialog\n */\n public onAdminAccountsChanged(): void {\n this.refresh();\n }\n\n /**\n * Handles inline edit click on an account item\n */\n public onEditAccountClick(item: StorageAccountWithProvider, event: MouseEvent): void {\n event.stopPropagation();\n this.openAdminDialog('accounts', item.account);\n }\n}\n","<div class=\"providers-list\">\n <!-- Header -->\n <div class=\"providers-header\">\n <span class=\"header-title\">Storage Accounts</span>\n @if (userCanManage) {\n <div class=\"admin-header-actions\">\n <button\n type=\"button\"\n class=\"admin-action-btn\"\n title=\"Add Storage Account\"\n (click)=\"openAdminDialog('accounts')\">\n <i class=\"fa-solid fa-plus\"></i>\n </button>\n <button\n type=\"button\"\n class=\"admin-action-btn\"\n title=\"Manage Storage Providers & Accounts\"\n (click)=\"openAdminDialog('accounts')\">\n <i class=\"fa-solid fa-gear\"></i>\n </button>\n </div>\n }\n </div>\n\n <!-- Loading State -->\n @if (isLoading) {\n <div class=\"loading-state\">\n <mj-loading text=\"Loading accounts...\"></mj-loading>\n </div>\n }\n\n <!-- Error State -->\n @if (errorMessage && !isLoading) {\n <mj-empty-state\n class=\"storage-providers-empty-fill\"\n Variant=\"error\"\n Icon=\"fa-solid fa-triangle-exclamation\"\n [Title]=\"errorMessage\"\n Size=\"compact\" />\n }\n\n <!-- Empty State -->\n @if (!isLoading && !errorMessage && accounts.length === 0) {\n <mj-empty-state\n class=\"storage-providers-empty-fill\"\n Icon=\"fa-solid fa-folder-plus\"\n [Title]=\"userCanManage ? 'No storage accounts configured' : 'No storage accounts available'\"\n [Message]=\"userCanManage ? 'Configure storage accounts to connect MemberJunction to cloud storage providers.' : 'Contact your administrator to set up file storage accounts.'\"\n Size=\"compact\">\n @if (userCanManage) {\n <button\n type=\"button\"\n class=\"mj-empty-action-btn\"\n (click)=\"openAdminDialog('accounts')\">\n <i class=\"fa-solid fa-plus\"></i>\n <span>Configure Account</span>\n </button>\n }\n </mj-empty-state>\n }\n\n <!-- Account List -->\n @if (!isLoading && !errorMessage && accounts.length > 0) {\n <div class=\"providers-container\">\n @for (item of accounts; track item.account.ID) {\n <div\n class=\"provider-item\"\n [class.selected]=\"isSelected(item)\"\n (click)=\"selectAccount(item)\"\n [title]=\"item.account.Description || item.account.Name\">\n <div class=\"provider-icon\">\n <i [class]=\"getProviderIcon(item.provider.Name)\"></i>\n </div>\n <div class=\"provider-info\">\n <div class=\"provider-name\">{{ item.account.Name }}</div>\n <div class=\"provider-meta\">\n <!-- Provider type badge -->\n <span class=\"provider-type-badge\" [title]=\"item.provider.Name\">\n {{ item.provider.Name }}\n </span>\n <!-- Search support badge -->\n @if (item.provider.SupportsSearch) {\n <span class=\"search-badge\" title=\"Supports search\">\n <i class=\"fa-solid fa-magnifying-glass\"></i>\n </span>\n }\n <!-- Credential configured badge -->\n @if (item.account.CredentialID) {\n <span class=\"credential-badge\" title=\"Credentials configured\">\n <i class=\"fa-solid fa-key\"></i>\n </span>\n }\n <!-- No credential warning -->\n @if (!item.account.CredentialID) {\n <span class=\"unconfigured-badge\" title=\"No credentials configured\">\n <i class=\"fa-solid fa-exclamation-triangle\"></i>\n </span>\n }\n </div>\n </div>\n @if (userCanManage) {\n <button\n type=\"button\"\n class=\"provider-edit-btn\"\n title=\"Edit Storage Account\"\n (click)=\"onEditAccountClick(item, $event)\">\n <i class=\"fa-solid fa-pen-to-square\"></i>\n </button>\n }\n </div>\n }\n </div>\n }\n</div>\n\n<!-- Storage Administration Dialog -->\n@if (isManageDialogOpen) {\n <mj-storage-admin-dialog\n [Visible]=\"isManageDialogOpen\"\n [ActiveTab]=\"adminDialogTab\"\n (Close)=\"closeAdminDialog()\"\n (AccountsChanged)=\"onAdminAccountsChanged()\">\n </mj-storage-admin-dialog>\n}\n"]}
@@ -8,15 +8,18 @@ import * as i6 from "./file-browser/file-browser-resource.component";
8
8
  import * as i7 from "./file-browser/storage-providers-list.component";
9
9
  import * as i8 from "./file-browser/folder-tree.component";
10
10
  import * as i9 from "./file-browser/file-grid.component";
11
- import * as i10 from "@angular/common";
12
- import * as i11 from "@angular/forms";
13
- import * as i12 from "ag-grid-angular";
14
- import * as i13 from "@memberjunction/ng-container-directives";
15
- import * as i14 from "@memberjunction/ng-shared-generic";
16
- import * as i15 from "@memberjunction/ng-ui-components";
11
+ import * as i10 from "./record-attachments/record-attachments.component";
12
+ import * as i11 from "./admin/storage-admin-dialog.component";
13
+ import * as i12 from "@angular/common";
14
+ import * as i13 from "@angular/forms";
15
+ import * as i14 from "ag-grid-angular";
16
+ import * as i15 from "@memberjunction/ng-container-directives";
17
+ import * as i16 from "@memberjunction/ng-shared-generic";
18
+ import * as i17 from "@memberjunction/ng-credentials";
19
+ import * as i18 from "@memberjunction/ng-ui-components";
17
20
  export declare class FileStorageModule {
18
21
  static ɵfac: i0.ɵɵFactoryDeclaration<FileStorageModule, never>;
19
- static ɵmod: i0.ɵɵNgModuleDeclaration<FileStorageModule, [typeof i1.CategoryTreeComponent, typeof i2.FilesGridComponent, typeof i3.FileUploadComponent, typeof i4.FileBrowserComponent, typeof i5.FileBrowserDemoComponent, typeof i6.FileBrowserResource, typeof i7.StorageProvidersListComponent, typeof i8.FolderTreeComponent, typeof i9.FileGridComponent], [typeof i10.CommonModule, typeof i11.FormsModule, typeof i12.AgGridModule, typeof i13.ContainerDirectivesModule, typeof i14.SharedGenericModule, typeof i15.MJButtonDirective, typeof i15.MJDialogComponent, typeof i15.MJDialogActionsComponent, typeof i15.MJEmptyStateComponent, typeof i15.MJPageHeaderComponent, typeof i15.MJPageLayoutComponent], [typeof i1.CategoryTreeComponent, typeof i2.FilesGridComponent, typeof i3.FileUploadComponent, typeof i4.FileBrowserComponent, typeof i5.FileBrowserDemoComponent, typeof i6.FileBrowserResource]>;
22
+ static ɵmod: i0.ɵɵNgModuleDeclaration<FileStorageModule, [typeof i1.CategoryTreeComponent, typeof i2.FilesGridComponent, typeof i3.FileUploadComponent, typeof i4.FileBrowserComponent, typeof i5.FileBrowserDemoComponent, typeof i6.FileBrowserResource, typeof i7.StorageProvidersListComponent, typeof i8.FolderTreeComponent, typeof i9.FileGridComponent, typeof i10.RecordAttachmentsComponent, typeof i11.StorageAdminDialogComponent], [typeof i12.CommonModule, typeof i13.FormsModule, typeof i14.AgGridModule, typeof i15.ContainerDirectivesModule, typeof i16.SharedGenericModule, typeof i17.CredentialsModule, typeof i18.MJButtonDirective, typeof i18.MJDialogComponent, typeof i18.MJDialogActionsComponent, typeof i18.MJEmptyStateComponent, typeof i18.MJAlertComponent, typeof i18.MJPageHeaderComponent, typeof i18.MJPageLayoutComponent, typeof i18.MjSlidePanelComponent], [typeof i1.CategoryTreeComponent, typeof i2.FilesGridComponent, typeof i3.FileUploadComponent, typeof i4.FileBrowserComponent, typeof i5.FileBrowserDemoComponent, typeof i6.FileBrowserResource, typeof i10.RecordAttachmentsComponent, typeof i11.StorageAdminDialogComponent]>;
20
23
  static ɵinj: i0.ɵɵInjectorDeclaration<FileStorageModule>;
21
24
  }
22
25
  //# sourceMappingURL=module.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"module.d.ts","sourceRoot":"","sources":["../../src/lib/module.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAyBA,qBAkCa,iBAAiB;yCAAjB,iBAAiB;0CAAjB,iBAAiB;0CAAjB,iBAAiB;CAAG"}
1
+ {"version":3,"file":"module.d.ts","sourceRoot":"","sources":["../../src/lib/module.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;AA8BA,qBAyCa,iBAAiB;yCAAjB,iBAAiB;0CAAjB,iBAAiB;0CAAjB,iBAAiB;CAAG"}