@jskit-ai/workspaces-web 0.1.61 → 0.1.63
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 +4 -8
- package/package.json +7 -8
- package/templates/src/pages/admin/workspace/settings/index.vue +21 -5
- package/templates/src/pages/admin/workspace/settings.vue +28 -3
- package/test/exportsContract.test.js +0 -1
- package/test/settingsPlacementContract.test.js +14 -7
- package/src/client/components/WorkspaceProfileClientElement.vue +0 -155
- package/src/client/components/WorkspaceSettingsClientElement.vue +0 -102
- package/src/client/components/WorkspaceSettingsFieldsClientElement.vue +0 -304
- package/src/client/composables/useBootstrapQuery.js +0 -52
package/package.descriptor.mjs
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
export default Object.freeze({
|
|
2
2
|
packageVersion: 1,
|
|
3
3
|
packageId: "@jskit-ai/workspaces-web",
|
|
4
|
-
version: "0.1.
|
|
4
|
+
version: "0.1.63",
|
|
5
5
|
kind: "runtime",
|
|
6
|
-
description: "Workspace web module: workspace selector, tools widget, workspace surfaces, and
|
|
6
|
+
description: "Workspace web module: workspace selector, tools widget, workspace surfaces, members UI, and settings hosts.",
|
|
7
7
|
dependsOn: [
|
|
8
8
|
"@jskit-ai/workspaces-core",
|
|
9
9
|
"@jskit-ai/users-web"
|
|
@@ -41,10 +41,6 @@ export default Object.freeze({
|
|
|
41
41
|
subpath: "./client/components/AccountSettingsInvitesSection",
|
|
42
42
|
summary: "Exports the default account invites section component used by multihoming installs."
|
|
43
43
|
},
|
|
44
|
-
{
|
|
45
|
-
subpath: "./client/components/WorkspaceSettingsClientElement",
|
|
46
|
-
summary: "Exports the workspace settings client element used by the workspace admin settings starter page."
|
|
47
|
-
},
|
|
48
44
|
{
|
|
49
45
|
subpath: "./client/providers/WorkspacesWebClientProvider",
|
|
50
46
|
summary: "Exports workspaces-web client provider class."
|
|
@@ -231,8 +227,8 @@ export default Object.freeze({
|
|
|
231
227
|
mutations: {
|
|
232
228
|
dependencies: {
|
|
233
229
|
runtime: {
|
|
234
|
-
"@jskit-ai/workspaces-core": "0.1.
|
|
235
|
-
"@jskit-ai/users-web": "0.1.
|
|
230
|
+
"@jskit-ai/workspaces-core": "0.1.62",
|
|
231
|
+
"@jskit-ai/users-web": "0.1.101"
|
|
236
232
|
},
|
|
237
233
|
dev: {}
|
|
238
234
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jskit-ai/workspaces-web",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.63",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"test": "node --test"
|
|
@@ -10,17 +10,16 @@
|
|
|
10
10
|
"./client/components/AccountSettingsInvitesSection": "./src/client/components/AccountSettingsInvitesSection.vue",
|
|
11
11
|
"./client/providers/WorkspacesWebClientProvider": "./src/client/providers/WorkspacesWebClientProvider.js",
|
|
12
12
|
"./client/components/WorkspaceMembersClientElement": "./src/client/components/WorkspaceMembersClientElement.vue",
|
|
13
|
-
"./client/components/WorkspaceSettingsClientElement": "./src/client/components/WorkspaceSettingsClientElement.vue",
|
|
14
13
|
"./client/composables/useWorkspaceRouteContext": "./src/client/composables/useWorkspaceRouteContext.js"
|
|
15
14
|
},
|
|
16
15
|
"dependencies": {
|
|
17
16
|
"@mdi/js": "^7.4.47",
|
|
18
|
-
"@jskit-ai/http-runtime": "0.1.
|
|
19
|
-
"@jskit-ai/kernel": "0.1.
|
|
20
|
-
"@jskit-ai/shell-web": "0.1.
|
|
21
|
-
"@jskit-ai/users-core": "0.1.
|
|
22
|
-
"@jskit-ai/users-web": "0.1.
|
|
23
|
-
"@jskit-ai/workspaces-core": "0.1.
|
|
17
|
+
"@jskit-ai/http-runtime": "0.1.85",
|
|
18
|
+
"@jskit-ai/kernel": "0.1.86",
|
|
19
|
+
"@jskit-ai/shell-web": "0.1.85",
|
|
20
|
+
"@jskit-ai/users-core": "0.1.96",
|
|
21
|
+
"@jskit-ai/users-web": "0.1.101",
|
|
22
|
+
"@jskit-ai/workspaces-core": "0.1.62"
|
|
24
23
|
},
|
|
25
24
|
"peerDependencies": {
|
|
26
25
|
"@tanstack/vue-query": "^5.90.5",
|
|
@@ -1,7 +1,23 @@
|
|
|
1
|
-
<script setup>
|
|
2
|
-
import WorkspaceSettingsClientElement from "@jskit-ai/workspaces-web/client/components/WorkspaceSettingsClientElement";
|
|
3
|
-
</script>
|
|
4
|
-
|
|
5
1
|
<template>
|
|
6
|
-
<
|
|
2
|
+
<section class="workspace-settings-landing">
|
|
3
|
+
<v-sheet rounded="lg" border class="workspace-settings-landing__panel">
|
|
4
|
+
<h2 class="workspace-settings-landing__title">No settings sections yet</h2>
|
|
5
|
+
<p class="text-body-2 text-medium-emphasis mb-0">
|
|
6
|
+
This app has no workspace-level settings configured.
|
|
7
|
+
</p>
|
|
8
|
+
</v-sheet>
|
|
9
|
+
</section>
|
|
7
10
|
</template>
|
|
11
|
+
|
|
12
|
+
<style scoped>
|
|
13
|
+
.workspace-settings-landing__panel {
|
|
14
|
+
padding: 1rem;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
.workspace-settings-landing__title {
|
|
18
|
+
font-size: 1rem;
|
|
19
|
+
font-weight: 650;
|
|
20
|
+
line-height: 1.2;
|
|
21
|
+
margin: 0 0 0.25rem;
|
|
22
|
+
}
|
|
23
|
+
</style>
|
|
@@ -13,7 +13,7 @@ import { RouterView } from "vue-router";
|
|
|
13
13
|
|
|
14
14
|
<v-sheet rounded="lg" border class="settings-shell__panel">
|
|
15
15
|
<nav class="settings-shell__nav" aria-label="Workspace settings sections">
|
|
16
|
-
<v-list nav density="
|
|
16
|
+
<v-list nav density="compact" class="settings-shell__nav-list">
|
|
17
17
|
<ShellOutlet target="admin-settings:primary-menu" />
|
|
18
18
|
</v-list>
|
|
19
19
|
</nav>
|
|
@@ -41,11 +41,31 @@ import { RouterView } from "vue-router";
|
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
.settings-shell__nav-list {
|
|
44
|
-
padding: 0;
|
|
44
|
+
padding: 0.35rem;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.settings-shell__nav-list :deep(.v-list-item) {
|
|
48
|
+
min-height: 48px;
|
|
49
|
+
padding-inline: 0.6rem 0.7rem;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
.settings-shell__nav-list :deep(.v-list-item__prepend) {
|
|
53
|
+
margin-inline-end: 0;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
.settings-shell__nav-list :deep(.v-list-item__spacer) {
|
|
57
|
+
min-width: 0.5rem;
|
|
58
|
+
width: 0.5rem;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
.settings-shell__nav-list :deep(.v-list-item-title) {
|
|
62
|
+
line-height: 1.2;
|
|
45
63
|
}
|
|
46
64
|
|
|
47
65
|
.settings-shell__content {
|
|
66
|
+
border-left: 1px solid rgba(var(--v-theme-on-surface), 0.18);
|
|
48
67
|
min-width: 0;
|
|
68
|
+
padding-left: 1rem;
|
|
49
69
|
}
|
|
50
70
|
|
|
51
71
|
@media (max-width: 960px) {
|
|
@@ -59,12 +79,17 @@ import { RouterView } from "vue-router";
|
|
|
59
79
|
|
|
60
80
|
.settings-shell__nav-list {
|
|
61
81
|
display: flex;
|
|
62
|
-
gap: 0.
|
|
82
|
+
gap: 0.25rem;
|
|
63
83
|
min-width: max-content;
|
|
64
84
|
}
|
|
65
85
|
|
|
66
86
|
.settings-shell__nav-list :deep(.v-list-item) {
|
|
67
87
|
min-height: 48px;
|
|
68
88
|
}
|
|
89
|
+
|
|
90
|
+
.settings-shell__content {
|
|
91
|
+
border-left: 0;
|
|
92
|
+
padding-left: 0;
|
|
93
|
+
}
|
|
69
94
|
}
|
|
70
95
|
</style>
|
|
@@ -16,7 +16,6 @@ test("workspaces-web exports are explicit and aligned with production usage", ()
|
|
|
16
16
|
requiredExports: [
|
|
17
17
|
"./client",
|
|
18
18
|
"./client/components/AccountSettingsInvitesSection",
|
|
19
|
-
"./client/components/WorkspaceSettingsClientElement",
|
|
20
19
|
"./client/providers/WorkspacesWebClientProvider",
|
|
21
20
|
"./client/composables/useWorkspaceRouteContext"
|
|
22
21
|
]
|
|
@@ -101,8 +101,6 @@ test("workspaces-web installs an app-owned account invites section wrapper", asy
|
|
|
101
101
|
test("workspaces-web settings components use direct panels instead of card scaffolds", async () => {
|
|
102
102
|
for (const relativePath of [
|
|
103
103
|
path.join("templates", "src", "components", "WorkspaceNotFoundCard.vue"),
|
|
104
|
-
path.join("src", "client", "components", "WorkspaceProfileClientElement.vue"),
|
|
105
|
-
path.join("src", "client", "components", "WorkspaceSettingsFieldsClientElement.vue"),
|
|
106
104
|
path.join("src", "client", "components", "WorkspacesClientElement.vue"),
|
|
107
105
|
path.join("src", "client", "components", "AccountSettingsInvitesSection.vue")
|
|
108
106
|
]) {
|
|
@@ -118,8 +116,6 @@ test("workspaces-web settings components use direct panels instead of card scaff
|
|
|
118
116
|
|
|
119
117
|
test("workspaces-web resource load states expose local retry actions", async () => {
|
|
120
118
|
const expectations = new Map([
|
|
121
|
-
["src/client/components/WorkspaceProfileClientElement.vue", /addEdit\.canRetryLoad[\s\S]*@click="addEdit\.refresh"/],
|
|
122
|
-
["src/client/components/WorkspaceSettingsFieldsClientElement.vue", /addEdit\.canRetryLoad[\s\S]*@click="addEdit\.refresh"/],
|
|
123
119
|
["src/client/components/WorkspacesClientElement.vue", /bootstrapLoadError[\s\S]*@click="refreshBootstrap"/],
|
|
124
120
|
["src/client/components/WorkspaceMembersClientElement.vue", /canRetryLoad[\s\S]*@click="refreshLoad"/]
|
|
125
121
|
]);
|
|
@@ -149,14 +145,25 @@ test("workspaces-web installs an account invites cue scaffold that reads placeme
|
|
|
149
145
|
});
|
|
150
146
|
});
|
|
151
147
|
|
|
152
|
-
test("workspaces-web admin settings index template
|
|
148
|
+
test("workspaces-web admin settings index template is an app-owned landing slot", async () => {
|
|
153
149
|
const source = await readFile(
|
|
154
150
|
path.join(PACKAGE_DIR, "templates", "src", "pages", "admin", "workspace", "settings", "index.vue"),
|
|
155
151
|
"utf8"
|
|
156
152
|
);
|
|
157
153
|
|
|
158
|
-
|
|
159
|
-
|
|
154
|
+
assertGeneratedUiSourceContract(source, {
|
|
155
|
+
forbidCardShell: true,
|
|
156
|
+
sourceName: "admin/workspace/settings/index.vue",
|
|
157
|
+
requiredPatterns: [
|
|
158
|
+
{
|
|
159
|
+
id: "app-owned-settings-landing",
|
|
160
|
+
pattern: /No settings sections yet/,
|
|
161
|
+
message: "Workspace settings index needs a product-shaped app-owned landing state."
|
|
162
|
+
}
|
|
163
|
+
]
|
|
164
|
+
});
|
|
165
|
+
assert.doesNotMatch(source, /@jskit-ai\/workspaces-web\/client\/components\/WorkspaceSettingsClientElement/);
|
|
166
|
+
assert.doesNotMatch(source, /<WorkspaceSettingsClientElement \/>/);
|
|
160
167
|
assert.doesNotMatch(source, /your_child_segment|To redirect this settings shell/);
|
|
161
168
|
});
|
|
162
169
|
|
|
@@ -1,155 +0,0 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<v-sheet rounded="lg" border class="workspace-profile-panel">
|
|
3
|
-
<header class="workspace-profile-panel__header">
|
|
4
|
-
<h2 class="workspace-profile-panel__title">Workspace profile</h2>
|
|
5
|
-
<p class="text-body-2 text-medium-emphasis mb-0">Name and avatar used across the workspace.</p>
|
|
6
|
-
</header>
|
|
7
|
-
|
|
8
|
-
<div class="workspace-profile-panel__body">
|
|
9
|
-
<template v-if="showSkeleton">
|
|
10
|
-
<v-skeleton-loader type="text@2, list-item-two-line@3, button" />
|
|
11
|
-
</template>
|
|
12
|
-
|
|
13
|
-
<div v-else-if="addEdit.loadError" class="workspace-profile-panel__state">
|
|
14
|
-
<p class="text-body-2 text-medium-emphasis mb-4">{{ addEdit.loadError }}</p>
|
|
15
|
-
<v-btn
|
|
16
|
-
v-if="addEdit.canRetryLoad"
|
|
17
|
-
color="primary"
|
|
18
|
-
variant="tonal"
|
|
19
|
-
:loading="addEdit.isFetching"
|
|
20
|
-
@click="addEdit.refresh"
|
|
21
|
-
>
|
|
22
|
-
Retry
|
|
23
|
-
</v-btn>
|
|
24
|
-
</div>
|
|
25
|
-
|
|
26
|
-
<p v-else-if="!addEdit.canView" class="text-body-2 text-medium-emphasis mb-4">
|
|
27
|
-
You do not have permission to view workspace profile.
|
|
28
|
-
</p>
|
|
29
|
-
|
|
30
|
-
<template v-else>
|
|
31
|
-
<v-form @submit.prevent="addEdit.submit" novalidate>
|
|
32
|
-
<v-progress-linear v-if="addEdit.isRefetching" indeterminate class="mb-4" />
|
|
33
|
-
<v-row>
|
|
34
|
-
<v-col cols="12" md="6">
|
|
35
|
-
<v-text-field
|
|
36
|
-
v-model="workspaceProfileForm.name"
|
|
37
|
-
label="Workspace name"
|
|
38
|
-
variant="outlined"
|
|
39
|
-
density="comfortable"
|
|
40
|
-
:readonly="!addEdit.canSave || addEdit.isSaving || addEdit.isRefetching"
|
|
41
|
-
:error-messages="addEdit.fieldErrors.name ? [addEdit.fieldErrors.name] : []"
|
|
42
|
-
/>
|
|
43
|
-
</v-col>
|
|
44
|
-
|
|
45
|
-
<v-col cols="12" md="6">
|
|
46
|
-
<v-text-field
|
|
47
|
-
v-model="workspaceProfileForm.avatarUrl"
|
|
48
|
-
label="Workspace avatar URL"
|
|
49
|
-
variant="outlined"
|
|
50
|
-
density="comfortable"
|
|
51
|
-
:readonly="!addEdit.canSave || addEdit.isSaving || addEdit.isRefetching"
|
|
52
|
-
placeholder="https://..."
|
|
53
|
-
hint="Optional"
|
|
54
|
-
persistent-hint
|
|
55
|
-
:error-messages="addEdit.fieldErrors.avatarUrl ? [addEdit.fieldErrors.avatarUrl] : []"
|
|
56
|
-
/>
|
|
57
|
-
</v-col>
|
|
58
|
-
|
|
59
|
-
<v-col cols="12" class="d-flex align-center justify-end ga-3">
|
|
60
|
-
<v-btn
|
|
61
|
-
v-if="addEdit.canSave"
|
|
62
|
-
type="submit"
|
|
63
|
-
color="primary"
|
|
64
|
-
:loading="addEdit.isSaving"
|
|
65
|
-
:disabled="addEdit.isInitialLoading || addEdit.isRefetching"
|
|
66
|
-
>
|
|
67
|
-
Save workspace profile
|
|
68
|
-
</v-btn>
|
|
69
|
-
<v-chip v-else color="secondary" label>Read-only</v-chip>
|
|
70
|
-
</v-col>
|
|
71
|
-
</v-row>
|
|
72
|
-
</v-form>
|
|
73
|
-
</template>
|
|
74
|
-
</div>
|
|
75
|
-
</v-sheet>
|
|
76
|
-
</template>
|
|
77
|
-
|
|
78
|
-
<script setup>
|
|
79
|
-
import { computed, reactive } from "vue";
|
|
80
|
-
import { ROUTE_VISIBILITY_WORKSPACE } from "@jskit-ai/kernel/shared/support/visibility";
|
|
81
|
-
import { WORKSPACES_TRANSPORT } from "@jskit-ai/workspaces-core/shared/jsonApiTransports";
|
|
82
|
-
import { workspaceResource } from "@jskit-ai/workspaces-core/shared/resources/workspaceResource";
|
|
83
|
-
import { useAddEdit } from "@jskit-ai/users-web/client/composables/useAddEdit";
|
|
84
|
-
import { buildWorkspaceQueryKey } from "../support/workspaceQueryKeys.js";
|
|
85
|
-
|
|
86
|
-
const emit = defineEmits(["saved"]);
|
|
87
|
-
|
|
88
|
-
const workspaceProfileForm = reactive({
|
|
89
|
-
name: "",
|
|
90
|
-
avatarUrl: ""
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
const addEdit = useAddEdit({
|
|
94
|
-
ownershipFilter: ROUTE_VISIBILITY_WORKSPACE,
|
|
95
|
-
resource: workspaceResource,
|
|
96
|
-
apiSuffix: "/",
|
|
97
|
-
queryKeyFactory: (surfaceId = "", scopeParamValue = "") =>
|
|
98
|
-
buildWorkspaceQueryKey("profile", surfaceId, scopeParamValue),
|
|
99
|
-
viewPermissions: ["workspace.settings.view", "workspace.settings.update"],
|
|
100
|
-
savePermissions: ["workspace.settings.update"],
|
|
101
|
-
transport: WORKSPACES_TRANSPORT,
|
|
102
|
-
placementSource: "workspaces-web.workspace-profile-view",
|
|
103
|
-
fallbackLoadError: "Unable to load workspace profile.",
|
|
104
|
-
fieldErrorKeys: ["name", "avatarUrl"],
|
|
105
|
-
model: workspaceProfileForm,
|
|
106
|
-
input: workspaceResource.operations.patch.body,
|
|
107
|
-
mapLoadedToModel: (model, payload = {}) => {
|
|
108
|
-
model.name = String(payload?.name || "");
|
|
109
|
-
model.avatarUrl = String(payload?.avatarUrl || "");
|
|
110
|
-
},
|
|
111
|
-
buildRawPayload: (model) => ({
|
|
112
|
-
name: model.name,
|
|
113
|
-
avatarUrl: model.avatarUrl
|
|
114
|
-
}),
|
|
115
|
-
onSaveSuccess: async () => {
|
|
116
|
-
emit("saved");
|
|
117
|
-
}
|
|
118
|
-
});
|
|
119
|
-
|
|
120
|
-
const showSkeleton = computed(() => Boolean(addEdit.isInitialLoading));
|
|
121
|
-
</script>
|
|
122
|
-
|
|
123
|
-
<style scoped>
|
|
124
|
-
.workspace-profile-panel {
|
|
125
|
-
overflow: hidden;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
.workspace-profile-panel__header {
|
|
129
|
-
padding: 1rem 1rem 0;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
.workspace-profile-panel__title {
|
|
133
|
-
font-size: 1rem;
|
|
134
|
-
font-weight: 650;
|
|
135
|
-
line-height: 1.2;
|
|
136
|
-
margin: 0 0 0.25rem;
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
.workspace-profile-panel__body {
|
|
140
|
-
padding: 1rem;
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
.workspace-profile-panel__state {
|
|
144
|
-
margin-inline: auto;
|
|
145
|
-
max-width: 30rem;
|
|
146
|
-
padding: 1.5rem 1rem;
|
|
147
|
-
text-align: center;
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
@media (max-width: 640px) {
|
|
151
|
-
.workspace-profile-panel__body :deep(.v-btn) {
|
|
152
|
-
min-height: 48px;
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
</style>
|
|
@@ -1,102 +0,0 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<section class="workspace-settings-client-element d-flex flex-column ga-4">
|
|
3
|
-
<WorkspaceProfileClientElement @saved="handleFormSaved" />
|
|
4
|
-
<WorkspaceSettingsFieldsClientElement @saved="handleFormSaved" />
|
|
5
|
-
</section>
|
|
6
|
-
</template>
|
|
7
|
-
|
|
8
|
-
<script setup>
|
|
9
|
-
import { computed, watch } from "vue";
|
|
10
|
-
import { useWebPlacementContext } from "@jskit-ai/shell-web/client/placement";
|
|
11
|
-
import { useBootstrapQuery } from "../composables/useBootstrapQuery.js";
|
|
12
|
-
import { useWorkspaceRouteContext } from "../composables/useWorkspaceRouteContext.js";
|
|
13
|
-
import { findWorkspaceBySlug, normalizeWorkspaceList } from "../lib/bootstrap.js";
|
|
14
|
-
import { arePermissionListsEqual, normalizePermissionList } from "@jskit-ai/users-web/client/lib/permissions";
|
|
15
|
-
import WorkspaceProfileClientElement from "./WorkspaceProfileClientElement.vue";
|
|
16
|
-
import WorkspaceSettingsFieldsClientElement from "./WorkspaceSettingsFieldsClientElement.vue";
|
|
17
|
-
|
|
18
|
-
const routeContext = useWorkspaceRouteContext();
|
|
19
|
-
const { context: placementContext, mergeContext: mergePlacementContext } = useWebPlacementContext();
|
|
20
|
-
const bootstrapQuery = useBootstrapQuery({
|
|
21
|
-
workspaceSlug: routeContext.workspaceSlugFromRoute,
|
|
22
|
-
enabled: computed(() => Boolean(routeContext.workspaceSlugFromRoute.value))
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
function toWorkspaceEntrySnapshot(entry = null) {
|
|
26
|
-
if (!entry || typeof entry !== "object") {
|
|
27
|
-
return "";
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
const normalizedEntry = normalizeWorkspaceList([entry])[0] || null;
|
|
31
|
-
if (!normalizedEntry) {
|
|
32
|
-
return "";
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
return JSON.stringify(normalizedEntry);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
function toWorkspaceListSnapshot(list = []) {
|
|
39
|
-
return JSON.stringify(normalizeWorkspaceList(list));
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
function toWorkspaceSettingsSnapshot(settings = null) {
|
|
43
|
-
if (!settings || typeof settings !== "object") {
|
|
44
|
-
return "";
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
return JSON.stringify(settings);
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
function applyShellWorkspaceContext(payload = {}) {
|
|
51
|
-
const availableWorkspaces = normalizeWorkspaceList(payload?.workspaces);
|
|
52
|
-
const currentWorkspace = findWorkspaceBySlug(
|
|
53
|
-
availableWorkspaces,
|
|
54
|
-
routeContext.workspaceSlugFromRoute.value
|
|
55
|
-
);
|
|
56
|
-
const permissions = normalizePermissionList(payload?.permissions);
|
|
57
|
-
const currentContext =
|
|
58
|
-
placementContext.value && typeof placementContext.value === "object"
|
|
59
|
-
? placementContext.value
|
|
60
|
-
: {};
|
|
61
|
-
const currentPermissions = normalizePermissionList(currentContext.permissions);
|
|
62
|
-
const samePermissions = arePermissionListsEqual(permissions, currentPermissions);
|
|
63
|
-
const sameWorkspace = toWorkspaceEntrySnapshot(currentContext.workspace) === toWorkspaceEntrySnapshot(currentWorkspace);
|
|
64
|
-
const sameWorkspaces =
|
|
65
|
-
toWorkspaceListSnapshot(currentContext.workspaces) === toWorkspaceListSnapshot(availableWorkspaces);
|
|
66
|
-
const sameWorkspaceSettings =
|
|
67
|
-
toWorkspaceSettingsSnapshot(currentContext.workspaceSettings) ===
|
|
68
|
-
toWorkspaceSettingsSnapshot(payload?.workspaceSettings);
|
|
69
|
-
|
|
70
|
-
if (samePermissions && sameWorkspace && sameWorkspaces && sameWorkspaceSettings) {
|
|
71
|
-
return;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
mergePlacementContext(
|
|
75
|
-
{
|
|
76
|
-
workspace: currentWorkspace,
|
|
77
|
-
workspaceSettings:
|
|
78
|
-
payload?.workspaceSettings && typeof payload.workspaceSettings === "object"
|
|
79
|
-
? payload.workspaceSettings
|
|
80
|
-
: null,
|
|
81
|
-
workspaces: availableWorkspaces,
|
|
82
|
-
permissions
|
|
83
|
-
},
|
|
84
|
-
"workspaces-web.workspace-settings-view"
|
|
85
|
-
);
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
watch(
|
|
89
|
-
() => bootstrapQuery.query.data.value,
|
|
90
|
-
(payload) => {
|
|
91
|
-
if (!payload || typeof payload !== "object") {
|
|
92
|
-
return;
|
|
93
|
-
}
|
|
94
|
-
applyShellWorkspaceContext(payload);
|
|
95
|
-
},
|
|
96
|
-
{ immediate: true }
|
|
97
|
-
);
|
|
98
|
-
|
|
99
|
-
async function handleFormSaved() {
|
|
100
|
-
await bootstrapQuery.query.refetch();
|
|
101
|
-
}
|
|
102
|
-
</script>
|
|
@@ -1,304 +0,0 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<v-sheet rounded="lg" border class="workspace-settings-panel">
|
|
3
|
-
<header class="workspace-settings-panel__header">
|
|
4
|
-
<h2 class="workspace-settings-panel__title">Workspace settings</h2>
|
|
5
|
-
<p class="text-body-2 text-medium-emphasis mb-0">These values apply to everyone in this workspace.</p>
|
|
6
|
-
</header>
|
|
7
|
-
|
|
8
|
-
<div class="workspace-settings-panel__body">
|
|
9
|
-
<template v-if="showSkeleton">
|
|
10
|
-
<v-skeleton-loader type="text@2, list-item-two-line@4, button" />
|
|
11
|
-
</template>
|
|
12
|
-
|
|
13
|
-
<div v-else-if="addEdit.loadError" class="workspace-settings-panel__state">
|
|
14
|
-
<p class="text-body-2 text-medium-emphasis mb-4">{{ addEdit.loadError }}</p>
|
|
15
|
-
<v-btn
|
|
16
|
-
v-if="addEdit.canRetryLoad"
|
|
17
|
-
color="primary"
|
|
18
|
-
variant="tonal"
|
|
19
|
-
:loading="addEdit.isFetching"
|
|
20
|
-
@click="addEdit.refresh"
|
|
21
|
-
>
|
|
22
|
-
Retry
|
|
23
|
-
</v-btn>
|
|
24
|
-
</div>
|
|
25
|
-
|
|
26
|
-
<p v-else-if="!addEdit.canView" class="text-body-2 text-medium-emphasis mb-4">
|
|
27
|
-
You do not have permission to view workspace settings.
|
|
28
|
-
</p>
|
|
29
|
-
|
|
30
|
-
<template v-else>
|
|
31
|
-
<v-form @submit.prevent="addEdit.submit" novalidate>
|
|
32
|
-
<v-progress-linear v-if="addEdit.isRefetching" indeterminate class="mb-4" />
|
|
33
|
-
<v-row>
|
|
34
|
-
<v-col cols="12">
|
|
35
|
-
<div class="text-subtitle-2 mb-2">Theme colors</div>
|
|
36
|
-
<v-row dense class="mb-2">
|
|
37
|
-
<v-col cols="12">
|
|
38
|
-
<div class="text-caption text-medium-emphasis mb-2">Light palette</div>
|
|
39
|
-
</v-col>
|
|
40
|
-
<v-col cols="12" md="3">
|
|
41
|
-
<v-text-field
|
|
42
|
-
v-model="workspaceSettingsForm.lightPrimaryColor"
|
|
43
|
-
label="Primary"
|
|
44
|
-
type="color"
|
|
45
|
-
variant="outlined"
|
|
46
|
-
density="comfortable"
|
|
47
|
-
:readonly="!addEdit.canSave || addEdit.isSaving || addEdit.isRefetching"
|
|
48
|
-
:error-messages="addEdit.fieldErrors.lightPrimaryColor ? [addEdit.fieldErrors.lightPrimaryColor] : []"
|
|
49
|
-
/>
|
|
50
|
-
</v-col>
|
|
51
|
-
|
|
52
|
-
<v-col cols="12" md="3">
|
|
53
|
-
<v-text-field
|
|
54
|
-
v-model="workspaceSettingsForm.lightSecondaryColor"
|
|
55
|
-
label="Secondary"
|
|
56
|
-
type="color"
|
|
57
|
-
variant="outlined"
|
|
58
|
-
density="comfortable"
|
|
59
|
-
:readonly="!addEdit.canSave || addEdit.isSaving || addEdit.isRefetching"
|
|
60
|
-
:error-messages="
|
|
61
|
-
addEdit.fieldErrors.lightSecondaryColor ? [addEdit.fieldErrors.lightSecondaryColor] : []
|
|
62
|
-
"
|
|
63
|
-
/>
|
|
64
|
-
</v-col>
|
|
65
|
-
|
|
66
|
-
<v-col cols="12" md="3">
|
|
67
|
-
<v-text-field
|
|
68
|
-
v-model="workspaceSettingsForm.lightSurfaceColor"
|
|
69
|
-
label="Surface"
|
|
70
|
-
type="color"
|
|
71
|
-
variant="outlined"
|
|
72
|
-
density="comfortable"
|
|
73
|
-
:readonly="!addEdit.canSave || addEdit.isSaving || addEdit.isRefetching"
|
|
74
|
-
:error-messages="addEdit.fieldErrors.lightSurfaceColor ? [addEdit.fieldErrors.lightSurfaceColor] : []"
|
|
75
|
-
/>
|
|
76
|
-
</v-col>
|
|
77
|
-
|
|
78
|
-
<v-col cols="12" md="3">
|
|
79
|
-
<v-text-field
|
|
80
|
-
v-model="workspaceSettingsForm.lightSurfaceVariantColor"
|
|
81
|
-
label="Surface variant"
|
|
82
|
-
type="color"
|
|
83
|
-
variant="outlined"
|
|
84
|
-
density="comfortable"
|
|
85
|
-
:readonly="!addEdit.canSave || addEdit.isSaving || addEdit.isRefetching"
|
|
86
|
-
:error-messages="
|
|
87
|
-
addEdit.fieldErrors.lightSurfaceVariantColor ? [addEdit.fieldErrors.lightSurfaceVariantColor] : []
|
|
88
|
-
"
|
|
89
|
-
/>
|
|
90
|
-
</v-col>
|
|
91
|
-
</v-row>
|
|
92
|
-
|
|
93
|
-
<v-row dense>
|
|
94
|
-
<v-col cols="12">
|
|
95
|
-
<div class="text-caption text-medium-emphasis mb-2">Dark palette</div>
|
|
96
|
-
</v-col>
|
|
97
|
-
<v-col cols="12" md="3">
|
|
98
|
-
<v-text-field
|
|
99
|
-
v-model="workspaceSettingsForm.darkPrimaryColor"
|
|
100
|
-
label="Primary"
|
|
101
|
-
type="color"
|
|
102
|
-
variant="outlined"
|
|
103
|
-
density="comfortable"
|
|
104
|
-
:readonly="!addEdit.canSave || addEdit.isSaving || addEdit.isRefetching"
|
|
105
|
-
:error-messages="addEdit.fieldErrors.darkPrimaryColor ? [addEdit.fieldErrors.darkPrimaryColor] : []"
|
|
106
|
-
/>
|
|
107
|
-
</v-col>
|
|
108
|
-
|
|
109
|
-
<v-col cols="12" md="3">
|
|
110
|
-
<v-text-field
|
|
111
|
-
v-model="workspaceSettingsForm.darkSecondaryColor"
|
|
112
|
-
label="Secondary"
|
|
113
|
-
type="color"
|
|
114
|
-
variant="outlined"
|
|
115
|
-
density="comfortable"
|
|
116
|
-
:readonly="!addEdit.canSave || addEdit.isSaving || addEdit.isRefetching"
|
|
117
|
-
:error-messages="addEdit.fieldErrors.darkSecondaryColor ? [addEdit.fieldErrors.darkSecondaryColor] : []"
|
|
118
|
-
/>
|
|
119
|
-
</v-col>
|
|
120
|
-
|
|
121
|
-
<v-col cols="12" md="3">
|
|
122
|
-
<v-text-field
|
|
123
|
-
v-model="workspaceSettingsForm.darkSurfaceColor"
|
|
124
|
-
label="Surface"
|
|
125
|
-
type="color"
|
|
126
|
-
variant="outlined"
|
|
127
|
-
density="comfortable"
|
|
128
|
-
:readonly="!addEdit.canSave || addEdit.isSaving || addEdit.isRefetching"
|
|
129
|
-
:error-messages="addEdit.fieldErrors.darkSurfaceColor ? [addEdit.fieldErrors.darkSurfaceColor] : []"
|
|
130
|
-
/>
|
|
131
|
-
</v-col>
|
|
132
|
-
|
|
133
|
-
<v-col cols="12" md="3">
|
|
134
|
-
<v-text-field
|
|
135
|
-
v-model="workspaceSettingsForm.darkSurfaceVariantColor"
|
|
136
|
-
label="Surface variant"
|
|
137
|
-
type="color"
|
|
138
|
-
variant="outlined"
|
|
139
|
-
density="comfortable"
|
|
140
|
-
:readonly="!addEdit.canSave || addEdit.isSaving || addEdit.isRefetching"
|
|
141
|
-
:error-messages="
|
|
142
|
-
addEdit.fieldErrors.darkSurfaceVariantColor ? [addEdit.fieldErrors.darkSurfaceVariantColor] : []
|
|
143
|
-
"
|
|
144
|
-
/>
|
|
145
|
-
</v-col>
|
|
146
|
-
</v-row>
|
|
147
|
-
</v-col>
|
|
148
|
-
|
|
149
|
-
<v-col cols="12" md="6" class="d-flex align-center">
|
|
150
|
-
<v-switch
|
|
151
|
-
v-model="workspaceSettingsForm.invitesEnabled"
|
|
152
|
-
color="primary"
|
|
153
|
-
hide-details
|
|
154
|
-
label="Enable invites"
|
|
155
|
-
:disabled="!addEdit.canSave || !workspaceSettingsForm.invitesAvailable || addEdit.isSaving || addEdit.isRefetching"
|
|
156
|
-
/>
|
|
157
|
-
</v-col>
|
|
158
|
-
|
|
159
|
-
<v-col cols="12" class="d-flex align-center justify-end ga-3">
|
|
160
|
-
<v-btn
|
|
161
|
-
v-if="addEdit.canSave"
|
|
162
|
-
type="submit"
|
|
163
|
-
color="primary"
|
|
164
|
-
:loading="addEdit.isSaving"
|
|
165
|
-
:disabled="addEdit.isInitialLoading || addEdit.isRefetching"
|
|
166
|
-
>
|
|
167
|
-
Save workspace settings
|
|
168
|
-
</v-btn>
|
|
169
|
-
<v-chip v-else color="secondary" label>Read-only</v-chip>
|
|
170
|
-
</v-col>
|
|
171
|
-
</v-row>
|
|
172
|
-
</v-form>
|
|
173
|
-
</template>
|
|
174
|
-
</div>
|
|
175
|
-
</v-sheet>
|
|
176
|
-
</template>
|
|
177
|
-
|
|
178
|
-
<script setup>
|
|
179
|
-
import { computed, reactive } from "vue";
|
|
180
|
-
import { ROUTE_VISIBILITY_WORKSPACE } from "@jskit-ai/kernel/shared/support/visibility";
|
|
181
|
-
import { WORKSPACE_SETTINGS_TRANSPORT } from "@jskit-ai/workspaces-core/shared/jsonApiTransports";
|
|
182
|
-
import { workspaceSettingsResource } from "@jskit-ai/workspaces-core/shared/resources/workspaceSettingsResource";
|
|
183
|
-
import {
|
|
184
|
-
DEFAULT_WORKSPACE_DARK_PALETTE,
|
|
185
|
-
DEFAULT_WORKSPACE_LIGHT_PALETTE,
|
|
186
|
-
resolveWorkspaceThemePalettes
|
|
187
|
-
} from "@jskit-ai/workspaces-core/shared/settings";
|
|
188
|
-
import { useAddEdit } from "@jskit-ai/users-web/client/composables/useAddEdit";
|
|
189
|
-
import { useWorkspaceRouteContext } from "../composables/useWorkspaceRouteContext.js";
|
|
190
|
-
import { createWorkspaceRealtimeMatcher } from "../support/realtimeWorkspace.js";
|
|
191
|
-
import { buildWorkspaceQueryKey } from "../support/workspaceQueryKeys.js";
|
|
192
|
-
|
|
193
|
-
const emit = defineEmits(["saved"]);
|
|
194
|
-
|
|
195
|
-
const workspaceSettingsForm = reactive({
|
|
196
|
-
lightPrimaryColor: DEFAULT_WORKSPACE_LIGHT_PALETTE.color,
|
|
197
|
-
lightSecondaryColor: DEFAULT_WORKSPACE_LIGHT_PALETTE.secondaryColor,
|
|
198
|
-
lightSurfaceColor: DEFAULT_WORKSPACE_LIGHT_PALETTE.surfaceColor,
|
|
199
|
-
lightSurfaceVariantColor: DEFAULT_WORKSPACE_LIGHT_PALETTE.surfaceVariantColor,
|
|
200
|
-
darkPrimaryColor: DEFAULT_WORKSPACE_DARK_PALETTE.color,
|
|
201
|
-
darkSecondaryColor: DEFAULT_WORKSPACE_DARK_PALETTE.secondaryColor,
|
|
202
|
-
darkSurfaceColor: DEFAULT_WORKSPACE_DARK_PALETTE.surfaceColor,
|
|
203
|
-
darkSurfaceVariantColor: DEFAULT_WORKSPACE_DARK_PALETTE.surfaceVariantColor,
|
|
204
|
-
invitesEnabled: false,
|
|
205
|
-
invitesAvailable: false
|
|
206
|
-
});
|
|
207
|
-
|
|
208
|
-
const routeContext = useWorkspaceRouteContext();
|
|
209
|
-
const matchesWorkspaceRealtime = createWorkspaceRealtimeMatcher(routeContext.workspaceSlugFromRoute);
|
|
210
|
-
|
|
211
|
-
const addEdit = useAddEdit({
|
|
212
|
-
ownershipFilter: ROUTE_VISIBILITY_WORKSPACE,
|
|
213
|
-
resource: workspaceSettingsResource,
|
|
214
|
-
apiSuffix: "/settings",
|
|
215
|
-
queryKeyFactory: (surfaceId = "", scopeParamValue = "") =>
|
|
216
|
-
buildWorkspaceQueryKey("settings", surfaceId, scopeParamValue),
|
|
217
|
-
viewPermissions: ["workspace.settings.view", "workspace.settings.update"],
|
|
218
|
-
savePermissions: ["workspace.settings.update"],
|
|
219
|
-
transport: WORKSPACE_SETTINGS_TRANSPORT,
|
|
220
|
-
placementSource: "workspaces-web.workspace-settings-view",
|
|
221
|
-
fallbackLoadError: "Unable to load workspace settings.",
|
|
222
|
-
fieldErrorKeys: [
|
|
223
|
-
"lightPrimaryColor",
|
|
224
|
-
"lightSecondaryColor",
|
|
225
|
-
"lightSurfaceColor",
|
|
226
|
-
"lightSurfaceVariantColor",
|
|
227
|
-
"darkPrimaryColor",
|
|
228
|
-
"darkSecondaryColor",
|
|
229
|
-
"darkSurfaceColor",
|
|
230
|
-
"darkSurfaceVariantColor"
|
|
231
|
-
],
|
|
232
|
-
realtime: {
|
|
233
|
-
event: "workspace.settings.changed",
|
|
234
|
-
matches: matchesWorkspaceRealtime
|
|
235
|
-
},
|
|
236
|
-
model: workspaceSettingsForm,
|
|
237
|
-
input: workspaceSettingsResource.operations.patch.body,
|
|
238
|
-
mapLoadedToModel: (model, payload = {}) => {
|
|
239
|
-
const settings = payload?.settings && typeof payload.settings === "object" ? payload.settings : {};
|
|
240
|
-
const normalizedTheme = resolveWorkspaceThemePalettes(settings);
|
|
241
|
-
|
|
242
|
-
model.lightPrimaryColor = normalizedTheme.light.color;
|
|
243
|
-
model.lightSecondaryColor = normalizedTheme.light.secondaryColor;
|
|
244
|
-
model.lightSurfaceColor = normalizedTheme.light.surfaceColor;
|
|
245
|
-
model.lightSurfaceVariantColor = normalizedTheme.light.surfaceVariantColor;
|
|
246
|
-
model.darkPrimaryColor = normalizedTheme.dark.color;
|
|
247
|
-
model.darkSecondaryColor = normalizedTheme.dark.secondaryColor;
|
|
248
|
-
model.darkSurfaceColor = normalizedTheme.dark.surfaceColor;
|
|
249
|
-
model.darkSurfaceVariantColor = normalizedTheme.dark.surfaceVariantColor;
|
|
250
|
-
model.invitesEnabled = settings.invitesEnabled !== false;
|
|
251
|
-
model.invitesAvailable = settings.invitesAvailable !== false;
|
|
252
|
-
},
|
|
253
|
-
buildRawPayload: (model) => ({
|
|
254
|
-
lightPrimaryColor: model.lightPrimaryColor,
|
|
255
|
-
lightSecondaryColor: model.lightSecondaryColor,
|
|
256
|
-
lightSurfaceColor: model.lightSurfaceColor,
|
|
257
|
-
lightSurfaceVariantColor: model.lightSurfaceVariantColor,
|
|
258
|
-
darkPrimaryColor: model.darkPrimaryColor,
|
|
259
|
-
darkSecondaryColor: model.darkSecondaryColor,
|
|
260
|
-
darkSurfaceColor: model.darkSurfaceColor,
|
|
261
|
-
darkSurfaceVariantColor: model.darkSurfaceVariantColor,
|
|
262
|
-
invitesEnabled: model.invitesEnabled
|
|
263
|
-
}),
|
|
264
|
-
onSaveSuccess: async () => {
|
|
265
|
-
emit("saved");
|
|
266
|
-
}
|
|
267
|
-
});
|
|
268
|
-
|
|
269
|
-
const showSkeleton = computed(() => Boolean(addEdit.isInitialLoading));
|
|
270
|
-
</script>
|
|
271
|
-
|
|
272
|
-
<style scoped>
|
|
273
|
-
.workspace-settings-panel {
|
|
274
|
-
overflow: hidden;
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
.workspace-settings-panel__header {
|
|
278
|
-
padding: 1rem 1rem 0;
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
.workspace-settings-panel__title {
|
|
282
|
-
font-size: 1rem;
|
|
283
|
-
font-weight: 650;
|
|
284
|
-
line-height: 1.2;
|
|
285
|
-
margin: 0 0 0.25rem;
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
.workspace-settings-panel__body {
|
|
289
|
-
padding: 1rem;
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
.workspace-settings-panel__state {
|
|
293
|
-
margin-inline: auto;
|
|
294
|
-
max-width: 30rem;
|
|
295
|
-
padding: 1.5rem 1rem;
|
|
296
|
-
text-align: center;
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
@media (max-width: 640px) {
|
|
300
|
-
.workspace-settings-panel__body :deep(.v-btn) {
|
|
301
|
-
min-height: 48px;
|
|
302
|
-
}
|
|
303
|
-
}
|
|
304
|
-
</style>
|
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
computed
|
|
3
|
-
} from "vue";
|
|
4
|
-
import { useQuery } from "@tanstack/vue-query";
|
|
5
|
-
import { normalizeQueryToken } from "@jskit-ai/kernel/shared/support/normalize";
|
|
6
|
-
import { workspacesWebHttpClient } from "../lib/httpClient.js";
|
|
7
|
-
import { buildBootstrapApiPath } from "../lib/bootstrap.js";
|
|
8
|
-
import { resolveEnabledRef, resolveTextRef } from "./support/refValueHelpers.js";
|
|
9
|
-
|
|
10
|
-
const DEFAULT_BOOTSTRAP_STALE_TIME_MS = 60_000;
|
|
11
|
-
|
|
12
|
-
function normalizeStaleTime(value, fallback = DEFAULT_BOOTSTRAP_STALE_TIME_MS) {
|
|
13
|
-
const parsed = Number(value);
|
|
14
|
-
if (!Number.isFinite(parsed) || parsed < 0) {
|
|
15
|
-
return fallback;
|
|
16
|
-
}
|
|
17
|
-
return parsed;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
function useBootstrapQuery({
|
|
21
|
-
workspaceSlug = "",
|
|
22
|
-
enabled = true,
|
|
23
|
-
staleTime = DEFAULT_BOOTSTRAP_STALE_TIME_MS,
|
|
24
|
-
refetchOnMount = false,
|
|
25
|
-
refetchOnWindowFocus = false
|
|
26
|
-
} = {}) {
|
|
27
|
-
const normalizedWorkspaceSlug = computed(() => resolveTextRef(workspaceSlug));
|
|
28
|
-
const queryKey = computed(() => ["workspaces-web", "bootstrap", normalizeQueryToken(normalizedWorkspaceSlug.value)]);
|
|
29
|
-
const bootstrapPath = computed(() => buildBootstrapApiPath(normalizedWorkspaceSlug.value));
|
|
30
|
-
const queryEnabled = computed(() => resolveEnabledRef(enabled));
|
|
31
|
-
const queryStaleTime = normalizeStaleTime(staleTime, DEFAULT_BOOTSTRAP_STALE_TIME_MS);
|
|
32
|
-
|
|
33
|
-
const query = useQuery({
|
|
34
|
-
queryKey,
|
|
35
|
-
queryFn: () =>
|
|
36
|
-
workspacesWebHttpClient.request(bootstrapPath.value, {
|
|
37
|
-
method: "GET"
|
|
38
|
-
}),
|
|
39
|
-
enabled: queryEnabled,
|
|
40
|
-
staleTime: queryStaleTime,
|
|
41
|
-
refetchOnMount,
|
|
42
|
-
refetchOnWindowFocus
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
return Object.freeze({
|
|
46
|
-
query,
|
|
47
|
-
queryKey,
|
|
48
|
-
workspaceSlug: normalizedWorkspaceSlug
|
|
49
|
-
});
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
export { useBootstrapQuery };
|