@jskit-ai/users-web 0.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.descriptor.mjs +507 -0
- package/package.json +31 -0
- package/src/client/components/ConsoleSettingsClientElement.vue +24 -0
- package/src/client/components/MembersAdminClientElement.vue +404 -0
- package/src/client/components/ProfileClientElement.vue +242 -0
- package/src/client/components/UsersProfileSurfaceSwitchMenuItem.vue +39 -0
- package/src/client/components/UsersShellMenuLinkItem.vue +140 -0
- package/src/client/components/UsersSurfaceAwareMenuLinkItem.vue +87 -0
- package/src/client/components/UsersWorkspaceMembersMenuItem.vue +36 -0
- package/src/client/components/UsersWorkspacePermissionMenuItem.vue +90 -0
- package/src/client/components/UsersWorkspaceSelector.vue +237 -0
- package/src/client/components/UsersWorkspaceSettingsMenuItem.vue +39 -0
- package/src/client/components/UsersWorkspaceToolsWidget.vue +23 -0
- package/src/client/components/WorkspaceMembersClientElement.vue +663 -0
- package/src/client/components/WorkspaceSettingsClientElement.vue +230 -0
- package/src/client/components/WorkspacesClientElement.vue +514 -0
- package/src/client/composables/accountSettingsAvatarUploadRuntime.js +241 -0
- package/src/client/composables/accountSettingsInvitesRuntime.js +88 -0
- package/src/client/composables/accountSettingsRuntimeConstants.js +77 -0
- package/src/client/composables/accountSettingsRuntimeHelpers.js +75 -0
- package/src/client/composables/errorMessageHelpers.js +66 -0
- package/src/client/composables/internal/useOperationScope.js +144 -0
- package/src/client/composables/modelStateHelpers.js +49 -0
- package/src/client/composables/operationUiHelpers.js +121 -0
- package/src/client/composables/operationValidationHelpers.js +52 -0
- package/src/client/composables/refValueHelpers.js +19 -0
- package/src/client/composables/scopeHelpers.js +145 -0
- package/src/client/composables/useAccess.js +109 -0
- package/src/client/composables/useAccountSettingsRuntime.js +533 -0
- package/src/client/composables/useAddEdit.js +135 -0
- package/src/client/composables/useAddEditCore.js +137 -0
- package/src/client/composables/useBootstrapQuery.js +52 -0
- package/src/client/composables/useCommand.js +112 -0
- package/src/client/composables/useCommandCore.js +130 -0
- package/src/client/composables/useEndpointResource.js +104 -0
- package/src/client/composables/useFieldErrorBag.js +61 -0
- package/src/client/composables/useList.js +85 -0
- package/src/client/composables/useListCore.js +65 -0
- package/src/client/composables/usePagedCollection.js +125 -0
- package/src/client/composables/usePaths.js +108 -0
- package/src/client/composables/useRealtimeQueryInvalidation.js +105 -0
- package/src/client/composables/useScopeRuntime.js +107 -0
- package/src/client/composables/useSurfaceRouteContext.js +31 -0
- package/src/client/composables/useUiFeedback.js +96 -0
- package/src/client/composables/useView.js +89 -0
- package/src/client/composables/useViewCore.js +104 -0
- package/src/client/composables/useWorkspaceRouteContext.js +28 -0
- package/src/client/composables/useWorkspaceSurfaceId.js +43 -0
- package/src/client/index.js +7 -0
- package/src/client/lib/bootstrap.js +95 -0
- package/src/client/lib/httpClient.js +67 -0
- package/src/client/lib/menuIcons.js +192 -0
- package/src/client/lib/permissions.js +34 -0
- package/src/client/lib/profileSurfaceMenuLinks.js +142 -0
- package/src/client/lib/surfaceAccessPolicy.js +350 -0
- package/src/client/lib/theme.js +99 -0
- package/src/client/lib/workspaceLinkResolver.js +207 -0
- package/src/client/lib/workspaceSurfaceContext.js +82 -0
- package/src/client/lib/workspaceSurfacePaths.js +163 -0
- package/src/client/providers/UsersWebClientProvider.js +85 -0
- package/src/client/runtime/bootstrapPlacementRouteGuards.js +371 -0
- package/src/client/runtime/bootstrapPlacementRuntime.js +413 -0
- package/src/client/runtime/bootstrapPlacementRuntimeConstants.js +32 -0
- package/src/client/runtime/bootstrapPlacementRuntimeHelpers.js +157 -0
- package/src/client/support/contractGuards.js +34 -0
- package/src/client/support/realtimeWorkspace.js +12 -0
- package/src/client/support/runtimeNormalization.js +27 -0
- package/src/client/support/workspaceQueryKeys.js +15 -0
- package/templates/packages/main/src/client/components/AccountPendingInvitesCue.vue +162 -0
- package/templates/src/components/WorkspaceNotFoundCard.vue +33 -0
- package/templates/src/components/account/settings/AccountSettingsClientElement.vue +153 -0
- package/templates/src/components/account/settings/AccountSettingsInvitesSection.vue +77 -0
- package/templates/src/components/account/settings/AccountSettingsNotificationsSection.vue +55 -0
- package/templates/src/components/account/settings/AccountSettingsPreferencesSection.vue +125 -0
- package/templates/src/components/account/settings/AccountSettingsProfileSection.vue +94 -0
- package/templates/src/composables/useWorkspaceNotFoundState.js +48 -0
- package/templates/src/pages/account/index.vue +17 -0
- package/templates/src/pages/admin/members/index.vue +7 -0
- package/templates/src/pages/admin/workspace/settings/index.vue +16 -0
- package/templates/src/pages/console/settings/index.vue +16 -0
- package/templates/src/surfaces/admin/index.vue +29 -0
- package/templates/src/surfaces/admin/root.vue +20 -0
- package/templates/src/surfaces/app/index.vue +27 -0
- package/templates/src/surfaces/app/root.vue +20 -0
- package/test/bootstrap.test.js +38 -0
- package/test/bootstrapPlacementRuntime.test.js +991 -0
- package/test/errorMessageHelpers.test.js +28 -0
- package/test/exportsContract.test.js +39 -0
- package/test/menuIcons.test.js +33 -0
- package/test/permissions.test.js +35 -0
- package/test/profileSurfaceMenuLinks.test.js +207 -0
- package/test/refValueHelpers.test.js +14 -0
- package/test/scopeHelpers.test.js +57 -0
- package/test/surfaceAccessPolicy.test.js +129 -0
- package/test/theme.test.js +95 -0
- package/test/workspaceLinkResolver.test.js +61 -0
- package/test/workspaceSurfacePaths.test.js +39 -0
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
const props = defineProps({
|
|
3
|
+
runtime: {
|
|
4
|
+
type: Object,
|
|
5
|
+
required: true
|
|
6
|
+
}
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
const profile = props.runtime.profile;
|
|
10
|
+
</script>
|
|
11
|
+
|
|
12
|
+
<template>
|
|
13
|
+
<v-card rounded="lg" elevation="0" border>
|
|
14
|
+
<v-card-item>
|
|
15
|
+
<v-card-title class="text-subtitle-1">Profile</v-card-title>
|
|
16
|
+
</v-card-item>
|
|
17
|
+
<v-divider />
|
|
18
|
+
<v-card-text>
|
|
19
|
+
<v-form @submit.prevent="profile.submit" novalidate>
|
|
20
|
+
<v-row class="mb-2">
|
|
21
|
+
<v-col cols="12" md="4" class="d-flex flex-column align-center justify-center">
|
|
22
|
+
<v-avatar :size="profile.avatar.size" color="surface-variant" rounded="circle" class="mb-3">
|
|
23
|
+
<v-img v-if="profile.avatar.effectiveUrl" :src="profile.avatar.effectiveUrl" cover />
|
|
24
|
+
<span v-else class="text-h6">{{ profile.initials.value }}</span>
|
|
25
|
+
</v-avatar>
|
|
26
|
+
<div class="text-caption text-medium-emphasis">Preview size: {{ profile.avatar.size }} px</div>
|
|
27
|
+
</v-col>
|
|
28
|
+
|
|
29
|
+
<v-col cols="12" md="8">
|
|
30
|
+
<div class="d-flex flex-wrap ga-2 mb-2">
|
|
31
|
+
<v-btn
|
|
32
|
+
variant="tonal"
|
|
33
|
+
color="secondary"
|
|
34
|
+
:disabled="profile.isSaving.value || profile.isDeletingAvatar.value || profile.isRefreshing.value"
|
|
35
|
+
@click="profile.openAvatarEditor"
|
|
36
|
+
>
|
|
37
|
+
Replace avatar
|
|
38
|
+
</v-btn>
|
|
39
|
+
<v-btn
|
|
40
|
+
v-if="profile.avatar.hasUploadedAvatar"
|
|
41
|
+
variant="text"
|
|
42
|
+
color="error"
|
|
43
|
+
:loading="profile.isDeletingAvatar.value"
|
|
44
|
+
:disabled="profile.isSaving.value || profile.isRefreshing.value"
|
|
45
|
+
@click="profile.removeAvatar"
|
|
46
|
+
>
|
|
47
|
+
Remove avatar
|
|
48
|
+
</v-btn>
|
|
49
|
+
</div>
|
|
50
|
+
|
|
51
|
+
<div v-if="profile.selectedAvatarFileName.value" class="text-caption text-medium-emphasis mb-2">
|
|
52
|
+
Selected file: {{ profile.selectedAvatarFileName.value }}
|
|
53
|
+
</div>
|
|
54
|
+
</v-col>
|
|
55
|
+
</v-row>
|
|
56
|
+
|
|
57
|
+
<v-row>
|
|
58
|
+
<v-col cols="12" md="6">
|
|
59
|
+
<v-text-field
|
|
60
|
+
v-model="profile.form.displayName"
|
|
61
|
+
label="Display name"
|
|
62
|
+
variant="outlined"
|
|
63
|
+
density="comfortable"
|
|
64
|
+
autocomplete="nickname"
|
|
65
|
+
:readonly="profile.isSaving.value || profile.isRefreshing.value"
|
|
66
|
+
:error-messages="profile.fieldErrors.displayName ? [profile.fieldErrors.displayName] : []"
|
|
67
|
+
/>
|
|
68
|
+
</v-col>
|
|
69
|
+
|
|
70
|
+
<v-col cols="12" md="6">
|
|
71
|
+
<v-text-field
|
|
72
|
+
v-model="profile.form.email"
|
|
73
|
+
label="Email"
|
|
74
|
+
variant="outlined"
|
|
75
|
+
density="comfortable"
|
|
76
|
+
readonly
|
|
77
|
+
hint="Managed by Supabase Auth"
|
|
78
|
+
persistent-hint
|
|
79
|
+
/>
|
|
80
|
+
</v-col>
|
|
81
|
+
</v-row>
|
|
82
|
+
|
|
83
|
+
<v-btn
|
|
84
|
+
type="submit"
|
|
85
|
+
color="primary"
|
|
86
|
+
:loading="profile.isSaving.value"
|
|
87
|
+
:disabled="profile.isDeletingAvatar.value || profile.isRefreshing.value"
|
|
88
|
+
>
|
|
89
|
+
Save profile
|
|
90
|
+
</v-btn>
|
|
91
|
+
</v-form>
|
|
92
|
+
</v-card-text>
|
|
93
|
+
</v-card>
|
|
94
|
+
</template>
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { computed } from "vue";
|
|
2
|
+
import { useRoute } from "vue-router";
|
|
3
|
+
import { useWebPlacementContext } from "@jskit-ai/shell-web/client/placement";
|
|
4
|
+
|
|
5
|
+
const STATUS_MESSAGES = {
|
|
6
|
+
not_found: "The requested workspace was not found.",
|
|
7
|
+
forbidden: "You do not have access to this workspace.",
|
|
8
|
+
unauthenticated: "You need to sign in to access this workspace.",
|
|
9
|
+
error: "Workspace data could not be loaded right now."
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
function useWorkspaceNotFoundState() {
|
|
13
|
+
const route = useRoute();
|
|
14
|
+
const { context: placementContext } = useWebPlacementContext();
|
|
15
|
+
|
|
16
|
+
const routeWorkspaceSlug = computed(() =>
|
|
17
|
+
String(route?.params?.workspaceSlug || "")
|
|
18
|
+
.trim()
|
|
19
|
+
.toLowerCase()
|
|
20
|
+
);
|
|
21
|
+
|
|
22
|
+
const workspaceBootstrapStatus = computed(() => {
|
|
23
|
+
const statuses =
|
|
24
|
+
placementContext.value?.workspaceBootstrapStatuses &&
|
|
25
|
+
typeof placementContext.value.workspaceBootstrapStatuses === "object"
|
|
26
|
+
? placementContext.value.workspaceBootstrapStatuses
|
|
27
|
+
: {};
|
|
28
|
+
return String(statuses[routeWorkspaceSlug.value] || "")
|
|
29
|
+
.trim()
|
|
30
|
+
.toLowerCase();
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
const workspaceUnavailable = computed(
|
|
34
|
+
() => Boolean(workspaceBootstrapStatus.value) && workspaceBootstrapStatus.value !== "resolved"
|
|
35
|
+
);
|
|
36
|
+
const workspaceUnavailableMessage = computed(
|
|
37
|
+
() => STATUS_MESSAGES[workspaceBootstrapStatus.value] || "Workspace is currently unavailable."
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
return Object.freeze({
|
|
41
|
+
routeWorkspaceSlug,
|
|
42
|
+
workspaceBootstrapStatus,
|
|
43
|
+
workspaceUnavailable,
|
|
44
|
+
workspaceUnavailableMessage
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export { useWorkspaceNotFoundState };
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
<route lang="json">
|
|
2
|
+
{
|
|
3
|
+
"meta": {
|
|
4
|
+
"guard": {
|
|
5
|
+
"policy": "authenticated"
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
</route>
|
|
10
|
+
|
|
11
|
+
<template>
|
|
12
|
+
<AccountSettingsClientElement />
|
|
13
|
+
</template>
|
|
14
|
+
|
|
15
|
+
<script setup>
|
|
16
|
+
import AccountSettingsClientElement from "../../components/account/settings/AccountSettingsClientElement.vue";
|
|
17
|
+
</script>
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<section class="settings-page">
|
|
3
|
+
<ShellOutlet host="workspace-settings" position="forms" />
|
|
4
|
+
</section>
|
|
5
|
+
</template>
|
|
6
|
+
|
|
7
|
+
<script setup>
|
|
8
|
+
import ShellOutlet from "@jskit-ai/shell-web/client/components/ShellOutlet";
|
|
9
|
+
</script>
|
|
10
|
+
|
|
11
|
+
<style scoped>
|
|
12
|
+
.settings-page {
|
|
13
|
+
display: grid;
|
|
14
|
+
gap: 1rem;
|
|
15
|
+
}
|
|
16
|
+
</style>
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<section class="settings-page">
|
|
3
|
+
<ShellOutlet host="console-settings" position="forms" />
|
|
4
|
+
</section>
|
|
5
|
+
</template>
|
|
6
|
+
|
|
7
|
+
<script setup>
|
|
8
|
+
import ShellOutlet from "@jskit-ai/shell-web/client/components/ShellOutlet";
|
|
9
|
+
</script>
|
|
10
|
+
|
|
11
|
+
<style scoped>
|
|
12
|
+
.settings-page {
|
|
13
|
+
display: grid;
|
|
14
|
+
gap: 1rem;
|
|
15
|
+
}
|
|
16
|
+
</style>
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import WorkspaceNotFoundCard from "@/components/WorkspaceNotFoundCard.vue";
|
|
3
|
+
import { useWorkspaceNotFoundState } from "@/composables/useWorkspaceNotFoundState";
|
|
4
|
+
|
|
5
|
+
const { workspaceUnavailable, workspaceUnavailableMessage } = useWorkspaceNotFoundState();
|
|
6
|
+
</script>
|
|
7
|
+
|
|
8
|
+
<template>
|
|
9
|
+
<WorkspaceNotFoundCard
|
|
10
|
+
v-if="workspaceUnavailable"
|
|
11
|
+
:message="workspaceUnavailableMessage"
|
|
12
|
+
surface-label="Admin"
|
|
13
|
+
/>
|
|
14
|
+
<v-card v-else rounded="lg" elevation="1" border>
|
|
15
|
+
<v-card-item>
|
|
16
|
+
<template #prepend>
|
|
17
|
+
<v-chip color="primary" size="small" label>Admin</v-chip>
|
|
18
|
+
</template>
|
|
19
|
+
<v-card-title class="text-h5">Workspace Admin</v-card-title>
|
|
20
|
+
<v-card-subtitle>Privileged workspace workflows.</v-card-subtitle>
|
|
21
|
+
</v-card-item>
|
|
22
|
+
<v-divider />
|
|
23
|
+
<v-card-text class="d-flex flex-column ga-4">
|
|
24
|
+
<p class="text-medium-emphasis mb-0">
|
|
25
|
+
Use this area for workspace administration modules.
|
|
26
|
+
</p>
|
|
27
|
+
</v-card-text>
|
|
28
|
+
</v-card>
|
|
29
|
+
</template>
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
<route lang="json">
|
|
2
|
+
{
|
|
3
|
+
"meta": {
|
|
4
|
+
"jskit": {
|
|
5
|
+
"surface": "admin"
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
</route>
|
|
10
|
+
|
|
11
|
+
<script setup>
|
|
12
|
+
import ShellLayout from "@/components/ShellLayout.vue";
|
|
13
|
+
import { RouterView } from "vue-router";
|
|
14
|
+
</script>
|
|
15
|
+
|
|
16
|
+
<template>
|
|
17
|
+
<ShellLayout title="" subtitle="">
|
|
18
|
+
<RouterView />
|
|
19
|
+
</ShellLayout>
|
|
20
|
+
</template>
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import WorkspaceNotFoundCard from "@/components/WorkspaceNotFoundCard.vue";
|
|
3
|
+
import { useWorkspaceNotFoundState } from "@/composables/useWorkspaceNotFoundState";
|
|
4
|
+
|
|
5
|
+
const { workspaceUnavailable, workspaceUnavailableMessage } = useWorkspaceNotFoundState();
|
|
6
|
+
</script>
|
|
7
|
+
|
|
8
|
+
<template>
|
|
9
|
+
<WorkspaceNotFoundCard
|
|
10
|
+
v-if="workspaceUnavailable"
|
|
11
|
+
:message="workspaceUnavailableMessage"
|
|
12
|
+
surface-label="App"
|
|
13
|
+
/>
|
|
14
|
+
<v-card v-else rounded="lg" elevation="1" border>
|
|
15
|
+
<v-card-item>
|
|
16
|
+
<template #prepend>
|
|
17
|
+
<v-chip color="primary" size="small" label>App</v-chip>
|
|
18
|
+
</template>
|
|
19
|
+
<v-card-title class="text-h5">Workspace Home</v-card-title>
|
|
20
|
+
<v-card-subtitle>Primary in-workspace surface.</v-card-subtitle>
|
|
21
|
+
</v-card-item>
|
|
22
|
+
<v-divider />
|
|
23
|
+
<v-card-text class="d-flex flex-column ga-4">
|
|
24
|
+
<p class="text-medium-emphasis mb-0">Replace this page with your workspace dashboard modules.</p>
|
|
25
|
+
</v-card-text>
|
|
26
|
+
</v-card>
|
|
27
|
+
</template>
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
<route lang="json">
|
|
2
|
+
{
|
|
3
|
+
"meta": {
|
|
4
|
+
"jskit": {
|
|
5
|
+
"surface": "app"
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
</route>
|
|
10
|
+
|
|
11
|
+
<script setup>
|
|
12
|
+
import ShellLayout from "@/components/ShellLayout.vue";
|
|
13
|
+
import { RouterView } from "vue-router";
|
|
14
|
+
</script>
|
|
15
|
+
|
|
16
|
+
<template>
|
|
17
|
+
<ShellLayout title="" subtitle="">
|
|
18
|
+
<RouterView />
|
|
19
|
+
</ShellLayout>
|
|
20
|
+
</template>
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import test from "node:test";
|
|
3
|
+
import { resolvePlacementUserFromBootstrapPayload } from "../src/client/lib/bootstrap.js";
|
|
4
|
+
|
|
5
|
+
test("resolvePlacementUserFromBootstrapPayload returns null for anonymous sessions", () => {
|
|
6
|
+
assert.equal(
|
|
7
|
+
resolvePlacementUserFromBootstrapPayload({
|
|
8
|
+
session: {
|
|
9
|
+
authenticated: false
|
|
10
|
+
}
|
|
11
|
+
}),
|
|
12
|
+
null
|
|
13
|
+
);
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
test("resolvePlacementUserFromBootstrapPayload maps profile fields used by placement avatar widget", () => {
|
|
17
|
+
const user = resolvePlacementUserFromBootstrapPayload({
|
|
18
|
+
session: {
|
|
19
|
+
authenticated: true,
|
|
20
|
+
userId: 42
|
|
21
|
+
},
|
|
22
|
+
profile: {
|
|
23
|
+
displayName: "Ada Lovelace",
|
|
24
|
+
email: "ADA@EXAMPLE.COM",
|
|
25
|
+
avatar: {
|
|
26
|
+
effectiveUrl: "https://cdn.example.com/ada.png"
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
assert.deepEqual(user, {
|
|
32
|
+
id: 42,
|
|
33
|
+
displayName: "Ada Lovelace",
|
|
34
|
+
name: "Ada Lovelace",
|
|
35
|
+
email: "ada@example.com",
|
|
36
|
+
avatarUrl: "https://cdn.example.com/ada.png"
|
|
37
|
+
});
|
|
38
|
+
});
|