@jskit-ai/users-web 0.1.52 → 0.1.54

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. package/package.descriptor.mjs +14 -96
  2. package/package.json +16 -11
  3. package/src/client/account-settings/sections.js +74 -0
  4. package/src/client/composables/account-settings/accountSettingsRuntimeHelpers.js +2 -38
  5. package/src/client/composables/crud/crudLookupFieldRuntime.js +2 -2
  6. package/src/client/composables/internal/crudListParentTitleSupport.js +1 -1
  7. package/src/client/composables/internal/useOperationScope.js +12 -12
  8. package/src/client/composables/records/useAddEdit.js +2 -2
  9. package/src/client/composables/records/useList.js +3 -3
  10. package/src/client/composables/records/useView.js +2 -2
  11. package/src/client/composables/support/scopeHelpers.js +19 -19
  12. package/src/client/composables/useAccess.js +3 -3
  13. package/src/client/composables/useAccountSettingsRuntime.js +8 -156
  14. package/src/client/composables/useCommand.js +2 -2
  15. package/src/client/composables/useCrudListParentTitle.js +2 -2
  16. package/src/client/composables/usePaths.js +50 -38
  17. package/src/client/composables/useScopeRuntime.js +55 -27
  18. package/src/client/composables/useSurfaceRouteContext.js +1 -7
  19. package/src/client/index.js +0 -1
  20. package/src/client/lib/bootstrap.js +0 -63
  21. package/src/client/lib/httpClient.js +2 -59
  22. package/src/client/lib/theme.js +12 -189
  23. package/src/client/providers/UsersWebClientProvider.js +2 -25
  24. package/src/client/providers/bootUsersWebClientProvider.js +28 -0
  25. package/src/shared/toolsOutletContracts.js +1 -8
  26. package/templates/src/components/account/settings/AccountSettingsClientElement.vue +33 -21
  27. package/test/accountSettingsSections.test.js +79 -0
  28. package/test/exportsContract.test.js +2 -2
  29. package/test/scopeHelpers.test.js +6 -6
  30. package/test/settingsPlacementContract.test.js +4 -49
  31. package/test/theme.test.js +0 -56
  32. package/src/client/components/ConsoleSettingsClientElement.vue +0 -24
  33. package/src/client/components/MembersAdminClientElement.vue +0 -400
  34. package/src/client/components/UsersProfileSurfaceSwitchMenuItem.vue +0 -39
  35. package/src/client/components/UsersWorkspaceMembersMenuItem.vue +0 -36
  36. package/src/client/components/UsersWorkspacePermissionMenuItem.vue +0 -90
  37. package/src/client/components/UsersWorkspaceSelector.vue +0 -248
  38. package/src/client/components/UsersWorkspaceSettingsMenuItem.vue +0 -39
  39. package/src/client/components/UsersWorkspaceToolsWidget.vue +0 -12
  40. package/src/client/components/WorkspaceMembersClientElement.vue +0 -655
  41. package/src/client/components/WorkspaceProfileClientElement.vue +0 -116
  42. package/src/client/components/WorkspaceSettingsClientElement.vue +0 -102
  43. package/src/client/components/WorkspaceSettingsFieldsClientElement.vue +0 -265
  44. package/src/client/components/WorkspacesClientElement.vue +0 -509
  45. package/src/client/composables/account-settings/accountSettingsInvitesRuntime.js +0 -88
  46. package/src/client/composables/useBootstrapQuery.js +0 -52
  47. package/src/client/composables/useWorkspaceRouteContext.js +0 -28
  48. package/src/client/composables/useWorkspaceSurfaceId.js +0 -43
  49. package/src/client/lib/menuIcons.js +0 -210
  50. package/src/client/lib/profileSurfaceMenuLinks.js +0 -142
  51. package/src/client/lib/surfaceAccessPolicy.js +0 -350
  52. package/src/client/lib/workspaceLinkResolver.js +0 -207
  53. package/src/client/lib/workspaceSurfaceContext.js +0 -82
  54. package/src/client/lib/workspaceSurfacePaths.js +0 -163
  55. package/src/client/providers/UsersWorkspacesClientProvider.js +0 -24
  56. package/src/client/runtime/bootstrapPlacementRouteGuards.js +0 -371
  57. package/src/client/runtime/bootstrapPlacementRuntime.js +0 -463
  58. package/src/client/runtime/bootstrapPlacementRuntimeConstants.js +0 -28
  59. package/src/client/runtime/bootstrapPlacementRuntimeHelpers.js +0 -147
  60. package/src/client/support/menuLinkTarget.js +0 -93
  61. package/src/client/support/realtimeWorkspace.js +0 -21
  62. package/src/client/support/runtimeNormalization.js +0 -27
  63. package/src/client/support/workspaceQueryKeys.js +0 -15
  64. package/templates/src/components/account/settings/AccountSettingsInvitesSection.vue +0 -77
  65. package/templates/src/pages/console/settings/index.vue +0 -8
  66. package/templates/src/pages/console/settings.vue +0 -32
  67. package/test/bootstrapPlacementRuntime.test.js +0 -1095
  68. package/test/menuIcons.test.js +0 -35
  69. package/test/menuLinkTarget.test.js +0 -116
  70. package/test/profileSurfaceMenuLinks.test.js +0 -207
  71. package/test/surfaceAccessPolicy.test.js +0 -129
  72. package/test/workspaceLinkResolver.test.js +0 -61
  73. package/test/workspaceSurfacePaths.test.js +0 -39
