@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.
Files changed (97) hide show
  1. package/package.descriptor.mjs +507 -0
  2. package/package.json +31 -0
  3. package/src/client/components/ConsoleSettingsClientElement.vue +24 -0
  4. package/src/client/components/MembersAdminClientElement.vue +404 -0
  5. package/src/client/components/ProfileClientElement.vue +242 -0
  6. package/src/client/components/UsersProfileSurfaceSwitchMenuItem.vue +39 -0
  7. package/src/client/components/UsersShellMenuLinkItem.vue +140 -0
  8. package/src/client/components/UsersSurfaceAwareMenuLinkItem.vue +87 -0
  9. package/src/client/components/UsersWorkspaceMembersMenuItem.vue +36 -0
  10. package/src/client/components/UsersWorkspacePermissionMenuItem.vue +90 -0
  11. package/src/client/components/UsersWorkspaceSelector.vue +237 -0
  12. package/src/client/components/UsersWorkspaceSettingsMenuItem.vue +39 -0
  13. package/src/client/components/UsersWorkspaceToolsWidget.vue +23 -0
  14. package/src/client/components/WorkspaceMembersClientElement.vue +663 -0
  15. package/src/client/components/WorkspaceSettingsClientElement.vue +230 -0
  16. package/src/client/components/WorkspacesClientElement.vue +514 -0
  17. package/src/client/composables/accountSettingsAvatarUploadRuntime.js +241 -0
  18. package/src/client/composables/accountSettingsInvitesRuntime.js +88 -0
  19. package/src/client/composables/accountSettingsRuntimeConstants.js +77 -0
  20. package/src/client/composables/accountSettingsRuntimeHelpers.js +75 -0
  21. package/src/client/composables/errorMessageHelpers.js +66 -0
  22. package/src/client/composables/internal/useOperationScope.js +144 -0
  23. package/src/client/composables/modelStateHelpers.js +49 -0
  24. package/src/client/composables/operationUiHelpers.js +121 -0
  25. package/src/client/composables/operationValidationHelpers.js +52 -0
  26. package/src/client/composables/refValueHelpers.js +19 -0
  27. package/src/client/composables/scopeHelpers.js +145 -0
  28. package/src/client/composables/useAccess.js +109 -0
  29. package/src/client/composables/useAccountSettingsRuntime.js +533 -0
  30. package/src/client/composables/useAddEdit.js +135 -0
  31. package/src/client/composables/useAddEditCore.js +137 -0
  32. package/src/client/composables/useBootstrapQuery.js +52 -0
  33. package/src/client/composables/useCommand.js +112 -0
  34. package/src/client/composables/useCommandCore.js +130 -0
  35. package/src/client/composables/useEndpointResource.js +104 -0
  36. package/src/client/composables/useFieldErrorBag.js +61 -0
  37. package/src/client/composables/useList.js +85 -0
  38. package/src/client/composables/useListCore.js +65 -0
  39. package/src/client/composables/usePagedCollection.js +125 -0
  40. package/src/client/composables/usePaths.js +108 -0
  41. package/src/client/composables/useRealtimeQueryInvalidation.js +105 -0
  42. package/src/client/composables/useScopeRuntime.js +107 -0
  43. package/src/client/composables/useSurfaceRouteContext.js +31 -0
  44. package/src/client/composables/useUiFeedback.js +96 -0
  45. package/src/client/composables/useView.js +89 -0
  46. package/src/client/composables/useViewCore.js +104 -0
  47. package/src/client/composables/useWorkspaceRouteContext.js +28 -0
  48. package/src/client/composables/useWorkspaceSurfaceId.js +43 -0
  49. package/src/client/index.js +7 -0
  50. package/src/client/lib/bootstrap.js +95 -0
  51. package/src/client/lib/httpClient.js +67 -0
  52. package/src/client/lib/menuIcons.js +192 -0
  53. package/src/client/lib/permissions.js +34 -0
  54. package/src/client/lib/profileSurfaceMenuLinks.js +142 -0
  55. package/src/client/lib/surfaceAccessPolicy.js +350 -0
  56. package/src/client/lib/theme.js +99 -0
  57. package/src/client/lib/workspaceLinkResolver.js +207 -0
  58. package/src/client/lib/workspaceSurfaceContext.js +82 -0
  59. package/src/client/lib/workspaceSurfacePaths.js +163 -0
  60. package/src/client/providers/UsersWebClientProvider.js +85 -0
  61. package/src/client/runtime/bootstrapPlacementRouteGuards.js +371 -0
  62. package/src/client/runtime/bootstrapPlacementRuntime.js +413 -0
  63. package/src/client/runtime/bootstrapPlacementRuntimeConstants.js +32 -0
  64. package/src/client/runtime/bootstrapPlacementRuntimeHelpers.js +157 -0
  65. package/src/client/support/contractGuards.js +34 -0
  66. package/src/client/support/realtimeWorkspace.js +12 -0
  67. package/src/client/support/runtimeNormalization.js +27 -0
  68. package/src/client/support/workspaceQueryKeys.js +15 -0
  69. package/templates/packages/main/src/client/components/AccountPendingInvitesCue.vue +162 -0
  70. package/templates/src/components/WorkspaceNotFoundCard.vue +33 -0
  71. package/templates/src/components/account/settings/AccountSettingsClientElement.vue +153 -0
  72. package/templates/src/components/account/settings/AccountSettingsInvitesSection.vue +77 -0
  73. package/templates/src/components/account/settings/AccountSettingsNotificationsSection.vue +55 -0
  74. package/templates/src/components/account/settings/AccountSettingsPreferencesSection.vue +125 -0
  75. package/templates/src/components/account/settings/AccountSettingsProfileSection.vue +94 -0
  76. package/templates/src/composables/useWorkspaceNotFoundState.js +48 -0
  77. package/templates/src/pages/account/index.vue +17 -0
  78. package/templates/src/pages/admin/members/index.vue +7 -0
  79. package/templates/src/pages/admin/workspace/settings/index.vue +16 -0
  80. package/templates/src/pages/console/settings/index.vue +16 -0
  81. package/templates/src/surfaces/admin/index.vue +29 -0
  82. package/templates/src/surfaces/admin/root.vue +20 -0
  83. package/templates/src/surfaces/app/index.vue +27 -0
  84. package/templates/src/surfaces/app/root.vue +20 -0
  85. package/test/bootstrap.test.js +38 -0
  86. package/test/bootstrapPlacementRuntime.test.js +991 -0
  87. package/test/errorMessageHelpers.test.js +28 -0
  88. package/test/exportsContract.test.js +39 -0
  89. package/test/menuIcons.test.js +33 -0
  90. package/test/permissions.test.js +35 -0
  91. package/test/profileSurfaceMenuLinks.test.js +207 -0
  92. package/test/refValueHelpers.test.js +14 -0
  93. package/test/scopeHelpers.test.js +57 -0
  94. package/test/surfaceAccessPolicy.test.js +129 -0
  95. package/test/theme.test.js +95 -0
  96. package/test/workspaceLinkResolver.test.js +61 -0
  97. 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,7 @@
1
+ <template>
2
+ <WorkspaceMembersClientElement />
3
+ </template>
4
+
5
+ <script setup>
6
+ import WorkspaceMembersClientElement from "@jskit-ai/users-web/client/components/WorkspaceMembersClientElement";
7
+ </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
+ });