@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.
- package/LICENSE +180 -4
- package/README.md +60 -15
- package/dist/lib/admin/storage-admin-dialog.component.d.ts +63 -0
- package/dist/lib/admin/storage-admin-dialog.component.d.ts.map +1 -0
- package/dist/lib/admin/storage-admin-dialog.component.js +917 -0
- package/dist/lib/admin/storage-admin-dialog.component.js.map +1 -0
- package/dist/lib/file-browser/file-browser-resource.component.d.ts.map +1 -1
- package/dist/lib/file-browser/file-browser-resource.component.js +11 -21
- package/dist/lib/file-browser/file-browser-resource.component.js.map +1 -1
- package/dist/lib/file-browser/file-browser.component.js +2 -2
- package/dist/lib/file-browser/file-grid.component.d.ts +63 -3
- package/dist/lib/file-browser/file-grid.component.d.ts.map +1 -1
- package/dist/lib/file-browser/file-grid.component.js +1088 -735
- package/dist/lib/file-browser/file-grid.component.js.map +1 -1
- package/dist/lib/file-browser/folder-tree.component.js +32 -30
- package/dist/lib/file-browser/folder-tree.component.js.map +1 -1
- package/dist/lib/file-browser/storage-providers-list.component.d.ts +37 -3
- package/dist/lib/file-browser/storage-providers-list.component.d.ts.map +1 -1
- package/dist/lib/file-browser/storage-providers-list.component.js +191 -65
- package/dist/lib/file-browser/storage-providers-list.component.js.map +1 -1
- package/dist/lib/module.d.ts +10 -7
- package/dist/lib/module.d.ts.map +1 -1
- package/dist/lib/module.js +27 -7
- package/dist/lib/module.js.map +1 -1
- package/dist/lib/record-attachments/record-attachments.component.d.ts +203 -0
- package/dist/lib/record-attachments/record-attachments.component.d.ts.map +1 -0
- package/dist/lib/record-attachments/record-attachments.component.js +2270 -0
- package/dist/lib/record-attachments/record-attachments.component.js.map +1 -0
- package/dist/lib/record-attachments/record-attachments.events.d.ts +124 -0
- package/dist/lib/record-attachments/record-attachments.events.d.ts.map +1 -0
- package/dist/lib/record-attachments/record-attachments.events.js +178 -0
- package/dist/lib/record-attachments/record-attachments.events.js.map +1 -0
- package/dist/lib/record-attachments/record-attachments.types.d.ts +90 -0
- package/dist/lib/record-attachments/record-attachments.types.d.ts.map +1 -0
- package/dist/lib/record-attachments/record-attachments.types.js +57 -0
- package/dist/lib/record-attachments/record-attachments.types.js.map +1 -0
- package/dist/public-api.d.ts +4 -0
- package/dist/public-api.d.ts.map +1 -1
- package/dist/public-api.js +4 -0
- package/dist/public-api.js.map +1 -1
- package/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.ɵɵ
|
|
9
|
-
i0.ɵɵ
|
|
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.ɵɵ
|
|
14
|
-
|
|
15
|
-
|
|
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
|
|
22
|
-
i0.ɵɵelementStart(0, "span",
|
|
23
|
-
i0.ɵɵelement(1, "i",
|
|
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
|
|
27
|
-
i0.ɵɵelementStart(0, "span",
|
|
28
|
-
i0.ɵɵelement(1, "i",
|
|
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
|
|
32
|
-
i0.ɵɵelementStart(0, "span",
|
|
33
|
-
i0.ɵɵelement(1, "i",
|
|
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
|
|
37
|
-
const
|
|
38
|
-
i0.ɵɵelementStart(0, "
|
|
39
|
-
i0.ɵɵlistener("click", function
|
|
40
|
-
i0.ɵɵ
|
|
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",
|
|
80
|
+
i0.ɵɵelementStart(3, "div", 19)(4, "div", 20);
|
|
44
81
|
i0.ɵɵtext(5);
|
|
45
82
|
i0.ɵɵelementEnd();
|
|
46
|
-
i0.ɵɵelementStart(6, "div",
|
|
83
|
+
i0.ɵɵelementStart(6, "div", 21)(7, "span", 22);
|
|
47
84
|
i0.ɵɵtext(8);
|
|
48
85
|
i0.ɵɵelementEnd();
|
|
49
|
-
i0.ɵɵconditionalCreate(9,
|
|
50
|
-
i0.ɵɵconditionalCreate(10,
|
|
51
|
-
i0.ɵɵconditionalCreate(11,
|
|
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
|
|
55
|
-
const
|
|
56
|
-
i0.ɵɵclassProp("selected",
|
|
57
|
-
i0.ɵɵproperty("title",
|
|
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(
|
|
98
|
+
i0.ɵɵclassMap(ctx_r1.getProviderIcon(item_r5.provider.Name));
|
|
60
99
|
i0.ɵɵadvance(3);
|
|
61
|
-
i0.ɵɵtextInterpolate(
|
|
100
|
+
i0.ɵɵtextInterpolate(item_r5.account.Name);
|
|
62
101
|
i0.ɵɵadvance(2);
|
|
63
|
-
i0.ɵɵproperty("title",
|
|
102
|
+
i0.ɵɵproperty("title", item_r5.provider.Name);
|
|
64
103
|
i0.ɵɵadvance();
|
|
65
|
-
i0.ɵɵtextInterpolate1(" ",
|
|
104
|
+
i0.ɵɵtextInterpolate1(" ", item_r5.provider.Name, " ");
|
|
66
105
|
i0.ɵɵadvance();
|
|
67
|
-
i0.ɵɵconditional(
|
|
106
|
+
i0.ɵɵconditional(item_r5.provider.SupportsSearch ? 9 : -1);
|
|
68
107
|
i0.ɵɵadvance();
|
|
69
|
-
i0.ɵɵconditional(
|
|
108
|
+
i0.ɵɵconditional(item_r5.account.CredentialID ? 10 : -1);
|
|
70
109
|
i0.ɵɵadvance();
|
|
71
|
-
i0.ɵɵconditional(!
|
|
110
|
+
i0.ɵɵconditional(!item_r5.account.CredentialID ? 11 : -1);
|
|
111
|
+
i0.ɵɵadvance();
|
|
112
|
+
i0.ɵɵconditional(ctx_r1.userCanManage ? 12 : -1);
|
|
72
113
|
} }
|
|
73
|
-
function
|
|
74
|
-
i0.ɵɵelementStart(0, "div",
|
|
75
|
-
i0.ɵɵrepeaterCreate(1,
|
|
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
|
|
119
|
+
const ctx_r1 = i0.ɵɵnextContext();
|
|
79
120
|
i0.ɵɵadvance();
|
|
80
|
-
i0.ɵɵrepeater(
|
|
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
|
-
|
|
112
|
-
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
201
|
-
|
|
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.
|
|
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.
|
|
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
|
|
342
|
+
i0.ɵɵconditional(!ctx.isLoading && !ctx.errorMessage && ctx.accounts.length > 0 ? 8 : -1);
|
|
217
343
|
i0.ɵɵadvance();
|
|
218
|
-
i0.ɵɵconditional(
|
|
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
|
-
}], () => [
|
|
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:
|
|
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"]}
|
package/dist/lib/module.d.ts
CHANGED
|
@@ -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 "
|
|
12
|
-
import * as i11 from "
|
|
13
|
-
import * as i12 from "
|
|
14
|
-
import * as i13 from "@
|
|
15
|
-
import * as i14 from "
|
|
16
|
-
import * as i15 from "@memberjunction/ng-
|
|
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
|
|
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
|
package/dist/lib/module.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"module.d.ts","sourceRoot":"","sources":["../../src/lib/module.ts"],"names":[],"mappings":"
|
|
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"}
|