@jskit-ai/users-web 0.1.173 → 0.1.174
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 +18 -149
- package/src/client/composables/account-settings/accountSettingsAvatarUploadRuntime.js +2 -2
- package/src/client/composables/useAccountSettingsRuntime.js +3 -3
- package/src/client/index.js +0 -11
- package/test/exportsContract.test.js +3 -29
- package/test/settingsPlacementContract.test.js +10 -0
- package/fixtures/crud-list-date-filters/App.vue +0 -107
- package/fixtures/crud-list-date-filters/index.html +0 -12
- package/fixtures/crud-list-date-filters/main.js +0 -24
- package/fixtures/crud-list-date-filters/vite.config.mjs +0 -18
- package/src/client/bulkActions.js +0 -47
- package/src/client/components/CrudAddEditScreen.vue +0 -186
- package/src/client/components/CrudDeleteAction.vue +0 -93
- package/src/client/components/CrudListBulkActionSurface.vue +0 -126
- package/src/client/components/CrudListDateFilterControl.vue +0 -188
- package/src/client/components/CrudListFilterSurface.vue +0 -388
- package/src/client/components/CrudListRecordActionMenu.vue +0 -120
- package/src/client/components/CrudListScreen.vue +0 -501
- package/src/client/components/CrudViewScreen.vue +0 -197
- package/src/client/composables/crud/crudBindingSupport.js +0 -75
- package/src/client/composables/crud/crudHttpClientSupport.js +0 -57
- package/src/client/composables/crud/crudJsonApiTransportSupport.js +0 -145
- package/src/client/composables/crud/crudLookupFieldLabelSupport.js +0 -151
- package/src/client/composables/crud/crudLookupFieldRuntime.js +0 -251
- package/src/client/composables/crud/crudSchemaFormHelpers.js +0 -464
- package/src/client/composables/internal/crudListFilterLookupSupport.js +0 -161
- package/src/client/composables/internal/crudListParentTitleSupport.js +0 -171
- package/src/client/composables/internal/useOperationScope.js +0 -144
- package/src/client/composables/records/useAddEdit.js +0 -224
- package/src/client/composables/records/useCrudAddEdit.js +0 -227
- package/src/client/composables/records/useCrudList.js +0 -101
- package/src/client/composables/records/useCrudView.js +0 -47
- package/src/client/composables/records/useList.js +0 -500
- package/src/client/composables/records/useView.js +0 -164
- package/src/client/composables/runtime/addEditUiRuntime.js +0 -130
- package/src/client/composables/runtime/listUiRuntime.js +0 -123
- package/src/client/composables/runtime/modelStateHelpers.js +0 -90
- package/src/client/composables/runtime/operationAdapters.js +0 -34
- package/src/client/composables/runtime/operationUiHelpers.js +0 -125
- package/src/client/composables/runtime/operationValidationHelpers.js +0 -51
- package/src/client/composables/runtime/useAddEditCore.js +0 -112
- package/src/client/composables/runtime/useCommandCore.js +0 -123
- package/src/client/composables/runtime/useEndpointResource.js +0 -199
- package/src/client/composables/runtime/useFieldErrorBag.js +0 -61
- package/src/client/composables/runtime/useListCore.js +0 -112
- package/src/client/composables/runtime/useUiFeedback.js +0 -98
- package/src/client/composables/runtime/useViewCore.js +0 -93
- package/src/client/composables/runtime/viewUiRuntime.js +0 -96
- package/src/client/composables/support/errorMessageHelpers.js +0 -66
- package/src/client/composables/support/listQueryParamSupport.js +0 -473
- package/src/client/composables/support/listSearchSupport.js +0 -70
- package/src/client/composables/support/refValueHelpers.js +0 -19
- package/src/client/composables/support/requestQueryRuntimeSupport.js +0 -141
- package/src/client/composables/support/resourceLoadStateHelpers.js +0 -208
- package/src/client/composables/support/routeTemplateHelpers.js +0 -285
- package/src/client/composables/support/scopeHelpers.js +0 -145
- package/src/client/composables/useAccess.js +0 -109
- package/src/client/composables/useCommand.js +0 -118
- package/src/client/composables/useCrudAddEditScreen.js +0 -88
- package/src/client/composables/useCrudDeleteAction.js +0 -160
- package/src/client/composables/useCrudListBulkActions.js +0 -147
- package/src/client/composables/useCrudListFilterLookups.js +0 -146
- package/src/client/composables/useCrudListFilters.js +0 -339
- package/src/client/composables/useCrudListParentTitle.js +0 -151
- package/src/client/composables/useCrudListRowActions.js +0 -144
- package/src/client/composables/useCrudListScreen.js +0 -237
- package/src/client/composables/useCrudViewScreen.js +0 -77
- package/src/client/composables/usePagedCollection.js +0 -181
- package/src/client/composables/useRealtimeQueryInvalidation.js +0 -156
- package/src/client/composables/useScopeRuntime.js +0 -135
- package/src/client/filters.js +0 -15
- package/src/client/lib/httpClient.js +0 -47
- package/src/client/lib/permissions.js +0 -34
- package/src/client/rowActions.js +0 -47
- package/src/client/support/contractGuards.js +0 -34
- package/src/client/support/crudListDateFilterSupport.js +0 -91
- package/test/addEditUiRuntime.test.js +0 -110
- package/test/crudBindingSupport.test.js +0 -110
- package/test/crudHttpClientSupport.test.js +0 -77
- package/test/crudJsonApiTransportSupport.test.js +0 -179
- package/test/crudListBulkActionSurface.test.js +0 -27
- package/test/crudListDateFilterSupport.test.js +0 -92
- package/test/crudListDateFilterSurface.browser.test.js +0 -182
- package/test/crudListFilterSurface.test.js +0 -90
- package/test/crudLookupFieldRuntime.test.js +0 -264
- package/test/crudScreenComponents.test.js +0 -117
- package/test/errorIntentContract.test.js +0 -31
- package/test/errorMessageHelpers.test.js +0 -28
- package/test/listQueryParamSupport.test.js +0 -223
- package/test/listUiRuntime.test.js +0 -102
- package/test/operationValidationHelpers.test.js +0 -64
- package/test/permissions.test.js +0 -35
- package/test/refValueHelpers.test.js +0 -14
- package/test/requestTransportOptions.test.js +0 -457
- package/test/resourceLoadStateHelpers.test.js +0 -189
- package/test/routeTemplateHelpers.test.js +0 -85
- package/test/scopeHelpers.test.js +0 -57
- package/test/useAddEditCore.test.js +0 -124
- package/test/useAddEditRequestQueryParams.test.js +0 -190
- package/test/useCrudAddEdit.test.js +0 -415
- package/test/useCrudDeleteAction.test.js +0 -139
- package/test/useCrudListBulkActions.test.js +0 -65
- package/test/useCrudListFilterLookups.test.js +0 -114
- package/test/useCrudListFilters.test.js +0 -293
- package/test/useCrudListParentTitle.test.js +0 -262
- package/test/useCrudListRowActions.test.js +0 -77
- package/test/useListRouteInitialization.test.js +0 -337
- package/test/useListSearchSupport.test.js +0 -61
- package/test/usePagedCollection.test.js +0 -53
- package/test/useViewRequestQueryParams.test.js +0 -20
- package/test/viewCoreLoading.test.js +0 -44
- package/test/viewUiRuntime.test.js +0 -95
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
import { unref } from "vue";
|
|
2
|
-
import { normalizeText } from "@jskit-ai/kernel/shared/support/normalize";
|
|
3
|
-
import { asPlainObject } from "../support/scopeHelpers.js";
|
|
4
|
-
|
|
5
|
-
const CRUD_BINDING_MODE_ROUTE = "route";
|
|
6
|
-
const CRUD_BINDING_MODE_MERGE = "merge";
|
|
7
|
-
const CRUD_BINDING_MODE_EXPLICIT = "explicit";
|
|
8
|
-
const CRUD_BINDING_MODE_NONE = "none";
|
|
9
|
-
|
|
10
|
-
function normalizeCrudBindingMode(value = "") {
|
|
11
|
-
const normalizedValue = normalizeText(value).toLowerCase();
|
|
12
|
-
if (normalizedValue === CRUD_BINDING_MODE_MERGE) {
|
|
13
|
-
return CRUD_BINDING_MODE_MERGE;
|
|
14
|
-
}
|
|
15
|
-
if (normalizedValue === CRUD_BINDING_MODE_EXPLICIT) {
|
|
16
|
-
return CRUD_BINDING_MODE_EXPLICIT;
|
|
17
|
-
}
|
|
18
|
-
if (normalizedValue === CRUD_BINDING_MODE_NONE) {
|
|
19
|
-
return CRUD_BINDING_MODE_NONE;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
return CRUD_BINDING_MODE_ROUTE;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
function normalizeCrudBindingConfig(binding = {}) {
|
|
26
|
-
const source = asPlainObject(unref(binding));
|
|
27
|
-
return Object.freeze({
|
|
28
|
-
mode: normalizeCrudBindingMode(source.mode),
|
|
29
|
-
values: source.values ?? null
|
|
30
|
-
});
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
function resolveCrudBindingValues(values, context = {}) {
|
|
34
|
-
if (typeof values === "function") {
|
|
35
|
-
return asPlainObject(values(context));
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
return asPlainObject(unref(values));
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
function resolveCrudBoundValues({
|
|
42
|
-
binding = {},
|
|
43
|
-
routeValues = {},
|
|
44
|
-
context = {}
|
|
45
|
-
} = {}) {
|
|
46
|
-
const normalizedBinding = normalizeCrudBindingConfig(binding);
|
|
47
|
-
const normalizedRouteValues = asPlainObject(routeValues);
|
|
48
|
-
const explicitValues = resolveCrudBindingValues(normalizedBinding.values, context);
|
|
49
|
-
|
|
50
|
-
if (normalizedBinding.mode === CRUD_BINDING_MODE_NONE) {
|
|
51
|
-
return {};
|
|
52
|
-
}
|
|
53
|
-
if (normalizedBinding.mode === CRUD_BINDING_MODE_EXPLICIT) {
|
|
54
|
-
return explicitValues;
|
|
55
|
-
}
|
|
56
|
-
if (normalizedBinding.mode === CRUD_BINDING_MODE_MERGE) {
|
|
57
|
-
return {
|
|
58
|
-
...normalizedRouteValues,
|
|
59
|
-
...explicitValues
|
|
60
|
-
};
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
return normalizedRouteValues;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
export {
|
|
67
|
-
CRUD_BINDING_MODE_ROUTE,
|
|
68
|
-
CRUD_BINDING_MODE_MERGE,
|
|
69
|
-
CRUD_BINDING_MODE_EXPLICIT,
|
|
70
|
-
CRUD_BINDING_MODE_NONE,
|
|
71
|
-
normalizeCrudBindingMode,
|
|
72
|
-
normalizeCrudBindingConfig,
|
|
73
|
-
resolveCrudBindingValues,
|
|
74
|
-
resolveCrudBoundValues
|
|
75
|
-
};
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
import { normalizeText } from "@jskit-ai/kernel/shared/support/normalize";
|
|
2
|
-
import { getUsersWebHttpClient } from "../../lib/httpClient.js";
|
|
3
|
-
|
|
4
|
-
const CRUD_API_ACCESS_AUTHENTICATED = "authenticated";
|
|
5
|
-
const CRUD_API_ACCESS_PUBLIC = "public";
|
|
6
|
-
const CRUD_API_ACCESS_VALUES = Object.freeze([
|
|
7
|
-
CRUD_API_ACCESS_AUTHENTICATED,
|
|
8
|
-
CRUD_API_ACCESS_PUBLIC
|
|
9
|
-
]);
|
|
10
|
-
const publicCrudClients = new WeakSet();
|
|
11
|
-
|
|
12
|
-
function normalizeCrudApiAccess(value = "") {
|
|
13
|
-
const normalized = normalizeText(value).toLowerCase() || CRUD_API_ACCESS_AUTHENTICATED;
|
|
14
|
-
if (CRUD_API_ACCESS_VALUES.includes(normalized)) {
|
|
15
|
-
return normalized;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
throw new TypeError(
|
|
19
|
-
`CRUD resource apiAccess must be one of: ${CRUD_API_ACCESS_VALUES.join(", ")}. ` +
|
|
20
|
-
`Received: ${String(value || "") || "(empty)"}`
|
|
21
|
-
);
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
function resolveCrudHttpClient(resource = null, { client = null } = {}) {
|
|
25
|
-
const activeClient = client || getUsersWebHttpClient();
|
|
26
|
-
if (!activeClient || typeof activeClient.request !== "function") {
|
|
27
|
-
throw new TypeError("resolveCrudHttpClient requires a client with request().");
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
if (normalizeCrudApiAccess(resource?.apiAccess) !== CRUD_API_ACCESS_PUBLIC) {
|
|
31
|
-
return activeClient;
|
|
32
|
-
}
|
|
33
|
-
if (publicCrudClients.has(activeClient)) {
|
|
34
|
-
return activeClient;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
const publicClient = Object.freeze({
|
|
38
|
-
request(url, options = {}) {
|
|
39
|
-
const requestOptions = options && typeof options === "object" && !Array.isArray(options)
|
|
40
|
-
? options
|
|
41
|
-
: {};
|
|
42
|
-
return activeClient.request(url, {
|
|
43
|
-
...requestOptions,
|
|
44
|
-
csrf: false
|
|
45
|
-
});
|
|
46
|
-
}
|
|
47
|
-
});
|
|
48
|
-
publicCrudClients.add(publicClient);
|
|
49
|
-
return publicClient;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
export {
|
|
53
|
-
CRUD_API_ACCESS_AUTHENTICATED,
|
|
54
|
-
CRUD_API_ACCESS_PUBLIC,
|
|
55
|
-
normalizeCrudApiAccess,
|
|
56
|
-
resolveCrudHttpClient
|
|
57
|
-
};
|
|
@@ -1,145 +0,0 @@
|
|
|
1
|
-
import { normalizeText } from "@jskit-ai/kernel/shared/support/normalize";
|
|
2
|
-
import { normalizeCrudLookupNamespace } from "@jskit-ai/kernel/shared/support/crudLookup";
|
|
3
|
-
|
|
4
|
-
function isRecord(value) {
|
|
5
|
-
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
function resolveSchemaFieldDefinitions(definition = null) {
|
|
9
|
-
const schema = definition?.schema;
|
|
10
|
-
if (!schema || typeof schema.getFieldDefinitions !== "function") {
|
|
11
|
-
return {};
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
const definitions = schema.getFieldDefinitions();
|
|
15
|
-
return isRecord(definitions) ? definitions : {};
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
function resolveLookupFieldMap(resource = null) {
|
|
19
|
-
const outputDefinition =
|
|
20
|
-
resource?.operations?.view?.output ||
|
|
21
|
-
resource?.operations?.create?.output ||
|
|
22
|
-
resource?.operations?.patch?.output ||
|
|
23
|
-
null;
|
|
24
|
-
const mapping = {};
|
|
25
|
-
|
|
26
|
-
for (const [fieldKey, fieldDefinition] of Object.entries(resolveSchemaFieldDefinitions(outputDefinition))) {
|
|
27
|
-
const normalizedFieldDefinition = isRecord(fieldDefinition) ? fieldDefinition : {};
|
|
28
|
-
if (!normalizeText(normalizedFieldDefinition.belongsTo)) {
|
|
29
|
-
continue;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
const relationshipName = normalizeText(normalizedFieldDefinition.as, {
|
|
33
|
-
fallback: fieldKey
|
|
34
|
-
});
|
|
35
|
-
const normalizedFieldKey = normalizeText(fieldKey);
|
|
36
|
-
if (!normalizedFieldKey || !relationshipName) {
|
|
37
|
-
continue;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
mapping[relationshipName] = normalizedFieldKey;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
return Object.freeze(mapping);
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
function isJsonApiResourceTransport(transport = null) {
|
|
47
|
-
return normalizeText(transport?.kind).toLowerCase() === "jsonapi-resource";
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
function resolveCrudJsonApiResourceType(resource = null) {
|
|
51
|
-
return normalizeText(resource?.namespace);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
function inferCrudLookupJsonApiTransport({ namespace = "", apiPath = "" } = {}) {
|
|
55
|
-
const resourceType =
|
|
56
|
-
normalizeCrudLookupNamespace(namespace) ||
|
|
57
|
-
normalizeCrudLookupNamespace(apiPath);
|
|
58
|
-
if (!resourceType) {
|
|
59
|
-
return null;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
return Object.freeze({
|
|
63
|
-
kind: "jsonapi-resource",
|
|
64
|
-
responseType: resourceType,
|
|
65
|
-
responseKind: "collection"
|
|
66
|
-
});
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
function inferCrudJsonApiTransport(resource = null, { mode = "", operationName = "" } = {}) {
|
|
70
|
-
const resourceType = resolveCrudJsonApiResourceType(resource);
|
|
71
|
-
if (!resourceType) {
|
|
72
|
-
return null;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
const normalizedMode = normalizeText(mode).toLowerCase();
|
|
76
|
-
const normalizedOperationName = normalizeText(operationName).toLowerCase();
|
|
77
|
-
if (normalizedMode === "list") {
|
|
78
|
-
return Object.freeze({
|
|
79
|
-
kind: "jsonapi-resource",
|
|
80
|
-
responseType: resourceType,
|
|
81
|
-
responseKind: "collection"
|
|
82
|
-
});
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
if (normalizedMode === "view") {
|
|
86
|
-
return Object.freeze({
|
|
87
|
-
kind: "jsonapi-resource",
|
|
88
|
-
responseType: resourceType,
|
|
89
|
-
responseKind: "record"
|
|
90
|
-
});
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
if (normalizedMode === "delete") {
|
|
94
|
-
return Object.freeze({
|
|
95
|
-
kind: "jsonapi-resource",
|
|
96
|
-
responseType: resourceType,
|
|
97
|
-
responseKind: "record"
|
|
98
|
-
});
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
if (normalizedMode === "add-edit") {
|
|
102
|
-
return Object.freeze({
|
|
103
|
-
kind: "jsonapi-resource",
|
|
104
|
-
...(normalizedOperationName ? { requestType: resourceType } : {}),
|
|
105
|
-
responseType: resourceType,
|
|
106
|
-
responseKind: "record"
|
|
107
|
-
});
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
return null;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
function resolveCrudJsonApiTransport(transport = null, resource = null, options = {}) {
|
|
114
|
-
if (transport != null) {
|
|
115
|
-
throw new TypeError(
|
|
116
|
-
"CRUD hooks do not accept explicit transport. Derive JSON:API transport from the shared resource."
|
|
117
|
-
);
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
const baseTransport = inferCrudJsonApiTransport(resource, options);
|
|
121
|
-
if (!isJsonApiResourceTransport(baseTransport)) {
|
|
122
|
-
return baseTransport;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
const lookupFieldMap = resolveLookupFieldMap(resource);
|
|
126
|
-
const lookupContainerKey = normalizeText(resource?.contract?.lookup?.containerKey, {
|
|
127
|
-
fallback: "lookups"
|
|
128
|
-
});
|
|
129
|
-
if (Object.keys(lookupFieldMap).length < 1 && !lookupContainerKey) {
|
|
130
|
-
return baseTransport;
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
return Object.freeze({
|
|
134
|
-
...baseTransport,
|
|
135
|
-
...(Object.keys(lookupFieldMap).length > 0 ? { lookupFieldMap } : {}),
|
|
136
|
-
...(lookupContainerKey ? { lookupContainerKey } : {})
|
|
137
|
-
});
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
export {
|
|
141
|
-
inferCrudJsonApiTransport,
|
|
142
|
-
inferCrudLookupJsonApiTransport,
|
|
143
|
-
resolveCrudJsonApiTransport,
|
|
144
|
-
resolveLookupFieldMap
|
|
145
|
-
};
|
|
@@ -1,151 +0,0 @@
|
|
|
1
|
-
import { normalizeText } from "@jskit-ai/kernel/shared/support/normalize";
|
|
2
|
-
import { normalizeCrudLookupContainerKey } from "@jskit-ai/kernel/shared/support/crudLookup";
|
|
3
|
-
import { asPlainObject } from "../support/scopeHelpers.js";
|
|
4
|
-
|
|
5
|
-
const LOOKUP_LABEL_COMPOSITION_CANDIDATES = Object.freeze([
|
|
6
|
-
Object.freeze(["name", "surname"]),
|
|
7
|
-
Object.freeze(["name", "lastName"]),
|
|
8
|
-
Object.freeze(["firstName", "surname"]),
|
|
9
|
-
Object.freeze(["firstName", "lastName"]),
|
|
10
|
-
Object.freeze(["name"]),
|
|
11
|
-
Object.freeze(["firstName"])
|
|
12
|
-
]);
|
|
13
|
-
const DEFAULT_RECORD_TITLE = "-";
|
|
14
|
-
|
|
15
|
-
function hasDisplayValue(value) {
|
|
16
|
-
if (value == null) {
|
|
17
|
-
return false;
|
|
18
|
-
}
|
|
19
|
-
if (typeof value === "string") {
|
|
20
|
-
return normalizeText(value).length > 0;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
return true;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
function resolveComposedLabel(source = {}, candidates = LOOKUP_LABEL_COMPOSITION_CANDIDATES) {
|
|
27
|
-
for (const candidate of candidates) {
|
|
28
|
-
const parts = [];
|
|
29
|
-
for (const key of candidate) {
|
|
30
|
-
const part = normalizeText(source[key]);
|
|
31
|
-
if (!part) {
|
|
32
|
-
parts.length = 0;
|
|
33
|
-
break;
|
|
34
|
-
}
|
|
35
|
-
parts.push(part);
|
|
36
|
-
}
|
|
37
|
-
if (parts.length === candidate.length) {
|
|
38
|
-
return parts.join(" ");
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
return "";
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
function resolveLookupItemLabel(item = {}, labelKey = "") {
|
|
46
|
-
const source = asPlainObject(item);
|
|
47
|
-
const composedLabel = resolveComposedLabel(source);
|
|
48
|
-
if (composedLabel) {
|
|
49
|
-
return composedLabel;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
const normalizedLabelKey = normalizeText(labelKey);
|
|
53
|
-
if (!normalizedLabelKey) {
|
|
54
|
-
return "";
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
return normalizeText(source[normalizedLabelKey]);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
function resolveRecordTitle(record = {}, { fallbackKey = "", defaultValue = DEFAULT_RECORD_TITLE } = {}) {
|
|
61
|
-
const source = asPlainObject(record);
|
|
62
|
-
const composedLabel = resolveComposedLabel(source);
|
|
63
|
-
if (composedLabel) {
|
|
64
|
-
return composedLabel;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
const normalizedFallbackKey = normalizeText(fallbackKey);
|
|
68
|
-
if (normalizedFallbackKey) {
|
|
69
|
-
const fallbackValue = normalizeText(source[normalizedFallbackKey]);
|
|
70
|
-
if (fallbackValue) {
|
|
71
|
-
return fallbackValue;
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
const normalizedDefaultValue = normalizeText(defaultValue);
|
|
76
|
-
return normalizedDefaultValue || DEFAULT_RECORD_TITLE;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
function resolveLookupFieldDescriptor(field = {}, relationKind = "", valueKey = "", labelKey = "") {
|
|
80
|
-
if (typeof field === "string") {
|
|
81
|
-
return {
|
|
82
|
-
key: normalizeText(field),
|
|
83
|
-
relation: {
|
|
84
|
-
kind: normalizeText(relationKind).toLowerCase(),
|
|
85
|
-
valueKey: normalizeText(valueKey) || "id",
|
|
86
|
-
labelKey: normalizeText(labelKey),
|
|
87
|
-
containerKey: ""
|
|
88
|
-
}
|
|
89
|
-
};
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
const sourceField = asPlainObject(field);
|
|
93
|
-
const relation = asPlainObject(sourceField.relation);
|
|
94
|
-
return {
|
|
95
|
-
key: normalizeText(sourceField.key),
|
|
96
|
-
relation: {
|
|
97
|
-
kind: normalizeText(relation.kind).toLowerCase(),
|
|
98
|
-
valueKey: normalizeText(relation.valueKey) || "id",
|
|
99
|
-
labelKey: normalizeText(relation.labelKey),
|
|
100
|
-
containerKey: normalizeText(relation.containerKey)
|
|
101
|
-
}
|
|
102
|
-
};
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
function resolveFallbackLookupKey(key = "") {
|
|
106
|
-
const normalizedKey = normalizeText(key);
|
|
107
|
-
if (!normalizedKey.endsWith("Id") || normalizedKey.length <= 2) {
|
|
108
|
-
return "";
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
return normalizedKey.slice(0, -2);
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
function resolveLookupFieldDisplayValue(record = {}, field = {}, relationKind = "", valueKey = "", labelKey = "") {
|
|
115
|
-
const sourceRecord = asPlainObject(record);
|
|
116
|
-
const descriptor = resolveLookupFieldDescriptor(field, relationKind, valueKey, labelKey);
|
|
117
|
-
const key = descriptor.key;
|
|
118
|
-
if (!key) {
|
|
119
|
-
return "";
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
if (descriptor.relation.kind !== "lookup") {
|
|
123
|
-
return sourceRecord[key];
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
const lookupContainerKey = normalizeCrudLookupContainerKey(descriptor.relation.containerKey, {
|
|
127
|
-
context: `lookup relation "${key}" containerKey`
|
|
128
|
-
});
|
|
129
|
-
const sourceLookups = asPlainObject(sourceRecord[lookupContainerKey]);
|
|
130
|
-
let lookupRecord = asPlainObject(sourceLookups[key]);
|
|
131
|
-
if (Object.keys(lookupRecord).length < 1) {
|
|
132
|
-
lookupRecord = asPlainObject(sourceLookups[resolveFallbackLookupKey(key)]);
|
|
133
|
-
}
|
|
134
|
-
const lookupLabel = resolveLookupItemLabel(lookupRecord, descriptor.relation.labelKey);
|
|
135
|
-
if (lookupLabel) {
|
|
136
|
-
return lookupLabel;
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
const lookupValue = lookupRecord[descriptor.relation.valueKey];
|
|
140
|
-
if (hasDisplayValue(lookupValue)) {
|
|
141
|
-
return lookupValue;
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
return sourceRecord[key];
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
export {
|
|
148
|
-
resolveLookupItemLabel,
|
|
149
|
-
resolveLookupFieldDisplayValue,
|
|
150
|
-
resolveRecordTitle
|
|
151
|
-
};
|
|
@@ -1,251 +0,0 @@
|
|
|
1
|
-
import { normalizeText } from "@jskit-ai/kernel/shared/support/normalize";
|
|
2
|
-
import {
|
|
3
|
-
normalizeCrudLookupApiPath,
|
|
4
|
-
normalizeCrudLookupNamespace,
|
|
5
|
-
normalizeCrudLookupContainerKey,
|
|
6
|
-
resolveCrudLookupApiPathFromNamespace
|
|
7
|
-
} from "@jskit-ai/kernel/shared/support/crudLookup";
|
|
8
|
-
import { normalizeSurfaceId } from "@jskit-ai/kernel/shared/surface/registry";
|
|
9
|
-
import { useList } from "../records/useList.js";
|
|
10
|
-
import {
|
|
11
|
-
resolveLookupItemLabel,
|
|
12
|
-
resolveLookupFieldDisplayValue
|
|
13
|
-
} from "./crudLookupFieldLabelSupport.js";
|
|
14
|
-
import { inferCrudLookupJsonApiTransport } from "./crudJsonApiTransportSupport.js";
|
|
15
|
-
import { asPlainObject } from "../support/scopeHelpers.js";
|
|
16
|
-
|
|
17
|
-
function normalizeQueryKeyPrefix(value) {
|
|
18
|
-
const source = Array.isArray(value) ? value : [];
|
|
19
|
-
return source
|
|
20
|
-
.map((entry) => normalizeText(entry))
|
|
21
|
-
.filter(Boolean);
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
function isSameLookupValue(left, right) {
|
|
25
|
-
if (left === right) {
|
|
26
|
-
return true;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
return String(left ?? "") === String(right ?? "");
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
function normalizeLookupValue(value) {
|
|
33
|
-
if (value == null) {
|
|
34
|
-
return "";
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
return String(value);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
function createSelectedLookupItem(selectedValue, selectedRecord = {}, entry = {}) {
|
|
41
|
-
if (selectedValue == null || selectedValue === "") {
|
|
42
|
-
return null;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
const sourceRecord = asPlainObject(selectedRecord);
|
|
46
|
-
const sourceLookups = asPlainObject(sourceRecord[entry.lookupContainerKey]);
|
|
47
|
-
const hydratedLookup = asPlainObject(sourceLookups[entry.fieldKey]);
|
|
48
|
-
const hydratedValue = hydratedLookup[entry.valueKey];
|
|
49
|
-
const value = normalizeLookupValue(
|
|
50
|
-
hydratedValue == null || hydratedValue === "" ? selectedValue : hydratedValue
|
|
51
|
-
);
|
|
52
|
-
if (!value) {
|
|
53
|
-
return null;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
const displayValue = resolveLookupFieldDisplayValue(
|
|
57
|
-
{
|
|
58
|
-
[entry.fieldKey]: value,
|
|
59
|
-
[entry.lookupContainerKey]: {
|
|
60
|
-
[entry.fieldKey]: hydratedLookup
|
|
61
|
-
}
|
|
62
|
-
},
|
|
63
|
-
{
|
|
64
|
-
key: entry.fieldKey,
|
|
65
|
-
relation: entry.relation
|
|
66
|
-
}
|
|
67
|
-
);
|
|
68
|
-
const label = displayValue == null || displayValue === "" ? value : displayValue;
|
|
69
|
-
return {
|
|
70
|
-
value,
|
|
71
|
-
label: String(label ?? ""),
|
|
72
|
-
record: hydratedLookup
|
|
73
|
-
};
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
function createCrudLookupFieldRuntime({
|
|
77
|
-
formFields = [],
|
|
78
|
-
adapter = null,
|
|
79
|
-
recordIdParam = "recordId",
|
|
80
|
-
queryKeyPrefix = [],
|
|
81
|
-
placementSourcePrefix = "",
|
|
82
|
-
lookupContainerKey = "lookups"
|
|
83
|
-
} = {}) {
|
|
84
|
-
const runtimes = new Map();
|
|
85
|
-
const normalizedRecordIdParam = normalizeText(recordIdParam) || "recordId";
|
|
86
|
-
const normalizedPlacementSourcePrefix = normalizeText(placementSourcePrefix);
|
|
87
|
-
const normalizedQueryKeyPrefix = normalizeQueryKeyPrefix(queryKeyPrefix);
|
|
88
|
-
const defaultLookupContainerKey = normalizeCrudLookupContainerKey(lookupContainerKey, {
|
|
89
|
-
context: "createCrudLookupFieldRuntime lookupContainerKey"
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
for (const field of Array.isArray(formFields) ? formFields : []) {
|
|
93
|
-
const key = normalizeText(field?.key);
|
|
94
|
-
const rawRelation = field?.relation;
|
|
95
|
-
if (!key || !rawRelation || typeof rawRelation !== "object" || Array.isArray(rawRelation) || runtimes.has(key)) {
|
|
96
|
-
continue;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
const relationKind = normalizeText(rawRelation.kind).toLowerCase();
|
|
100
|
-
const namespace =
|
|
101
|
-
normalizeCrudLookupNamespace(rawRelation.namespace) ||
|
|
102
|
-
normalizeCrudLookupNamespace(rawRelation.apiPath);
|
|
103
|
-
if (relationKind !== "lookup" || !namespace) {
|
|
104
|
-
continue;
|
|
105
|
-
}
|
|
106
|
-
const explicitApiPath = normalizeCrudLookupApiPath(rawRelation.apiPath);
|
|
107
|
-
const apiPath = explicitApiPath || resolveCrudLookupApiPathFromNamespace(namespace);
|
|
108
|
-
const transport = inferCrudLookupJsonApiTransport({
|
|
109
|
-
namespace,
|
|
110
|
-
apiPath
|
|
111
|
-
});
|
|
112
|
-
const valueKey = normalizeText(rawRelation.valueKey);
|
|
113
|
-
const labelKey = normalizeText(rawRelation.labelKey);
|
|
114
|
-
const relationLookupContainerKey = normalizeCrudLookupContainerKey(rawRelation.containerKey, {
|
|
115
|
-
defaultValue: defaultLookupContainerKey,
|
|
116
|
-
context: `createCrudLookupFieldRuntime formFields["${key}"].relation.containerKey`
|
|
117
|
-
});
|
|
118
|
-
const relationSurfaceId = normalizeSurfaceId(rawRelation.surfaceId);
|
|
119
|
-
if (!valueKey) {
|
|
120
|
-
continue;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
const runtime = useList({
|
|
124
|
-
adapter: adapter || undefined,
|
|
125
|
-
...(relationSurfaceId ? { surfaceId: relationSurfaceId } : {}),
|
|
126
|
-
apiSuffix: apiPath,
|
|
127
|
-
...(transport ? { transport } : {}),
|
|
128
|
-
queryKeyFactory: (surfaceId = "", scopeParamValue = "") => [
|
|
129
|
-
...normalizedQueryKeyPrefix,
|
|
130
|
-
key,
|
|
131
|
-
String(surfaceId || ""),
|
|
132
|
-
String(scopeParamValue || "")
|
|
133
|
-
],
|
|
134
|
-
search: {
|
|
135
|
-
enabled: true,
|
|
136
|
-
mode: "query",
|
|
137
|
-
queryParam: "q"
|
|
138
|
-
},
|
|
139
|
-
placementSource: normalizedPlacementSourcePrefix
|
|
140
|
-
? `${normalizedPlacementSourcePrefix}.${key}`
|
|
141
|
-
: `lookup.${key}`,
|
|
142
|
-
fallbackLoadError: `Unable to load lookup options (${apiPath}).`,
|
|
143
|
-
recordIdParam: normalizedRecordIdParam,
|
|
144
|
-
recordIdSelector: (item = {}) => item[valueKey],
|
|
145
|
-
viewUrlTemplate: "",
|
|
146
|
-
editUrlTemplate: ""
|
|
147
|
-
});
|
|
148
|
-
|
|
149
|
-
runtimes.set(key, Object.freeze({
|
|
150
|
-
runtime,
|
|
151
|
-
fieldKey: key,
|
|
152
|
-
lookupContainerKey: relationLookupContainerKey,
|
|
153
|
-
valueKey,
|
|
154
|
-
labelKey,
|
|
155
|
-
relation: Object.freeze({
|
|
156
|
-
kind: "lookup",
|
|
157
|
-
namespace,
|
|
158
|
-
...(explicitApiPath ? { apiPath: explicitApiPath } : {}),
|
|
159
|
-
...(relationSurfaceId ? { surfaceId: relationSurfaceId } : {}),
|
|
160
|
-
containerKey: relationLookupContainerKey,
|
|
161
|
-
valueKey,
|
|
162
|
-
...(labelKey ? { labelKey } : {})
|
|
163
|
-
})
|
|
164
|
-
}));
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
function resolveLookupItems(fieldKey = "", options = {}) {
|
|
168
|
-
const key = normalizeText(fieldKey);
|
|
169
|
-
const entry = runtimes.get(key);
|
|
170
|
-
if (!entry) {
|
|
171
|
-
return [];
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
const items = (Array.isArray(entry.runtime.items) ? entry.runtime.items : []).map((item = {}) => {
|
|
175
|
-
const sourceRecord = asPlainObject(item);
|
|
176
|
-
const value = normalizeLookupValue(item?.[entry.valueKey]);
|
|
177
|
-
const resolvedLabel = resolveLookupItemLabel(item, entry.labelKey);
|
|
178
|
-
const label = resolvedLabel || value;
|
|
179
|
-
return {
|
|
180
|
-
value,
|
|
181
|
-
label: String(label ?? ""),
|
|
182
|
-
record: sourceRecord
|
|
183
|
-
};
|
|
184
|
-
});
|
|
185
|
-
|
|
186
|
-
const selectedItem = createSelectedLookupItem(
|
|
187
|
-
options?.selectedValue,
|
|
188
|
-
options?.selectedRecord,
|
|
189
|
-
entry
|
|
190
|
-
);
|
|
191
|
-
if (!selectedItem) {
|
|
192
|
-
return items;
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
if (items.some((item) => item?.value === selectedItem.value)) {
|
|
196
|
-
return items;
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
const matchingItem = items.find((item) => isSameLookupValue(item?.value, selectedItem.value));
|
|
200
|
-
if (matchingItem) {
|
|
201
|
-
return [
|
|
202
|
-
{
|
|
203
|
-
...selectedItem,
|
|
204
|
-
label: String(matchingItem.label ?? selectedItem.label ?? "")
|
|
205
|
-
},
|
|
206
|
-
...items
|
|
207
|
-
];
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
return [selectedItem, ...items];
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
function resolveLookupLoading(fieldKey = "") {
|
|
214
|
-
const key = normalizeText(fieldKey);
|
|
215
|
-
const entry = runtimes.get(key);
|
|
216
|
-
if (!entry) {
|
|
217
|
-
return false;
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
return Boolean(entry.runtime.isInitialLoading || entry.runtime.isFetching || entry.runtime.isRefetching);
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
function resolveLookupSearch(fieldKey = "") {
|
|
224
|
-
const key = normalizeText(fieldKey);
|
|
225
|
-
const entry = runtimes.get(key);
|
|
226
|
-
if (!entry) {
|
|
227
|
-
return "";
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
return String(entry.runtime.searchQuery || "");
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
function setLookupSearch(fieldKey = "", searchValue = "") {
|
|
234
|
-
const key = normalizeText(fieldKey);
|
|
235
|
-
const entry = runtimes.get(key);
|
|
236
|
-
if (!entry) {
|
|
237
|
-
return;
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
entry.runtime.searchQuery = String(searchValue || "");
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
return Object.freeze({
|
|
244
|
-
resolveLookupItems,
|
|
245
|
-
resolveLookupLoading,
|
|
246
|
-
resolveLookupSearch,
|
|
247
|
-
setLookupSearch
|
|
248
|
-
});
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
export { createCrudLookupFieldRuntime };
|