@jskit-ai/http-web 0.1.92 → 0.1.93
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.json +6 -6
- package/patterns/crud-screen-set/PATTERN.md +11 -1
- package/src/client/bulkActions.js +13 -40
- package/src/client/components/CrudAddEditScreen.vue +1 -9
- package/src/client/components/CrudListBulkActionSurface.vue +2 -5
- package/src/client/components/CrudListFilterControls.vue +175 -0
- package/src/client/components/CrudListFilterSurface.vue +5 -248
- package/src/client/components/CrudListScreen.vue +1 -28
- package/src/client/components/CrudViewScreen.vue +1 -9
- package/src/client/composables/crud/crudJsonApiTransportSupport.js +20 -42
- package/src/client/composables/crud/crudLookupFieldLabelSupport.js +2 -13
- package/src/client/composables/crud/crudLookupFieldRuntime.js +2 -27
- package/src/client/composables/crud/crudSchemaFormHelpers.js +1 -7
- package/src/client/composables/internal/crudListFilterLookupSupport.js +2 -1
- package/src/client/composables/internal/useOperationScope.js +5 -7
- package/src/client/composables/records/useAddEdit.js +12 -11
- package/src/client/composables/records/useCrudAddEdit.js +74 -22
- package/src/client/composables/records/useCrudList.js +0 -6
- package/src/client/composables/records/useCrudView.js +0 -6
- package/src/client/composables/records/useList.js +2 -8
- package/src/client/composables/records/useView.js +3 -8
- package/src/client/composables/runtime/addEditUiRuntime.js +12 -60
- package/src/client/composables/runtime/listUiRuntime.js +11 -36
- package/src/client/composables/runtime/useFieldErrorBag.js +3 -3
- package/src/client/composables/runtime/viewUiRuntime.js +14 -62
- package/src/client/composables/support/errorMessageHelpers.js +6 -48
- package/src/client/composables/support/resourceLoadStateHelpers.js +67 -156
- package/src/client/composables/support/routeTemplateHelpers.js +57 -0
- package/src/client/composables/useAccess.js +12 -17
- package/src/client/composables/useCommand.js +0 -1
- package/src/client/composables/useCrudDeleteAction.js +7 -5
- package/src/client/composables/useCrudListBulkActions.js +9 -21
- package/src/client/composables/useCrudListFilterLookups.js +1 -3
- package/src/client/composables/useCrudListParentTitle.js +0 -2
- package/src/client/composables/useCrudListRowActions.js +8 -24
- package/src/client/composables/useCrudListScreen.js +4 -5
- package/src/client/composables/useCrudViewScreen.js +2 -3
- package/src/client/composables/useRealtimeQueryInvalidation.js +3 -10
- package/src/client/composables/useScopeRuntime.js +2 -5
- package/src/client/rowActions.js +13 -40
- package/src/client/support/crudListActionSupport.js +54 -0
- package/test/crudAddEditActions.test.js +59 -19
- package/test/crudListFilterSurface.test.js +7 -4
- package/test/listUiRuntime.test.js +3 -1
- package/test/requestTransportOptions.test.js +24 -0
- package/test/resourceLoadStateHelpers.test.js +32 -0
- package/test/useAccess.test.js +62 -0
- package/test/useCrudDeleteAction.test.js +57 -2
- package/test/useCrudListScreen.test.js +45 -0
- package/test/useListRouteInitialization.test.js +5 -44
- package/src/client/composables/runtime/operationAdapters.js +0 -34
|
@@ -220,6 +220,62 @@ function resolveRouteTemplateLocation(routeTemplate = "", { params = {}, current
|
|
|
220
220
|
return resolvedPathname;
|
|
221
221
|
}
|
|
222
222
|
|
|
223
|
+
function createRouteTemplateResolver({
|
|
224
|
+
recordIdParam,
|
|
225
|
+
routeParams = null,
|
|
226
|
+
routeParamNames = null,
|
|
227
|
+
routePath = "",
|
|
228
|
+
routeRecordId = null,
|
|
229
|
+
anchorMode = "at",
|
|
230
|
+
anchorOnTargetRecord = false,
|
|
231
|
+
fillRecordId = true
|
|
232
|
+
} = {}) {
|
|
233
|
+
function resolveCurrentRecordId(currentRouteParams = resolveRouteParamsSource(routeParams)) {
|
|
234
|
+
const explicitRecordId = toRouteParamValue(resolveRouteSourceValue(routeRecordId));
|
|
235
|
+
if (explicitRecordId) {
|
|
236
|
+
return explicitRecordId;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
return toRouteParamValue(currentRouteParams[recordIdParam]);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
function resolveTemplatePath(urlTemplate = "", extraParams = {}) {
|
|
243
|
+
const normalizedTemplate = String(urlTemplate || "").trim();
|
|
244
|
+
if (!normalizedTemplate) {
|
|
245
|
+
return "";
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
const currentRouteParams = resolveRouteParamsSource(routeParams);
|
|
249
|
+
const sourceParams = {
|
|
250
|
+
...currentRouteParams,
|
|
251
|
+
...asPlainObject(extraParams)
|
|
252
|
+
};
|
|
253
|
+
const currentRecordId = resolveCurrentRecordId(currentRouteParams);
|
|
254
|
+
const targetRecordId = toRouteParamValue(sourceParams[recordIdParam]) || currentRecordId;
|
|
255
|
+
if (fillRecordId) {
|
|
256
|
+
sourceParams[recordIdParam] = targetRecordId;
|
|
257
|
+
}
|
|
258
|
+
const currentPathname = resolveScopedRoutePathname({
|
|
259
|
+
currentPathname: routePath,
|
|
260
|
+
params: currentRouteParams,
|
|
261
|
+
orderedParamNames: routeParamNames,
|
|
262
|
+
anchorParamName: recordIdParam,
|
|
263
|
+
anchorParamValue: anchorOnTargetRecord ? targetRecordId : currentRecordId,
|
|
264
|
+
anchorMode
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
return resolveRouteTemplateLocation(normalizedTemplate, {
|
|
268
|
+
params: sourceParams,
|
|
269
|
+
currentPathname
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
return Object.freeze({
|
|
274
|
+
resolveRecordId: () => resolveCurrentRecordId(),
|
|
275
|
+
resolveTemplatePath
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
|
|
223
279
|
function extractRouteParamNames(pathTemplate = "") {
|
|
224
280
|
const normalizedTemplate = String(pathTemplate || "").trim();
|
|
225
281
|
if (!normalizedTemplate) {
|
|
@@ -280,6 +336,7 @@ export {
|
|
|
280
336
|
resolveScopedRoutePathname,
|
|
281
337
|
resolveRouteTemplatePath,
|
|
282
338
|
resolveRouteTemplateLocation,
|
|
339
|
+
createRouteTemplateResolver,
|
|
283
340
|
extractRouteParamNames,
|
|
284
341
|
resolveRouteParamNamesInOrder
|
|
285
342
|
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { computed } from "vue";
|
|
2
2
|
import { hasPermission, normalizePermissionList } from "../lib/permissions.js";
|
|
3
|
-
import { resolveEnabledRef
|
|
3
|
+
import { resolveEnabledRef } from "./support/refValueHelpers.js";
|
|
4
4
|
import {
|
|
5
5
|
normalizeAccessMode,
|
|
6
6
|
resolveAccessModeEnabled
|
|
@@ -19,8 +19,10 @@ function asPermissionList(value) {
|
|
|
19
19
|
return [value];
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
+
// Permissions come only from the web placement context, which the shell fills
|
|
23
|
+
// from its bootstrap. Until that context carries a `permissions` key, access is
|
|
24
|
+
// awaiting placement permissions and reports them as unavailable.
|
|
22
25
|
function useAccess({
|
|
23
|
-
scopeParamValue = "",
|
|
24
26
|
enabled = true,
|
|
25
27
|
access = "always",
|
|
26
28
|
hasPermissionRequirements = false
|
|
@@ -30,9 +32,8 @@ function useAccess({
|
|
|
30
32
|
hasPermissionRequirements: hasPermissionRequirements === true
|
|
31
33
|
});
|
|
32
34
|
const { context: placementContext } = useWebPlacementContext();
|
|
33
|
-
const normalizedScopeParamValue = computed(() => resolveTextRef(scopeParamValue));
|
|
34
35
|
const queryEnabled = computed(() => resolveEnabledRef(enabled) && accessRequired);
|
|
35
|
-
const
|
|
36
|
+
const hasPlacementPermissions = computed(() => {
|
|
36
37
|
const source = placementContext.value;
|
|
37
38
|
if (!source || typeof source !== "object") {
|
|
38
39
|
return false;
|
|
@@ -41,18 +42,18 @@ function useAccess({
|
|
|
41
42
|
});
|
|
42
43
|
const placementPermissions = computed(() => normalizePermissionList(placementContext.value?.permissions));
|
|
43
44
|
const permissions = computed(() => {
|
|
44
|
-
if (!queryEnabled.value || !
|
|
45
|
+
if (!queryEnabled.value || !hasPlacementPermissions.value) {
|
|
45
46
|
return [];
|
|
46
47
|
}
|
|
47
48
|
return placementPermissions.value;
|
|
48
49
|
});
|
|
49
|
-
const
|
|
50
|
-
if (!queryEnabled.value ||
|
|
50
|
+
const permissionsUnavailableError = computed(() => {
|
|
51
|
+
if (!queryEnabled.value || hasPlacementPermissions.value) {
|
|
51
52
|
return "";
|
|
52
53
|
}
|
|
53
54
|
return "Permissions are unavailable in placement context.";
|
|
54
55
|
});
|
|
55
|
-
const
|
|
56
|
+
const awaitingPlacementPermissions = computed(() => queryEnabled.value && !hasPlacementPermissions.value);
|
|
56
57
|
|
|
57
58
|
function can(permission) {
|
|
58
59
|
return hasPermission(permissions.value, permission);
|
|
@@ -88,21 +89,15 @@ function useAccess({
|
|
|
88
89
|
return true;
|
|
89
90
|
}
|
|
90
91
|
|
|
91
|
-
async function refreshBootstrap() {
|
|
92
|
-
return null;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
92
|
return Object.freeze({
|
|
96
93
|
accessMode: normalizedAccessMode,
|
|
97
94
|
accessRequired,
|
|
98
|
-
scopeParamValue: normalizedScopeParamValue,
|
|
99
95
|
permissions,
|
|
100
|
-
|
|
101
|
-
|
|
96
|
+
permissionsUnavailableError,
|
|
97
|
+
awaitingPlacementPermissions,
|
|
102
98
|
can,
|
|
103
99
|
canAny,
|
|
104
|
-
canAll
|
|
105
|
-
refreshBootstrap
|
|
100
|
+
canAll
|
|
106
101
|
});
|
|
107
102
|
}
|
|
108
103
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { computed, proxyRefs, ref, unref } from "vue";
|
|
1
|
+
import { computed, nextTick, proxyRefs, ref, unref } from "vue";
|
|
2
2
|
import { useRouter } from "vue-router";
|
|
3
3
|
import { normalizeText } from "@jskit-ai/kernel/shared/support/normalize";
|
|
4
4
|
import { resolveCrudJsonApiTransport } from "./crud/crudJsonApiTransportSupport.js";
|
|
@@ -62,17 +62,19 @@ function useCrudDeleteAction({
|
|
|
62
62
|
const deleteApiSuffix = computed(() => resolveDeleteApiSuffix(resolvedScreen, apiUrlTemplate));
|
|
63
63
|
|
|
64
64
|
async function handleDeleteSuccess(_response, context = {}) {
|
|
65
|
-
await context?.queryClient?.invalidateQueries?.({
|
|
66
|
-
queryKey: ["crud", namespace]
|
|
67
|
-
});
|
|
68
|
-
|
|
69
65
|
const listLocation = resolveListLocation(resolvedScreen);
|
|
70
66
|
if (!listLocation) {
|
|
71
67
|
throw new Error("Deleted the record, but the generated list route is unavailable.");
|
|
72
68
|
}
|
|
73
69
|
|
|
74
70
|
isOpen.value = false;
|
|
71
|
+
// Leave the deleted record's view before invalidating: while its view query is
|
|
72
|
+
// still observed, invalidation would refetch it and report "Record not found.".
|
|
75
73
|
await activeRouter.push(listLocation);
|
|
74
|
+
await nextTick();
|
|
75
|
+
await context?.queryClient?.invalidateQueries?.({
|
|
76
|
+
queryKey: ["crud", namespace]
|
|
77
|
+
});
|
|
76
78
|
}
|
|
77
79
|
|
|
78
80
|
function handleDeleteError(cause) {
|
|
@@ -1,18 +1,10 @@
|
|
|
1
1
|
import { computed, ref } from "vue";
|
|
2
|
-
import { normalizeText } from "@jskit-ai/kernel/shared/support/normalize";
|
|
3
2
|
import { defineCrudListBulkActions } from "../bulkActions.js";
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
function resolveActionKey(actionOrKey = "") {
|
|
10
|
-
return normalizeText(
|
|
11
|
-
actionOrKey && typeof actionOrKey === "object"
|
|
12
|
-
? actionOrKey.key
|
|
13
|
-
: actionOrKey
|
|
14
|
-
);
|
|
15
|
-
}
|
|
3
|
+
import {
|
|
4
|
+
normalizeListActionKey,
|
|
5
|
+
resolveListActionBaseContext,
|
|
6
|
+
resolveListRecordId
|
|
7
|
+
} from "../support/crudListActionSupport.js";
|
|
16
8
|
|
|
17
9
|
function useCrudListBulkActions(actions = [], {
|
|
18
10
|
resolveRecordId = null,
|
|
@@ -28,10 +20,7 @@ function useCrudListBulkActions(actions = [], {
|
|
|
28
20
|
const hasActions = computed(() => normalizedActions.length > 0);
|
|
29
21
|
|
|
30
22
|
function getRecordId(record = {}, index = 0) {
|
|
31
|
-
|
|
32
|
-
? resolveRecordId(record, index)
|
|
33
|
-
: record?.id ?? record?.attributes?.id ?? index;
|
|
34
|
-
return normalizeSelectedId(resolvedId);
|
|
23
|
+
return resolveListRecordId(record, index, resolveRecordId);
|
|
35
24
|
}
|
|
36
25
|
|
|
37
26
|
function setRecordSelected(record = {}, index = 0, selected = true) {
|
|
@@ -78,12 +67,12 @@ function useCrudListBulkActions(actions = [], {
|
|
|
78
67
|
}
|
|
79
68
|
|
|
80
69
|
function findAction(actionOrKey = "") {
|
|
81
|
-
const actionKey =
|
|
70
|
+
const actionKey = normalizeListActionKey(actionOrKey);
|
|
82
71
|
return normalizedActions.find((action) => action.key === actionKey) || null;
|
|
83
72
|
}
|
|
84
73
|
|
|
85
74
|
function isActionExecuting(actionOrKey = "") {
|
|
86
|
-
const actionKey =
|
|
75
|
+
const actionKey = normalizeListActionKey(actionOrKey);
|
|
87
76
|
return Boolean(actionKey && executingActionKey.value === actionKey);
|
|
88
77
|
}
|
|
89
78
|
|
|
@@ -110,9 +99,8 @@ function useCrudListBulkActions(actions = [], {
|
|
|
110
99
|
|
|
111
100
|
executingActionKey.value = action.key;
|
|
112
101
|
try {
|
|
113
|
-
const baseContext = typeof resolveContext === "function" ? resolveContext() : {};
|
|
114
102
|
return await action.run({
|
|
115
|
-
...(
|
|
103
|
+
...resolveListActionBaseContext(resolveContext),
|
|
116
104
|
action,
|
|
117
105
|
ids: selectedIds.value.slice(),
|
|
118
106
|
selectedIds: selectedIds.value.slice(),
|
|
@@ -21,7 +21,6 @@ function useCrudListFilterLookups(
|
|
|
21
21
|
definitions = {},
|
|
22
22
|
{
|
|
23
23
|
values = {},
|
|
24
|
-
adapter = null,
|
|
25
24
|
recordIdParam = "recordId",
|
|
26
25
|
queryKeyPrefix = [],
|
|
27
26
|
placementSourcePrefix = "",
|
|
@@ -52,7 +51,6 @@ function useCrudListFilterLookups(
|
|
|
52
51
|
});
|
|
53
52
|
|
|
54
53
|
const runtime = useList({
|
|
55
|
-
adapter: adapter || undefined,
|
|
56
54
|
apiSuffix: filter.lookup.apiSuffix,
|
|
57
55
|
...(transport ? { transport } : {}),
|
|
58
56
|
queryKeyFactory: (surfaceId = "", scopeParamValue = "") => [
|
|
@@ -106,7 +104,7 @@ function useCrudListFilterLookups(
|
|
|
106
104
|
options,
|
|
107
105
|
searchQuery: computed(() => String(runtime.searchQuery || "")),
|
|
108
106
|
isLoading: computed(() => {
|
|
109
|
-
return Boolean(runtime.isInitialLoading || runtime.isFetching || runtime.
|
|
107
|
+
return Boolean(runtime.isInitialLoading || runtime.isFetching || runtime.isSearchDebouncing);
|
|
110
108
|
}),
|
|
111
109
|
resolveLabel(value, fallback = filter.label || "Option") {
|
|
112
110
|
return resolveLookupOptionLabel(options.value, cachedOptions.value, value, fallback);
|
|
@@ -27,7 +27,6 @@ function normalizeQueryKeyPrefix(value = []) {
|
|
|
27
27
|
function useCrudListParentTitle({
|
|
28
28
|
listRuntime = null,
|
|
29
29
|
resource = {},
|
|
30
|
-
adapter = null,
|
|
31
30
|
recordIdParam = "recordId",
|
|
32
31
|
queryKeyPrefix = ["http-web", "crud-list-parent-title"],
|
|
33
32
|
placementSource = "http-web.crud-list-parent-title",
|
|
@@ -93,7 +92,6 @@ function useCrudListParentTitle({
|
|
|
93
92
|
});
|
|
94
93
|
|
|
95
94
|
const parentView = viewRuntimeFactory({
|
|
96
|
-
adapter,
|
|
97
95
|
apiUrlTemplate: normalizeText(initialParentDescriptor.apiUrlTemplate),
|
|
98
96
|
transport: parentTransport,
|
|
99
97
|
readEnabled: shouldLoadParentRecord,
|
|
@@ -1,18 +1,10 @@
|
|
|
1
1
|
import { computed, ref } from "vue";
|
|
2
|
-
import { normalizeText } from "@jskit-ai/kernel/shared/support/normalize";
|
|
3
2
|
import { defineCrudListRowActions } from "../rowActions.js";
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
: actionOrKey
|
|
10
|
-
);
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
function normalizeRowId(value = "") {
|
|
14
|
-
return normalizeText(value);
|
|
15
|
-
}
|
|
3
|
+
import {
|
|
4
|
+
normalizeListActionKey,
|
|
5
|
+
resolveListActionBaseContext,
|
|
6
|
+
resolveListRecordId
|
|
7
|
+
} from "../support/crudListActionSupport.js";
|
|
16
8
|
|
|
17
9
|
function createExecutionKey(actionKey = "", recordId = "", index = 0) {
|
|
18
10
|
return [
|
|
@@ -21,10 +13,6 @@ function createExecutionKey(actionKey = "", recordId = "", index = 0) {
|
|
|
21
13
|
].join(":");
|
|
22
14
|
}
|
|
23
15
|
|
|
24
|
-
function asContextObject(value = null) {
|
|
25
|
-
return value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
26
|
-
}
|
|
27
|
-
|
|
28
16
|
function useCrudListRowActions(actions = [], {
|
|
29
17
|
resolveRecordId = null,
|
|
30
18
|
resolveContext = null
|
|
@@ -34,16 +22,12 @@ function useCrudListRowActions(actions = [], {
|
|
|
34
22
|
const hasActions = computed(() => normalizedActions.length > 0);
|
|
35
23
|
|
|
36
24
|
function getRecordId(record = {}, index = 0) {
|
|
37
|
-
|
|
38
|
-
? resolveRecordId(record, index)
|
|
39
|
-
: record?.id ?? record?.attributes?.id ?? index;
|
|
40
|
-
return normalizeRowId(resolvedId);
|
|
25
|
+
return resolveListRecordId(record, index, resolveRecordId);
|
|
41
26
|
}
|
|
42
27
|
|
|
43
28
|
function createActionContext(action = {}, record = {}, index = 0) {
|
|
44
|
-
const baseContext = typeof resolveContext === "function" ? resolveContext() : {};
|
|
45
29
|
return {
|
|
46
|
-
...
|
|
30
|
+
...resolveListActionBaseContext(resolveContext),
|
|
47
31
|
action,
|
|
48
32
|
record,
|
|
49
33
|
index,
|
|
@@ -52,7 +36,7 @@ function useCrudListRowActions(actions = [], {
|
|
|
52
36
|
}
|
|
53
37
|
|
|
54
38
|
function findAction(actionOrKey = "") {
|
|
55
|
-
const actionKey =
|
|
39
|
+
const actionKey = normalizeListActionKey(actionOrKey);
|
|
56
40
|
return normalizedActions.find((action) => action.key === actionKey) || null;
|
|
57
41
|
}
|
|
58
42
|
|
|
@@ -4,6 +4,7 @@ import { useCrudListBulkActions } from "./useCrudListBulkActions.js";
|
|
|
4
4
|
import { useCrudListFilters } from "./useCrudListFilters.js";
|
|
5
5
|
import { useCrudListRowActions } from "./useCrudListRowActions.js";
|
|
6
6
|
import { resolveCrudHttpClient } from "./crud/crudHttpClientSupport.js";
|
|
7
|
+
import { resolveRecordTitle as resolveCrudRecordTitle } from "./crud/crudLookupFieldLabelSupport.js";
|
|
7
8
|
|
|
8
9
|
function buildCrudListActionContext(records, client) {
|
|
9
10
|
return Object.freeze({
|
|
@@ -110,7 +111,6 @@ function createCrudListDisplayRow(record = {}, index = 0, records = {}) {
|
|
|
110
111
|
}
|
|
111
112
|
|
|
112
113
|
function useCrudListScreen({
|
|
113
|
-
adapter = null,
|
|
114
114
|
client = null,
|
|
115
115
|
resource = null,
|
|
116
116
|
resourceNamespace = "resource",
|
|
@@ -140,7 +140,6 @@ function useCrudListScreen({
|
|
|
140
140
|
: [];
|
|
141
141
|
const normalizedResourceNamespace = String(resourceNamespace || "resource").trim() || "resource";
|
|
142
142
|
const records = useCrudList({
|
|
143
|
-
adapter: adapter || undefined,
|
|
144
143
|
client: crudHttpClient,
|
|
145
144
|
resource,
|
|
146
145
|
apiSuffix,
|
|
@@ -207,7 +206,7 @@ function useCrudListScreen({
|
|
|
207
206
|
);
|
|
208
207
|
|
|
209
208
|
function resolveRecordTitle(record) {
|
|
210
|
-
return
|
|
209
|
+
return resolveCrudRecordTitle(record, {
|
|
211
210
|
fallbackKey: titleFallbackFieldKey,
|
|
212
211
|
defaultValue: "Record"
|
|
213
212
|
});
|
|
@@ -223,8 +222,8 @@ function useCrudListScreen({
|
|
|
223
222
|
displayRows,
|
|
224
223
|
selectableRows,
|
|
225
224
|
listPrimaryAction,
|
|
226
|
-
hasViewUrl:
|
|
227
|
-
hasEditUrl:
|
|
225
|
+
hasViewUrl: records.hasViewUrl,
|
|
226
|
+
hasEditUrl: records.hasEditUrl,
|
|
228
227
|
resolveRecordTitle,
|
|
229
228
|
formatListCardValue: formatCrudListCardValue
|
|
230
229
|
});
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { computed, unref } from "vue";
|
|
2
2
|
import { useRoute } from "vue-router";
|
|
3
3
|
import { useCrudView } from "./records/useCrudView.js";
|
|
4
|
+
import { resolveRecordTitle } from "./crud/crudLookupFieldLabelSupport.js";
|
|
4
5
|
|
|
5
6
|
function useCrudViewScreen({
|
|
6
|
-
adapter = null,
|
|
7
7
|
resource = null,
|
|
8
8
|
resourceNamespace = "resource",
|
|
9
9
|
apiUrlTemplate = "",
|
|
@@ -30,7 +30,6 @@ function useCrudViewScreen({
|
|
|
30
30
|
String(workspaceSlug || "")
|
|
31
31
|
];
|
|
32
32
|
const view = useCrudView({
|
|
33
|
-
adapter: adapter || undefined,
|
|
34
33
|
resource,
|
|
35
34
|
apiUrlTemplate,
|
|
36
35
|
recordIdParam,
|
|
@@ -52,7 +51,7 @@ function useCrudViewScreen({
|
|
|
52
51
|
: null
|
|
53
52
|
});
|
|
54
53
|
const recordTitle = computed(() =>
|
|
55
|
-
|
|
54
|
+
resolveRecordTitle(view.record, {
|
|
56
55
|
fallbackKey: titleFallbackFieldKey,
|
|
57
56
|
defaultValue: "Record"
|
|
58
57
|
})
|
|
@@ -2,6 +2,7 @@ import { computed, unref } from "vue";
|
|
|
2
2
|
import { useQueryClient } from "@tanstack/vue-query";
|
|
3
3
|
import { normalizeText } from "@jskit-ai/kernel/shared/support/normalize";
|
|
4
4
|
import { useRealtimeEvent } from "@jskit-ai/realtime/client/composables/useRealtimeEvent";
|
|
5
|
+
import { resolveEnabledRef } from "./support/refValueHelpers.js";
|
|
5
6
|
|
|
6
7
|
function normalizeRealtimeOptions(value = {}) {
|
|
7
8
|
if (value === null || value === undefined || value === false) {
|
|
@@ -39,14 +40,6 @@ function resolveOperationRealtimeOptions({
|
|
|
39
40
|
});
|
|
40
41
|
}
|
|
41
42
|
|
|
42
|
-
function resolveEnabled(value) {
|
|
43
|
-
if (typeof value === "undefined") {
|
|
44
|
-
return true;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
return Boolean(unref(value));
|
|
48
|
-
}
|
|
49
|
-
|
|
50
43
|
function toQueryKeyList(value) {
|
|
51
44
|
const resolved = typeof value === "function" ? value() : unref(value);
|
|
52
45
|
if (!Array.isArray(resolved) || resolved.length < 1) {
|
|
@@ -71,7 +64,7 @@ function useRealtimeQueryInvalidation({
|
|
|
71
64
|
} = {}) {
|
|
72
65
|
const queryClient = useQueryClient();
|
|
73
66
|
const normalizedEvent = computed(() => normalizeText(unref(event)));
|
|
74
|
-
const active = computed(() =>
|
|
67
|
+
const active = computed(() => resolveEnabledRef(enabled) && Boolean(normalizedEvent.value));
|
|
75
68
|
|
|
76
69
|
const listener = useRealtimeEvent({
|
|
77
70
|
event: normalizedEvent,
|
|
@@ -131,7 +124,7 @@ function useOperationRealtime({
|
|
|
131
124
|
const resolvedQueryKey = Object.hasOwn(source, "queryKey") ? source.queryKey : queryKey;
|
|
132
125
|
const active = computed(() => {
|
|
133
126
|
const sourceEnabled = Object.hasOwn(source, "enabled") ? source.enabled : true;
|
|
134
|
-
return
|
|
127
|
+
return resolveEnabledRef(enabled) && resolveEnabledRef(sourceEnabled);
|
|
135
128
|
});
|
|
136
129
|
|
|
137
130
|
const bindings = eventList.map((event) =>
|
|
@@ -25,8 +25,7 @@ function useScopeRuntime({
|
|
|
25
25
|
ownershipFilter = ROUTE_VISIBILITY_WORKSPACE,
|
|
26
26
|
surfaceId = "",
|
|
27
27
|
accessMode = "auto",
|
|
28
|
-
hasPermissionRequirements = false
|
|
29
|
-
placementSource = "http-web.scope-runtime"
|
|
28
|
+
hasPermissionRequirements = false
|
|
30
29
|
} = {}) {
|
|
31
30
|
const normalizedOwnershipFilter = normalizeOwnershipFilter(ownershipFilter);
|
|
32
31
|
const normalizedAccessMode = ensureAccessModeCompatibility({
|
|
@@ -83,11 +82,9 @@ function useScopeRuntime({
|
|
|
83
82
|
});
|
|
84
83
|
|
|
85
84
|
const accessRuntime = useAccess({
|
|
86
|
-
scopeParamValue,
|
|
87
85
|
enabled: computed(() => accessRequired && hasRequiredRouteScope.value),
|
|
88
86
|
access: normalizedAccessMode,
|
|
89
|
-
hasPermissionRequirements
|
|
90
|
-
placementSource: String(placementSource || "http-web.scope-runtime")
|
|
87
|
+
hasPermissionRequirements
|
|
91
88
|
});
|
|
92
89
|
|
|
93
90
|
function resolveApiPath(apiSuffix = "", context = {}) {
|
package/src/client/rowActions.js
CHANGED
|
@@ -1,47 +1,20 @@
|
|
|
1
1
|
import { normalizeText } from "@jskit-ai/kernel/shared/support/normalize";
|
|
2
|
-
|
|
3
|
-
function normalizeCrudListRowAction(rawAction = {}, index = 0) {
|
|
4
|
-
if (!rawAction || typeof rawAction !== "object" || Array.isArray(rawAction)) {
|
|
5
|
-
return null;
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
const key = normalizeText(rawAction.key || rawAction.id || `action-${index + 1}`);
|
|
9
|
-
const label = normalizeText(rawAction.label || rawAction.title);
|
|
10
|
-
if (!key || !label) {
|
|
11
|
-
return null;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
return Object.freeze({
|
|
15
|
-
key,
|
|
16
|
-
label,
|
|
17
|
-
icon: normalizeText(rawAction.icon),
|
|
18
|
-
color: normalizeText(rawAction.color, { fallback: "primary" }),
|
|
19
|
-
run: typeof rawAction.run === "function" ? rawAction.run : null,
|
|
20
|
-
visible: rawAction.visible,
|
|
21
|
-
disabled: rawAction.disabled,
|
|
22
|
-
loading: rawAction.loading
|
|
23
|
-
});
|
|
24
|
-
}
|
|
2
|
+
import { defineCrudListActions } from "./support/crudListActionSupport.js";
|
|
25
3
|
|
|
26
4
|
function defineCrudListRowActions(actions = []) {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
seenKeys.add(action.key);
|
|
41
|
-
normalizedActions.push(action);
|
|
5
|
+
return defineCrudListActions(actions, {
|
|
6
|
+
name: "defineCrudListRowActions",
|
|
7
|
+
createAction: (rawAction, { key, label }) => ({
|
|
8
|
+
key,
|
|
9
|
+
label,
|
|
10
|
+
icon: normalizeText(rawAction.icon),
|
|
11
|
+
color: normalizeText(rawAction.color, { fallback: "primary" }),
|
|
12
|
+
run: typeof rawAction.run === "function" ? rawAction.run : null,
|
|
13
|
+
visible: rawAction.visible,
|
|
14
|
+
disabled: rawAction.disabled,
|
|
15
|
+
loading: rawAction.loading
|
|
16
|
+
})
|
|
42
17
|
});
|
|
43
|
-
|
|
44
|
-
return Object.freeze(normalizedActions);
|
|
45
18
|
}
|
|
46
19
|
|
|
47
20
|
export { defineCrudListRowActions };
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { normalizeText } from "@jskit-ai/kernel/shared/support/normalize";
|
|
2
|
+
|
|
3
|
+
function defineCrudListActions(actions, { name, createAction }) {
|
|
4
|
+
if (!Array.isArray(actions)) {
|
|
5
|
+
throw new TypeError(`${name} requires an array.`);
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
const normalizedActions = [];
|
|
9
|
+
const seenKeys = new Set();
|
|
10
|
+
|
|
11
|
+
actions.forEach((rawAction, index) => {
|
|
12
|
+
if (!rawAction || typeof rawAction !== "object" || Array.isArray(rawAction)) {
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const key = normalizeText(rawAction.key || rawAction.id || `action-${index + 1}`);
|
|
17
|
+
const label = normalizeText(rawAction.label || rawAction.title);
|
|
18
|
+
if (!key || !label || seenKeys.has(key)) {
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
seenKeys.add(key);
|
|
23
|
+
normalizedActions.push(Object.freeze(createAction(rawAction, { key, label })));
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
return Object.freeze(normalizedActions);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function normalizeListActionKey(actionOrKey = "") {
|
|
30
|
+
return normalizeText(
|
|
31
|
+
actionOrKey && typeof actionOrKey === "object"
|
|
32
|
+
? actionOrKey.key
|
|
33
|
+
: actionOrKey
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function resolveListRecordId(record = {}, index = 0, resolveRecordId = null) {
|
|
38
|
+
const resolvedId = typeof resolveRecordId === "function"
|
|
39
|
+
? resolveRecordId(record, index)
|
|
40
|
+
: record?.id ?? record?.attributes?.id ?? index;
|
|
41
|
+
return normalizeText(resolvedId);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function resolveListActionBaseContext(resolveContext = null) {
|
|
45
|
+
const baseContext = typeof resolveContext === "function" ? resolveContext() : {};
|
|
46
|
+
return baseContext && typeof baseContext === "object" && !Array.isArray(baseContext) ? baseContext : {};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export {
|
|
50
|
+
defineCrudListActions,
|
|
51
|
+
normalizeListActionKey,
|
|
52
|
+
resolveListRecordId,
|
|
53
|
+
resolveListActionBaseContext
|
|
54
|
+
};
|