@jskit-ai/users-web 0.1.37 → 0.1.40

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 (87) hide show
  1. package/package.descriptor.mjs +13 -244
  2. package/package.json +17 -13
  3. package/src/client/components/ConsoleSettingsClientElement.vue +1 -1
  4. package/src/client/components/UsersSurfaceAwareMenuLinkItem.vue +14 -25
  5. package/src/client/components/WorkspaceMembersClientElement.vue +3 -3
  6. package/src/client/components/WorkspaceProfileClientElement.vue +1 -1
  7. package/src/client/components/WorkspaceSettingsFieldsClientElement.vue +1 -1
  8. package/src/client/components/WorkspacesClientElement.vue +2 -2
  9. package/src/client/composables/account-settings/accountSettingsAvatarUploadRuntime.js +61 -0
  10. package/src/client/composables/{accountSettingsInvitesRuntime.js → account-settings/accountSettingsInvitesRuntime.js} +1 -1
  11. package/src/client/composables/{accountSettingsRuntimeHelpers.js → account-settings/accountSettingsRuntimeHelpers.js} +1 -1
  12. package/src/client/composables/crud/crudBindingSupport.js +75 -0
  13. package/src/client/composables/{crudLookupFieldLabelSupport.js → crud/crudLookupFieldLabelSupport.js} +1 -1
  14. package/src/client/composables/{crudLookupFieldRuntime.js → crud/crudLookupFieldRuntime.js} +6 -2
  15. package/src/client/composables/{crudSchemaFormHelpers.js → crud/crudSchemaFormHelpers.js} +155 -2
  16. package/src/client/composables/internal/crudListParentTitleSupport.js +168 -0
  17. package/src/client/composables/internal/useOperationScope.js +1 -1
  18. package/src/client/composables/{useAddEdit.js → records/useAddEdit.js} +9 -9
  19. package/src/client/composables/{useCrudSchemaForm.js → records/useCrudAddEdit.js} +32 -15
  20. package/src/client/composables/records/useCrudList.js +83 -0
  21. package/src/client/composables/records/useCrudView.js +35 -0
  22. package/src/client/composables/{useList.js → records/useList.js} +31 -57
  23. package/src/client/composables/{useView.js → records/useView.js} +6 -9
  24. package/src/client/composables/{addEditUiRuntime.js → runtime/addEditUiRuntime.js} +2 -2
  25. package/src/client/composables/{listUiRuntime.js → runtime/listUiRuntime.js} +2 -2
  26. package/src/client/composables/{operationAdapters.js → runtime/operationAdapters.js} +1 -1
  27. package/src/client/composables/{useEndpointResource.js → runtime/useEndpointResource.js} +5 -5
  28. package/src/client/composables/{useListCore.js → runtime/useListCore.js} +4 -4
  29. package/src/client/composables/{useUiFeedback.js → runtime/useUiFeedback.js} +1 -1
  30. package/src/client/composables/{viewUiRuntime.js → runtime/viewUiRuntime.js} +2 -2
  31. package/src/client/composables/useAccess.js +2 -2
  32. package/src/client/composables/useAccountSettingsRuntime.js +6 -6
  33. package/src/client/composables/useBootstrapQuery.js +1 -1
  34. package/src/client/composables/useCommand.js +5 -5
  35. package/src/client/composables/useCrudListParentTitle.js +131 -0
  36. package/src/client/composables/usePagedCollection.js +3 -3
  37. package/src/client/composables/useScopeRuntime.js +1 -1
  38. package/src/client/index.js +1 -0
  39. package/src/client/providers/UsersWebClientProvider.js +0 -12
  40. package/src/client/providers/UsersWorkspacesClientProvider.js +26 -0
  41. package/src/client/support/menuLinkTarget.js +93 -0
  42. package/templates/src/components/account/settings/AccountSettingsClientElement.vue +16 -9
  43. package/templates/src/pages/console/settings/index.vue +1 -0
  44. package/test/addEditUiRuntime.test.js +1 -1
  45. package/test/crudBindingSupport.test.js +110 -0
  46. package/test/crudLookupFieldRuntime.test.js +1 -1
  47. package/test/errorMessageHelpers.test.js +1 -1
  48. package/test/exportsContract.test.js +10 -1
  49. package/test/listQueryParamSupport.test.js +1 -1
  50. package/test/listUiRuntime.test.js +1 -1
  51. package/test/menuLinkTarget.test.js +116 -0
  52. package/test/permissions.test.js +2 -2
  53. package/test/refValueHelpers.test.js +1 -1
  54. package/test/resourceLoadStateHelpers.test.js +1 -1
  55. package/test/routeTemplateHelpers.test.js +1 -1
  56. package/test/scopeHelpers.test.js +1 -1
  57. package/test/settingsPlacementContract.test.js +44 -0
  58. package/test/{useCrudSchemaForm.test.js → useCrudAddEdit.test.js} +81 -1
  59. package/test/useCrudListParentTitle.test.js +143 -0
  60. package/test/useListSearchSupport.test.js +1 -1
  61. package/test/viewCoreLoading.test.js +1 -1
  62. package/test/viewUiRuntime.test.js +1 -1
  63. package/src/client/composables/accountSettingsAvatarUploadRuntime.js +0 -95
  64. package/templates/packages/main/src/client/components/AccountPendingInvitesCue.vue +0 -162
  65. package/templates/src/components/WorkspaceNotFoundCard.vue +0 -34
  66. package/templates/src/composables/useWorkspaceNotFoundState.js +0 -41
  67. package/templates/src/pages/admin/members/index.vue +0 -7
  68. package/templates/src/pages/admin/workspace/settings/index.vue +0 -16
  69. package/templates/src/surfaces/admin/index.vue +0 -29
  70. package/templates/src/surfaces/admin/root.vue +0 -20
  71. package/templates/src/surfaces/app/index.vue +0 -27
  72. package/templates/src/surfaces/app/root.vue +0 -20
  73. /package/src/client/composables/{accountSettingsRuntimeConstants.js → account-settings/accountSettingsRuntimeConstants.js} +0 -0
  74. /package/src/client/composables/{modelStateHelpers.js → runtime/modelStateHelpers.js} +0 -0
  75. /package/src/client/composables/{operationUiHelpers.js → runtime/operationUiHelpers.js} +0 -0
  76. /package/src/client/composables/{operationValidationHelpers.js → runtime/operationValidationHelpers.js} +0 -0
  77. /package/src/client/composables/{useAddEditCore.js → runtime/useAddEditCore.js} +0 -0
  78. /package/src/client/composables/{useCommandCore.js → runtime/useCommandCore.js} +0 -0
  79. /package/src/client/composables/{useFieldErrorBag.js → runtime/useFieldErrorBag.js} +0 -0
  80. /package/src/client/composables/{useViewCore.js → runtime/useViewCore.js} +0 -0
  81. /package/src/client/composables/{errorMessageHelpers.js → support/errorMessageHelpers.js} +0 -0
  82. /package/src/client/composables/{listQueryParamSupport.js → support/listQueryParamSupport.js} +0 -0
  83. /package/src/client/composables/{listSearchSupport.js → support/listSearchSupport.js} +0 -0
  84. /package/src/client/composables/{refValueHelpers.js → support/refValueHelpers.js} +0 -0
  85. /package/src/client/composables/{resourceLoadStateHelpers.js → support/resourceLoadStateHelpers.js} +0 -0
  86. /package/src/client/composables/{routeTemplateHelpers.js → support/routeTemplateHelpers.js} +0 -0
  87. /package/src/client/composables/{scopeHelpers.js → support/scopeHelpers.js} +0 -0