@@ -1,655 +0,0 @@
1
- <template>
2
- <section class="workspace-members-page">
3
- <p v-if="loadError" class="text-body-2 text-medium-emphasis mb-4">
4
- {{ loadError }}
5
- </p>
6
-
7
- <MembersAdminClientElement
8
- v-else
9
- :forms="forms"
10
- :options="options"
11
- :collections="collections"
12
- :permissions="permissionState"
13
- :revokeInviteId="revokeInviteId"
14
- :removeMemberUserId="removeMemberUserId"
15
- :status="status"
16
- :actions="actions"
17
- />
18
- </section>
19
- </template>
20
-
21
- <script setup>
22
- import { computed, reactive, ref, watch } from "vue";
23
- import { formatDateTime } from "@jskit-ai/kernel/shared/support";
24
- import { normalizeRecordId } from "@jskit-ai/kernel/shared/support/normalize";
25
- import MembersAdminClientElement from "./MembersAdminClientElement.vue";
26
- import { useCommand } from "../composables/useCommand.js";
27
- import { useList } from "../composables/records/useList.js";
28
- import { useView } from "../composables/records/useView.js";
29
- import { usePaths } from "../composables/usePaths.js";
30
- import { useAccess } from "../composables/useAccess.js";
31
- import { useUiFeedback } from "../composables/runtime/useUiFeedback.js";
32
- import { useWorkspaceRouteContext } from "../composables/useWorkspaceRouteContext.js";
33
- import { useShellWebErrorRuntime } from "@jskit-ai/shell-web/client/error";
34
- import { createWorkspaceRealtimeMatcher } from "../support/realtimeWorkspace.js";
35
- import { buildWorkspaceQueryKey } from "../support/workspaceQueryKeys.js";
36
- import { USERS_ROUTE_VISIBILITY_WORKSPACE } from "@jskit-ai/users-core/shared/support/usersVisibility";
37
-
38
- const forms = reactive({
39
- invite: {
40
- email: "",
41
- roleSid: "member"
42
- },
43
- workspace: {
44
- invitesEnabled: false,
45
- invitesAvailable: false
46
- }
47
- });
48
-
49
- const options = reactive({
50
- inviteRoleOptions: [],
51
- memberRoleOptions: [],
52
- formatDateTime(value) {
53
- return formatDateTime(value);
54
- }
55
- });
56
-
57
- const collections = reactive({
58
- members: [],
59
- invites: []
60
- });
61
-
62
- const inviteFeedback = useUiFeedback();
63
- const membersFeedback = useUiFeedback();
64
- const teamFeedback = useUiFeedback();
65
- const revokeInviteId = ref("");
66
- const removeMemberUserId = ref("");
67
-
68
- const { route, currentSurfaceId, workspaceSlugFromRoute, mergePlacementContext } =
69
- useWorkspaceRouteContext();
70
- const usersPaths = usePaths();
71
- const errorRuntime = useShellWebErrorRuntime();
72
- const OWNERSHIP_WORKSPACE = USERS_ROUTE_VISIBILITY_WORKSPACE;
73
-
74
- const hasRouteWorkspaceSlug = computed(() => Boolean(workspaceSlugFromRoute.value));
75
- const workspaceMembersApiPath = computed(() =>
76
- usersPaths.api("/members", {
77
- workspaceSlug: workspaceSlugFromRoute.value
78
- })
79
- );
80
- const workspaceInvitesApiPath = computed(() =>
81
- usersPaths.api("/invites", {
82
- workspaceSlug: workspaceSlugFromRoute.value
83
- })
84
- );
85
-
86
- function workspaceMembersPath(memberId) {
87
- const normalizedMemberId = encodeURIComponent(String(memberId || "").trim());
88
- return `${workspaceMembersApiPath.value}/${normalizedMemberId}`;
89
- }
90
-
91
- function workspaceInvitePath(inviteId) {
92
- const encodedInviteId = encodeURIComponent(String(inviteId || ""));
93
- return `${workspaceInvitesApiPath.value}/${encodedInviteId}`;
94
- }
95
-
96
- const access = useAccess({
97
- workspaceSlug: workspaceSlugFromRoute,
98
- enabled: hasRouteWorkspaceSlug,
99
- mergePlacementContext,
100
- placementSource: "users-web.workspace-members-view"
101
- });
102
-
103
- const matchesWorkspaceRealtime = createWorkspaceRealtimeMatcher(workspaceSlugFromRoute);
104
-
105
- const canViewMembers = computed(() => {
106
- return access.canAny(["workspace.members.view", "workspace.members.manage"]);
107
- });
108
-
109
- const canInviteMembers = computed(() => {
110
- return access.can("workspace.members.invite");
111
- });
112
-
113
- const canManageMembers = computed(() => {
114
- return access.can("workspace.members.manage");
115
- });
116
-
117
- const canRevokeInvites = computed(() => {
118
- return access.can("workspace.invites.revoke");
119
- });
120
-
121
- const permissionState = computed(() => {
122
- return {
123
- canViewMembers: canViewMembers.value,
124
- canInviteMembers: canInviteMembers.value,
125
- canManageMembers: canManageMembers.value,
126
- canRevokeInvites: canRevokeInvites.value
127
- };
128
- });
129
-
130
- function resetMessages() {
131
- inviteFeedback.clear();
132
- membersFeedback.clear();
133
- teamFeedback.clear();
134
- }
135
-
136
- function clearRoleOptions() {
137
- options.inviteRoleOptions = [];
138
- options.memberRoleOptions = [];
139
- }
140
-
141
- function resetViewState() {
142
- resetMessages();
143
- forms.invite.email = "";
144
- forms.invite.roleSid = "member";
145
- forms.workspace.invitesEnabled = false;
146
- forms.workspace.invitesAvailable = false;
147
- collections.members = [];
148
- collections.invites = [];
149
- clearRoleOptions();
150
- revokeInviteId.value = "";
151
- removeMemberUserId.value = "";
152
- }
153
-
154
- function toRoleTitle(roleSid) {
155
- const normalizedRoleId = String(roleSid || "").trim();
156
- if (!normalizedRoleId) {
157
- return "";
158
- }
159
- return normalizedRoleId.charAt(0).toUpperCase() + normalizedRoleId.slice(1);
160
- }
161
-
162
- function normalizeRoleCatalog(payload = {}) {
163
- const source =
164
- payload?.roleCatalog && typeof payload.roleCatalog === "object"
165
- ? payload.roleCatalog
166
- : payload && typeof payload === "object"
167
- ? payload
168
- : {};
169
-
170
- const roles = Array.isArray(source.roles) ? source.roles : [];
171
- const assignableRoleIdsFromCatalog = Array.isArray(source.assignableRoleIds)
172
- ? source.assignableRoleIds
173
- : [];
174
-
175
- let assignableRoleIds = assignableRoleIdsFromCatalog
176
- .map((entry) => String(entry || "").trim().toLowerCase())
177
- .filter(Boolean);
178
-
179
- if (assignableRoleIds.length < 1) {
180
- assignableRoleIds = roles
181
- .filter((entry) => entry?.assignable === true)
182
- .map((entry) => String(entry?.id || "").trim().toLowerCase())
183
- .filter(Boolean);
184
- }
185
-
186
- const uniqueRoleIds = Array.from(new Set(assignableRoleIds));
187
- const roleOptions = uniqueRoleIds.map((roleSid) => ({
188
- title: toRoleTitle(roleSid),
189
- value: roleSid
190
- }));
191
-
192
- const defaultInviteRole = String(source.defaultInviteRole || "")
193
- .trim()
194
- .toLowerCase();
195
-
196
- return {
197
- roleOptions,
198
- defaultInviteRole
199
- };
200
- }
201
-
202
- function applyRoleCatalog(payload = {}) {
203
- const normalizedCatalog = normalizeRoleCatalog(payload);
204
- options.inviteRoleOptions = [...normalizedCatalog.roleOptions];
205
- options.memberRoleOptions = [...normalizedCatalog.roleOptions];
206
-
207
- const selectedInviteRole = String(forms.invite.roleSid || "").trim().toLowerCase();
208
- const hasSelectedInviteRole = normalizedCatalog.roleOptions.some((entry) => entry.value === selectedInviteRole);
209
-
210
- if (
211
- normalizedCatalog.defaultInviteRole &&
212
- normalizedCatalog.roleOptions.some((entry) => entry.value === normalizedCatalog.defaultInviteRole)
213
- ) {
214
- forms.invite.roleSid = normalizedCatalog.defaultInviteRole;
215
- return;
216
- }
217
-
218
- if (!hasSelectedInviteRole && normalizedCatalog.roleOptions.length > 0) {
219
- forms.invite.roleSid = normalizedCatalog.roleOptions[0].value;
220
- }
221
- }
222
-
223
- function normalizeMembers(entries) {
224
- const source = Array.isArray(entries) ? entries : [];
225
- return source.map((entry) => {
226
- const value = entry && typeof entry === "object" ? entry : {};
227
- return {
228
- userId: normalizeRecordId(value.userId, { fallback: "" }),
229
- roleSid: String(value.roleSid || "").trim().toLowerCase(),
230
- status: String(value.status || "").trim().toLowerCase(),
231
- displayName: String(value.displayName || "").trim(),
232
- email: String(value.email || "").trim().toLowerCase(),
233
- isOwner: Boolean(value.isOwner)
234
- };
235
- });
236
- }
237
-
238
- function normalizeInvites(entries) {
239
- const source = Array.isArray(entries) ? entries : [];
240
- return source.map((entry) => {
241
- const value = entry && typeof entry === "object" ? entry : {};
242
- return {
243
- id: normalizeRecordId(value.id, { fallback: "" }),
244
- email: String(value.email || "").trim().toLowerCase(),
245
- roleSid: String(value.roleSid || "").trim().toLowerCase(),
246
- status: String(value.status || "").trim().toLowerCase(),
247
- expiresAt: value.expiresAt || "",
248
- invitedByUserId: normalizeRecordId(value.invitedByUserId, { fallback: null })
249
- };
250
- });
251
- }
252
-
253
- function latestPage(pages) {
254
- if (!Array.isArray(pages) || pages.length < 1) {
255
- return null;
256
- }
257
-
258
- return pages[pages.length - 1];
259
- }
260
-
261
- function applyWorkspaceSettingsPolicy(payload = {}) {
262
- const settings = payload?.settings && typeof payload.settings === "object" ? payload.settings : {};
263
- forms.workspace.invitesEnabled = settings.invitesEnabled !== false;
264
- forms.workspace.invitesAvailable = settings.invitesAvailable !== false;
265
- }
266
-
267
- const workspaceSettingsView = useView({
268
- ownershipFilter: OWNERSHIP_WORKSPACE,
269
- apiSuffix: "/settings",
270
- queryKeyFactory: (surfaceId = "", workspaceSlug = "") =>
271
- buildWorkspaceQueryKey("settings", surfaceId, workspaceSlug),
272
- viewPermissions: ["workspace.members.invite"],
273
- realtime: {
274
- event: "workspace.settings.changed",
275
- matches: matchesWorkspaceRealtime
276
- },
277
- fallbackLoadError: "Unable to load workspace settings."
278
- });
279
-
280
- const workspaceRolesView = useView({
281
- ownershipFilter: OWNERSHIP_WORKSPACE,
282
- apiSuffix: "/roles",
283
- queryKeyFactory: (surfaceId = "", workspaceSlug = "") => buildWorkspaceQueryKey("roles", surfaceId, workspaceSlug),
284
- viewPermissions: ["workspace.members.view", "workspace.members.invite", "workspace.members.manage"],
285
- fallbackLoadError: "Unable to load workspace roles."
286
- });
287
-
288
- const workspaceMembersList = useList({
289
- ownershipFilter: OWNERSHIP_WORKSPACE,
290
- apiSuffix: "/members",
291
- queryKeyFactory: (surfaceId = "", workspaceSlug = "") =>
292
- buildWorkspaceQueryKey("members", surfaceId, workspaceSlug),
293
- viewPermissions: ["workspace.members.view", "workspace.members.manage"],
294
- realtime: {
295
- event: "workspace.members.changed",
296
- matches: matchesWorkspaceRealtime
297
- },
298
- selectItems: (payload) => normalizeMembers(payload?.members),
299
- fallbackLoadError: "Unable to load workspace members."
300
- });
301
-
302
- const workspaceInvitesList = useList({
303
- ownershipFilter: OWNERSHIP_WORKSPACE,
304
- apiSuffix: "/invites",
305
- queryKeyFactory: (surfaceId = "", workspaceSlug = "") =>
306
- buildWorkspaceQueryKey("invites", surfaceId, workspaceSlug),
307
- viewPermissions: ["workspace.members.view", "workspace.members.manage"],
308
- realtime: {
309
- event: "workspace.invites.changed",
310
- matches: matchesWorkspaceRealtime
311
- },
312
- selectItems: (payload) => normalizeInvites(payload?.invites),
313
- fallbackLoadError: "Unable to load workspace invites."
314
- });
315
-
316
- const inviteCreateCommand = useCommand({
317
- ownershipFilter: OWNERSHIP_WORKSPACE,
318
- apiSuffix: "/invites",
319
- runPermissions: ["workspace.members.invite"],
320
- writeMethod: "POST",
321
- fallbackRunError: "Unable to send invite.",
322
- buildRawPayload: () => ({
323
- email: forms.invite.email,
324
- roleSid: forms.invite.roleSid
325
- }),
326
- messages: {
327
- success: "Invite sent.",
328
- error: "Unable to send invite."
329
- }
330
- });
331
-
332
- const revokeInviteCommand = useCommand({
333
- ownershipFilter: OWNERSHIP_WORKSPACE,
334
- apiSuffix: "/invites",
335
- runPermissions: ["workspace.invites.revoke"],
336
- writeMethod: "DELETE",
337
- fallbackRunError: "Unable to revoke invite.",
338
- buildCommandOptions: (_parsed, { context }) => {
339
- return {
340
- method: "DELETE",
341
- path: workspaceInvitePath(context?.inviteId)
342
- };
343
- },
344
- messages: {
345
- success: "Invite revoked.",
346
- error: "Unable to revoke invite."
347
- }
348
- });
349
-
350
- const memberRoleCommand = useCommand({
351
- ownershipFilter: OWNERSHIP_WORKSPACE,
352
- apiSuffix: "/members",
353
- runPermissions: ["workspace.members.manage"],
354
- writeMethod: "PATCH",
355
- fallbackRunError: "Unable to update member role.",
356
- buildRawPayload: (_model, { context }) => ({
357
- roleSid: String(context?.roleSid || "").trim().toLowerCase()
358
- }),
359
- buildCommandOptions: (_parsed, { context }) => {
360
- return {
361
- method: "PATCH",
362
- path: `${workspaceMembersPath(context?.memberUserId)}/role`
363
- };
364
- },
365
- messages: {
366
- success: "Member role updated.",
367
- error: "Unable to update member role."
368
- }
369
- });
370
-
371
- const memberRemoveCommand = useCommand({
372
- ownershipFilter: OWNERSHIP_WORKSPACE,
373
- apiSuffix: "/members",
374
- runPermissions: ["workspace.members.manage"],
375
- writeMethod: "DELETE",
376
- fallbackRunError: "Unable to remove member.",
377
- buildCommandOptions: (_parsed, { context }) => {
378
- return {
379
- method: "DELETE",
380
- path: workspaceMembersPath(context?.memberUserId)
381
- };
382
- },
383
- messages: {
384
- success: "Member removed.",
385
- error: "Unable to remove member."
386
- }
387
- });
388
-
389
- const status = computed(() => {
390
- return {
391
- isCreatingInvite: Boolean(inviteCreateCommand.isRunning.value),
392
- isRevokingInvite: Boolean(revokeInviteCommand.isRunning.value),
393
- isRemovingMember: Boolean(memberRemoveCommand.isRunning.value),
394
- hasLoadedWorkspaceSettings: !canInviteMembers.value || !workspaceSettingsView.isLoading,
395
- hasLoadedMembersList: !canViewMembers.value || !workspaceMembersList.isInitialLoading,
396
- hasLoadedInviteList: !canViewMembers.value || !workspaceInvitesList.isInitialLoading,
397
- isRefreshingWorkspaceSettings: canInviteMembers.value && Boolean(workspaceSettingsView.isRefetching),
398
- isRefreshingMembersList: canViewMembers.value && Boolean(workspaceMembersList.isRefetching),
399
- isRefreshingInviteList: canViewMembers.value && Boolean(workspaceInvitesList.isRefetching)
400
- };
401
- });
402
-
403
- const loadError = computed(() => {
404
- if (!hasRouteWorkspaceSlug.value) {
405
- return "Workspace slug is required in the URL.";
406
- }
407
-
408
- return access.bootstrapError.value;
409
- });
410
-
411
- watch(
412
- loadError,
413
- (nextLoadError) => {
414
- if (!nextLoadError) {
415
- return;
416
- }
417
- errorRuntime.report({
418
- source: "users-web.workspace-members-view",
419
- severity: "error",
420
- channel: "banner",
421
- message: String(nextLoadError || "Unable to load workspace members."),
422
- dedupeKey: `users-web.workspace-members-view:bootstrap:${nextLoadError}`,
423
- dedupeWindowMs: 3000
424
- });
425
- },
426
- { immediate: true }
427
- );
428
-
429
- const actions = Object.freeze({
430
- submitInvite,
431
- submitRevokeInvite,
432
- submitMemberRoleUpdate,
433
- submitRemoveMember
434
- });
435
-
436
- watch(
437
- () => `${currentSurfaceId.value}:${workspaceSlugFromRoute.value}`,
438
- () => {
439
- resetViewState();
440
- },
441
- { immediate: true }
442
- );
443
-
444
- watch(
445
- () => workspaceSettingsView.record,
446
- (payload) => {
447
- if (!payload) {
448
- return;
449
- }
450
- applyWorkspaceSettingsPolicy(payload);
451
- },
452
- { immediate: true }
453
- );
454
-
455
- watch(
456
- () => workspaceSettingsView.loadError,
457
- (nextLoadError) => {
458
- if (!nextLoadError) {
459
- return;
460
- }
461
- forms.workspace.invitesEnabled = false;
462
- forms.workspace.invitesAvailable = false;
463
- }
464
- );
465
-
466
- watch(
467
- () => workspaceRolesView.record,
468
- (payload) => {
469
- if (!payload) {
470
- return;
471
- }
472
- applyRoleCatalog(payload);
473
- },
474
- { immediate: true }
475
- );
476
-
477
- watch(
478
- () => workspaceRolesView.loadError,
479
- (nextLoadError) => {
480
- if (!nextLoadError) {
481
- return;
482
- }
483
- clearRoleOptions();
484
- }
485
- );
486
-
487
- watch(
488
- () => workspaceMembersList.items,
489
- (nextMembers) => {
490
- collections.members = Array.isArray(nextMembers) ? [...nextMembers] : [];
491
- },
492
- { immediate: true }
493
- );
494
-
495
- watch(
496
- () => workspaceMembersList.pages,
497
- (pages) => {
498
- const payload = latestPage(pages);
499
- if (!payload) {
500
- return;
501
- }
502
- applyRoleCatalog(payload);
503
- },
504
- { immediate: true }
505
- );
506
-
507
- watch(
508
- () => workspaceMembersList.loadError,
509
- (nextLoadError) => {
510
- if (!nextLoadError) {
511
- membersFeedback.clear();
512
- return;
513
- }
514
- membersFeedback.error(null, nextLoadError);
515
- }
516
- );
517
-
518
- watch(
519
- () => workspaceInvitesList.items,
520
- (nextInvites) => {
521
- collections.invites = Array.isArray(nextInvites) ? [...nextInvites] : [];
522
- },
523
- { immediate: true }
524
- );
525
-
526
- watch(
527
- () => workspaceInvitesList.pages,
528
- (pages) => {
529
- const payload = latestPage(pages);
530
- if (!payload) {
531
- return;
532
- }
533
- applyRoleCatalog(payload);
534
- },
535
- { immediate: true }
536
- );
537
-
538
- watch(
539
- () => workspaceInvitesList.loadError,
540
- (nextLoadError) => {
541
- if (!nextLoadError) {
542
- teamFeedback.clear();
543
- return;
544
- }
545
- teamFeedback.error(null, nextLoadError);
546
- }
547
- );
548
-
549
- watch(
550
- () => route.fullPath,
551
- () => {
552
- resetMessages();
553
- }
554
- );
555
-
556
- async function submitInvite() {
557
- if (inviteCreateCommand.isRunning.value || !canInviteMembers.value) {
558
- return;
559
- }
560
-
561
- inviteFeedback.clear();
562
-
563
- try {
564
- await inviteCreateCommand.run();
565
- forms.invite.email = "";
566
- await Promise.all([
567
- workspaceInvitesList.reload(),
568
- workspaceRolesView.refresh()
569
- ]);
570
- inviteFeedback.success("Invite sent.");
571
- } catch (error) {
572
- inviteFeedback.error(error, "Unable to send invite.");
573
- }
574
- }
575
-
576
- async function submitRevokeInvite(inviteId) {
577
- if (revokeInviteCommand.isRunning.value || !canRevokeInvites.value) {
578
- return;
579
- }
580
-
581
- revokeInviteId.value = normalizeRecordId(inviteId, { fallback: "" });
582
- teamFeedback.clear();
583
-
584
- try {
585
- await revokeInviteCommand.run({
586
- inviteId
587
- });
588
- await Promise.all([
589
- workspaceInvitesList.reload(),
590
- workspaceRolesView.refresh()
591
- ]);
592
- teamFeedback.success("Invite revoked.");
593
- } catch (error) {
594
- teamFeedback.error(error, "Unable to revoke invite.");
595
- } finally {
596
- revokeInviteId.value = "";
597
- }
598
- }
599
-
600
- async function submitMemberRoleUpdate(member, roleSid) {
601
- if (!canManageMembers.value) {
602
- return;
603
- }
604
-
605
- membersFeedback.clear();
606
-
607
- try {
608
- const memberUserId = normalizeRecordId(member?.userId, { fallback: null });
609
- if (!memberUserId) {
610
- throw new Error("Member user id is invalid.");
611
- }
612
-
613
- await memberRoleCommand.run({
614
- memberUserId,
615
- roleSid
616
- });
617
- await Promise.all([
618
- workspaceMembersList.reload(),
619
- workspaceRolesView.refresh()
620
- ]);
621
- membersFeedback.success("Member role updated.");
622
- } catch (error) {
623
- membersFeedback.error(error, "Unable to update member role.");
624
- }
625
- }
626
-
627
- async function submitRemoveMember(member) {
628
- if (memberRemoveCommand.isRunning.value || !canManageMembers.value) {
629
- return;
630
- }
631
-
632
- membersFeedback.clear();
633
-
634
- try {
635
- const memberUserId = normalizeRecordId(member?.userId, { fallback: null });
636
- if (!memberUserId) {
637
- throw new Error("Member user id is invalid.");
638
- }
639
-
640
- removeMemberUserId.value = normalizeRecordId(memberUserId, { fallback: "" });
641
- await memberRemoveCommand.run({
642
- memberUserId
643
- });
644
- await Promise.all([
645
- workspaceMembersList.reload(),
646
- workspaceRolesView.refresh()
647
- ]);
648
- membersFeedback.success("Member removed.");
649
- } catch (error) {
650
- membersFeedback.error(error, "Unable to remove member.");
651
- } finally {
652
- removeMemberUserId.value = "";
653
- }
654
- }
655
- </script>