@jskit-ai/workspaces-web 0.1.41 → 0.1.43

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.
@@ -1,7 +1,7 @@
1
1
  export default Object.freeze({
2
2
  packageVersion: 1,
3
3
  packageId: "@jskit-ai/workspaces-web",
4
- version: "0.1.41",
4
+ version: "0.1.43",
5
5
  kind: "runtime",
6
6
  description: "Workspace web module: workspace selector, tools widget, workspace surfaces, and members/settings UI.",
7
7
  dependsOn: [
@@ -41,6 +41,10 @@ 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
+ },
44
48
  {
45
49
  subpath: "./client/providers/WorkspacesWebClientProvider",
46
50
  summary: "Exports workspaces-web client provider class."
@@ -69,39 +73,93 @@ export default Object.freeze({
69
73
  outlets: [
70
74
  {
71
75
  target: "admin-settings:primary-menu",
72
- defaultLinkComponentToken: "local.main.ui.surface-aware-menu-link-item",
73
76
  surfaces: ["admin"],
74
77
  source: "templates/src/pages/admin/workspace/settings.vue"
75
78
  },
76
79
  {
77
80
  target: "admin-cog:primary-menu",
78
- defaultLinkComponentToken: "local.main.ui.surface-aware-menu-link-item",
79
81
  surfaces: ["admin"],
80
82
  source: "src/client/components/UsersWorkspaceToolsWidget.vue"
81
83
  }
82
84
  ],
85
+ topology: {
86
+ placements: [
87
+ {
88
+ id: "page.section-nav",
89
+ owner: "admin-settings",
90
+ description: "Navigation between workspace admin settings child pages.",
91
+ surfaces: ["admin"],
92
+ variants: {
93
+ compact: {
94
+ outlet: "admin-settings:primary-menu",
95
+ renderers: {
96
+ link: "local.main.ui.surface-aware-menu-link-item"
97
+ }
98
+ },
99
+ medium: {
100
+ outlet: "admin-settings:primary-menu",
101
+ renderers: {
102
+ link: "local.main.ui.surface-aware-menu-link-item"
103
+ }
104
+ },
105
+ expanded: {
106
+ outlet: "admin-settings:primary-menu",
107
+ renderers: {
108
+ link: "local.main.ui.surface-aware-menu-link-item"
109
+ }
110
+ }
111
+ }
112
+ },
113
+ {
114
+ id: "admin.tools-menu",
115
+ description: "Admin surface tools menu actions.",
116
+ surfaces: ["admin"],
117
+ variants: {
118
+ compact: {
119
+ outlet: "admin-cog:primary-menu",
120
+ renderers: {
121
+ link: "local.main.ui.surface-aware-menu-link-item"
122
+ }
123
+ },
124
+ medium: {
125
+ outlet: "admin-cog:primary-menu",
126
+ renderers: {
127
+ link: "local.main.ui.surface-aware-menu-link-item"
128
+ }
129
+ },
130
+ expanded: {
131
+ outlet: "admin-cog:primary-menu",
132
+ renderers: {
133
+ link: "local.main.ui.surface-aware-menu-link-item"
134
+ }
135
+ }
136
+ }
137
+ }
138
+ ]
139
+ },
83
140
  contributions: [
84
141
  {
85
142
  id: "workspaces.workspace.menu.app",
86
- target: "shell-layout:primary-menu",
143
+ target: "shell.primary-nav",
144
+ kind: "link",
87
145
  surfaces: ["app"],
88
146
  order: 50,
89
- componentToken: "local.main.ui.surface-aware-menu-link-item",
90
147
  when: "auth.authenticated === true",
91
148
  source: "mutations.text#workspaces-web-placement-block"
92
149
  },
93
150
  {
94
151
  id: "workspaces.workspace.menu.admin",
95
- target: "shell-layout:primary-menu",
152
+ target: "shell.primary-nav",
153
+ kind: "link",
96
154
  surfaces: ["admin"],
97
155
  order: 60,
98
- componentToken: "local.main.ui.surface-aware-menu-link-item",
99
156
  when: "auth.authenticated === true",
100
157
  source: "mutations.text#workspaces-web-placement-block"
101
158
  },
102
159
  {
103
160
  id: "workspaces.profile.menu.surface-switch",
104
- target: "auth-profile-menu:primary-menu",
161
+ target: "auth.profile-menu",
162
+ kind: "component",
105
163
  surfaces: ["*"],
106
164
  order: 100,
107
165
  componentToken: "workspaces.web.profile.menu.surface-switch-item",
@@ -110,7 +168,8 @@ export default Object.freeze({
110
168
  },
111
169
  {
112
170
  id: "workspaces.workspace.selector",
113
- target: "shell-layout:top-left",
171
+ target: "shell.identity",
172
+ kind: "component",
114
173
  surfaces: ["*"],
115
174
  order: 200,
116
175
  componentToken: "workspaces.web.workspace.selector",
@@ -119,7 +178,8 @@ export default Object.freeze({
119
178
  },
120
179
  {
121
180
  id: "workspaces.account.invites.cue",
122
- target: "shell-layout:top-right",
181
+ target: "shell.status",
182
+ kind: "component",
123
183
  surfaces: ["*"],
124
184
  order: 850,
125
185
  componentToken: "local.main.account.pending-invites.cue",
@@ -128,7 +188,9 @@ export default Object.freeze({
128
188
  },
129
189
  {
130
190
  id: "workspaces.account.settings.invites",
131
- target: "account-settings:sections",
191
+ target: "settings.sections",
192
+ owner: "account-settings",
193
+ kind: "component",
132
194
  surfaces: ["account"],
133
195
  order: 400,
134
196
  componentToken: "local.main.account-settings.section.invites",
@@ -137,7 +199,8 @@ export default Object.freeze({
137
199
  },
138
200
  {
139
201
  id: "workspaces.workspace.tools.widget",
140
- target: "shell-layout:top-right",
202
+ target: "shell.status",
203
+ kind: "component",
141
204
  surfaces: ["admin"],
142
205
  order: 900,
143
206
  componentToken: "workspaces.web.workspace.tools.widget",
@@ -145,7 +208,8 @@ export default Object.freeze({
145
208
  },
146
209
  {
147
210
  id: "workspaces.workspace.menu.workspace-settings",
148
- target: "admin-cog:primary-menu",
211
+ target: "admin.tools-menu",
212
+ kind: "component",
149
213
  surfaces: ["admin"],
150
214
  order: 100,
151
215
  componentToken: "workspaces.web.workspace-settings.menu-item",
@@ -153,7 +217,8 @@ export default Object.freeze({
153
217
  },
154
218
  {
155
219
  id: "workspaces.workspace.menu.members",
156
- target: "admin-cog:primary-menu",
220
+ target: "admin.tools-menu",
221
+ kind: "component",
157
222
  surfaces: ["admin"],
158
223
  order: 200,
159
224
  componentToken: "workspaces.web.workspace-members.menu-item",
@@ -166,9 +231,8 @@ export default Object.freeze({
166
231
  mutations: {
167
232
  dependencies: {
168
233
  runtime: {
169
- "@jskit-ai/workspaces-core": "0.1.41",
170
- "@jskit-ai/users-web": "0.1.80",
171
- "vuetify": "^4.0.0"
234
+ "@jskit-ai/workspaces-core": "0.1.43",
235
+ "@jskit-ai/users-web": "0.1.82"
172
236
  },
173
237
  dev: {}
174
238
  },
@@ -310,7 +374,7 @@ export default Object.freeze({
310
374
  position: "bottom",
311
375
  skipIfContains: "id: \"workspaces.profile.menu.surface-switch\"",
312
376
  value:
313
- "\naddPlacement({\n id: \"workspaces.profile.menu.surface-switch\",\n target: \"auth-profile-menu:primary-menu\",\n surfaces: [\"*\"],\n order: 100,\n componentToken: \"workspaces.web.profile.menu.surface-switch-item\",\n when: ({ auth }) => auth?.authenticated === true\n});\n",
377
+ "\naddPlacement({\n id: \"workspaces.profile.menu.surface-switch\",\n target: \"auth.profile-menu\",\n kind: \"component\",\n surfaces: [\"*\"],\n order: 100,\n componentToken: \"workspaces.web.profile.menu.surface-switch-item\",\n when: ({ auth }) => auth?.authenticated === true\n});\n",
314
378
  reason: "Append workspaces-web profile surface switch placement into app-owned placement registry.",
315
379
  category: "workspaces-web",
316
380
  id: "workspaces-web-profile-surface-switch-placement",
@@ -324,7 +388,7 @@ export default Object.freeze({
324
388
  file: "src/placement.js",
325
389
  position: "bottom",
326
390
  skipIfContains: "id: \"workspaces.workspace.selector\"",
327
- value: "\naddPlacement({\n id: \"workspaces.workspace.menu.app\",\n target: \"shell-layout:primary-menu\",\n surfaces: [\"app\"],\n order: 50,\n componentToken: \"local.main.ui.surface-aware-menu-link-item\",\n props: {\n label: \"Home\",\n surface: \"app\",\n scopedSuffix: \"/\",\n unscopedSuffix: \"/\",\n exact: true\n },\n when: ({ auth }) => auth?.authenticated === true\n});\n\naddPlacement({\n id: \"workspaces.workspace.menu.admin\",\n target: \"shell-layout:primary-menu\",\n surfaces: [\"admin\"],\n order: 60,\n componentToken: \"local.main.ui.surface-aware-menu-link-item\",\n props: {\n label: \"Home\",\n surface: \"admin\",\n scopedSuffix: \"/\",\n unscopedSuffix: \"/\",\n exact: true\n },\n when: ({ auth }) => auth?.authenticated === true\n});\n\naddPlacement({\n id: \"workspaces.workspace.selector\",\n target: \"shell-layout:top-left\",\n surfaces: [\"*\"],\n order: 200,\n componentToken: \"workspaces.web.workspace.selector\",\n props: {\n allowOnNonWorkspaceSurface: true,\n targetSurfaceId: \"app\"\n },\n when: ({ auth }) => auth?.authenticated === true\n});\n\naddPlacement({\n id: \"workspaces.account.invites.cue\",\n target: \"shell-layout:top-right\",\n surfaces: [\"*\"],\n order: 850,\n componentToken: \"local.main.account.pending-invites.cue\",\n when: ({ auth }) => auth?.authenticated === true\n});\n\naddPlacement({\n id: \"workspaces.workspace.tools.widget\",\n target: \"shell-layout:top-right\",\n surfaces: [\"admin\"],\n order: 900,\n componentToken: \"workspaces.web.workspace.tools.widget\"\n});\n\naddPlacement({\n id: \"workspaces.workspace.menu.workspace-settings\",\n target: \"admin-cog:primary-menu\",\n surfaces: [\"admin\"],\n order: 100,\n componentToken: \"workspaces.web.workspace-settings.menu-item\"\n});\n\naddPlacement({\n id: \"workspaces.workspace.menu.members\",\n target: \"admin-cog:primary-menu\",\n surfaces: [\"admin\"],\n order: 200,\n componentToken: \"workspaces.web.workspace-members.menu-item\"\n});\n",
391
+ value: "\naddPlacement({\n id: \"workspaces.workspace.menu.app\",\n target: \"shell.primary-nav\",\n kind: \"link\",\n surfaces: [\"app\"],\n order: 50,\n props: {\n label: \"Home\",\n surface: \"app\",\n scopedSuffix: \"/\",\n unscopedSuffix: \"/\",\n exact: true\n },\n when: ({ auth }) => auth?.authenticated === true\n});\n\naddPlacement({\n id: \"workspaces.workspace.menu.admin\",\n target: \"shell.primary-nav\",\n kind: \"link\",\n surfaces: [\"admin\"],\n order: 60,\n props: {\n label: \"Home\",\n surface: \"admin\",\n scopedSuffix: \"/\",\n unscopedSuffix: \"/\",\n exact: true\n },\n when: ({ auth }) => auth?.authenticated === true\n});\n\naddPlacement({\n id: \"workspaces.workspace.selector\",\n target: \"shell.identity\",\n kind: \"component\",\n surfaces: [\"*\"],\n order: 200,\n componentToken: \"workspaces.web.workspace.selector\",\n props: {\n allowOnNonWorkspaceSurface: true,\n targetSurfaceId: \"app\"\n },\n when: ({ auth }) => auth?.authenticated === true\n});\n\naddPlacement({\n id: \"workspaces.account.invites.cue\",\n target: \"shell.status\",\n kind: \"component\",\n surfaces: [\"*\"],\n order: 850,\n componentToken: \"local.main.account.pending-invites.cue\",\n when: ({ auth }) => auth?.authenticated === true\n});\n\naddPlacement({\n id: \"workspaces.workspace.tools.widget\",\n target: \"shell.status\",\n kind: \"component\",\n surfaces: [\"admin\"],\n order: 900,\n componentToken: \"workspaces.web.workspace.tools.widget\"\n});\n\naddPlacement({\n id: \"workspaces.workspace.menu.workspace-settings\",\n target: \"admin.tools-menu\",\n kind: \"component\",\n surfaces: [\"admin\"],\n order: 100,\n componentToken: \"workspaces.web.workspace-settings.menu-item\"\n});\n\naddPlacement({\n id: \"workspaces.workspace.menu.members\",\n target: \"admin.tools-menu\",\n kind: \"component\",\n surfaces: [\"admin\"],\n order: 200,\n componentToken: \"workspaces.web.workspace-members.menu-item\"\n});\n",
328
392
  reason: "Append workspace placement entries into app-owned placement registry.",
329
393
  category: "workspaces-web",
330
394
  id: "workspaces-web-placement-block",
@@ -333,13 +397,27 @@ export default Object.freeze({
333
397
  in: ["personal", "workspaces"]
334
398
  }
335
399
  },
400
+ {
401
+ op: "append-text",
402
+ file: "src/placementTopology.js",
403
+ position: "bottom",
404
+ skipIfContains: "id: \"admin.tools-menu\"",
405
+ value: "\naddPlacementTopology({\n id: \"page.section-nav\",\n owner: \"admin-settings\",\n description: \"Navigation between workspace admin settings child pages.\",\n surfaces: [\"admin\"],\n variants: {\n compact: {\n outlet: \"admin-settings:primary-menu\",\n renderers: {\n link: \"local.main.ui.surface-aware-menu-link-item\"\n }\n },\n medium: {\n outlet: \"admin-settings:primary-menu\",\n renderers: {\n link: \"local.main.ui.surface-aware-menu-link-item\"\n }\n },\n expanded: {\n outlet: \"admin-settings:primary-menu\",\n renderers: {\n link: \"local.main.ui.surface-aware-menu-link-item\"\n }\n }\n }\n});\n\naddPlacementTopology({\n id: \"admin.tools-menu\",\n description: \"Admin surface tools menu actions.\",\n surfaces: [\"admin\"],\n variants: {\n compact: {\n outlet: \"admin-cog:primary-menu\",\n renderers: {\n link: \"local.main.ui.surface-aware-menu-link-item\"\n }\n },\n medium: {\n outlet: \"admin-cog:primary-menu\",\n renderers: {\n link: \"local.main.ui.surface-aware-menu-link-item\"\n }\n },\n expanded: {\n outlet: \"admin-cog:primary-menu\",\n renderers: {\n link: \"local.main.ui.surface-aware-menu-link-item\"\n }\n }\n }\n});\n",
406
+ reason: "Append workspaces-web admin semantic topology into app-owned placement topology.",
407
+ category: "workspaces-web",
408
+ id: "workspaces-web-admin-placement-topology",
409
+ when: {
410
+ config: "tenancyMode",
411
+ in: ["personal", "workspaces"]
412
+ }
413
+ },
336
414
  {
337
415
  op: "append-text",
338
416
  file: "src/placement.js",
339
417
  position: "bottom",
340
418
  skipIfContains: "id: \"workspaces.account.settings.invites\"",
341
419
  value:
342
- "\naddPlacement({\n id: \"workspaces.account.settings.invites\",\n target: \"account-settings:sections\",\n surfaces: [\"account\"],\n order: 400,\n componentToken: \"local.main.account-settings.section.invites\",\n props: {\n title: \"Invites\",\n value: \"invites\",\n usesSharedRuntime: false\n },\n when: ({ auth, workspaceInvitesEnabled }) => auth?.authenticated === true && workspaceInvitesEnabled === true\n});\n",
420
+ "\naddPlacement({\n id: \"workspaces.account.settings.invites\",\n target: \"settings.sections\",\n owner: \"account-settings\",\n kind: \"component\",\n surfaces: [\"account\"],\n order: 400,\n componentToken: \"local.main.account-settings.section.invites\",\n props: {\n title: \"Invites\",\n value: \"invites\",\n usesSharedRuntime: false\n },\n when: ({ auth, workspaceInvitesEnabled }) => auth?.authenticated === true && workspaceInvitesEnabled === true\n});\n",
343
421
  reason: "Append workspaces-web account settings invites section placement into app-owned placement registry.",
344
422
  category: "workspaces-web",
345
423
  id: "workspaces-web-account-settings-placement",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jskit-ai/workspaces-web",
3
- "version": "0.1.41",
3
+ "version": "0.1.43",
4
4
  "type": "module",
5
5
  "scripts": {
6
6
  "test": "node --test"
@@ -10,21 +10,22 @@
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",
13
14
  "./client/composables/useWorkspaceRouteContext": "./src/client/composables/useWorkspaceRouteContext.js"
14
15
  },
15
16
  "dependencies": {
16
- "@tanstack/vue-query": "5.92.12",
17
17
  "@mdi/js": "^7.4.47",
18
- "@jskit-ai/http-runtime": "0.1.64",
19
- "@jskit-ai/kernel": "0.1.65",
20
- "@jskit-ai/shell-web": "0.1.64",
21
- "@jskit-ai/users-core": "0.1.75",
22
- "@jskit-ai/users-web": "0.1.80",
23
- "@jskit-ai/workspaces-core": "0.1.41",
24
- "vuetify": "^4.0.0"
18
+ "@jskit-ai/http-runtime": "0.1.66",
19
+ "@jskit-ai/kernel": "0.1.67",
20
+ "@jskit-ai/shell-web": "0.1.66",
21
+ "@jskit-ai/users-core": "0.1.77",
22
+ "@jskit-ai/users-web": "0.1.82",
23
+ "@jskit-ai/workspaces-core": "0.1.43"
25
24
  },
26
25
  "peerDependencies": {
26
+ "@tanstack/vue-query": "^5.90.5",
27
27
  "vue": "^3.5.13",
28
- "vue-router": "^5.0.4"
28
+ "vue-router": "^5.0.4",
29
+ "vuetify": "^4.0.0"
29
30
  }
30
31
  }
@@ -5,13 +5,13 @@ const invites = useAccountSettingsInvitesSectionRuntime();
5
5
  </script>
6
6
 
7
7
  <template>
8
- <v-card rounded="lg" elevation="0" border>
9
- <v-card-item>
10
- <v-card-title class="text-subtitle-1">Invitations</v-card-title>
11
- <v-card-subtitle>Accept or refuse workspace invitations.</v-card-subtitle>
12
- </v-card-item>
13
- <v-divider />
14
- <v-card-text>
8
+ <v-sheet rounded="lg" border class="account-invites-section">
9
+ <header class="account-invites-section__header">
10
+ <h2 class="account-invites-section__title">Invitations</h2>
11
+ <p class="text-body-2 text-medium-emphasis mb-0">Accept or refuse workspace invitations.</p>
12
+ </header>
13
+
14
+ <div class="account-invites-section__body">
15
15
  <template v-if="invites.isLoading.value">
16
16
  <v-skeleton-loader type="text@2, list-item-two-line@3" />
17
17
  </template>
@@ -67,6 +67,33 @@ const invites = useAccountSettingsInvitesSectionRuntime();
67
67
  </v-list-item>
68
68
  </v-list>
69
69
  </template>
70
- </v-card-text>
71
- </v-card>
70
+ </div>
71
+ </v-sheet>
72
72
  </template>
73
+
74
+ <style scoped>
75
+ .account-invites-section {
76
+ overflow: hidden;
77
+ }
78
+
79
+ .account-invites-section__header {
80
+ padding: 1rem 1rem 0;
81
+ }
82
+
83
+ .account-invites-section__title {
84
+ font-size: 1rem;
85
+ font-weight: 650;
86
+ line-height: 1.2;
87
+ margin: 0 0 0.25rem;
88
+ }
89
+
90
+ .account-invites-section__body {
91
+ padding: 1rem;
92
+ }
93
+
94
+ @media (max-width: 640px) {
95
+ .account-invites-section__body :deep(.v-btn) {
96
+ min-height: 48px;
97
+ }
98
+ }
99
+ </style>
@@ -6,7 +6,6 @@ import { ADMIN_COG_OUTLET } from "../../shared/toolsOutletContracts.js";
6
6
  <template>
7
7
  <ShellOutletMenuWidget
8
8
  :target="ADMIN_COG_OUTLET.target"
9
- :default-link-component-token="ADMIN_COG_OUTLET.defaultLinkComponentToken"
10
9
  :aria-label="ADMIN_COG_OUTLET.ariaLabel"
11
10
  />
12
11
  </template>
@@ -1,8 +1,17 @@
1
1
  <template>
2
2
  <section class="workspace-members-page">
3
- <p v-if="loadError" class="text-body-2 text-medium-emphasis mb-4">
4
- {{ loadError }}
5
- </p>
3
+ <div v-if="loadError" class="workspace-members-page__state">
4
+ <p class="text-body-2 text-medium-emphasis mb-4">{{ loadError }}</p>
5
+ <v-btn
6
+ v-if="canRetryLoad"
7
+ color="primary"
8
+ variant="tonal"
9
+ :loading="isRetryingLoad"
10
+ @click="refreshLoad"
11
+ >
12
+ Retry
13
+ </v-btn>
14
+ </div>
6
15
 
7
16
  <MembersAdminClientElement
8
17
  v-else
@@ -38,7 +47,6 @@ import { useView } from "@jskit-ai/users-web/client/composables/useView";
38
47
  import { usePaths } from "@jskit-ai/users-web/client/composables/usePaths";
39
48
  import { useAccess } from "@jskit-ai/users-web/client/composables/useAccess";
40
49
  import { useUiFeedback } from "@jskit-ai/users-web/client/composables/runtime/useUiFeedback";
41
- import { useShellWebErrorRuntime } from "@jskit-ai/shell-web/client/error";
42
50
  import { useWorkspaceRouteContext } from "../composables/useWorkspaceRouteContext.js";
43
51
  import { createWorkspaceRealtimeMatcher } from "../support/realtimeWorkspace.js";
44
52
  import { buildWorkspaceQueryKey } from "../support/workspaceQueryKeys.js";
@@ -75,7 +83,6 @@ const removeMemberUserId = ref("");
75
83
 
76
84
  const { route, currentSurfaceId, workspaceSlugFromRoute } = useWorkspaceRouteContext();
77
85
  const usersPaths = usePaths();
78
- const errorRuntime = useShellWebErrorRuntime();
79
86
  const OWNERSHIP_WORKSPACE = ROUTE_VISIBILITY_WORKSPACE;
80
87
 
81
88
  const hasRouteWorkspaceSlug = computed(() => Boolean(workspaceSlugFromRoute.value));
@@ -423,27 +430,44 @@ const loadError = computed(() => {
423
430
  return "Workspace slug is required in the URL.";
424
431
  }
425
432
 
426
- return access.bootstrapError.value;
433
+ return (
434
+ access.bootstrapError.value ||
435
+ workspaceSettingsView.loadError ||
436
+ workspaceRolesView.loadError ||
437
+ workspaceMembersList.loadError ||
438
+ workspaceInvitesList.loadError ||
439
+ ""
440
+ );
427
441
  });
428
-
429
- watch(
430
- loadError,
431
- (nextLoadError) => {
432
- if (!nextLoadError) {
433
- return;
434
- }
435
- errorRuntime.report({
436
- source: "users-web.workspace-members-view",
437
- severity: "error",
438
- channel: "banner",
439
- message: String(nextLoadError || "Unable to load workspace members."),
440
- dedupeKey: `users-web.workspace-members-view:bootstrap:${nextLoadError}`,
441
- dedupeWindowMs: 3000
442
- });
443
- },
444
- { immediate: true }
442
+ const canRetryLoad = computed(() => Boolean(hasRouteWorkspaceSlug.value && !access.bootstrapError.value));
443
+ const isRetryingLoad = computed(() =>
444
+ Boolean(
445
+ workspaceSettingsView.isFetching ||
446
+ workspaceRolesView.isFetching ||
447
+ workspaceMembersList.isFetching ||
448
+ workspaceInvitesList.isFetching
449
+ )
445
450
  );
446
451
 
452
+ async function refreshLoad() {
453
+ if (!canRetryLoad.value) {
454
+ return;
455
+ }
456
+
457
+ const jobs = [];
458
+ if (canInviteMembers.value) {
459
+ jobs.push(workspaceSettingsView.refresh());
460
+ }
461
+ if (canInviteMembers.value || canViewMembers.value) {
462
+ jobs.push(workspaceRolesView.refresh());
463
+ }
464
+ if (canViewMembers.value) {
465
+ jobs.push(workspaceMembersList.reload());
466
+ jobs.push(workspaceInvitesList.reload());
467
+ }
468
+ await Promise.all(jobs);
469
+ }
470
+
447
471
  const actions = Object.freeze({
448
472
  submitInvite,
449
473
  submitRevokeInvite,
@@ -522,17 +546,6 @@ watch(
522
546
  { immediate: true }
523
547
  );
524
548
 
525
- watch(
526
- () => workspaceMembersList.loadError,
527
- (nextLoadError) => {
528
- if (!nextLoadError) {
529
- membersFeedback.clear();
530
- return;
531
- }
532
- membersFeedback.error(null, nextLoadError);
533
- }
534
- );
535
-
536
549
  watch(
537
550
  () => workspaceInvitesList.items,
538
551
  (nextInvites) => {
@@ -553,17 +566,6 @@ watch(
553
566
  { immediate: true }
554
567
  );
555
568
 
556
- watch(
557
- () => workspaceInvitesList.loadError,
558
- (nextLoadError) => {
559
- if (!nextLoadError) {
560
- teamFeedback.clear();
561
- return;
562
- }
563
- teamFeedback.error(null, nextLoadError);
564
- }
565
- );
566
-
567
569
  watch(
568
570
  () => route.fullPath,
569
571
  () => {
@@ -671,3 +673,18 @@ async function submitRemoveMember(member) {
671
673
  }
672
674
  }
673
675
  </script>
676
+
677
+ <style scoped>
678
+ .workspace-members-page__state {
679
+ margin-inline: auto;
680
+ max-width: 30rem;
681
+ padding: 2rem 1rem;
682
+ text-align: center;
683
+ }
684
+
685
+ @media (max-width: 640px) {
686
+ .workspace-members-page__state :deep(.v-btn) {
687
+ min-height: 48px;
688
+ }
689
+ }
690
+ </style>
@@ -1,18 +1,27 @@
1
1
  <template>
2
- <v-card class="mb-4" rounded="lg" elevation="1" border>
3
- <v-card-item>
4
- <v-card-title class="text-h6">Workspace profile</v-card-title>
5
- <v-card-subtitle>Name and avatar used across the workspace.</v-card-subtitle>
6
- </v-card-item>
7
- <v-divider />
8
- <v-card-text class="pt-4">
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
9
  <template v-if="showSkeleton">
10
10
  <v-skeleton-loader type="text@2, list-item-two-line@3, button" />
11
11
  </template>
12
12
 
13
- <p v-else-if="addEdit.loadError" class="text-body-2 text-medium-emphasis mb-4">
14
- {{ addEdit.loadError }}
15
- </p>
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>
16
25
 
17
26
  <p v-else-if="!addEdit.canView" class="text-body-2 text-medium-emphasis mb-4">
18
27
  You do not have permission to view workspace profile.
@@ -62,8 +71,8 @@
62
71
  </v-row>
63
72
  </v-form>
64
73
  </template>
65
- </v-card-text>
66
- </v-card>
74
+ </div>
75
+ </v-sheet>
67
76
  </template>
68
77
 
69
78
  <script setup>
@@ -110,3 +119,37 @@ const addEdit = useAddEdit({
110
119
 
111
120
  const showSkeleton = computed(() => Boolean(addEdit.isInitialLoading));
112
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,5 +1,5 @@
1
1
  <template>
2
- <section class="workspace-settings-client-element">
2
+ <section class="workspace-settings-client-element d-flex flex-column ga-4">
3
3
  <WorkspaceProfileClientElement @saved="handleFormSaved" />
4
4
  <WorkspaceSettingsFieldsClientElement @saved="handleFormSaved" />
5
5
  </section>