@@ -0,0 +1,83 @@
1
+ import { computed, unref } from "vue";
2
+ import { useRoute } from "vue-router";
3
+ import { resolveLookupFieldDisplayValue } from "../crud/crudLookupFieldLabelSupport.js";
4
+ import { resolveCrudBoundValues } from "../crud/crudBindingSupport.js";
5
+ import { resolveCrudListParentDescriptor } from "../internal/crudListParentTitleSupport.js";
6
+ import {
7
+ resolveRouteParamsSource,
8
+ toRouteParamValue
9
+ } from "../support/routeTemplateHelpers.js";
10
+ import { asPlainObject } from "../support/scopeHelpers.js";
11
+ import { useList } from "./useList.js";
12
+
13
+ function resolveRequestQueryParamsInput(requestQueryParams, context = {}) {
14
+ if (typeof requestQueryParams === "function") {
15
+ return asPlainObject(requestQueryParams(context));
16
+ }
17
+
18
+ return asPlainObject(unref(requestQueryParams));
19
+ }
20
+
21
+ function resolveCrudParentRequestQueryParams({ resource = {}, route = null, recordIdParam = "recordId" } = {}) {
22
+ const descriptor = resolveCrudListParentDescriptor({
23
+ resource,
24
+ route,
25
+ recordIdParam
26
+ });
27
+ if (!descriptor?.fieldKey || !descriptor?.routeParamKey) {
28
+ return {};
29
+ }
30
+
31
+ const routeParams = resolveRouteParamsSource(route?.params || {});
32
+ const routeParamValue = toRouteParamValue(routeParams[descriptor.routeParamKey]);
33
+ if (!routeParamValue) {
34
+ return {};
35
+ }
36
+
37
+ return {
38
+ [descriptor.fieldKey]: routeParamValue
39
+ };
40
+ }
41
+
42
+ function useCrudList({
43
+ resource = null,
44
+ requestQueryParams = null,
45
+ parentBinding = null,
46
+ recordIdParam = "recordId",
47
+ route = null,
48
+ ...listOptions
49
+ } = {}) {
50
+ const sourceRoute = route && typeof route === "object" ? route : useRoute();
51
+ const boundParentRequestQueryParams = computed(() => {
52
+ return resolveCrudBoundValues({
53
+ binding: parentBinding,
54
+ routeValues: resolveCrudParentRequestQueryParams({
55
+ resource,
56
+ route: sourceRoute,
57
+ recordIdParam
58
+ }),
59
+ context: Object.freeze({
60
+ route: sourceRoute,
61
+ resource,
62
+ recordIdParam
63
+ })
64
+ });
65
+ });
66
+ const records = useList({
67
+ ...listOptions,
68
+ recordIdParam,
69
+ requestQueryParams(context = {}) {
70
+ const baseRequestQueryParams = resolveRequestQueryParamsInput(requestQueryParams, context);
71
+
72
+ return {
73
+ ...baseRequestQueryParams,
74
+ ...boundParentRequestQueryParams.value
75
+ };
76
+ }
77
+ });
78
+
79
+ records.resolveFieldDisplay = resolveLookupFieldDisplayValue;
80
+ return records;
81
+ }
82
+
83
+ export { useCrudList };
@@ -0,0 +1,35 @@
1
+ import { computed } from "vue";
2
+ import { useRoute } from "vue-router";
3
+ import {
4
+ resolveLookupFieldDisplayValue,
5
+ resolveRecordTitle
6
+ } from "../crud/crudLookupFieldLabelSupport.js";
7
+ import { resolveCrudBoundValues } from "../crud/crudBindingSupport.js";
8
+ import { asPlainObject } from "../support/scopeHelpers.js";
9
+ import { useView } from "./useView.js";
10
+
11
+ function useCrudView({
12
+ paramBinding = null,
13
+ route = null,
14
+ ...viewOptions
15
+ } = {}) {
16
+ const sourceRoute = route && typeof route === "object" ? route : useRoute();
17
+ const boundRouteParams = computed(() => {
18
+ return resolveCrudBoundValues({
19
+ binding: paramBinding,
20
+ routeValues: asPlainObject(sourceRoute?.params || {}),
21
+ context: Object.freeze({
22
+ route: sourceRoute
23
+ })
24
+ });
25
+ });
26
+ const view = useView({
27
+ ...viewOptions,
28
+ routeParams: boundRouteParams
29
+ });
30
+ view.resolveFieldDisplay = resolveLookupFieldDisplayValue;
31
+ view.resolveRecordTitle = resolveRecordTitle;
32
+ return view;
33
+ }
34
+
35
+ export { useCrudView };
@@ -2,17 +2,16 @@ import { computed, onScopeDispose, proxyRefs, ref, watch } from "vue";
2
2
  import { useRouter } from "vue-router";
