@memberjunction/ng-resource-permissions 5.29.0 → 5.30.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/lib/mj-resource-permission-share-adapter.d.ts +23 -0
- package/dist/lib/mj-resource-permission-share-adapter.d.ts.map +1 -0
- package/dist/lib/mj-resource-permission-share-adapter.js +81 -0
- package/dist/lib/mj-resource-permission-share-adapter.js.map +1 -0
- package/dist/lib/resource-permissions.component.js +4 -4
- package/dist/lib/resource-permissions.component.js.map +1 -1
- package/dist/lib/resource-share-adapter.d.ts +78 -0
- package/dist/lib/resource-share-adapter.d.ts.map +1 -0
- package/dist/lib/resource-share-adapter.js +6 -0
- package/dist/lib/resource-share-adapter.js.map +1 -0
- package/dist/lib/resource-share-dialog.component.d.ts +51 -0
- package/dist/lib/resource-share-dialog.component.d.ts.map +1 -0
- package/dist/lib/resource-share-dialog.component.js +458 -0
- package/dist/lib/resource-share-dialog.component.js.map +1 -0
- package/dist/lib/user-sharing-center.component.d.ts +105 -0
- package/dist/lib/user-sharing-center.component.d.ts.map +1 -0
- package/dist/lib/user-sharing-center.component.js +536 -0
- package/dist/lib/user-sharing-center.component.js.map +1 -0
- package/dist/module.d.ts +9 -8
- package/dist/module.d.ts.map +1 -1
- package/dist/module.js +19 -8
- package/dist/module.js.map +1 -1
- 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 +10 -10
|
@@ -0,0 +1,458 @@
|
|
|
1
|
+
import { Component, Input, Output, EventEmitter, ViewEncapsulation } from '@angular/core';
|
|
2
|
+
import { RunView } from '@memberjunction/core';
|
|
3
|
+
import { UUIDsEqual } from '@memberjunction/global';
|
|
4
|
+
import { RESOURCE_SHARE_LEVELS } from './resource-share-adapter';
|
|
5
|
+
import * as i0 from "@angular/core";
|
|
6
|
+
import * as i1 from "@angular/forms";
|
|
7
|
+
import * as i2 from "@memberjunction/ng-ui-components";
|
|
8
|
+
const _forTrack0 = ($index, $item) => $item.User.ID;
|
|
9
|
+
const _forTrack1 = ($index, $item) => $item.ID;
|
|
10
|
+
function GenericShareDialogComponent_Conditional_0_Conditional_7_Template(rf, ctx) { if (rf & 1) {
|
|
11
|
+
i0.ɵɵelementStart(0, "div", 5);
|
|
12
|
+
i0.ɵɵelement(1, "i", 22);
|
|
13
|
+
i0.ɵɵelementStart(2, "span");
|
|
14
|
+
i0.ɵɵtext(3);
|
|
15
|
+
i0.ɵɵelementEnd()();
|
|
16
|
+
} if (rf & 2) {
|
|
17
|
+
const ctx_r1 = i0.ɵɵnextContext(2);
|
|
18
|
+
i0.ɵɵadvance(3);
|
|
19
|
+
i0.ɵɵtextInterpolate(ctx_r1.Error);
|
|
20
|
+
} }
|
|
21
|
+
function GenericShareDialogComponent_Conditional_0_Conditional_12_For_2_Conditional_5_Template(rf, ctx) { if (rf & 1) {
|
|
22
|
+
i0.ɵɵelementStart(0, "span", 27);
|
|
23
|
+
i0.ɵɵtext(1);
|
|
24
|
+
i0.ɵɵelementEnd();
|
|
25
|
+
} if (rf & 2) {
|
|
26
|
+
const user_r4 = i0.ɵɵnextContext().$implicit;
|
|
27
|
+
i0.ɵɵadvance();
|
|
28
|
+
i0.ɵɵtextInterpolate(user_r4.Email);
|
|
29
|
+
} }
|
|
30
|
+
function GenericShareDialogComponent_Conditional_0_Conditional_12_For_2_Template(rf, ctx) { if (rf & 1) {
|
|
31
|
+
const _r3 = i0.ɵɵgetCurrentView();
|
|
32
|
+
i0.ɵɵelementStart(0, "button", 24);
|
|
33
|
+
i0.ɵɵlistener("click", function GenericShareDialogComponent_Conditional_0_Conditional_12_For_2_Template_button_click_0_listener() { const user_r4 = i0.ɵɵrestoreView(_r3).$implicit; const ctx_r1 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r1.addUserShare(user_r4)); });
|
|
34
|
+
i0.ɵɵelementStart(1, "span", 25);
|
|
35
|
+
i0.ɵɵtext(2);
|
|
36
|
+
i0.ɵɵelementEnd();
|
|
37
|
+
i0.ɵɵelementStart(3, "span", 26);
|
|
38
|
+
i0.ɵɵtext(4);
|
|
39
|
+
i0.ɵɵelementEnd();
|
|
40
|
+
i0.ɵɵconditionalCreate(5, GenericShareDialogComponent_Conditional_0_Conditional_12_For_2_Conditional_5_Template, 2, 1, "span", 27);
|
|
41
|
+
i0.ɵɵelement(6, "i", 28);
|
|
42
|
+
i0.ɵɵelementEnd();
|
|
43
|
+
} if (rf & 2) {
|
|
44
|
+
const user_r4 = ctx.$implicit;
|
|
45
|
+
const ctx_r1 = i0.ɵɵnextContext(3);
|
|
46
|
+
i0.ɵɵproperty("disabled", ctx_r1.IsLoading);
|
|
47
|
+
i0.ɵɵadvance(2);
|
|
48
|
+
i0.ɵɵtextInterpolate(ctx_r1.getUserInitials(user_r4));
|
|
49
|
+
i0.ɵɵadvance(2);
|
|
50
|
+
i0.ɵɵtextInterpolate(user_r4.Name);
|
|
51
|
+
i0.ɵɵadvance();
|
|
52
|
+
i0.ɵɵconditional(user_r4.Email ? 5 : -1);
|
|
53
|
+
} }
|
|
54
|
+
function GenericShareDialogComponent_Conditional_0_Conditional_12_Template(rf, ctx) { if (rf & 1) {
|
|
55
|
+
i0.ɵɵelementStart(0, "div", 10);
|
|
56
|
+
i0.ɵɵrepeaterCreate(1, GenericShareDialogComponent_Conditional_0_Conditional_12_For_2_Template, 7, 4, "button", 23, _forTrack1);
|
|
57
|
+
i0.ɵɵelementEnd();
|
|
58
|
+
} if (rf & 2) {
|
|
59
|
+
const ctx_r1 = i0.ɵɵnextContext(2);
|
|
60
|
+
i0.ɵɵadvance();
|
|
61
|
+
i0.ɵɵrepeater(ctx_r1.FilteredAvailableUsers);
|
|
62
|
+
} }
|
|
63
|
+
function GenericShareDialogComponent_Conditional_0_Conditional_13_Template(rf, ctx) { if (rf & 1) {
|
|
64
|
+
i0.ɵɵelementStart(0, "div", 11);
|
|
65
|
+
i0.ɵɵtext(1);
|
|
66
|
+
i0.ɵɵelementEnd();
|
|
67
|
+
} if (rf & 2) {
|
|
68
|
+
const ctx_r1 = i0.ɵɵnextContext(2);
|
|
69
|
+
i0.ɵɵadvance();
|
|
70
|
+
i0.ɵɵtextInterpolate1(" No users found matching \"", ctx_r1.UserSearchFilter, "\" ");
|
|
71
|
+
} }
|
|
72
|
+
function GenericShareDialogComponent_Conditional_0_Conditional_17_Template(rf, ctx) { if (rf & 1) {
|
|
73
|
+
i0.ɵɵelementStart(0, "div", 13)(1, "span", 29);
|
|
74
|
+
i0.ɵɵelement(2, "i", 30);
|
|
75
|
+
i0.ɵɵelementEnd();
|
|
76
|
+
i0.ɵɵelementStart(3, "div", 31)(4, "span", 32);
|
|
77
|
+
i0.ɵɵtext(5);
|
|
78
|
+
i0.ɵɵelementEnd();
|
|
79
|
+
i0.ɵɵelementStart(6, "span", 33);
|
|
80
|
+
i0.ɵɵtext(7, "Owner");
|
|
81
|
+
i0.ɵɵelementEnd()()();
|
|
82
|
+
} if (rf & 2) {
|
|
83
|
+
const ctx_r1 = i0.ɵɵnextContext(2);
|
|
84
|
+
i0.ɵɵadvance(5);
|
|
85
|
+
i0.ɵɵtextInterpolate(ctx_r1.Context == null ? null : ctx_r1.Context.OwnerDisplayName);
|
|
86
|
+
} }
|
|
87
|
+
function GenericShareDialogComponent_Conditional_0_For_19_Conditional_6_Template(rf, ctx) { if (rf & 1) {
|
|
88
|
+
i0.ɵɵelementStart(0, "span", 35);
|
|
89
|
+
i0.ɵɵtext(1, "New");
|
|
90
|
+
i0.ɵɵelementEnd();
|
|
91
|
+
} }
|
|
92
|
+
function GenericShareDialogComponent_Conditional_0_For_19_Conditional_7_Template(rf, ctx) { if (rf & 1) {
|
|
93
|
+
i0.ɵɵelementStart(0, "span", 36);
|
|
94
|
+
i0.ɵɵtext(1);
|
|
95
|
+
i0.ɵɵelementEnd();
|
|
96
|
+
} if (rf & 2) {
|
|
97
|
+
const share_r6 = i0.ɵɵnextContext().$implicit;
|
|
98
|
+
i0.ɵɵadvance();
|
|
99
|
+
i0.ɵɵtextInterpolate(share_r6.User.Email);
|
|
100
|
+
} }
|
|
101
|
+
function GenericShareDialogComponent_Conditional_0_For_19_For_10_Template(rf, ctx) { if (rf & 1) {
|
|
102
|
+
const _r7 = i0.ɵɵgetCurrentView();
|
|
103
|
+
i0.ɵɵelementStart(0, "button", 41);
|
|
104
|
+
i0.ɵɵlistener("click", function GenericShareDialogComponent_Conditional_0_For_19_For_10_Template_button_click_0_listener() { const level_r8 = i0.ɵɵrestoreView(_r7).$implicit; const share_r6 = i0.ɵɵnextContext().$implicit; const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.setLevel(share_r6, level_r8)); });
|
|
105
|
+
i0.ɵɵtext(1);
|
|
106
|
+
i0.ɵɵelementEnd();
|
|
107
|
+
} if (rf & 2) {
|
|
108
|
+
const level_r8 = ctx.$implicit;
|
|
109
|
+
const share_r6 = i0.ɵɵnextContext().$implicit;
|
|
110
|
+
i0.ɵɵclassProp("active", share_r6.Level === level_r8);
|
|
111
|
+
i0.ɵɵproperty("title", level_r8 + " access");
|
|
112
|
+
i0.ɵɵadvance();
|
|
113
|
+
i0.ɵɵtextInterpolate1(" ", level_r8, " ");
|
|
114
|
+
} }
|
|
115
|
+
function GenericShareDialogComponent_Conditional_0_For_19_Template(rf, ctx) { if (rf & 1) {
|
|
116
|
+
const _r5 = i0.ɵɵgetCurrentView();
|
|
117
|
+
i0.ɵɵelementStart(0, "div", 34)(1, "span", 25);
|
|
118
|
+
i0.ɵɵtext(2);
|
|
119
|
+
i0.ɵɵelementEnd();
|
|
120
|
+
i0.ɵɵelementStart(3, "div", 31)(4, "span", 32);
|
|
121
|
+
i0.ɵɵtext(5);
|
|
122
|
+
i0.ɵɵconditionalCreate(6, GenericShareDialogComponent_Conditional_0_For_19_Conditional_6_Template, 2, 0, "span", 35);
|
|
123
|
+
i0.ɵɵelementEnd();
|
|
124
|
+
i0.ɵɵconditionalCreate(7, GenericShareDialogComponent_Conditional_0_For_19_Conditional_7_Template, 2, 1, "span", 36);
|
|
125
|
+
i0.ɵɵelementEnd();
|
|
126
|
+
i0.ɵɵelementStart(8, "div", 37);
|
|
127
|
+
i0.ɵɵrepeaterCreate(9, GenericShareDialogComponent_Conditional_0_For_19_For_10_Template, 2, 4, "button", 38, i0.ɵɵrepeaterTrackByIdentity);
|
|
128
|
+
i0.ɵɵelementEnd();
|
|
129
|
+
i0.ɵɵelementStart(11, "button", 39);
|
|
130
|
+
i0.ɵɵlistener("click", function GenericShareDialogComponent_Conditional_0_For_19_Template_button_click_11_listener() { const share_r6 = i0.ɵɵrestoreView(_r5).$implicit; const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.removeUserShare(share_r6)); });
|
|
131
|
+
i0.ɵɵelement(12, "i", 40);
|
|
132
|
+
i0.ɵɵelementEnd()();
|
|
133
|
+
} if (rf & 2) {
|
|
134
|
+
const share_r6 = ctx.$implicit;
|
|
135
|
+
const ctx_r1 = i0.ɵɵnextContext(2);
|
|
136
|
+
i0.ɵɵclassProp("share-person-new", share_r6.IsNew)("share-person-modified", ctx_r1.isModified(share_r6));
|
|
137
|
+
i0.ɵɵadvance(2);
|
|
138
|
+
i0.ɵɵtextInterpolate(ctx_r1.getUserInitials(share_r6.User));
|
|
139
|
+
i0.ɵɵadvance(3);
|
|
140
|
+
i0.ɵɵtextInterpolate1(" ", share_r6.User.Name, " ");
|
|
141
|
+
i0.ɵɵadvance();
|
|
142
|
+
i0.ɵɵconditional(share_r6.IsNew ? 6 : -1);
|
|
143
|
+
i0.ɵɵadvance();
|
|
144
|
+
i0.ɵɵconditional(share_r6.User.Email ? 7 : -1);
|
|
145
|
+
i0.ɵɵadvance(2);
|
|
146
|
+
i0.ɵɵrepeater(ctx_r1.Levels);
|
|
147
|
+
} }
|
|
148
|
+
function GenericShareDialogComponent_Conditional_0_Conditional_20_Template(rf, ctx) { if (rf & 1) {
|
|
149
|
+
i0.ɵɵelementStart(0, "div", 15);
|
|
150
|
+
i0.ɵɵelement(1, "i", 42);
|
|
151
|
+
i0.ɵɵelementStart(2, "span");
|
|
152
|
+
i0.ɵɵtext(3, "Not shared with anyone yet");
|
|
153
|
+
i0.ɵɵelementEnd()();
|
|
154
|
+
} }
|
|
155
|
+
function GenericShareDialogComponent_Conditional_0_Conditional_21_For_2_Template(rf, ctx) { if (rf & 1) {
|
|
156
|
+
const _r9 = i0.ɵɵgetCurrentView();
|
|
157
|
+
i0.ɵɵelementStart(0, "div", 43)(1, "span", 44);
|
|
158
|
+
i0.ɵɵtext(2);
|
|
159
|
+
i0.ɵɵelementEnd();
|
|
160
|
+
i0.ɵɵelementStart(3, "span", 45);
|
|
161
|
+
i0.ɵɵtext(4);
|
|
162
|
+
i0.ɵɵelementEnd();
|
|
163
|
+
i0.ɵɵelementStart(5, "button", 46);
|
|
164
|
+
i0.ɵɵlistener("click", function GenericShareDialogComponent_Conditional_0_Conditional_21_For_2_Template_button_click_5_listener() { const share_r10 = i0.ɵɵrestoreView(_r9).$implicit; const ctx_r1 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r1.undoRemove(share_r10)); });
|
|
165
|
+
i0.ɵɵelement(6, "i", 47);
|
|
166
|
+
i0.ɵɵtext(7, " Undo ");
|
|
167
|
+
i0.ɵɵelementEnd()();
|
|
168
|
+
} if (rf & 2) {
|
|
169
|
+
const share_r10 = ctx.$implicit;
|
|
170
|
+
const ctx_r1 = i0.ɵɵnextContext(3);
|
|
171
|
+
i0.ɵɵadvance(2);
|
|
172
|
+
i0.ɵɵtextInterpolate(ctx_r1.getUserInitials(share_r10.User));
|
|
173
|
+
i0.ɵɵadvance(2);
|
|
174
|
+
i0.ɵɵtextInterpolate(share_r10.User.Name);
|
|
175
|
+
} }
|
|
176
|
+
function GenericShareDialogComponent_Conditional_0_Conditional_21_Template(rf, ctx) { if (rf & 1) {
|
|
177
|
+
i0.ɵɵelementStart(0, "div", 16);
|
|
178
|
+
i0.ɵɵrepeaterCreate(1, GenericShareDialogComponent_Conditional_0_Conditional_21_For_2_Template, 8, 2, "div", 43, _forTrack0);
|
|
179
|
+
i0.ɵɵelementEnd();
|
|
180
|
+
} if (rf & 2) {
|
|
181
|
+
const ctx_r1 = i0.ɵɵnextContext(2);
|
|
182
|
+
i0.ɵɵadvance();
|
|
183
|
+
i0.ɵɵrepeater(ctx_r1.RemovedShares);
|
|
184
|
+
} }
|
|
185
|
+
function GenericShareDialogComponent_Conditional_0_Conditional_23_Template(rf, ctx) { if (rf & 1) {
|
|
186
|
+
i0.ɵɵelementStart(0, "span", 18);
|
|
187
|
+
i0.ɵɵelement(1, "i", 48);
|
|
188
|
+
i0.ɵɵtext(2, " Unsaved changes ");
|
|
189
|
+
i0.ɵɵelementEnd();
|
|
190
|
+
} }
|
|
191
|
+
function GenericShareDialogComponent_Conditional_0_Conditional_28_Template(rf, ctx) { if (rf & 1) {
|
|
192
|
+
i0.ɵɵelement(0, "i", 49);
|
|
193
|
+
i0.ɵɵtext(1, " Saving... ");
|
|
194
|
+
} }
|
|
195
|
+
function GenericShareDialogComponent_Conditional_0_Conditional_29_Template(rf, ctx) { if (rf & 1) {
|
|
196
|
+
i0.ɵɵtext(0, " Save ");
|
|
197
|
+
} }
|
|
198
|
+
function GenericShareDialogComponent_Conditional_0_Template(rf, ctx) { if (rf & 1) {
|
|
199
|
+
const _r1 = i0.ɵɵgetCurrentView();
|
|
200
|
+
i0.ɵɵelementStart(0, "mj-window", 1);
|
|
201
|
+
i0.ɵɵlistener("Close", function GenericShareDialogComponent_Conditional_0_Template_mj_window_Close_0_listener() { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.onCancel()); });
|
|
202
|
+
i0.ɵɵelementStart(1, "mj-window-titlebar")(2, "div", 2);
|
|
203
|
+
i0.ɵɵelement(3, "i", 3);
|
|
204
|
+
i0.ɵɵelementStart(4, "span");
|
|
205
|
+
i0.ɵɵtext(5);
|
|
206
|
+
i0.ɵɵelementEnd()()();
|
|
207
|
+
i0.ɵɵelementStart(6, "div", 4);
|
|
208
|
+
i0.ɵɵconditionalCreate(7, GenericShareDialogComponent_Conditional_0_Conditional_7_Template, 4, 1, "div", 5);
|
|
209
|
+
i0.ɵɵelementStart(8, "div", 6)(9, "div", 7);
|
|
210
|
+
i0.ɵɵelement(10, "i", 8);
|
|
211
|
+
i0.ɵɵelementStart(11, "input", 9);
|
|
212
|
+
i0.ɵɵtwoWayListener("ngModelChange", function GenericShareDialogComponent_Conditional_0_Template_input_ngModelChange_11_listener($event) { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); i0.ɵɵtwoWayBindingSet(ctx_r1.UserSearchFilter, $event) || (ctx_r1.UserSearchFilter = $event); return i0.ɵɵresetView($event); });
|
|
213
|
+
i0.ɵɵelementEnd()();
|
|
214
|
+
i0.ɵɵconditionalCreate(12, GenericShareDialogComponent_Conditional_0_Conditional_12_Template, 3, 0, "div", 10);
|
|
215
|
+
i0.ɵɵconditionalCreate(13, GenericShareDialogComponent_Conditional_0_Conditional_13_Template, 2, 1, "div", 11);
|
|
216
|
+
i0.ɵɵelementEnd();
|
|
217
|
+
i0.ɵɵelementStart(14, "div", 6)(15, "div", 12);
|
|
218
|
+
i0.ɵɵtext(16, "People with access");
|
|
219
|
+
i0.ɵɵelementEnd();
|
|
220
|
+
i0.ɵɵconditionalCreate(17, GenericShareDialogComponent_Conditional_0_Conditional_17_Template, 8, 1, "div", 13);
|
|
221
|
+
i0.ɵɵrepeaterCreate(18, GenericShareDialogComponent_Conditional_0_For_19_Template, 13, 8, "div", 14, _forTrack0);
|
|
222
|
+
i0.ɵɵconditionalCreate(20, GenericShareDialogComponent_Conditional_0_Conditional_20_Template, 4, 0, "div", 15);
|
|
223
|
+
i0.ɵɵconditionalCreate(21, GenericShareDialogComponent_Conditional_0_Conditional_21_Template, 3, 0, "div", 16);
|
|
224
|
+
i0.ɵɵelementEnd();
|
|
225
|
+
i0.ɵɵelementStart(22, "div", 17);
|
|
226
|
+
i0.ɵɵconditionalCreate(23, GenericShareDialogComponent_Conditional_0_Conditional_23_Template, 3, 0, "span", 18);
|
|
227
|
+
i0.ɵɵelementStart(24, "div", 19)(25, "button", 20);
|
|
228
|
+
i0.ɵɵlistener("click", function GenericShareDialogComponent_Conditional_0_Template_button_click_25_listener() { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.onCancel()); });
|
|
229
|
+
i0.ɵɵtext(26, " Cancel ");
|
|
230
|
+
i0.ɵɵelementEnd();
|
|
231
|
+
i0.ɵɵelementStart(27, "button", 21);
|
|
232
|
+
i0.ɵɵlistener("click", function GenericShareDialogComponent_Conditional_0_Template_button_click_27_listener() { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.onSave()); });
|
|
233
|
+
i0.ɵɵconditionalCreate(28, GenericShareDialogComponent_Conditional_0_Conditional_28_Template, 2, 0)(29, GenericShareDialogComponent_Conditional_0_Conditional_29_Template, 1, 0);
|
|
234
|
+
i0.ɵɵelementEnd()()()()();
|
|
235
|
+
} if (rf & 2) {
|
|
236
|
+
const ctx_r1 = i0.ɵɵnextContext();
|
|
237
|
+
i0.ɵɵproperty("Visible", true)("Width", 560)("MinHeight", 200)("Resizable", false)("Draggable", true);
|
|
238
|
+
i0.ɵɵadvance(5);
|
|
239
|
+
i0.ɵɵtextInterpolate1("Share \"", ctx_r1.Context == null ? null : ctx_r1.Context.ResourceName, "\"");
|
|
240
|
+
i0.ɵɵadvance(2);
|
|
241
|
+
i0.ɵɵconditional(ctx_r1.Error ? 7 : -1);
|
|
242
|
+
i0.ɵɵadvance(4);
|
|
243
|
+
i0.ɵɵtwoWayProperty("ngModel", ctx_r1.UserSearchFilter);
|
|
244
|
+
i0.ɵɵproperty("disabled", ctx_r1.IsLoading);
|
|
245
|
+
i0.ɵɵadvance();
|
|
246
|
+
i0.ɵɵconditional(ctx_r1.FilteredAvailableUsers.length > 0 ? 12 : -1);
|
|
247
|
+
i0.ɵɵadvance();
|
|
248
|
+
i0.ɵɵconditional(ctx_r1.UserSearchFilter && ctx_r1.FilteredAvailableUsers.length === 0 ? 13 : -1);
|
|
249
|
+
i0.ɵɵadvance(4);
|
|
250
|
+
i0.ɵɵconditional((ctx_r1.Context == null ? null : ctx_r1.Context.OwnerDisplayName) ? 17 : -1);
|
|
251
|
+
i0.ɵɵadvance();
|
|
252
|
+
i0.ɵɵrepeater(ctx_r1.ActiveShares);
|
|
253
|
+
i0.ɵɵadvance(2);
|
|
254
|
+
i0.ɵɵconditional(ctx_r1.ActiveShares.length === 0 ? 20 : -1);
|
|
255
|
+
i0.ɵɵadvance();
|
|
256
|
+
i0.ɵɵconditional(ctx_r1.RemovedShares.length > 0 ? 21 : -1);
|
|
257
|
+
i0.ɵɵadvance(2);
|
|
258
|
+
i0.ɵɵconditional(ctx_r1.HasChanges ? 23 : -1);
|
|
259
|
+
i0.ɵɵadvance(4);
|
|
260
|
+
i0.ɵɵproperty("disabled", !ctx_r1.HasChanges || ctx_r1.IsLoading);
|
|
261
|
+
i0.ɵɵadvance();
|
|
262
|
+
i0.ɵɵconditional(ctx_r1.IsLoading ? 28 : 29);
|
|
263
|
+
} }
|
|
264
|
+
/**
|
|
265
|
+
* Resource-type-agnostic share dialog. Every resource's sharing UX collapses to
|
|
266
|
+
* three tiers — View / Edit / Owner — so the dialog exposes a single level
|
|
267
|
+
* selector per grantee rather than a grab bag of `Can*` checkboxes. Adapters
|
|
268
|
+
* translate the level to whatever shape their backing entity actually persists.
|
|
269
|
+
*/
|
|
270
|
+
export class GenericShareDialogComponent {
|
|
271
|
+
cdr;
|
|
272
|
+
Visible = false;
|
|
273
|
+
Context = null;
|
|
274
|
+
Adapter = null;
|
|
275
|
+
Result = new EventEmitter();
|
|
276
|
+
Levels = RESOURCE_SHARE_LEVELS;
|
|
277
|
+
UserShares = [];
|
|
278
|
+
AvailableUsers = [];
|
|
279
|
+
allUsers = [];
|
|
280
|
+
IsLoading = false;
|
|
281
|
+
Error = null;
|
|
282
|
+
UserSearchFilter = '';
|
|
283
|
+
constructor(cdr) {
|
|
284
|
+
this.cdr = cdr;
|
|
285
|
+
}
|
|
286
|
+
ngOnChanges(changes) {
|
|
287
|
+
if (changes['Visible'] && this.Visible && this.Context && this.Adapter) {
|
|
288
|
+
this.resetDialog();
|
|
289
|
+
this.loadData();
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
resetDialog() {
|
|
293
|
+
this.Error = null;
|
|
294
|
+
this.IsLoading = false;
|
|
295
|
+
this.UserShares = [];
|
|
296
|
+
this.AvailableUsers = [];
|
|
297
|
+
this.UserSearchFilter = '';
|
|
298
|
+
}
|
|
299
|
+
async loadData() {
|
|
300
|
+
if (!this.Context || !this.Adapter)
|
|
301
|
+
return;
|
|
302
|
+
this.IsLoading = true;
|
|
303
|
+
this.cdr.detectChanges();
|
|
304
|
+
try {
|
|
305
|
+
const rv = new RunView();
|
|
306
|
+
const usersResult = await rv.RunView({
|
|
307
|
+
EntityName: 'MJ: Users',
|
|
308
|
+
ExtraFilter: 'IsActive = 1',
|
|
309
|
+
OrderBy: 'Name',
|
|
310
|
+
ResultType: 'entity_object'
|
|
311
|
+
});
|
|
312
|
+
this.allUsers = usersResult.Success ? usersResult.Results : [];
|
|
313
|
+
this.UserShares = await this.Adapter.LoadShares(this.Context);
|
|
314
|
+
this.UserShares.forEach((s) => (s._InitialLevel = s.Level));
|
|
315
|
+
this.updateAvailableUsers();
|
|
316
|
+
}
|
|
317
|
+
catch (error) {
|
|
318
|
+
console.error('Error loading share data:', error);
|
|
319
|
+
this.Error = 'Failed to load sharing data. Please try again.';
|
|
320
|
+
}
|
|
321
|
+
finally {
|
|
322
|
+
this.IsLoading = false;
|
|
323
|
+
this.cdr.detectChanges();
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
updateAvailableUsers() {
|
|
327
|
+
if (!this.Context)
|
|
328
|
+
return;
|
|
329
|
+
const sharedUserIds = new Set(this.UserShares.filter((s) => !s.MarkedForRemoval).map((s) => s.User.ID));
|
|
330
|
+
const ownerId = this.Context.OwnerUserID;
|
|
331
|
+
this.AvailableUsers = this.allUsers.filter((user) => {
|
|
332
|
+
if (ownerId && UUIDsEqual(user.ID, ownerId))
|
|
333
|
+
return false;
|
|
334
|
+
return !sharedUserIds.has(user.ID);
|
|
335
|
+
});
|
|
336
|
+
}
|
|
337
|
+
get FilteredAvailableUsers() {
|
|
338
|
+
if (!this.UserSearchFilter.trim()) {
|
|
339
|
+
return this.AvailableUsers.slice(0, 10);
|
|
340
|
+
}
|
|
341
|
+
const filter = this.UserSearchFilter.toLowerCase();
|
|
342
|
+
return this.AvailableUsers
|
|
343
|
+
.filter((u) => u.Name.toLowerCase().includes(filter) || (u.Email && u.Email.toLowerCase().includes(filter)))
|
|
344
|
+
.slice(0, 10);
|
|
345
|
+
}
|
|
346
|
+
get HasChanges() {
|
|
347
|
+
return this.UserShares.some((s) => s.IsNew || s.MarkedForRemoval || s.Level !== s._InitialLevel);
|
|
348
|
+
}
|
|
349
|
+
get ActiveShares() {
|
|
350
|
+
return this.UserShares.filter((s) => !s.MarkedForRemoval);
|
|
351
|
+
}
|
|
352
|
+
get RemovedShares() {
|
|
353
|
+
return this.UserShares.filter((s) => s.MarkedForRemoval);
|
|
354
|
+
}
|
|
355
|
+
/** Highlight rows whose level has been changed from their loaded state. */
|
|
356
|
+
isModified(share) {
|
|
357
|
+
return !share.IsNew && share.Level !== share._InitialLevel;
|
|
358
|
+
}
|
|
359
|
+
async addUserShare(user) {
|
|
360
|
+
if (!this.Context || !this.Adapter)
|
|
361
|
+
return;
|
|
362
|
+
const row = await this.Adapter.CreateShare(this.Context, user);
|
|
363
|
+
row._InitialLevel = row.Level;
|
|
364
|
+
this.UserShares.push(row);
|
|
365
|
+
this.updateAvailableUsers();
|
|
366
|
+
this.UserSearchFilter = '';
|
|
367
|
+
this.cdr.detectChanges();
|
|
368
|
+
}
|
|
369
|
+
removeUserShare(share) {
|
|
370
|
+
if (share.IsNew) {
|
|
371
|
+
this.UserShares = this.UserShares.filter((s) => s !== share);
|
|
372
|
+
}
|
|
373
|
+
else {
|
|
374
|
+
share.MarkedForRemoval = true;
|
|
375
|
+
}
|
|
376
|
+
this.updateAvailableUsers();
|
|
377
|
+
this.cdr.detectChanges();
|
|
378
|
+
}
|
|
379
|
+
undoRemove(share) {
|
|
380
|
+
share.MarkedForRemoval = false;
|
|
381
|
+
this.updateAvailableUsers();
|
|
382
|
+
this.cdr.detectChanges();
|
|
383
|
+
}
|
|
384
|
+
setLevel(share, level) {
|
|
385
|
+
share.Level = level;
|
|
386
|
+
this.cdr.detectChanges();
|
|
387
|
+
}
|
|
388
|
+
async onSave() {
|
|
389
|
+
if (!this.Adapter || !this.Context)
|
|
390
|
+
return;
|
|
391
|
+
if (!this.HasChanges) {
|
|
392
|
+
this.onCancel();
|
|
393
|
+
return;
|
|
394
|
+
}
|
|
395
|
+
this.IsLoading = true;
|
|
396
|
+
this.Error = null;
|
|
397
|
+
this.cdr.detectChanges();
|
|
398
|
+
try {
|
|
399
|
+
for (const share of this.UserShares.filter((s) => s.MarkedForRemoval && !s.IsNew)) {
|
|
400
|
+
const deleted = await share.PermissionEntity.Delete();
|
|
401
|
+
if (!deleted) {
|
|
402
|
+
throw new Error(`Failed to remove share for ${share.User.Name}: ${share.PermissionEntity.LatestResult?.Message ?? 'unknown error'}`);
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
const dirtyRows = this.UserShares.filter((s) => !s.MarkedForRemoval && (s.IsNew || s.Level !== s._InitialLevel));
|
|
406
|
+
for (const share of dirtyRows) {
|
|
407
|
+
this.Adapter.SyncLevelToEntity(share);
|
|
408
|
+
const saved = await share.PermissionEntity.Save();
|
|
409
|
+
if (!saved) {
|
|
410
|
+
throw new Error(`Failed to save share for ${share.User.Name}: ${share.PermissionEntity.LatestResult?.Message ?? 'unknown error'}`);
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
if (this.Adapter.AfterSave) {
|
|
414
|
+
await this.Adapter.AfterSave(this.Context);
|
|
415
|
+
}
|
|
416
|
+
this.Result.emit({ Action: 'save' });
|
|
417
|
+
}
|
|
418
|
+
catch (error) {
|
|
419
|
+
console.error('Error saving shares:', error);
|
|
420
|
+
this.Error = error instanceof Error ? error.message : 'Failed to save sharing settings.';
|
|
421
|
+
}
|
|
422
|
+
finally {
|
|
423
|
+
this.IsLoading = false;
|
|
424
|
+
this.cdr.detectChanges();
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
onCancel() {
|
|
428
|
+
this.Result.emit({ Action: 'cancel' });
|
|
429
|
+
}
|
|
430
|
+
getUserInitials(user) {
|
|
431
|
+
const name = user.Name || user.Email || '?';
|
|
432
|
+
const parts = name.split(' ');
|
|
433
|
+
if (parts.length >= 2) {
|
|
434
|
+
return (parts[0][0] + parts[1][0]).toUpperCase();
|
|
435
|
+
}
|
|
436
|
+
return name.substring(0, 2).toUpperCase();
|
|
437
|
+
}
|
|
438
|
+
static ɵfac = function GenericShareDialogComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || GenericShareDialogComponent)(i0.ɵɵdirectiveInject(i0.ChangeDetectorRef)); };
|
|
439
|
+
static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: GenericShareDialogComponent, selectors: [["mj-resource-share-dialog"]], inputs: { Visible: "Visible", Context: "Context", Adapter: "Adapter" }, outputs: { Result: "Result" }, standalone: false, features: [i0.ɵɵNgOnChangesFeature], decls: 1, vars: 1, consts: [[1, "mj-share-dialog-window", 3, "Visible", "Width", "MinHeight", "Resizable", "Draggable"], [1, "mj-share-dialog-window", 3, "Close", "Visible", "Width", "MinHeight", "Resizable", "Draggable"], [1, "share-dialog-header"], [1, "fa-solid", "fa-share-nodes"], [1, "share-dialog-body"], [1, "share-alert", "share-alert-error"], [1, "share-section"], [1, "share-search-box"], [1, "fa-solid", "fa-search"], ["type", "text", "placeholder", "Add people by name or email...", 3, "ngModelChange", "ngModel", "disabled"], [1, "share-user-dropdown"], [1, "share-no-results"], [1, "share-section-label"], [1, "share-person", "share-owner"], [1, "share-person", 3, "share-person-new", "share-person-modified"], [1, "share-empty"], [1, "share-removed-section"], [1, "share-footer"], [1, "share-changes-hint"], [1, "share-footer-actions"], ["type", "button", 1, "share-btn", "share-btn-secondary", 3, "click"], ["type", "button", 1, "share-btn", "share-btn-primary", 3, "click", "disabled"], [1, "fa-solid", "fa-exclamation-triangle"], ["type", "button", 1, "share-user-option", 3, "disabled"], ["type", "button", 1, "share-user-option", 3, "click", "disabled"], [1, "share-avatar"], [1, "share-user-name"], [1, "share-user-email"], [1, "fa-solid", "fa-plus"], [1, "share-avatar", "share-avatar-owner"], [1, "fa-solid", "fa-crown"], [1, "share-person-info"], [1, "share-person-name"], [1, "share-person-role"], [1, "share-person"], [1, "share-badge-new"], [1, "share-person-email"], ["role", "group", "aria-label", "Permission level", 1, "share-level-select"], ["type", "button", 1, "share-level-btn", 3, "active", "title"], ["type", "button", "title", "Remove", 1, "share-remove-btn", 3, "click"], [1, "fa-solid", "fa-times"], ["type", "button", 1, "share-level-btn", 3, "click", "title"], [1, "fa-solid", "fa-user-lock"], [1, "share-removed-item"], [1, "share-avatar", "share-avatar-removed"], [1, "share-removed-name"], ["type", "button", 1, "share-undo-btn", 3, "click"], [1, "fa-solid", "fa-undo"], [1, "fa-solid", "fa-circle"], [1, "fa-solid", "fa-spinner", "fa-spin"]], template: function GenericShareDialogComponent_Template(rf, ctx) { if (rf & 1) {
|
|
440
|
+
i0.ɵɵconditionalCreate(0, GenericShareDialogComponent_Conditional_0_Template, 30, 17, "mj-window", 0);
|
|
441
|
+
} if (rf & 2) {
|
|
442
|
+
i0.ɵɵconditional(ctx.Visible ? 0 : -1);
|
|
443
|
+
} }, dependencies: [i1.DefaultValueAccessor, i1.NgControlStatus, i1.NgModel, i2.MJWindowComponent, i2.MJWindowTitlebarComponent], styles: ["/* Generic Resource Share Dialog - Compact Design */\n\n/* Window container \u2014 overrides the default `<mj-window>` surface styling with a\n * larger radius + accent shadow so the dialog reads as an \"action surface\". */\n.mj-share-dialog-window .mj-window {\n border-radius: 12px !important;\n box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2) !important;\n overflow: hidden !important;\n}\n\n/* Branded titlebar: accent background so the header reads as chrome rather than\n * another surface panel. Text uses `--mj-text-inverse` because the background is\n * `--mj-brand-primary` \u2014 `--mj-text-inverse` is defined as \"legible on a\n * colored/dark accent\", not \"light mode-only white\". */\n.mj-share-dialog-window .mj-window-titlebar {\n background: var(--mj-brand-primary) !important;\n border: none !important;\n padding: 16px 20px !important;\n cursor: move;\n}\n\n/* The default title slot isn't rendered (we pass no `Title` input), but hide\n * it defensively in case a future refactor sets one \u2014 we already show our own\n * branded header via `<mj-window-titlebar>`. */\n.mj-share-dialog-window .mj-window-title {\n display: none !important;\n}\n\n/* Close button: tint against the accent titlebar to match the inverse text color. */\n.mj-share-dialog-window .mj-window-close {\n background: color-mix(in srgb, var(--mj-bg-surface) 15%, transparent) !important;\n border: none !important;\n color: var(--mj-text-inverse) !important;\n border-radius: 4px !important;\n}\n\n.mj-share-dialog-window .mj-window-close:hover {\n background: color-mix(in srgb, var(--mj-bg-surface) 25%, transparent) !important;\n color: var(--mj-text-inverse) !important;\n}\n\n.mj-share-dialog-window .mj-window-body {\n padding: 0 !important;\n background: var(--mj-bg-surface) !important;\n}\n\n/* Header */\n.share-dialog-header {\n display: flex;\n align-items: center;\n gap: 10px;\n color: var(--mj-text-inverse);\n font-size: 16px;\n font-weight: 500;\n}\n\n.share-dialog-header i {\n font-size: 18px;\n opacity: 0.9;\n}\n\n/* Body */\n.share-dialog-body {\n padding: 0;\n}\n\n/* Alert */\n.share-alert {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 12px 20px;\n font-size: 13px;\n}\n\n.share-alert-error {\n background: var(--mj-bg-error);\n color: var(--mj-status-error);\n border-bottom: 1px solid var(--mj-border-error);\n}\n\n/* Sections */\n.share-section {\n padding: 16px 20px;\n}\n\n.share-section:not(:last-child) {\n border-bottom: 1px solid var(--mj-border-default);\n}\n\n.share-section-label {\n font-size: 11px;\n font-weight: 600;\n color: var(--mj-text-secondary);\n text-transform: uppercase;\n letter-spacing: 0.5px;\n margin-bottom: 12px;\n}\n\n/* Search Box */\n.share-search-box {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 10px 14px;\n background: var(--mj-bg-surface-card);\n border-radius: 8px;\n border: 1px solid transparent;\n transition: all 0.15s;\n}\n\n.share-search-box:focus-within {\n background: var(--mj-bg-surface);\n border-color: var(--mj-brand-primary);\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--mj-brand-primary) 10%, transparent);\n}\n\n.share-search-box i {\n color: var(--mj-text-disabled);\n font-size: 14px;\n}\n\n.share-search-box input {\n flex: 1;\n border: none;\n background: transparent;\n font-size: 14px;\n outline: none;\n}\n\n.share-search-box input::placeholder {\n color: var(--mj-text-disabled);\n}\n\n/* User Dropdown */\n.share-user-dropdown {\n margin-top: 8px;\n max-height: 180px;\n overflow-y: auto;\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n background: var(--mj-bg-surface-card);\n}\n\n/* User Option */\n.share-user-option {\n display: flex;\n align-items: center;\n gap: 10px;\n width: 100%;\n padding: 10px 12px;\n border: none;\n background: transparent;\n cursor: pointer;\n text-align: left;\n transition: background 0.1s;\n}\n\n.share-user-option:hover {\n background: color-mix(in srgb, var(--mj-brand-primary) 8%, transparent);\n}\n\n.share-user-option:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n\n.share-user-option .share-user-name {\n flex: 1;\n font-size: 14px;\n font-weight: 500;\n color: var(--mj-text-primary);\n}\n\n.share-user-option .share-user-email {\n font-size: 12px;\n color: var(--mj-text-secondary);\n margin-right: auto;\n}\n\n.share-user-option i.fa-plus {\n color: var(--mj-brand-primary);\n font-size: 12px;\n opacity: 0;\n transition: opacity 0.1s;\n}\n\n.share-user-option:hover i.fa-plus {\n opacity: 1;\n}\n\n.share-no-results {\n padding: 16px;\n text-align: center;\n color: var(--mj-text-secondary);\n font-size: 13px;\n}\n\n/* Avatar */\n.share-avatar {\n width: 32px;\n height: 32px;\n border-radius: 50%;\n background: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n font-size: 12px;\n font-weight: 600;\n display: flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n}\n\n.share-avatar-owner {\n background: var(--mj-status-warning);\n font-size: 14px;\n}\n\n.share-avatar-removed {\n background: var(--mj-border-strong);\n}\n\n/* Person Row */\n.share-person {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 10px 0;\n}\n\n.share-person:not(:last-child) {\n border-bottom: 1px solid var(--mj-bg-surface-sunken);\n}\n\n.share-person-new {\n background: color-mix(in srgb, var(--mj-status-success) 5%, transparent);\n margin: 0 -20px;\n padding: 10px 20px;\n}\n\n.share-person-modified {\n background: color-mix(in srgb, var(--mj-brand-primary) 5%, transparent);\n margin: 0 -20px;\n padding: 10px 20px;\n}\n\n.share-person-info {\n flex: 1;\n min-width: 0;\n}\n\n.share-person-name {\n display: flex;\n align-items: center;\n gap: 8px;\n font-size: 14px;\n font-weight: 500;\n color: var(--mj-text-primary);\n}\n\n.share-person-email {\n font-size: 12px;\n color: var(--mj-text-secondary);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.share-person-role {\n font-size: 12px;\n color: var(--mj-status-warning);\n font-weight: 500;\n}\n\n.share-badge-new {\n padding: 2px 6px;\n background: var(--mj-status-success);\n color: var(--mj-text-inverse);\n font-size: 10px;\n font-weight: 600;\n border-radius: 4px;\n}\n\n/* Level selector (segmented control) */\n.share-level-select {\n display: inline-flex;\n border: 1px solid var(--mj-border-default);\n border-radius: 6px;\n overflow: hidden;\n background: var(--mj-bg-surface-card);\n}\n\n.share-level-btn {\n padding: 4px 12px;\n border: none;\n background: transparent;\n font-size: 12px;\n font-weight: 500;\n color: var(--mj-text-secondary);\n cursor: pointer;\n transition: background 0.1s, color 0.1s;\n}\n\n.share-level-btn:not(:last-child) {\n border-right: 1px solid var(--mj-border-default);\n}\n\n.share-level-btn:hover:not(.active) {\n background: color-mix(in srgb, var(--mj-brand-primary) 8%, transparent);\n color: var(--mj-text-primary);\n}\n\n.share-level-btn.active {\n background: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n}\n\n/* Legacy checkbox classes (kept but unused now). */\n.share-permissions {\n display: flex;\n gap: 4px;\n}\n\n.share-perm {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 28px;\n height: 28px;\n border-radius: 4px;\n cursor: pointer;\n transition: all 0.1s;\n position: relative;\n}\n\n.share-perm input {\n position: absolute;\n opacity: 0;\n cursor: pointer;\n}\n\n.share-perm i {\n font-size: 12px;\n color: var(--mj-border-strong);\n transition: color 0.1s;\n}\n\n.share-perm:hover {\n background: var(--mj-bg-surface-sunken);\n}\n\n.share-perm input:checked + i {\n color: var(--mj-brand-primary);\n}\n\n.share-perm input:disabled + i {\n color: var(--mj-brand-primary);\n opacity: 0.7;\n}\n\n/* Remove Button */\n.share-remove-btn {\n width: 28px;\n height: 28px;\n border: none;\n background: transparent;\n color: var(--mj-border-strong);\n border-radius: 4px;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: all 0.1s;\n}\n\n.share-remove-btn:hover {\n background: var(--mj-bg-error);\n color: var(--mj-status-error);\n}\n\n/* Empty State */\n.share-empty {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 20px;\n color: var(--mj-text-disabled);\n font-size: 13px;\n background: var(--mj-bg-surface-card);\n border-radius: 8px;\n justify-content: center;\n}\n\n.share-empty i {\n font-size: 16px;\n}\n\n/* Removed Section */\n.share-removed-section {\n margin-top: 12px;\n padding: 12px;\n background: var(--mj-bg-warning);\n border-radius: 8px;\n border: 1px solid var(--mj-border-warning);\n}\n\n.share-removed-item {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 6px 0;\n}\n\n.share-removed-name {\n flex: 1;\n font-size: 13px;\n color: var(--mj-text-secondary);\n text-decoration: line-through;\n}\n\n.share-undo-btn {\n padding: 4px 10px;\n border: none;\n background: transparent;\n color: var(--mj-status-warning);\n font-size: 12px;\n font-weight: 500;\n cursor: pointer;\n display: flex;\n align-items: center;\n gap: 4px;\n}\n\n.share-undo-btn:hover {\n text-decoration: underline;\n}\n\n/* Footer */\n.share-footer {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 16px 20px;\n background: var(--mj-bg-surface-card);\n border-top: 1px solid var(--mj-border-default);\n}\n\n.share-changes-hint {\n display: flex;\n align-items: center;\n gap: 6px;\n font-size: 12px;\n color: var(--mj-brand-primary);\n}\n\n.share-changes-hint i {\n font-size: 6px;\n}\n\n.share-footer-actions {\n display: flex;\n gap: 10px;\n margin-left: auto;\n}\n\n.share-btn {\n padding: 8px 18px;\n border-radius: 6px;\n font-size: 14px;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.15s;\n display: flex;\n align-items: center;\n gap: 6px;\n}\n\n.share-btn:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n\n.share-btn-secondary {\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-strong);\n color: var(--mj-text-secondary);\n}\n\n.share-btn-secondary:hover:not(:disabled) {\n background: var(--mj-bg-surface-card);\n border-color: var(--mj-border-strong);\n}\n\n.share-btn-primary {\n background: var(--mj-brand-primary);\n border: none;\n color: var(--mj-text-inverse);\n}\n\n.share-btn-primary:hover:not(:disabled) {\n background: var(--mj-brand-primary-hover);\n box-shadow: 0 2px 8px color-mix(in srgb, var(--mj-brand-primary) 30%, transparent);\n}\n\n/* Owner row specific styling */\n.share-owner {\n background: var(--mj-bg-warning);\n margin: 0 -20px;\n padding: 10px 20px;\n border-radius: 0;\n}\n"], encapsulation: 2 });
|
|
444
|
+
}
|
|
445
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(GenericShareDialogComponent, [{
|
|
446
|
+
type: Component,
|
|
447
|
+
args: [{ standalone: false, selector: 'mj-resource-share-dialog', encapsulation: ViewEncapsulation.None, template: "@if (Visible) {\n <mj-window\n [Visible]=\"true\"\n [Width]=\"560\"\n [MinHeight]=\"200\"\n [Resizable]=\"false\"\n [Draggable]=\"true\"\n (Close)=\"onCancel()\"\n class=\"mj-share-dialog-window\">\n <mj-window-titlebar>\n <div class=\"share-dialog-header\">\n <i class=\"fa-solid fa-share-nodes\"></i>\n <span>Share \"{{ Context?.ResourceName }}\"</span>\n </div>\n </mj-window-titlebar>\n <div class=\"share-dialog-body\">\n @if (Error) {\n <div class=\"share-alert share-alert-error\">\n <i class=\"fa-solid fa-exclamation-triangle\"></i>\n <span>{{ Error }}</span>\n </div>\n }\n <!-- Add Users Section -->\n <div class=\"share-section\">\n <div class=\"share-search-box\">\n <i class=\"fa-solid fa-search\"></i>\n <input\n type=\"text\"\n placeholder=\"Add people by name or email...\"\n [(ngModel)]=\"UserSearchFilter\"\n [disabled]=\"IsLoading\">\n </div>\n @if (FilteredAvailableUsers.length > 0) {\n <div class=\"share-user-dropdown\">\n @for (user of FilteredAvailableUsers; track user.ID) {\n <button type=\"button\" class=\"share-user-option\" (click)=\"addUserShare(user)\" [disabled]=\"IsLoading\">\n <span class=\"share-avatar\">{{ getUserInitials(user) }}</span>\n <span class=\"share-user-name\">{{ user.Name }}</span>\n @if (user.Email) {\n <span class=\"share-user-email\">{{ user.Email }}</span>\n }\n <i class=\"fa-solid fa-plus\"></i>\n </button>\n }\n </div>\n }\n @if (UserSearchFilter && FilteredAvailableUsers.length === 0) {\n <div class=\"share-no-results\">\n No users found matching \"{{ UserSearchFilter }}\"\n </div>\n }\n </div>\n <!-- People with Access -->\n <div class=\"share-section\">\n <div class=\"share-section-label\">People with access</div>\n @if (Context?.OwnerDisplayName) {\n <div class=\"share-person share-owner\">\n <span class=\"share-avatar share-avatar-owner\">\n <i class=\"fa-solid fa-crown\"></i>\n </span>\n <div class=\"share-person-info\">\n <span class=\"share-person-name\">{{ Context?.OwnerDisplayName }}</span>\n <span class=\"share-person-role\">Owner</span>\n </div>\n </div>\n }\n @for (share of ActiveShares; track share.User.ID) {\n <div class=\"share-person\" [class.share-person-new]=\"share.IsNew\" [class.share-person-modified]=\"isModified(share)\">\n <span class=\"share-avatar\">{{ getUserInitials(share.User) }}</span>\n <div class=\"share-person-info\">\n <span class=\"share-person-name\">\n {{ share.User.Name }}\n @if (share.IsNew) {\n <span class=\"share-badge-new\">New</span>\n }\n </span>\n @if (share.User.Email) {\n <span class=\"share-person-email\">{{ share.User.Email }}</span>\n }\n </div>\n <div class=\"share-level-select\" role=\"group\" aria-label=\"Permission level\">\n @for (level of Levels; track level) {\n <button type=\"button\"\n class=\"share-level-btn\"\n [class.active]=\"share.Level === level\"\n (click)=\"setLevel(share, level)\"\n [title]=\"level + ' access'\">\n {{ level }}\n </button>\n }\n </div>\n <button type=\"button\" class=\"share-remove-btn\" (click)=\"removeUserShare(share)\" title=\"Remove\">\n <i class=\"fa-solid fa-times\"></i>\n </button>\n </div>\n }\n @if (ActiveShares.length === 0) {\n <div class=\"share-empty\">\n <i class=\"fa-solid fa-user-lock\"></i>\n <span>Not shared with anyone yet</span>\n </div>\n }\n @if (RemovedShares.length > 0) {\n <div class=\"share-removed-section\">\n @for (share of RemovedShares; track share.User.ID) {\n <div class=\"share-removed-item\">\n <span class=\"share-avatar share-avatar-removed\">{{ getUserInitials(share.User) }}</span>\n <span class=\"share-removed-name\">{{ share.User.Name }}</span>\n <button type=\"button\" class=\"share-undo-btn\" (click)=\"undoRemove(share)\">\n <i class=\"fa-solid fa-undo\"></i> Undo\n </button>\n </div>\n }\n </div>\n }\n </div>\n <div class=\"share-footer\">\n @if (HasChanges) {\n <span class=\"share-changes-hint\">\n <i class=\"fa-solid fa-circle\"></i> Unsaved changes\n </span>\n }\n <div class=\"share-footer-actions\">\n <button type=\"button\" class=\"share-btn share-btn-secondary\" (click)=\"onCancel()\">\n Cancel\n </button>\n <button\n type=\"button\"\n class=\"share-btn share-btn-primary\"\n (click)=\"onSave()\"\n [disabled]=\"!HasChanges || IsLoading\">\n @if (IsLoading) {\n <i class=\"fa-solid fa-spinner fa-spin\"></i> Saving...\n } @else {\n Save\n }\n </button>\n </div>\n </div>\n </div>\n </mj-window>\n}\n", styles: ["/* Generic Resource Share Dialog - Compact Design */\n\n/* Window container \u2014 overrides the default `<mj-window>` surface styling with a\n * larger radius + accent shadow so the dialog reads as an \"action surface\". */\n.mj-share-dialog-window .mj-window {\n border-radius: 12px !important;\n box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2) !important;\n overflow: hidden !important;\n}\n\n/* Branded titlebar: accent background so the header reads as chrome rather than\n * another surface panel. Text uses `--mj-text-inverse` because the background is\n * `--mj-brand-primary` \u2014 `--mj-text-inverse` is defined as \"legible on a\n * colored/dark accent\", not \"light mode-only white\". */\n.mj-share-dialog-window .mj-window-titlebar {\n background: var(--mj-brand-primary) !important;\n border: none !important;\n padding: 16px 20px !important;\n cursor: move;\n}\n\n/* The default title slot isn't rendered (we pass no `Title` input), but hide\n * it defensively in case a future refactor sets one \u2014 we already show our own\n * branded header via `<mj-window-titlebar>`. */\n.mj-share-dialog-window .mj-window-title {\n display: none !important;\n}\n\n/* Close button: tint against the accent titlebar to match the inverse text color. */\n.mj-share-dialog-window .mj-window-close {\n background: color-mix(in srgb, var(--mj-bg-surface) 15%, transparent) !important;\n border: none !important;\n color: var(--mj-text-inverse) !important;\n border-radius: 4px !important;\n}\n\n.mj-share-dialog-window .mj-window-close:hover {\n background: color-mix(in srgb, var(--mj-bg-surface) 25%, transparent) !important;\n color: var(--mj-text-inverse) !important;\n}\n\n.mj-share-dialog-window .mj-window-body {\n padding: 0 !important;\n background: var(--mj-bg-surface) !important;\n}\n\n/* Header */\n.share-dialog-header {\n display: flex;\n align-items: center;\n gap: 10px;\n color: var(--mj-text-inverse);\n font-size: 16px;\n font-weight: 500;\n}\n\n.share-dialog-header i {\n font-size: 18px;\n opacity: 0.9;\n}\n\n/* Body */\n.share-dialog-body {\n padding: 0;\n}\n\n/* Alert */\n.share-alert {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 12px 20px;\n font-size: 13px;\n}\n\n.share-alert-error {\n background: var(--mj-bg-error);\n color: var(--mj-status-error);\n border-bottom: 1px solid var(--mj-border-error);\n}\n\n/* Sections */\n.share-section {\n padding: 16px 20px;\n}\n\n.share-section:not(:last-child) {\n border-bottom: 1px solid var(--mj-border-default);\n}\n\n.share-section-label {\n font-size: 11px;\n font-weight: 600;\n color: var(--mj-text-secondary);\n text-transform: uppercase;\n letter-spacing: 0.5px;\n margin-bottom: 12px;\n}\n\n/* Search Box */\n.share-search-box {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 10px 14px;\n background: var(--mj-bg-surface-card);\n border-radius: 8px;\n border: 1px solid transparent;\n transition: all 0.15s;\n}\n\n.share-search-box:focus-within {\n background: var(--mj-bg-surface);\n border-color: var(--mj-brand-primary);\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--mj-brand-primary) 10%, transparent);\n}\n\n.share-search-box i {\n color: var(--mj-text-disabled);\n font-size: 14px;\n}\n\n.share-search-box input {\n flex: 1;\n border: none;\n background: transparent;\n font-size: 14px;\n outline: none;\n}\n\n.share-search-box input::placeholder {\n color: var(--mj-text-disabled);\n}\n\n/* User Dropdown */\n.share-user-dropdown {\n margin-top: 8px;\n max-height: 180px;\n overflow-y: auto;\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n background: var(--mj-bg-surface-card);\n}\n\n/* User Option */\n.share-user-option {\n display: flex;\n align-items: center;\n gap: 10px;\n width: 100%;\n padding: 10px 12px;\n border: none;\n background: transparent;\n cursor: pointer;\n text-align: left;\n transition: background 0.1s;\n}\n\n.share-user-option:hover {\n background: color-mix(in srgb, var(--mj-brand-primary) 8%, transparent);\n}\n\n.share-user-option:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n\n.share-user-option .share-user-name {\n flex: 1;\n font-size: 14px;\n font-weight: 500;\n color: var(--mj-text-primary);\n}\n\n.share-user-option .share-user-email {\n font-size: 12px;\n color: var(--mj-text-secondary);\n margin-right: auto;\n}\n\n.share-user-option i.fa-plus {\n color: var(--mj-brand-primary);\n font-size: 12px;\n opacity: 0;\n transition: opacity 0.1s;\n}\n\n.share-user-option:hover i.fa-plus {\n opacity: 1;\n}\n\n.share-no-results {\n padding: 16px;\n text-align: center;\n color: var(--mj-text-secondary);\n font-size: 13px;\n}\n\n/* Avatar */\n.share-avatar {\n width: 32px;\n height: 32px;\n border-radius: 50%;\n background: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n font-size: 12px;\n font-weight: 600;\n display: flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n}\n\n.share-avatar-owner {\n background: var(--mj-status-warning);\n font-size: 14px;\n}\n\n.share-avatar-removed {\n background: var(--mj-border-strong);\n}\n\n/* Person Row */\n.share-person {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 10px 0;\n}\n\n.share-person:not(:last-child) {\n border-bottom: 1px solid var(--mj-bg-surface-sunken);\n}\n\n.share-person-new {\n background: color-mix(in srgb, var(--mj-status-success) 5%, transparent);\n margin: 0 -20px;\n padding: 10px 20px;\n}\n\n.share-person-modified {\n background: color-mix(in srgb, var(--mj-brand-primary) 5%, transparent);\n margin: 0 -20px;\n padding: 10px 20px;\n}\n\n.share-person-info {\n flex: 1;\n min-width: 0;\n}\n\n.share-person-name {\n display: flex;\n align-items: center;\n gap: 8px;\n font-size: 14px;\n font-weight: 500;\n color: var(--mj-text-primary);\n}\n\n.share-person-email {\n font-size: 12px;\n color: var(--mj-text-secondary);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.share-person-role {\n font-size: 12px;\n color: var(--mj-status-warning);\n font-weight: 500;\n}\n\n.share-badge-new {\n padding: 2px 6px;\n background: var(--mj-status-success);\n color: var(--mj-text-inverse);\n font-size: 10px;\n font-weight: 600;\n border-radius: 4px;\n}\n\n/* Level selector (segmented control) */\n.share-level-select {\n display: inline-flex;\n border: 1px solid var(--mj-border-default);\n border-radius: 6px;\n overflow: hidden;\n background: var(--mj-bg-surface-card);\n}\n\n.share-level-btn {\n padding: 4px 12px;\n border: none;\n background: transparent;\n font-size: 12px;\n font-weight: 500;\n color: var(--mj-text-secondary);\n cursor: pointer;\n transition: background 0.1s, color 0.1s;\n}\n\n.share-level-btn:not(:last-child) {\n border-right: 1px solid var(--mj-border-default);\n}\n\n.share-level-btn:hover:not(.active) {\n background: color-mix(in srgb, var(--mj-brand-primary) 8%, transparent);\n color: var(--mj-text-primary);\n}\n\n.share-level-btn.active {\n background: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n}\n\n/* Legacy checkbox classes (kept but unused now). */\n.share-permissions {\n display: flex;\n gap: 4px;\n}\n\n.share-perm {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 28px;\n height: 28px;\n border-radius: 4px;\n cursor: pointer;\n transition: all 0.1s;\n position: relative;\n}\n\n.share-perm input {\n position: absolute;\n opacity: 0;\n cursor: pointer;\n}\n\n.share-perm i {\n font-size: 12px;\n color: var(--mj-border-strong);\n transition: color 0.1s;\n}\n\n.share-perm:hover {\n background: var(--mj-bg-surface-sunken);\n}\n\n.share-perm input:checked + i {\n color: var(--mj-brand-primary);\n}\n\n.share-perm input:disabled + i {\n color: var(--mj-brand-primary);\n opacity: 0.7;\n}\n\n/* Remove Button */\n.share-remove-btn {\n width: 28px;\n height: 28px;\n border: none;\n background: transparent;\n color: var(--mj-border-strong);\n border-radius: 4px;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: all 0.1s;\n}\n\n.share-remove-btn:hover {\n background: var(--mj-bg-error);\n color: var(--mj-status-error);\n}\n\n/* Empty State */\n.share-empty {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 20px;\n color: var(--mj-text-disabled);\n font-size: 13px;\n background: var(--mj-bg-surface-card);\n border-radius: 8px;\n justify-content: center;\n}\n\n.share-empty i {\n font-size: 16px;\n}\n\n/* Removed Section */\n.share-removed-section {\n margin-top: 12px;\n padding: 12px;\n background: var(--mj-bg-warning);\n border-radius: 8px;\n border: 1px solid var(--mj-border-warning);\n}\n\n.share-removed-item {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 6px 0;\n}\n\n.share-removed-name {\n flex: 1;\n font-size: 13px;\n color: var(--mj-text-secondary);\n text-decoration: line-through;\n}\n\n.share-undo-btn {\n padding: 4px 10px;\n border: none;\n background: transparent;\n color: var(--mj-status-warning);\n font-size: 12px;\n font-weight: 500;\n cursor: pointer;\n display: flex;\n align-items: center;\n gap: 4px;\n}\n\n.share-undo-btn:hover {\n text-decoration: underline;\n}\n\n/* Footer */\n.share-footer {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 16px 20px;\n background: var(--mj-bg-surface-card);\n border-top: 1px solid var(--mj-border-default);\n}\n\n.share-changes-hint {\n display: flex;\n align-items: center;\n gap: 6px;\n font-size: 12px;\n color: var(--mj-brand-primary);\n}\n\n.share-changes-hint i {\n font-size: 6px;\n}\n\n.share-footer-actions {\n display: flex;\n gap: 10px;\n margin-left: auto;\n}\n\n.share-btn {\n padding: 8px 18px;\n border-radius: 6px;\n font-size: 14px;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.15s;\n display: flex;\n align-items: center;\n gap: 6px;\n}\n\n.share-btn:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n\n.share-btn-secondary {\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-strong);\n color: var(--mj-text-secondary);\n}\n\n.share-btn-secondary:hover:not(:disabled) {\n background: var(--mj-bg-surface-card);\n border-color: var(--mj-border-strong);\n}\n\n.share-btn-primary {\n background: var(--mj-brand-primary);\n border: none;\n color: var(--mj-text-inverse);\n}\n\n.share-btn-primary:hover:not(:disabled) {\n background: var(--mj-brand-primary-hover);\n box-shadow: 0 2px 8px color-mix(in srgb, var(--mj-brand-primary) 30%, transparent);\n}\n\n/* Owner row specific styling */\n.share-owner {\n background: var(--mj-bg-warning);\n margin: 0 -20px;\n padding: 10px 20px;\n border-radius: 0;\n}\n"] }]
|
|
448
|
+
}], () => [{ type: i0.ChangeDetectorRef }], { Visible: [{
|
|
449
|
+
type: Input
|
|
450
|
+
}], Context: [{
|
|
451
|
+
type: Input
|
|
452
|
+
}], Adapter: [{
|
|
453
|
+
type: Input
|
|
454
|
+
}], Result: [{
|
|
455
|
+
type: Output
|
|
456
|
+
}] }); })();
|
|
457
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(GenericShareDialogComponent, { className: "GenericShareDialogComponent", filePath: "src/lib/resource-share-dialog.component.ts", lineNumber: 33 }); })();
|
|
458
|
+
//# sourceMappingURL=resource-share-dialog.component.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resource-share-dialog.component.js","sourceRoot":"","sources":["../../src/lib/resource-share-dialog.component.ts","../../src/lib/resource-share-dialog.component.html"],"names":[],"mappings":"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","sourcesContent":["import { Component, Input, Output, EventEmitter, OnChanges, SimpleChanges, ChangeDetectorRef, ViewEncapsulation } from '@angular/core';\nimport { RunView } from '@memberjunction/core';\nimport { MJUserEntity } from '@memberjunction/core-entities';\nimport { UUIDsEqual } from '@memberjunction/global';\nimport {\n RESOURCE_SHARE_LEVELS,\n ResourceShareAdapter,\n ResourceShareContext,\n ResourceShareLevel,\n ResourceSharePermissionModel\n} from './resource-share-adapter';\n\n/**\n * Result emitted when {@link GenericShareDialogComponent} closes.\n */\nexport interface ResourceShareDialogResult {\n Action: 'save' | 'cancel';\n}\n\n/**\n * Resource-type-agnostic share dialog. Every resource's sharing UX collapses to\n * three tiers — View / Edit / Owner — so the dialog exposes a single level\n * selector per grantee rather than a grab bag of `Can*` checkboxes. Adapters\n * translate the level to whatever shape their backing entity actually persists.\n */\n@Component({\n standalone: false,\n selector: 'mj-resource-share-dialog',\n templateUrl: './resource-share-dialog.component.html',\n styleUrls: ['./resource-share-dialog.component.css'],\n encapsulation: ViewEncapsulation.None\n})\nexport class GenericShareDialogComponent implements OnChanges {\n @Input() Visible = false;\n @Input() Context: ResourceShareContext | null = null;\n @Input() Adapter: ResourceShareAdapter | null = null;\n @Output() Result = new EventEmitter<ResourceShareDialogResult>();\n\n public readonly Levels = RESOURCE_SHARE_LEVELS;\n\n public UserShares: ResourceSharePermissionModel[] = [];\n public AvailableUsers: MJUserEntity[] = [];\n private allUsers: MJUserEntity[] = [];\n\n public IsLoading = false;\n public Error: string | null = null;\n public UserSearchFilter = '';\n\n constructor(private cdr: ChangeDetectorRef) {}\n\n ngOnChanges(changes: SimpleChanges): void {\n if (changes['Visible'] && this.Visible && this.Context && this.Adapter) {\n this.resetDialog();\n this.loadData();\n }\n }\n\n private resetDialog(): void {\n this.Error = null;\n this.IsLoading = false;\n this.UserShares = [];\n this.AvailableUsers = [];\n this.UserSearchFilter = '';\n }\n\n private async loadData(): Promise<void> {\n if (!this.Context || !this.Adapter) return;\n\n this.IsLoading = true;\n this.cdr.detectChanges();\n\n try {\n const rv = new RunView();\n const usersResult = await rv.RunView<MJUserEntity>({\n EntityName: 'MJ: Users',\n ExtraFilter: 'IsActive = 1',\n OrderBy: 'Name',\n ResultType: 'entity_object'\n });\n this.allUsers = usersResult.Success ? usersResult.Results : [];\n\n this.UserShares = await this.Adapter.LoadShares(this.Context);\n this.UserShares.forEach((s) => (s._InitialLevel = s.Level));\n this.updateAvailableUsers();\n } catch (error) {\n console.error('Error loading share data:', error);\n this.Error = 'Failed to load sharing data. Please try again.';\n } finally {\n this.IsLoading = false;\n this.cdr.detectChanges();\n }\n }\n\n private updateAvailableUsers(): void {\n if (!this.Context) return;\n const sharedUserIds = new Set(\n this.UserShares.filter((s) => !s.MarkedForRemoval).map((s) => s.User.ID)\n );\n const ownerId = this.Context.OwnerUserID;\n this.AvailableUsers = this.allUsers.filter((user) => {\n if (ownerId && UUIDsEqual(user.ID, ownerId)) return false;\n return !sharedUserIds.has(user.ID);\n });\n }\n\n public get FilteredAvailableUsers(): MJUserEntity[] {\n if (!this.UserSearchFilter.trim()) {\n return this.AvailableUsers.slice(0, 10);\n }\n const filter = this.UserSearchFilter.toLowerCase();\n return this.AvailableUsers\n .filter((u) => u.Name.toLowerCase().includes(filter) || (u.Email && u.Email.toLowerCase().includes(filter)))\n .slice(0, 10);\n }\n\n public get HasChanges(): boolean {\n return this.UserShares.some(\n (s) => s.IsNew || s.MarkedForRemoval || s.Level !== s._InitialLevel\n );\n }\n\n public get ActiveShares(): ResourceSharePermissionModel[] {\n return this.UserShares.filter((s) => !s.MarkedForRemoval);\n }\n\n public get RemovedShares(): ResourceSharePermissionModel[] {\n return this.UserShares.filter((s) => s.MarkedForRemoval);\n }\n\n /** Highlight rows whose level has been changed from their loaded state. */\n public isModified(share: ResourceSharePermissionModel): boolean {\n return !share.IsNew && share.Level !== share._InitialLevel;\n }\n\n public async addUserShare(user: MJUserEntity): Promise<void> {\n if (!this.Context || !this.Adapter) return;\n const row = await this.Adapter.CreateShare(this.Context, user);\n row._InitialLevel = row.Level;\n this.UserShares.push(row);\n this.updateAvailableUsers();\n this.UserSearchFilter = '';\n this.cdr.detectChanges();\n }\n\n public removeUserShare(share: ResourceSharePermissionModel): void {\n if (share.IsNew) {\n this.UserShares = this.UserShares.filter((s) => s !== share);\n } else {\n share.MarkedForRemoval = true;\n }\n this.updateAvailableUsers();\n this.cdr.detectChanges();\n }\n\n public undoRemove(share: ResourceSharePermissionModel): void {\n share.MarkedForRemoval = false;\n this.updateAvailableUsers();\n this.cdr.detectChanges();\n }\n\n public setLevel(share: ResourceSharePermissionModel, level: ResourceShareLevel): void {\n share.Level = level;\n this.cdr.detectChanges();\n }\n\n public async onSave(): Promise<void> {\n if (!this.Adapter || !this.Context) return;\n if (!this.HasChanges) {\n this.onCancel();\n return;\n }\n this.IsLoading = true;\n this.Error = null;\n this.cdr.detectChanges();\n\n try {\n for (const share of this.UserShares.filter((s) => s.MarkedForRemoval && !s.IsNew)) {\n const deleted = await share.PermissionEntity.Delete();\n if (!deleted) {\n throw new Error(\n `Failed to remove share for ${share.User.Name}: ${\n share.PermissionEntity.LatestResult?.Message ?? 'unknown error'\n }`\n );\n }\n }\n\n const dirtyRows = this.UserShares.filter(\n (s) => !s.MarkedForRemoval && (s.IsNew || s.Level !== s._InitialLevel)\n );\n for (const share of dirtyRows) {\n this.Adapter.SyncLevelToEntity(share);\n const saved = await share.PermissionEntity.Save();\n if (!saved) {\n throw new Error(\n `Failed to save share for ${share.User.Name}: ${\n share.PermissionEntity.LatestResult?.Message ?? 'unknown error'\n }`\n );\n }\n }\n\n if (this.Adapter.AfterSave) {\n await this.Adapter.AfterSave(this.Context);\n }\n\n this.Result.emit({ Action: 'save' });\n } catch (error) {\n console.error('Error saving shares:', error);\n this.Error = error instanceof Error ? error.message : 'Failed to save sharing settings.';\n } finally {\n this.IsLoading = false;\n this.cdr.detectChanges();\n }\n }\n\n public onCancel(): void {\n this.Result.emit({ Action: 'cancel' });\n }\n\n public getUserInitials(user: MJUserEntity): string {\n const name = user.Name || user.Email || '?';\n const parts = name.split(' ');\n if (parts.length >= 2) {\n return (parts[0][0] + parts[1][0]).toUpperCase();\n }\n return name.substring(0, 2).toUpperCase();\n }\n}\n","@if (Visible) {\n <mj-window\n [Visible]=\"true\"\n [Width]=\"560\"\n [MinHeight]=\"200\"\n [Resizable]=\"false\"\n [Draggable]=\"true\"\n (Close)=\"onCancel()\"\n class=\"mj-share-dialog-window\">\n <mj-window-titlebar>\n <div class=\"share-dialog-header\">\n <i class=\"fa-solid fa-share-nodes\"></i>\n <span>Share \"{{ Context?.ResourceName }}\"</span>\n </div>\n </mj-window-titlebar>\n <div class=\"share-dialog-body\">\n @if (Error) {\n <div class=\"share-alert share-alert-error\">\n <i class=\"fa-solid fa-exclamation-triangle\"></i>\n <span>{{ Error }}</span>\n </div>\n }\n <!-- Add Users Section -->\n <div class=\"share-section\">\n <div class=\"share-search-box\">\n <i class=\"fa-solid fa-search\"></i>\n <input\n type=\"text\"\n placeholder=\"Add people by name or email...\"\n [(ngModel)]=\"UserSearchFilter\"\n [disabled]=\"IsLoading\">\n </div>\n @if (FilteredAvailableUsers.length > 0) {\n <div class=\"share-user-dropdown\">\n @for (user of FilteredAvailableUsers; track user.ID) {\n <button type=\"button\" class=\"share-user-option\" (click)=\"addUserShare(user)\" [disabled]=\"IsLoading\">\n <span class=\"share-avatar\">{{ getUserInitials(user) }}</span>\n <span class=\"share-user-name\">{{ user.Name }}</span>\n @if (user.Email) {\n <span class=\"share-user-email\">{{ user.Email }}</span>\n }\n <i class=\"fa-solid fa-plus\"></i>\n </button>\n }\n </div>\n }\n @if (UserSearchFilter && FilteredAvailableUsers.length === 0) {\n <div class=\"share-no-results\">\n No users found matching \"{{ UserSearchFilter }}\"\n </div>\n }\n </div>\n <!-- People with Access -->\n <div class=\"share-section\">\n <div class=\"share-section-label\">People with access</div>\n @if (Context?.OwnerDisplayName) {\n <div class=\"share-person share-owner\">\n <span class=\"share-avatar share-avatar-owner\">\n <i class=\"fa-solid fa-crown\"></i>\n </span>\n <div class=\"share-person-info\">\n <span class=\"share-person-name\">{{ Context?.OwnerDisplayName }}</span>\n <span class=\"share-person-role\">Owner</span>\n </div>\n </div>\n }\n @for (share of ActiveShares; track share.User.ID) {\n <div class=\"share-person\" [class.share-person-new]=\"share.IsNew\" [class.share-person-modified]=\"isModified(share)\">\n <span class=\"share-avatar\">{{ getUserInitials(share.User) }}</span>\n <div class=\"share-person-info\">\n <span class=\"share-person-name\">\n {{ share.User.Name }}\n @if (share.IsNew) {\n <span class=\"share-badge-new\">New</span>\n }\n </span>\n @if (share.User.Email) {\n <span class=\"share-person-email\">{{ share.User.Email }}</span>\n }\n </div>\n <div class=\"share-level-select\" role=\"group\" aria-label=\"Permission level\">\n @for (level of Levels; track level) {\n <button type=\"button\"\n class=\"share-level-btn\"\n [class.active]=\"share.Level === level\"\n (click)=\"setLevel(share, level)\"\n [title]=\"level + ' access'\">\n {{ level }}\n </button>\n }\n </div>\n <button type=\"button\" class=\"share-remove-btn\" (click)=\"removeUserShare(share)\" title=\"Remove\">\n <i class=\"fa-solid fa-times\"></i>\n </button>\n </div>\n }\n @if (ActiveShares.length === 0) {\n <div class=\"share-empty\">\n <i class=\"fa-solid fa-user-lock\"></i>\n <span>Not shared with anyone yet</span>\n </div>\n }\n @if (RemovedShares.length > 0) {\n <div class=\"share-removed-section\">\n @for (share of RemovedShares; track share.User.ID) {\n <div class=\"share-removed-item\">\n <span class=\"share-avatar share-avatar-removed\">{{ getUserInitials(share.User) }}</span>\n <span class=\"share-removed-name\">{{ share.User.Name }}</span>\n <button type=\"button\" class=\"share-undo-btn\" (click)=\"undoRemove(share)\">\n <i class=\"fa-solid fa-undo\"></i> Undo\n </button>\n </div>\n }\n </div>\n }\n </div>\n <div class=\"share-footer\">\n @if (HasChanges) {\n <span class=\"share-changes-hint\">\n <i class=\"fa-solid fa-circle\"></i> Unsaved changes\n </span>\n }\n <div class=\"share-footer-actions\">\n <button type=\"button\" class=\"share-btn share-btn-secondary\" (click)=\"onCancel()\">\n Cancel\n </button>\n <button\n type=\"button\"\n class=\"share-btn share-btn-primary\"\n (click)=\"onSave()\"\n [disabled]=\"!HasChanges || IsLoading\">\n @if (IsLoading) {\n <i class=\"fa-solid fa-spinner fa-spin\"></i> Saving...\n } @else {\n Save\n }\n </button>\n </div>\n </div>\n </div>\n </mj-window>\n}\n"]}
|