@jskit-ai/users-web 0.1.36 → 0.1.38
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/package.descriptor.mjs +8 -22
- package/package.json +16 -11
- package/src/client/components/MembersAdminClientElement.vue +5 -5
- package/src/client/components/UsersSurfaceAwareMenuLinkItem.vue +14 -25
- package/src/client/components/WorkspaceMembersClientElement.vue +19 -19
- package/src/client/components/WorkspaceProfileClientElement.vue +1 -1
- package/src/client/components/WorkspaceSettingsFieldsClientElement.vue +1 -1
- package/src/client/components/WorkspacesClientElement.vue +4 -4
- package/src/client/composables/account-settings/accountSettingsAvatarUploadRuntime.js +61 -0
- package/src/client/composables/{accountSettingsInvitesRuntime.js → account-settings/accountSettingsInvitesRuntime.js} +1 -1
- package/src/client/composables/{accountSettingsRuntimeConstants.js → account-settings/accountSettingsRuntimeConstants.js} +0 -4
- package/src/client/composables/{accountSettingsRuntimeHelpers.js → account-settings/accountSettingsRuntimeHelpers.js} +2 -2
- package/src/client/composables/crud/crudBindingSupport.js +75 -0
- package/src/client/composables/{crudLookupFieldLabelSupport.js → crud/crudLookupFieldLabelSupport.js} +37 -5
- package/src/client/composables/{crudLookupFieldRuntime.js → crud/crudLookupFieldRuntime.js} +11 -4
- package/src/client/composables/{crudSchemaFormHelpers.js → crud/crudSchemaFormHelpers.js} +178 -5
- package/src/client/composables/internal/crudListParentTitleSupport.js +168 -0
- package/src/client/composables/internal/useOperationScope.js +1 -1
- package/src/client/composables/{useAddEdit.js → records/useAddEdit.js} +18 -8
- package/src/client/composables/{useCrudSchemaForm.js → records/useCrudAddEdit.js} +32 -15
- package/src/client/composables/records/useCrudList.js +83 -0
- package/src/client/composables/records/useCrudView.js +35 -0
- package/src/client/composables/records/useList.js +482 -0
- package/src/client/composables/{useView.js → records/useView.js} +7 -7
- package/src/client/composables/{addEditUiRuntime.js → runtime/addEditUiRuntime.js} +13 -4
- package/src/client/composables/{listUiRuntime.js → runtime/listUiRuntime.js} +20 -8
- package/src/client/composables/{operationAdapters.js → runtime/operationAdapters.js} +1 -1
- package/src/client/composables/{useEndpointResource.js → runtime/useEndpointResource.js} +5 -5
- package/src/client/composables/{useListCore.js → runtime/useListCore.js} +4 -4
- package/src/client/composables/{useUiFeedback.js → runtime/useUiFeedback.js} +1 -1
- package/src/client/composables/{viewUiRuntime.js → runtime/viewUiRuntime.js} +13 -4
- package/src/client/composables/support/listQueryParamSupport.js +459 -0
- package/src/client/composables/{routeTemplateHelpers.js → support/routeTemplateHelpers.js} +122 -0
- package/src/client/composables/useAccess.js +2 -2
- package/src/client/composables/useAccountSettingsRuntime.js +6 -6
- package/src/client/composables/useBootstrapQuery.js +1 -1
- package/src/client/composables/useCommand.js +5 -5
- package/src/client/composables/useCrudListParentTitle.js +131 -0
- package/src/client/composables/usePagedCollection.js +58 -7
- package/src/client/composables/useScopeRuntime.js +1 -1
- package/src/client/lib/bootstrap.js +1 -1
- package/src/client/lib/menuIcons.js +27 -6
- package/src/client/support/menuLinkTarget.js +93 -0
- package/templates/src/components/WorkspaceNotFoundCard.vue +2 -1
- package/templates/src/components/account/settings/AccountSettingsInvitesSection.vue +1 -1
- package/test/addEditUiRuntime.test.js +19 -1
- package/test/crudBindingSupport.test.js +110 -0
- package/test/crudLookupFieldRuntime.test.js +52 -2
- package/test/errorMessageHelpers.test.js +1 -1
- package/test/exportsContract.test.js +10 -1
- package/test/listQueryParamSupport.test.js +190 -0
- package/test/listUiRuntime.test.js +22 -1
- package/test/menuIcons.test.js +2 -0
- package/test/menuLinkTarget.test.js +116 -0
- package/test/permissions.test.js +2 -2
- package/test/refValueHelpers.test.js +1 -1
- package/test/resourceLoadStateHelpers.test.js +1 -1
- package/test/routeTemplateHelpers.test.js +57 -1
- package/test/scopeHelpers.test.js +1 -1
- package/test/{useCrudSchemaForm.test.js → useCrudAddEdit.test.js} +81 -1
- package/test/useCrudListParentTitle.test.js +143 -0
- package/test/useListSearchSupport.test.js +1 -1
- package/test/usePagedCollection.test.js +53 -0
- package/test/viewCoreLoading.test.js +1 -1
- package/test/viewUiRuntime.test.js +36 -1
- package/src/client/composables/accountSettingsAvatarUploadRuntime.js +0 -241
- package/src/client/composables/useList.js +0 -268
- /package/src/client/composables/{modelStateHelpers.js → runtime/modelStateHelpers.js} +0 -0
- /package/src/client/composables/{operationUiHelpers.js → runtime/operationUiHelpers.js} +0 -0
- /package/src/client/composables/{operationValidationHelpers.js → runtime/operationValidationHelpers.js} +0 -0
- /package/src/client/composables/{useAddEditCore.js → runtime/useAddEditCore.js} +0 -0
- /package/src/client/composables/{useCommandCore.js → runtime/useCommandCore.js} +0 -0
- /package/src/client/composables/{useFieldErrorBag.js → runtime/useFieldErrorBag.js} +0 -0
- /package/src/client/composables/{useViewCore.js → runtime/useViewCore.js} +0 -0
- /package/src/client/composables/{errorMessageHelpers.js → support/errorMessageHelpers.js} +0 -0
- /package/src/client/composables/{listSearchSupport.js → support/listSearchSupport.js} +0 -0
- /package/src/client/composables/{refValueHelpers.js → support/refValueHelpers.js} +0 -0
- /package/src/client/composables/{resourceLoadStateHelpers.js → support/resourceLoadStateHelpers.js} +0 -0
- /package/src/client/composables/{scopeHelpers.js → support/scopeHelpers.js} +0 -0
package/package.descriptor.mjs
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
export default Object.freeze({
|
|
2
2
|
packageVersion: 1,
|
|
3
3
|
packageId: "@jskit-ai/users-web",
|
|
4
|
-
version: "0.1.
|
|
4
|
+
version: "0.1.38",
|
|
5
5
|
kind: "runtime",
|
|
6
6
|
description: "Users web module: workspace selector shell element plus workspace/profile/members UI elements.",
|
|
7
7
|
dependsOn: [
|
|
8
8
|
"@jskit-ai/http-runtime",
|
|
9
9
|
"@jskit-ai/shell-web",
|
|
10
|
+
"@jskit-ai/uploads-image-web",
|
|
10
11
|
"@jskit-ai/users-core"
|
|
11
12
|
],
|
|
12
13
|
capabilities: {
|
|
@@ -32,17 +33,6 @@ export default Object.freeze({
|
|
|
32
33
|
}
|
|
33
34
|
},
|
|
34
35
|
metadata: {
|
|
35
|
-
client: {
|
|
36
|
-
optimizeDeps: {
|
|
37
|
-
include: [
|
|
38
|
-
"@uppy/core",
|
|
39
|
-
"@uppy/dashboard",
|
|
40
|
-
"@uppy/image-editor",
|
|
41
|
-
"@uppy/compressor",
|
|
42
|
-
"@uppy/xhr-upload"
|
|
43
|
-
]
|
|
44
|
-
}
|
|
45
|
-
},
|
|
46
36
|
apiSummary: {
|
|
47
37
|
surfaces: [
|
|
48
38
|
{
|
|
@@ -241,16 +231,12 @@ export default Object.freeze({
|
|
|
241
231
|
runtime: {
|
|
242
232
|
"@tanstack/vue-query": "5.92.12",
|
|
243
233
|
"@mdi/js": "^7.4.47",
|
|
244
|
-
"@
|
|
245
|
-
"@
|
|
246
|
-
"@
|
|
247
|
-
"@
|
|
248
|
-
"@
|
|
249
|
-
"@jskit-ai/
|
|
250
|
-
"@jskit-ai/realtime": "0.1.21",
|
|
251
|
-
"@jskit-ai/kernel": "0.1.22",
|
|
252
|
-
"@jskit-ai/shell-web": "0.1.21",
|
|
253
|
-
"@jskit-ai/users-core": "0.1.31",
|
|
234
|
+
"@jskit-ai/http-runtime": "0.1.23",
|
|
235
|
+
"@jskit-ai/realtime": "0.1.23",
|
|
236
|
+
"@jskit-ai/kernel": "0.1.24",
|
|
237
|
+
"@jskit-ai/shell-web": "0.1.23",
|
|
238
|
+
"@jskit-ai/uploads-image-web": "0.1.2",
|
|
239
|
+
"@jskit-ai/users-core": "0.1.33",
|
|
254
240
|
"vuetify": "^4.0.0"
|
|
255
241
|
},
|
|
256
242
|
dev: {}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jskit-ai/users-web",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.38",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"test": "node --test"
|
|
@@ -8,25 +8,30 @@
|
|
|
8
8
|
"exports": {
|
|
9
9
|
"./client": "./src/client/index.js",
|
|
10
10
|
"./client/components/WorkspaceMembersClientElement": "./src/client/components/WorkspaceMembersClientElement.vue",
|
|
11
|
-
"./client/composables/useAddEdit": "./src/client/composables/useAddEdit.js",
|
|
12
|
-
"./client/composables/
|
|
13
|
-
"./client/composables/crudLookupFieldRuntime": "./src/client/composables/crudLookupFieldRuntime.js",
|
|
14
|
-
"./client/composables/useList": "./src/client/composables/useList.js",
|
|
15
|
-
"./client/composables/
|
|
11
|
+
"./client/composables/useAddEdit": "./src/client/composables/records/useAddEdit.js",
|
|
12
|
+
"./client/composables/useCrudAddEdit": "./src/client/composables/records/useCrudAddEdit.js",
|
|
13
|
+
"./client/composables/crudLookupFieldRuntime": "./src/client/composables/crud/crudLookupFieldRuntime.js",
|
|
14
|
+
"./client/composables/useList": "./src/client/composables/records/useList.js",
|
|
15
|
+
"./client/composables/useCrudList": "./src/client/composables/records/useCrudList.js",
|
|
16
|
+
"./client/composables/useCrudListParentTitle": "./src/client/composables/useCrudListParentTitle.js",
|
|
17
|
+
"./client/composables/useView": "./src/client/composables/records/useView.js",
|
|
18
|
+
"./client/composables/useCrudView": "./src/client/composables/records/useCrudView.js",
|
|
16
19
|
"./client/composables/usePagedCollection": "./src/client/composables/usePagedCollection.js",
|
|
17
20
|
"./client/composables/useAccountSettingsRuntime": "./src/client/composables/useAccountSettingsRuntime.js",
|
|
18
21
|
"./client/composables/usePaths": "./src/client/composables/usePaths.js",
|
|
19
22
|
"./client/composables/useWorkspaceRouteContext": "./src/client/composables/useWorkspaceRouteContext.js",
|
|
23
|
+
"./client/support/menuLinkTarget": "./src/client/support/menuLinkTarget.js",
|
|
20
24
|
"./client/support/realtimeWorkspace": "./src/client/support/realtimeWorkspace.js"
|
|
21
25
|
},
|
|
22
26
|
"dependencies": {
|
|
23
27
|
"@tanstack/vue-query": "5.92.12",
|
|
24
28
|
"@mdi/js": "^7.4.47",
|
|
25
|
-
"@jskit-ai/
|
|
26
|
-
"@jskit-ai/
|
|
27
|
-
"@jskit-ai/
|
|
28
|
-
"@jskit-ai/
|
|
29
|
-
"@jskit-ai/
|
|
29
|
+
"@jskit-ai/http-runtime": "0.1.23",
|
|
30
|
+
"@jskit-ai/kernel": "0.1.24",
|
|
31
|
+
"@jskit-ai/realtime": "0.1.23",
|
|
32
|
+
"@jskit-ai/shell-web": "0.1.23",
|
|
33
|
+
"@jskit-ai/uploads-image-web": "0.1.2",
|
|
34
|
+
"@jskit-ai/users-core": "0.1.33",
|
|
30
35
|
"vuetify": "^4.0.0"
|
|
31
36
|
}
|
|
32
37
|
}
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
class="mb-3"
|
|
45
45
|
/>
|
|
46
46
|
<v-select
|
|
47
|
-
v-model="inviteForm.
|
|
47
|
+
v-model="inviteForm.roleSid"
|
|
48
48
|
label="Role"
|
|
49
49
|
:items="inviteRoleOptions"
|
|
50
50
|
item-title="title"
|
|
@@ -105,7 +105,7 @@
|
|
|
105
105
|
<template #append>
|
|
106
106
|
<div class="d-flex align-center ga-2">
|
|
107
107
|
<v-select
|
|
108
|
-
v-model="member.
|
|
108
|
+
v-model="member.roleSid"
|
|
109
109
|
:items="memberRoleOptions"
|
|
110
110
|
item-title="title"
|
|
111
111
|
item-value="value"
|
|
@@ -139,7 +139,7 @@
|
|
|
139
139
|
{{ invite.email }}
|
|
140
140
|
</template>
|
|
141
141
|
<template #subtitle>
|
|
142
|
-
Role: {{ invite.
|
|
142
|
+
Role: {{ invite.roleSid }} • expires {{ formatDateTime(invite.expiresAt) }}
|
|
143
143
|
</template>
|
|
144
144
|
<template #append>
|
|
145
145
|
<v-btn
|
|
@@ -375,12 +375,12 @@ async function onRevokeInvite(inviteId) {
|
|
|
375
375
|
await actionHandlers.submitRevokeInvite(inviteId);
|
|
376
376
|
}
|
|
377
377
|
|
|
378
|
-
async function onMemberRoleUpdate(member,
|
|
378
|
+
async function onMemberRoleUpdate(member, roleSid) {
|
|
379
379
|
if (isMemberRoleLocked(member)) {
|
|
380
380
|
return;
|
|
381
381
|
}
|
|
382
382
|
|
|
383
|
-
await actionHandlers.submitMemberRoleUpdate(member,
|
|
383
|
+
await actionHandlers.submitMemberRoleUpdate(member, roleSid);
|
|
384
384
|
}
|
|
385
385
|
|
|
386
386
|
async function onRemoveMember(member) {
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
<script setup>
|
|
2
2
|
import { computed } from "vue";
|
|
3
|
+
import { useRoute } from "vue-router";
|
|
3
4
|
import { useWebPlacementContext } from "@jskit-ai/shell-web/client/placement";
|
|
4
5
|
import { usePaths } from "../composables/usePaths.js";
|
|
5
|
-
import { surfaceRequiresWorkspaceFromPlacementContext } from "../lib/workspaceSurfaceContext.js";
|
|
6
6
|
import { resolveMenuLinkIcon } from "../lib/menuIcons.js";
|
|
7
|
+
import { resolveMenuLinkTarget } from "../support/menuLinkTarget.js";
|
|
7
8
|
|
|
8
9
|
const props = defineProps({
|
|
9
10
|
label: {
|
|
@@ -36,34 +37,22 @@ const props = defineProps({
|
|
|
36
37
|
}
|
|
37
38
|
});
|
|
38
39
|
|
|
40
|
+
const route = useRoute();
|
|
39
41
|
const paths = usePaths();
|
|
40
42
|
const { context: placementContext } = useWebPlacementContext();
|
|
41
43
|
|
|
42
|
-
const targetSurfaceId = computed(() => {
|
|
43
|
-
const explicitSurface = String(props.surface || "").trim().toLowerCase();
|
|
44
|
-
if (explicitSurface && explicitSurface !== "*") {
|
|
45
|
-
return explicitSurface;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
return String(paths.currentSurfaceId.value || "").trim().toLowerCase();
|
|
49
|
-
});
|
|
50
|
-
|
|
51
44
|
const resolvedTo = computed(() => {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
return paths.page(normalizedSuffix, {
|
|
65
|
-
surface: targetSurfaceId.value,
|
|
66
|
-
mode: "auto"
|
|
45
|
+
return resolveMenuLinkTarget({
|
|
46
|
+
to: props.to,
|
|
47
|
+
surface: props.surface,
|
|
48
|
+
currentSurfaceId: paths.currentSurfaceId.value,
|
|
49
|
+
placementContext: placementContext.value,
|
|
50
|
+
workspaceSuffix: props.workspaceSuffix,
|
|
51
|
+
nonWorkspaceSuffix: props.nonWorkspaceSuffix,
|
|
52
|
+
routeParams: route.params || {},
|
|
53
|
+
resolvePagePath(relativePath, options = {}) {
|
|
54
|
+
return paths.page(relativePath, options);
|
|
55
|
+
}
|
|
67
56
|
});
|
|
68
57
|
});
|
|
69
58
|
|
|
@@ -23,11 +23,11 @@ import { computed, reactive, ref, watch } from "vue";
|
|
|
23
23
|
import { formatDateTime } from "@jskit-ai/kernel/shared/support";
|
|
24
24
|
import MembersAdminClientElement from "./MembersAdminClientElement.vue";
|
|
25
25
|
import { useCommand } from "../composables/useCommand.js";
|
|
26
|
-
import { useList } from "../composables/useList.js";
|
|
27
|
-
import { useView } from "../composables/useView.js";
|
|
26
|
+
import { useList } from "../composables/records/useList.js";
|
|
27
|
+
import { useView } from "../composables/records/useView.js";
|
|
28
28
|
import { usePaths } from "../composables/usePaths.js";
|
|
29
29
|
import { useAccess } from "../composables/useAccess.js";
|
|
30
|
-
import { useUiFeedback } from "../composables/useUiFeedback.js";
|
|
30
|
+
import { useUiFeedback } from "../composables/runtime/useUiFeedback.js";
|
|
31
31
|
import { useWorkspaceRouteContext } from "../composables/useWorkspaceRouteContext.js";
|
|
32
32
|
import { useShellWebErrorRuntime } from "@jskit-ai/shell-web/client/error";
|
|
33
33
|
import { createWorkspaceRealtimeMatcher } from "../support/realtimeWorkspace.js";
|
|
@@ -37,7 +37,7 @@ import { USERS_ROUTE_VISIBILITY_WORKSPACE } from "@jskit-ai/users-core/shared/su
|
|
|
37
37
|
const forms = reactive({
|
|
38
38
|
invite: {
|
|
39
39
|
email: "",
|
|
40
|
-
|
|
40
|
+
roleSid: "member"
|
|
41
41
|
},
|
|
42
42
|
workspace: {
|
|
43
43
|
invitesEnabled: false,
|
|
@@ -139,7 +139,7 @@ function clearRoleOptions() {
|
|
|
139
139
|
function resetViewState() {
|
|
140
140
|
resetMessages();
|
|
141
141
|
forms.invite.email = "";
|
|
142
|
-
forms.invite.
|
|
142
|
+
forms.invite.roleSid = "member";
|
|
143
143
|
forms.workspace.invitesEnabled = false;
|
|
144
144
|
forms.workspace.invitesAvailable = false;
|
|
145
145
|
collections.members = [];
|
|
@@ -149,8 +149,8 @@ function resetViewState() {
|
|
|
149
149
|
removeMemberUserId.value = 0;
|
|
150
150
|
}
|
|
151
151
|
|
|
152
|
-
function toRoleTitle(
|
|
153
|
-
const normalizedRoleId = String(
|
|
152
|
+
function toRoleTitle(roleSid) {
|
|
153
|
+
const normalizedRoleId = String(roleSid || "").trim();
|
|
154
154
|
if (!normalizedRoleId) {
|
|
155
155
|
return "";
|
|
156
156
|
}
|
|
@@ -182,9 +182,9 @@ function normalizeRoleCatalog(payload = {}) {
|
|
|
182
182
|
}
|
|
183
183
|
|
|
184
184
|
const uniqueRoleIds = Array.from(new Set(assignableRoleIds));
|
|
185
|
-
const roleOptions = uniqueRoleIds.map((
|
|
186
|
-
title: toRoleTitle(
|
|
187
|
-
value:
|
|
185
|
+
const roleOptions = uniqueRoleIds.map((roleSid) => ({
|
|
186
|
+
title: toRoleTitle(roleSid),
|
|
187
|
+
value: roleSid
|
|
188
188
|
}));
|
|
189
189
|
|
|
190
190
|
const defaultInviteRole = String(source.defaultInviteRole || "")
|
|
@@ -202,19 +202,19 @@ function applyRoleCatalog(payload = {}) {
|
|
|
202
202
|
options.inviteRoleOptions = [...normalizedCatalog.roleOptions];
|
|
203
203
|
options.memberRoleOptions = [...normalizedCatalog.roleOptions];
|
|
204
204
|
|
|
205
|
-
const selectedInviteRole = String(forms.invite.
|
|
205
|
+
const selectedInviteRole = String(forms.invite.roleSid || "").trim().toLowerCase();
|
|
206
206
|
const hasSelectedInviteRole = normalizedCatalog.roleOptions.some((entry) => entry.value === selectedInviteRole);
|
|
207
207
|
|
|
208
208
|
if (
|
|
209
209
|
normalizedCatalog.defaultInviteRole &&
|
|
210
210
|
normalizedCatalog.roleOptions.some((entry) => entry.value === normalizedCatalog.defaultInviteRole)
|
|
211
211
|
) {
|
|
212
|
-
forms.invite.
|
|
212
|
+
forms.invite.roleSid = normalizedCatalog.defaultInviteRole;
|
|
213
213
|
return;
|
|
214
214
|
}
|
|
215
215
|
|
|
216
216
|
if (!hasSelectedInviteRole && normalizedCatalog.roleOptions.length > 0) {
|
|
217
|
-
forms.invite.
|
|
217
|
+
forms.invite.roleSid = normalizedCatalog.roleOptions[0].value;
|
|
218
218
|
}
|
|
219
219
|
}
|
|
220
220
|
|
|
@@ -224,7 +224,7 @@ function normalizeMembers(entries) {
|
|
|
224
224
|
const value = entry && typeof entry === "object" ? entry : {};
|
|
225
225
|
return {
|
|
226
226
|
userId: Number(value.userId || 0),
|
|
227
|
-
|
|
227
|
+
roleSid: String(value.roleSid || "").trim().toLowerCase(),
|
|
228
228
|
status: String(value.status || "").trim().toLowerCase(),
|
|
229
229
|
displayName: String(value.displayName || "").trim(),
|
|
230
230
|
email: String(value.email || "").trim().toLowerCase(),
|
|
@@ -240,7 +240,7 @@ function normalizeInvites(entries) {
|
|
|
240
240
|
return {
|
|
241
241
|
id: Number(value.id || 0),
|
|
242
242
|
email: String(value.email || "").trim().toLowerCase(),
|
|
243
|
-
|
|
243
|
+
roleSid: String(value.roleSid || "").trim().toLowerCase(),
|
|
244
244
|
status: String(value.status || "").trim().toLowerCase(),
|
|
245
245
|
expiresAt: value.expiresAt || "",
|
|
246
246
|
invitedByUserId: value.invitedByUserId == null ? null : Number(value.invitedByUserId)
|
|
@@ -319,7 +319,7 @@ const inviteCreateCommand = useCommand({
|
|
|
319
319
|
fallbackRunError: "Unable to send invite.",
|
|
320
320
|
buildRawPayload: () => ({
|
|
321
321
|
email: forms.invite.email,
|
|
322
|
-
|
|
322
|
+
roleSid: forms.invite.roleSid
|
|
323
323
|
}),
|
|
324
324
|
messages: {
|
|
325
325
|
success: "Invite sent.",
|
|
@@ -352,7 +352,7 @@ const memberRoleCommand = useCommand({
|
|
|
352
352
|
writeMethod: "PATCH",
|
|
353
353
|
fallbackRunError: "Unable to update member role.",
|
|
354
354
|
buildRawPayload: (_model, { context }) => ({
|
|
355
|
-
|
|
355
|
+
roleSid: String(context?.roleSid || "").trim().toLowerCase()
|
|
356
356
|
}),
|
|
357
357
|
buildCommandOptions: (_parsed, { context }) => {
|
|
358
358
|
return {
|
|
@@ -595,7 +595,7 @@ async function submitRevokeInvite(inviteId) {
|
|
|
595
595
|
}
|
|
596
596
|
}
|
|
597
597
|
|
|
598
|
-
async function submitMemberRoleUpdate(member,
|
|
598
|
+
async function submitMemberRoleUpdate(member, roleSid) {
|
|
599
599
|
if (!canManageMembers.value) {
|
|
600
600
|
return;
|
|
601
601
|
}
|
|
@@ -610,7 +610,7 @@ async function submitMemberRoleUpdate(member, roleId) {
|
|
|
610
610
|
|
|
611
611
|
await memberRoleCommand.run({
|
|
612
612
|
memberUserId,
|
|
613
|
-
|
|
613
|
+
roleSid
|
|
614
614
|
});
|
|
615
615
|
await Promise.all([
|
|
616
616
|
workspaceMembersList.reload(),
|
|
@@ -71,7 +71,7 @@ import { computed, reactive } from "vue";
|
|
|
71
71
|
import { validateOperationSection } from "@jskit-ai/http-runtime/shared/validators/operationValidation";
|
|
72
72
|
import { workspaceResource } from "@jskit-ai/users-core/shared/resources/workspaceResource";
|
|
73
73
|
import { USERS_ROUTE_VISIBILITY_WORKSPACE } from "@jskit-ai/users-core/shared/support/usersVisibility";
|
|
74
|
-
import { useAddEdit } from "../composables/useAddEdit.js";
|
|
74
|
+
import { useAddEdit } from "../composables/records/useAddEdit.js";
|
|
75
75
|
import { buildWorkspaceQueryKey } from "../support/workspaceQueryKeys.js";
|
|
76
76
|
|
|
77
77
|
const emit = defineEmits(["saved"]);
|
|
@@ -176,7 +176,7 @@ import {
|
|
|
176
176
|
DEFAULT_WORKSPACE_LIGHT_PALETTE,
|
|
177
177
|
resolveWorkspaceThemePalettes
|
|
178
178
|
} from "@jskit-ai/users-core/shared/settings";
|
|
179
|
-
import { useAddEdit } from "../composables/useAddEdit.js";
|
|
179
|
+
import { useAddEdit } from "../composables/records/useAddEdit.js";
|
|
180
180
|
import { useWorkspaceRouteContext } from "../composables/useWorkspaceRouteContext.js";
|
|
181
181
|
import { createWorkspaceRealtimeMatcher } from "../support/realtimeWorkspace.js";
|
|
182
182
|
import { buildWorkspaceQueryKey } from "../support/workspaceQueryKeys.js";
|
|
@@ -8,12 +8,12 @@ import {
|
|
|
8
8
|
import { useShellWebErrorRuntime } from "@jskit-ai/shell-web/client/error";
|
|
9
9
|
import { normalizeWorkspaceList } from "../lib/bootstrap.js";
|
|
10
10
|
import { useCommand } from "../composables/useCommand.js";
|
|
11
|
-
import { useView } from "../composables/useView.js";
|
|
11
|
+
import { useView } from "../composables/records/useView.js";
|
|
12
12
|
import { usePaths } from "../composables/usePaths.js";
|
|
13
13
|
import { useRealtimeQueryInvalidation } from "../composables/useRealtimeQueryInvalidation.js";
|
|
14
14
|
import { useWorkspaceSurfaceId } from "../composables/useWorkspaceSurfaceId.js";
|
|
15
15
|
import { USERS_ROUTE_VISIBILITY_PUBLIC } from "@jskit-ai/users-core/shared/support/usersVisibility";
|
|
16
|
-
import { normalizePendingInvite } from "../composables/accountSettingsRuntimeHelpers.js";
|
|
16
|
+
import { normalizePendingInvite } from "../composables/account-settings/accountSettingsRuntimeHelpers.js";
|
|
17
17
|
|
|
18
18
|
const route = useRoute();
|
|
19
19
|
const router = useRouter();
|
|
@@ -421,7 +421,7 @@ watch(
|
|
|
421
421
|
:title="workspace.name"
|
|
422
422
|
:subtitle="
|
|
423
423
|
workspace.isAccessible
|
|
424
|
-
? `/${workspace.slug} • role: ${workspace.
|
|
424
|
+
? `/${workspace.slug} • role: ${workspace.roleSid || 'member'}`
|
|
425
425
|
: `/${workspace.slug} • unavailable on this surface`
|
|
426
426
|
"
|
|
427
427
|
class="px-0"
|
|
@@ -466,7 +466,7 @@ watch(
|
|
|
466
466
|
v-for="invite in pendingInvites"
|
|
467
467
|
:key="invite.id"
|
|
468
468
|
:title="invite.workspaceName"
|
|
469
|
-
:subtitle="`Role: ${invite.
|
|
469
|
+
:subtitle="`Role: ${invite.roleSid}`"
|
|
470
470
|
class="px-0"
|
|
471
471
|
>
|
|
472
472
|
<template #prepend>
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import "@jskit-ai/uploads-image-web/client/styles";
|
|
2
|
+
import { createManagedImageAssetRuntime } from "@jskit-ai/uploads-image-web/client/composables/createManagedImageAssetRuntime";
|
|
3
|
+
import { resolveFieldErrors } from "@jskit-ai/http-runtime/client";
|
|
4
|
+
import { usersWebHttpClient } from "../../lib/httpClient.js";
|
|
5
|
+
|
|
6
|
+
function createAccountSettingsAvatarUploadRuntime({
|
|
7
|
+
queryClient,
|
|
8
|
+
sessionQueryKey,
|
|
9
|
+
accountSettingsQueryKey,
|
|
10
|
+
selectedAvatarFileName,
|
|
11
|
+
applySettingsData,
|
|
12
|
+
reportAccountFeedback
|
|
13
|
+
} = {}) {
|
|
14
|
+
async function resolveCsrfToken() {
|
|
15
|
+
const sessionPayload = await queryClient.fetchQuery({
|
|
16
|
+
queryKey: sessionQueryKey,
|
|
17
|
+
queryFn: () =>
|
|
18
|
+
usersWebHttpClient.request("/api/session", {
|
|
19
|
+
method: "GET"
|
|
20
|
+
}),
|
|
21
|
+
staleTime: 60_000
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
const csrfToken = String(sessionPayload?.csrfToken || "");
|
|
25
|
+
if (!csrfToken) {
|
|
26
|
+
throw new Error("Unable to prepare secure avatar upload request.");
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return csrfToken;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return createManagedImageAssetRuntime({
|
|
33
|
+
uploadEndpoint: "/api/settings/profile/avatar",
|
|
34
|
+
fieldName: "avatar",
|
|
35
|
+
selectedFileName: selectedAvatarFileName,
|
|
36
|
+
resolveRequestHeaders: async () => ({
|
|
37
|
+
"csrf-token": await resolveCsrfToken()
|
|
38
|
+
}),
|
|
39
|
+
onUploadSuccess: ({ data }) => {
|
|
40
|
+
applySettingsData(data);
|
|
41
|
+
queryClient.setQueryData(accountSettingsQueryKey, data);
|
|
42
|
+
},
|
|
43
|
+
reportFeedback: reportAccountFeedback,
|
|
44
|
+
resolveUploadErrorMessage: ({ error, response, defaultMessage }) => {
|
|
45
|
+
const body = response?.body && typeof response.body === "object" ? response.body : {};
|
|
46
|
+
const fieldErrors = resolveFieldErrors(body);
|
|
47
|
+
return String(fieldErrors.avatar || body?.error || error?.message || defaultMessage);
|
|
48
|
+
},
|
|
49
|
+
messages: {
|
|
50
|
+
uploadSuccess: "Avatar uploaded.",
|
|
51
|
+
uploadInvalidResponse: "Avatar uploaded, but the response payload was invalid.",
|
|
52
|
+
uploadError: "Unable to upload avatar.",
|
|
53
|
+
uploadRestriction: "Selected avatar file does not meet upload restrictions.",
|
|
54
|
+
editorUnavailable: "Avatar editor is unavailable in this environment.",
|
|
55
|
+
changeError: "Avatar uploaded, but the settings view could not refresh."
|
|
56
|
+
},
|
|
57
|
+
source: "users-web.account-settings-runtime:avatar"
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export { createAccountSettingsAvatarUploadRuntime };
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
const AVATAR_ALLOWED_MIME_TYPES = Object.freeze(["image/jpeg", "image/png", "image/webp"]);
|
|
2
|
-
const AVATAR_MAX_UPLOAD_BYTES = 5 * 1024 * 1024;
|
|
3
1
|
const AVATAR_DEFAULT_SIZE = 64;
|
|
4
2
|
|
|
5
3
|
const THEME_OPTIONS = Object.freeze([
|
|
@@ -64,9 +62,7 @@ const ACCOUNT_SETTINGS_DEFAULTS = Object.freeze({
|
|
|
64
62
|
|
|
65
63
|
export {
|
|
66
64
|
ACCOUNT_SETTINGS_DEFAULTS,
|
|
67
|
-
AVATAR_ALLOWED_MIME_TYPES,
|
|
68
65
|
AVATAR_DEFAULT_SIZE,
|
|
69
|
-
AVATAR_MAX_UPLOAD_BYTES,
|
|
70
66
|
AVATAR_SIZE_OPTIONS,
|
|
71
67
|
CURRENCY_OPTIONS,
|
|
72
68
|
DATE_FORMAT_OPTIONS,
|
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
normalizeReturnToPath as normalizeSharedReturnToPath,
|
|
4
4
|
resolveAllowedOriginsFromPlacementContext
|
|
5
5
|
} from "@jskit-ai/kernel/shared/support";
|
|
6
|
-
import { normalizeRecord } from "
|
|
6
|
+
import { normalizeRecord } from "../../support/runtimeNormalization.js";
|
|
7
7
|
|
|
8
8
|
function normalizeReturnToPath(value, { fallback = "/", accountSettingsPath = "/account", allowedOrigins = [] } = {}) {
|
|
9
9
|
return normalizeSharedReturnToPath(value, {
|
|
@@ -50,7 +50,7 @@ function normalizePendingInvite(entry) {
|
|
|
50
50
|
workspaceSlug,
|
|
51
51
|
workspaceName: String(entry.workspaceName || workspaceSlug).trim() || workspaceSlug,
|
|
52
52
|
workspaceAvatarUrl: String(entry.workspaceAvatarUrl || "").trim(),
|
|
53
|
-
|
|
53
|
+
roleSid: String(entry.roleSid || "member").trim().toLowerCase() || "member",
|
|
54
54
|
status: String(entry.status || "pending").trim().toLowerCase() || "pending",
|
|
55
55
|
expiresAt: String(entry.expiresAt || "").trim()
|
|
56
56
|
};
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { unref } from "vue";
|
|
2
|
+
import { normalizeText } from "@jskit-ai/kernel/shared/support/normalize";
|
|
3
|
+
import { asPlainObject } from "../support/scopeHelpers.js";
|
|
4
|
+
|
|
5
|
+
const CRUD_BINDING_MODE_ROUTE = "route";
|
|
6
|
+
const CRUD_BINDING_MODE_MERGE = "merge";
|
|
7
|
+
const CRUD_BINDING_MODE_EXPLICIT = "explicit";
|
|
8
|
+
const CRUD_BINDING_MODE_NONE = "none";
|
|
9
|
+
|
|
10
|
+
function normalizeCrudBindingMode(value = "") {
|
|
11
|
+
const normalizedValue = normalizeText(value).toLowerCase();
|
|
12
|
+
if (normalizedValue === CRUD_BINDING_MODE_MERGE) {
|
|
13
|
+
return CRUD_BINDING_MODE_MERGE;
|
|
14
|
+
}
|
|
15
|
+
if (normalizedValue === CRUD_BINDING_MODE_EXPLICIT) {
|
|
16
|
+
return CRUD_BINDING_MODE_EXPLICIT;
|
|
17
|
+
}
|
|
18
|
+
if (normalizedValue === CRUD_BINDING_MODE_NONE) {
|
|
19
|
+
return CRUD_BINDING_MODE_NONE;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return CRUD_BINDING_MODE_ROUTE;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function normalizeCrudBindingConfig(binding = {}) {
|
|
26
|
+
const source = asPlainObject(unref(binding));
|
|
27
|
+
return Object.freeze({
|
|
28
|
+
mode: normalizeCrudBindingMode(source.mode),
|
|
29
|
+
values: source.values ?? null
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function resolveCrudBindingValues(values, context = {}) {
|
|
34
|
+
if (typeof values === "function") {
|
|
35
|
+
return asPlainObject(values(context));
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return asPlainObject(unref(values));
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function resolveCrudBoundValues({
|
|
42
|
+
binding = {},
|
|
43
|
+
routeValues = {},
|
|
44
|
+
context = {}
|
|
45
|
+
} = {}) {
|
|
46
|
+
const normalizedBinding = normalizeCrudBindingConfig(binding);
|
|
47
|
+
const normalizedRouteValues = asPlainObject(routeValues);
|
|
48
|
+
const explicitValues = resolveCrudBindingValues(normalizedBinding.values, context);
|
|
49
|
+
|
|
50
|
+
if (normalizedBinding.mode === CRUD_BINDING_MODE_NONE) {
|
|
51
|
+
return {};
|
|
52
|
+
}
|
|
53
|
+
if (normalizedBinding.mode === CRUD_BINDING_MODE_EXPLICIT) {
|
|
54
|
+
return explicitValues;
|
|
55
|
+
}
|
|
56
|
+
if (normalizedBinding.mode === CRUD_BINDING_MODE_MERGE) {
|
|
57
|
+
return {
|
|
58
|
+
...normalizedRouteValues,
|
|
59
|
+
...explicitValues
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return normalizedRouteValues;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export {
|
|
67
|
+
CRUD_BINDING_MODE_ROUTE,
|
|
68
|
+
CRUD_BINDING_MODE_MERGE,
|
|
69
|
+
CRUD_BINDING_MODE_EXPLICIT,
|
|
70
|
+
CRUD_BINDING_MODE_NONE,
|
|
71
|
+
normalizeCrudBindingMode,
|
|
72
|
+
normalizeCrudBindingConfig,
|
|
73
|
+
resolveCrudBindingValues,
|
|
74
|
+
resolveCrudBoundValues
|
|
75
|
+
};
|
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
import { normalizeText } from "@jskit-ai/kernel/shared/support/normalize";
|
|
2
2
|
import { normalizeCrudLookupContainerKey } from "@jskit-ai/kernel/shared/support/crudLookup";
|
|
3
|
-
import { asPlainObject } from "
|
|
3
|
+
import { asPlainObject } from "../support/scopeHelpers.js";
|
|
4
4
|
|
|
5
5
|
const LOOKUP_LABEL_COMPOSITION_CANDIDATES = Object.freeze([
|
|
6
6
|
Object.freeze(["name", "surname"]),
|
|
7
|
+
Object.freeze(["name", "lastName"]),
|
|
7
8
|
Object.freeze(["firstName", "surname"]),
|
|
9
|
+
Object.freeze(["firstName", "lastName"]),
|
|
8
10
|
Object.freeze(["name"]),
|
|
9
11
|
Object.freeze(["firstName"])
|
|
10
12
|
]);
|
|
13
|
+
const DEFAULT_RECORD_TITLE = "-";
|
|
11
14
|
|
|
12
15
|
function hasDisplayValue(value) {
|
|
13
16
|
if (value == null) {
|
|
@@ -20,9 +23,8 @@ function hasDisplayValue(value) {
|
|
|
20
23
|
return true;
|
|
21
24
|
}
|
|
22
25
|
|
|
23
|
-
function
|
|
24
|
-
const
|
|
25
|
-
for (const candidate of LOOKUP_LABEL_COMPOSITION_CANDIDATES) {
|
|
26
|
+
function resolveComposedLabel(source = {}, candidates = LOOKUP_LABEL_COMPOSITION_CANDIDATES) {
|
|
27
|
+
for (const candidate of candidates) {
|
|
26
28
|
const parts = [];
|
|
27
29
|
for (const key of candidate) {
|
|
28
30
|
const part = normalizeText(source[key]);
|
|
@@ -37,6 +39,16 @@ function resolveLookupItemLabel(item = {}, labelKey = "") {
|
|
|
37
39
|
}
|
|
38
40
|
}
|
|
39
41
|
|
|
42
|
+
return "";
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function resolveLookupItemLabel(item = {}, labelKey = "") {
|
|
46
|
+
const source = asPlainObject(item);
|
|
47
|
+
const composedLabel = resolveComposedLabel(source);
|
|
48
|
+
if (composedLabel) {
|
|
49
|
+
return composedLabel;
|
|
50
|
+
}
|
|
51
|
+
|
|
40
52
|
const normalizedLabelKey = normalizeText(labelKey);
|
|
41
53
|
if (!normalizedLabelKey) {
|
|
42
54
|
return "";
|
|
@@ -45,6 +57,25 @@ function resolveLookupItemLabel(item = {}, labelKey = "") {
|
|
|
45
57
|
return normalizeText(source[normalizedLabelKey]);
|
|
46
58
|
}
|
|
47
59
|
|
|
60
|
+
function resolveRecordTitle(record = {}, { fallbackKey = "", defaultValue = DEFAULT_RECORD_TITLE } = {}) {
|
|
61
|
+
const source = asPlainObject(record);
|
|
62
|
+
const composedLabel = resolveComposedLabel(source);
|
|
63
|
+
if (composedLabel) {
|
|
64
|
+
return composedLabel;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const normalizedFallbackKey = normalizeText(fallbackKey);
|
|
68
|
+
if (normalizedFallbackKey) {
|
|
69
|
+
const fallbackValue = normalizeText(source[normalizedFallbackKey]);
|
|
70
|
+
if (fallbackValue) {
|
|
71
|
+
return fallbackValue;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const normalizedDefaultValue = normalizeText(defaultValue);
|
|
76
|
+
return normalizedDefaultValue || DEFAULT_RECORD_TITLE;
|
|
77
|
+
}
|
|
78
|
+
|
|
48
79
|
function resolveLookupFieldDescriptor(field = {}, relationKind = "", valueKey = "", labelKey = "") {
|
|
49
80
|
if (typeof field === "string") {
|
|
50
81
|
return {
|
|
@@ -103,5 +134,6 @@ function resolveLookupFieldDisplayValue(record = {}, field = {}, relationKind =
|
|
|
103
134
|
|
|
104
135
|
export {
|
|
105
136
|
resolveLookupItemLabel,
|
|
106
|
-
resolveLookupFieldDisplayValue
|
|
137
|
+
resolveLookupFieldDisplayValue,
|
|
138
|
+
resolveRecordTitle
|
|
107
139
|
};
|