3
3
  import { appendQueryString } from "@jskit-ai/kernel/shared/support";
4
4
  import { normalizeText } from "@jskit-ai/kernel/shared/support/normalize";
5
- import { resolveCrudLookupFieldKeys } from "@jskit-ai/kernel/shared/support/crudLookup";
6
5
  import { USERS_ROUTE_VISIBILITY_WORKSPACE } from "@jskit-ai/users-core/shared/support/usersVisibility";
7
- import { useListCore } from "./useListCore.js";
8
- import { resolveOperationAdapter } from "./operationAdapters.js";
9
- import { setupOperationErrorReporting } from "./operationUiHelpers.js";
10
- import { createListUiRuntime } from "./listUiRuntime.js";
11
- import { asPlainObject } from "./scopeHelpers.js";
6
+ import { useListCore } from "../runtime/useListCore.js";
7
+ import { resolveOperationAdapter } from "../runtime/operationAdapters.js";
8
+ import { setupOperationErrorReporting } from "../runtime/operationUiHelpers.js";
9
+ import { createListUiRuntime } from "../runtime/listUiRuntime.js";
10
+ import { asPlainObject } from "../support/scopeHelpers.js";
12
11
  import {
13
12
  normalizeListSearchConfig,
14
13
  matchesLocalSearch
15
- } from "./listSearchSupport.js";
14
+ } from "../support/listSearchSupport.js";
16
15
  import {
17
16
  normalizeListSyncToRouteConfig,
18
17
  resolveQueryParamDescriptors,
@@ -24,13 +23,10 @@ import {
24
23
  buildRouteQueryCompareToken,
25
24
  mergeManagedQueryParamKeyHistory,
26
25
  resolveRouteSyncManagedKeys
27
- } from "./listQueryParamSupport.js";
28
- import { resolveLookupFieldDisplayValue } from "./crudLookupFieldLabelSupport.js";
26
+ } from "../support/listQueryParamSupport.js";
29
27
  import {
30
28
  resolveRouteParamNamesInOrder,
31
- resolveRouteParamsSource,
32
- toRouteParamValue
33
- } from "./routeTemplateHelpers.js";
29
+ } from "../support/routeTemplateHelpers.js";
34
30
 
35
31
  const EMPTY_ROUTE_SYNC_QUERY_PARAM_BLACKLIST = Object.freeze([]);
36
32
 
@@ -51,13 +47,13 @@ function useList({
51
47
  queryOptions,
52
48
  realtime = null,
53
49
  adapter = null,
54
- resource = null,
55
50
  recordIdParam = "recordId",
56
51
  recordIdSelector = null,
57
52
  viewUrlTemplate = "",
58
53
  editUrlTemplate = "",
59
54
  search = null,
60
55
  queryParams = null,
56
+ requestQueryParams = null,
61
57
  syncToRoute = false
62
58
  } = {}) {
63
59
  const searchConfig = normalizeListSearchConfig(search);
@@ -142,52 +138,25 @@ function useList({
142
138
  const queryParamDescriptors = computed(() => {
143
139
  return resolveQueryParamDescriptors(queryParams, queryParamsContext.value);
144
140
  });
141
+ const requestQueryParamDescriptors = computed(() => {
142
+ return resolveQueryParamDescriptors(requestQueryParams, queryParamsContext.value);
143
+ });
145
144
  const declaredQueryParamKeys = computed(() => {
146
145
  return queryParamDescriptors.value.map((descriptor) => descriptor.key);
147
146
  });
148
147
  const activeQueryParamEntries = computed(() => {
149
148
  return resolveActiveQueryParamEntries(queryParamDescriptors.value);
150
149
  });
150
+ const activeRequestQueryParamEntries = computed(() => {
151
+ return resolveActiveQueryParamEntries(requestQueryParamDescriptors.value);
152
+ });
151
153
  const activeQueryParamsToken = computed(() => buildQueryParamEntriesToken(activeQueryParamEntries.value));
154
+ const activeRequestQueryParamsToken = computed(() => {
155
+ return buildQueryParamEntriesToken(activeRequestQueryParamEntries.value);
156
+ });
152
157
  const writableQueryParamBindings = computed(() => {
153
158
  return resolveWritableQueryParamBindings(queryParamDescriptors.value);
154
159
  });
155
- const parentRouteFilter = computed(() => {
156
- const lookupFieldKeys = resolveCrudLookupFieldKeys(resource);
157
- if (lookupFieldKeys.length < 1) {
158
- return null;
159
- }
160
-
161
- const lookupFieldKeySet = new Set(lookupFieldKeys);
162
- const sourceRoute = operationScope.routeContext.route;
163
- const orderedRouteParamNames = resolveRouteParamNamesInOrder(sourceRoute);
164
- if (orderedRouteParamNames.length < 1) {
165
- return null;
166
- }
167
-
168
- const normalizedRecordIdParam = normalizeText(recordIdParam) || "recordId";
169
- const parentParamName = [...orderedRouteParamNames]
170
- .reverse()
171
- .find((name) => (
172
- name !== "workspaceSlug" &&
173
- name !== normalizedRecordIdParam &&
174
- lookupFieldKeySet.has(name)
175
- )) || "";
176
- if (!parentParamName) {
177
- return null;
178
- }
179
-
180
- const routeParams = resolveRouteParamsSource(sourceRoute?.params || {});
181
- const parentParamValue = toRouteParamValue(routeParams[parentParamName]);
182
- if (!parentParamValue) {
183
- return null;
184
- }
185
-
186
- return Object.freeze({
187
- key: parentParamName,
188
- value: parentParamValue
189
- });
190
- });
191
160
  const activeSearchQuery = computed(() => {
192
161
  if (searchConfig.enabled !== true) {
193
162
  return "";
@@ -213,11 +182,11 @@ function useList({
213
182
  }
214
183
  }
215
184
 
216
- const parentFilter = parentRouteFilter.value;
217
- if (parentFilter) {
218
- searchParams.set(parentFilter.key, parentFilter.value);
185
+ for (const entry of activeRequestQueryParamEntries.value) {
186
+ for (const value of entry.values) {
187
+ searchParams.append(entry.key, value);
188
+ }
219
189
  }
220
-
221
190
  for (const entry of activeQueryParamEntries.value) {
222
191
  for (const value of entry.values) {
223
192
  searchParams.append(entry.key, value);
@@ -238,9 +207,8 @@ function useList({
238
207
  : sourceQueryKey == null
239
208
  ? []
240
209
  : [sourceQueryKey];
241
- const parentFilter = parentRouteFilter.value;
242
- if (parentFilter) {
243
- baseQueryKey.push("__parent__", parentFilter.key, parentFilter.value);
210
+ if (activeRequestQueryParamsToken.value) {
211
+ baseQueryKey.push("__request_query__", activeRequestQueryParamsToken.value);
244
212
  }
245
213
  if (querySearchEnabled.value) {
246
214
  baseQueryKey.push("__search__", searchConfig.queryParam, activeSearchQuery.value);
@@ -430,6 +398,13 @@ function useList({
430
398
 
431
399
  list.trimToFirstPage();
432
400
  });
401
+ watch(activeRequestQueryParamsToken, (nextValue, previousValue) => {
402
+ if (nextValue === previousValue) {
403
+ return;
404
+ }
405
+
406
+ list.trimToFirstPage();
407
+ });
433
408
  const filteredItems = computed(() => {
434
409
  const sourceItems = Array.isArray(list.items.value) ? list.items.value : [];
435
410
  if (searchConfig.enabled !== true || searchConfig.mode !== "local") {
@@ -494,7 +469,6 @@ function useList({
494
469
  resolveParams: listUiRuntime.resolveParams,
495
470
  resolveViewUrl: listUiRuntime.resolveViewUrl,
496
471
  resolveEditUrl: listUiRuntime.resolveEditUrl,
497
- resolveFieldDisplay: resolveLookupFieldDisplayValue,
498
472
  searchEnabled: searchConfig.enabled,
499
473
  searchMode: searchConfig.mode,
500
474
  searchQuery,
@@ -1,13 +1,12 @@
1
1
  import { computed, proxyRefs } from "vue";
2
2
  import { useRoute } from "vue-router";
3
3
  import { USERS_ROUTE_VISIBILITY_WORKSPACE } from "@jskit-ai/users-core/shared/support/usersVisibility";
4
- import { useViewCore } from "./useViewCore.js";
5
- import { useEndpointResource } from "./useEndpointResource.js";
6
- import { resolveOperationAdapter } from "./operationAdapters.js";
7
- import { setupOperationErrorReporting } from "./operationUiHelpers.js";
8
- import { createViewUiRuntime } from "./viewUiRuntime.js";
9
- import { resolveLookupFieldDisplayValue, resolveRecordTitle } from "./crudLookupFieldLabelSupport.js";
10
- import { resolveRouteParamNamesInOrder } from "./routeTemplateHelpers.js";
4
+ import { useViewCore } from "../runtime/useViewCore.js";
5
+ import { useEndpointResource } from "../runtime/useEndpointResource.js";
6
+ import { resolveOperationAdapter } from "../runtime/operationAdapters.js";
7
+ import { setupOperationErrorReporting } from "../runtime/operationUiHelpers.js";
8
+ import { createViewUiRuntime } from "../runtime/viewUiRuntime.js";
9
+ import { resolveRouteParamNamesInOrder } from "../support/routeTemplateHelpers.js";
11
10
 
12
11
  function useView({
13
12
  ownershipFilter = USERS_ROUTE_VISIBILITY_WORKSPACE,
@@ -115,8 +114,6 @@ function useView({
115
114
  listUrl: viewUiRuntime.listUrl,
116
115
  editUrl: viewUiRuntime.editUrl,
117
116
  resolveParams: viewUiRuntime.resolveParams,
118
- resolveFieldDisplay: resolveLookupFieldDisplayValue,
119
- resolveRecordTitle,
120
117
  canView,
121
118
  isLoading,
122
119
  isFetching,
@@ -1,12 +1,12 @@
1
1
  import { computed, unref } from "vue";
2
- import { asPlainObject } from "./scopeHelpers.js";
2
+ import { asPlainObject } from "../support/scopeHelpers.js";
3
3
  import {
4
4
  normalizeRouteParamName,
5
5
  resolveRouteParamsSource,
6
6
  resolveScopedRoutePathname,
7
7
  resolveRouteTemplateLocation,
8
8
  toRouteParamValue
9
- } from "./routeTemplateHelpers.js";
9
+ } from "../support/routeTemplateHelpers.js";
10
10
 
11
11
  function toResolvedRecordId({ routeParams, recordIdParam, routeRecordId }) {
12
12
  const explicitRecordId = toRouteParamValue(
@@ -1,12 +1,12 @@
1
1
  import { computed } from "vue";
2
- import { asPlainObject } from "./scopeHelpers.js";
2
+ import { asPlainObject } from "../support/scopeHelpers.js";
3
3
  import {
4
4
  normalizeRouteParamName,
5
5
  toRouteParamValue,
6
6
  resolveRouteParamsSource,
7
7
  resolveScopedRoutePathname,
8
8
  resolveRouteTemplateLocation
9
- } from "./routeTemplateHelpers.js";
9
+ } from "../support/routeTemplateHelpers.js";
10
10
 
11
11
  function resolveRecordId(record, recordIdSelector) {
12
12
  const item = asPlainObject(record);
@@ -1,4 +1,4 @@
1
- import { useOperationScope } from "./internal/useOperationScope.js";
1
+ import { useOperationScope } from "../internal/useOperationScope.js";
2
2
 
3
3
  const USERS_OPERATION_ADAPTER_ID = "users-default";
4
4
 
@@ -1,10 +1,10 @@
1
1
  import { computed } from "vue";
2
2
  import { useMutation, useQuery } from "@tanstack/vue-query";
3
- import { usersWebHttpClient } from "../lib/httpClient.js";
4
- import { asPlainObject } from "./scopeHelpers.js";
5
- import { resolveEnabledRef, resolveTextRef } from "./refValueHelpers.js";
6
- import { toQueryErrorMessage } from "./errorMessageHelpers.js";
7
- import { hasResolvedQueryData } from "./resourceLoadStateHelpers.js";
3
+ import { usersWebHttpClient } from "../../lib/httpClient.js";
4
+ import { asPlainObject } from "../support/scopeHelpers.js";
5
+ import { resolveEnabledRef, resolveTextRef } from "../support/refValueHelpers.js";
6
+ import { toQueryErrorMessage } from "../support/errorMessageHelpers.js";
7
+ import { hasResolvedQueryData } from "../support/resourceLoadStateHelpers.js";
8
8
 
9
9
  function useEndpointResource({
10
10
  queryKey,
@@ -1,9 +1,9 @@
1
1
  import { computed } from "vue";
2
2
  import { appendQueryString } from "@jskit-ai/kernel/shared/support";
3
- import { usersWebHttpClient } from "../lib/httpClient.js";
4
- import { asPlainObject } from "./scopeHelpers.js";
5
- import { resolveEnabledRef, resolveTextRef } from "./refValueHelpers.js";
6
- import { usePagedCollection } from "./usePagedCollection.js";
3
+ import { usersWebHttpClient } from "../../lib/httpClient.js";
4
+ import { asPlainObject } from "../support/scopeHelpers.js";
5
+ import { resolveEnabledRef, resolveTextRef } from "../support/refValueHelpers.js";
6
+ import { usePagedCollection } from "../usePagedCollection.js";
7
7
 
8
8
  function appendPageParam(path, pageParam) {
9
9
  const normalizedPath = String(path || "").trim();
@@ -1,6 +1,6 @@
1
1
  import { ref } from "vue";
2
2
  import { useShellWebErrorRuntime } from "@jskit-ai/shell-web/client/error";
3
- import { toUiErrorMessage } from "./errorMessageHelpers.js";
3
+ import { toUiErrorMessage } from "../support/errorMessageHelpers.js";
4
4
 
5
5
  function useUiFeedback({
6
6
  initialType = "success",
@@ -1,12 +1,12 @@
1
1
  import { computed, unref } from "vue";
2
- import { asPlainObject } from "./scopeHelpers.js";
2
+ import { asPlainObject } from "../support/scopeHelpers.js";
3
3
  import {
4
4
  normalizeRouteParamName,
5
5
  resolveRouteParamsSource,
6
6
  resolveScopedRoutePathname,
7
7
  resolveRouteTemplateLocation,
8
8
  toRouteParamValue
9
- } from "./routeTemplateHelpers.js";
9
+ } from "../support/routeTemplateHelpers.js";
10
10
 
11
11
  function resolveRecordId({ routeParams, recordIdParam, routeRecordId }) {
12
12
  const explicitRecordId = toRouteParamValue(
@@ -1,10 +1,10 @@
1
1
  import { computed } from "vue";
2
2
  import { hasPermission, normalizePermissionList } from "../lib/permissions.js";
3
- import { resolveEnabledRef, resolveTextRef } from "./refValueHelpers.js";
3
+ import { resolveEnabledRef, resolveTextRef } from "./support/refValueHelpers.js";
4
4
  import {
5
5
  normalizeAccessMode,
6
6
  resolveAccessModeEnabled
7
- } from "./scopeHelpers.js";
7
+ } from "./support/scopeHelpers.js";
8
8
  import { useWebPlacementContext } from "@jskit-ai/shell-web/client/placement";
9
9
 
10
10
  function asPermissionList(value) {
@@ -19,9 +19,9 @@ import {
19
19
  import {
20
20
  useWorkspaceSurfaceId
21
21
  } from "./useWorkspaceSurfaceId.js";
22
- import { useAddEdit } from "./useAddEdit.js";
22
+ import { useAddEdit } from "./records/useAddEdit.js";
23
23
  import { useCommand } from "./useCommand.js";
24
- import { useView } from "./useView.js";
24
+ import { useView } from "./records/useView.js";
25
25
  import { usePaths } from "./usePaths.js";
26
26
  import { resolveAccountSettingsPathFromPlacementContext } from "../lib/workspaceSurfacePaths.js";
27
27
  import {
@@ -34,16 +34,16 @@ import {
34
34
  NUMBER_FORMAT_OPTIONS,
35
35
  THEME_OPTIONS,
36
36
  TIME_ZONE_OPTIONS
37
- } from "./accountSettingsRuntimeConstants.js";
37
+ } from "./account-settings/accountSettingsRuntimeConstants.js";
38
38
  import {
39
39
  normalizeAvatarSize,
40
40
  normalizePendingInvite,
41
41
  normalizeReturnToPath,
42
42
  normalizeSettingsPayload,
43
43
  resolveAllowedReturnToOrigins
44
- } from "./accountSettingsRuntimeHelpers.js";
45
- import { createAccountSettingsAvatarUploadRuntime } from "./accountSettingsAvatarUploadRuntime.js";
46
- import { createAccountSettingsInvitesRuntime } from "./accountSettingsInvitesRuntime.js";
44
+ } from "./account-settings/accountSettingsRuntimeHelpers.js";
45
+ import { createAccountSettingsAvatarUploadRuntime } from "./account-settings/accountSettingsAvatarUploadRuntime.js";
46
+ import { createAccountSettingsInvitesRuntime } from "./account-settings/accountSettingsInvitesRuntime.js";
47
47
 
48
48
  function useAccountSettingsRuntime() {
49
49
  const route = useRoute();
@@ -5,7 +5,7 @@ import { useQuery } from "@tanstack/vue-query";
5
5
  import { normalizeQueryToken } from "@jskit-ai/kernel/shared/support/normalize";
6
6
  import { usersWebHttpClient } from "../lib/httpClient.js";
7
7
  import { buildBootstrapApiPath } from "../lib/bootstrap.js";
8
- import { resolveEnabledRef, resolveTextRef } from "./refValueHelpers.js";
8
+ import { resolveEnabledRef, resolveTextRef } from "./support/refValueHelpers.js";
9
9
 
10
10
  const DEFAULT_BOOTSTRAP_STALE_TIME_MS = 60_000;
11
11
 
@@ -1,14 +1,14 @@
1
1
  import { proxyRefs } from "vue";
2
2
  import { USERS_ROUTE_VISIBILITY_WORKSPACE } from "@jskit-ai/users-core/shared/support/usersVisibility";
3
- import { useCommandCore } from "./useCommandCore.js";
4
- import { useEndpointResource } from "./useEndpointResource.js";
3
+ import { useCommandCore } from "./runtime/useCommandCore.js";
4
+ import { useEndpointResource } from "./runtime/useEndpointResource.js";
5
5
  import { useOperationScope } from "./internal/useOperationScope.js";
6
- import { useUiFeedback } from "./useUiFeedback.js";
7
- import { useFieldErrorBag } from "./useFieldErrorBag.js";
6
+ import { useUiFeedback } from "./runtime/useUiFeedback.js";
7
+ import { useFieldErrorBag } from "./runtime/useFieldErrorBag.js";
8
8
  import {
9
9
  setupRouteChangeCleanup,
10
10
  setupOperationErrorReporting
11
- } from "./operationUiHelpers.js";
11
+ } from "./runtime/operationUiHelpers.js";
12
12
 
13
13
  function useCommand({
14
14
  ownershipFilter = USERS_ROUTE_VISIBILITY_WORKSPACE,
@@ -0,0 +1,131 @@
1
+ import { computed, proxyRefs } from "vue";
2
+ import { useRoute } from "vue-router";
3
+ import { normalizeText } from "@jskit-ai/kernel/shared/support/normalize";
4
+ import {
5
+ resolveRouteParamsSource,
6
+ toRouteParamValue
7
+ } from "./support/routeTemplateHelpers.js";
8
+ import { useView } from "./records/useView.js";
9
+ import {
10
+ resolveCrudListParentDescriptor,
11
+ resolveCrudListParentRecordTitle,
12
+ resolveCrudListParentTitleFromItems
13
+ } from "./internal/crudListParentTitleSupport.js";
14
+
15
+ function normalizeQueryKeyPrefix(value = []) {
16
+ if (Array.isArray(value)) {
17
+ return value
18
+ .map((entry) => normalizeText(entry))
19
+ .filter(Boolean);
20
+ }
21
+
22
+ const normalizedValue = normalizeText(value);
23
+ return normalizedValue ? [normalizedValue] : [];
24
+ }
25
+
26
+ function useCrudListParentTitle({
27
+ listRuntime = null,
28
+ resource = {},
29
+ adapter = null,
30
+ recordIdParam = "recordId",
31
+ queryKeyPrefix = ["users-web", "crud-list-parent-title"],
32
+ placementSource = "users-web.crud-list-parent-title",
33
+ fallbackLoadError = "Unable to load parent record.",
34
+ notFoundMessage = "Parent record not found.",
35
+ route = null,
36
+ viewRuntimeFactory = useView
37
+ } = {}) {
38
+ const sourceRoute = route && typeof route === "object" ? route : useRoute();
39
+ const parentDescriptor = computed(() => {
40
+ const descriptor = resolveCrudListParentDescriptor({
41
+ resource,
42
+ route: sourceRoute,
43
+ recordIdParam
44
+ });
45
+ if (!descriptor) {
46
+ return null;
47
+ }
48
+
49
+ const routeParams = resolveRouteParamsSource(sourceRoute?.params || {});
50
+ const routeParamValue = toRouteParamValue(routeParams[descriptor.routeParamKey]);
51
+ if (!routeParamValue) {
52
+ return null;
53
+ }
54
+
55
+ return Object.freeze({
56
+ ...descriptor,
57
+ routeParamValue
58
+ });
59
+ });
60
+
61
+ const initialParentDescriptor = parentDescriptor.value || {};
62
+ const normalizedQueryKeyPrefix = normalizeQueryKeyPrefix(queryKeyPrefix);
63
+ const shouldLoadParentRecord = computed(() => {
64
+ const descriptor = parentDescriptor.value;
65
+ if (!descriptor?.apiUrlTemplate) {
66
+ return false;
67
+ }
68
+ if (Boolean(listRuntime?.isInitialLoading) || normalizeText(listRuntime?.loadError)) {
69
+ return false;
70
+ }
71
+
72
+ const items = Array.isArray(listRuntime?.items) ? listRuntime.items : [];
73
+ return items.length < 1;
74
+ });
75
+
76
+ const parentView = viewRuntimeFactory({
77
+ adapter,
78
+ apiUrlTemplate: normalizeText(initialParentDescriptor.apiUrlTemplate),
79
+ readEnabled: shouldLoadParentRecord,
80
+ recordIdParam: normalizeText(initialParentDescriptor.routeParamKey) || "recordId",
81
+ includeRecordIdInQueryKey: true,
82
+ queryKeyFactory: (surfaceId = "", workspaceSlug = "") => [
83
+ ...normalizedQueryKeyPrefix,
84
+ normalizeText(initialParentDescriptor.relationNamespace),
85
+ normalizeText(initialParentDescriptor.routeParamKey),
86
+ String(surfaceId || ""),
87
+ String(workspaceSlug || "")
88
+ ],
89
+ placementSource,
90
+ fallbackLoadError,
91
+ notFoundMessage
92
+ });
93
+
94
+ const title = computed(() => {
95
+ const descriptor = parentDescriptor.value;
96
+ if (!descriptor) {
97
+ return "";
98
+ }
99
+
100
+ const parentRecordTitle = resolveCrudListParentRecordTitle(parentView?.record || {}, descriptor);
101
+ if (parentRecordTitle && shouldLoadParentRecord.value) {
102
+ return parentRecordTitle;
103
+ }
104
+
105
+ const listTitle = resolveCrudListParentTitleFromItems(listRuntime?.items, descriptor);
106
+ if (listTitle) {
107
+ return listTitle;
108
+ }
109
+
110
+ if (parentRecordTitle) {
111
+ return parentRecordTitle;
112
+ }
113
+
114
+ if (descriptor.routeParamValue) {
115
+ return `${descriptor.entityLabel} #${descriptor.routeParamValue}`;
116
+ }
117
+
118
+ return descriptor.entityLabel;
119
+ });
120
+
121
+ return proxyRefs({
122
+ title,
123
+ descriptor: parentDescriptor,
124
+ shouldLoadParentRecord,
125
+ record: computed(() => parentView?.record || null),
126
+ isLoading: computed(() => Boolean(parentView?.isLoading)),
127
+ loadError: computed(() => normalizeText(parentView?.loadError))
128
+ });
129
+ }
130
+
131
+ export { useCrudListParentTitle };
@@ -1,8 +1,8 @@
1
1
  import { computed, unref } from "vue";
2
2
  import { useInfiniteQuery, useQueryClient } from "@tanstack/vue-query";
3
- import { asPlainObject } from "./scopeHelpers.js";
4
- import { resolveEnabledRef } from "./refValueHelpers.js";
5
- import { toQueryErrorMessage } from "./errorMessageHelpers.js";
3
+ import { asPlainObject } from "./support/scopeHelpers.js";
4
+ import { resolveEnabledRef } from "./support/refValueHelpers.js";
5
+ import { toQueryErrorMessage } from "./support/errorMessageHelpers.js";
6
6
 
7
7
  function defaultSelectItems(page) {
8
8
  return Array.isArray(page?.items) ? page.items : [];
@@ -11,7 +11,7 @@ import {
11
11
  resolveAccessModeEnabled,
12
12
  normalizeOwnershipFilter,
13
13
  resolveApiSuffix
14
- } from "./scopeHelpers.js";
14
+ } from "./support/scopeHelpers.js";
15
15
 
16
16
  function useScopeRuntime({
17
17
  ownershipFilter = USERS_ROUTE_VISIBILITY_WORKSPACE,
@@ -1,6 +1,7 @@
1
1
  import { UsersWebClientProvider } from "./providers/UsersWebClientProvider.js";
2
2
 
3
3
  export { UsersWebClientProvider } from "./providers/UsersWebClientProvider.js";
4
+ export { UsersWorkspacesClientProvider } from "./providers/UsersWorkspacesClientProvider.js";
4
5
 
5
6
  const clientProviders = Object.freeze([UsersWebClientProvider]);
6
7