@jskit-ai/crud-ui-generator 0.1.31 → 0.1.32
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.descriptor.mjs +2 -2
- package/package.json +3 -3
- package/src/server/buildTemplateContext.js +100 -2
- package/src/server/resourceSupport.js +81 -61
- package/templates/src/pages/admin/ui-generator/AddEditForm.vue +15 -7
- package/templates/src/pages/admin/ui-generator/EditElement.vue +11 -18
- package/templates/src/pages/admin/ui-generator/EditWrapperElement.vue +5 -21
- package/templates/src/pages/admin/ui-generator/ListElement.vue +5 -5
- package/templates/src/pages/admin/ui-generator/NewElement.vue +11 -18
- package/templates/src/pages/admin/ui-generator/NewWrapperElement.vue +5 -21
- package/templates/src/pages/admin/ui-generator/ViewElement.vue +2 -2
- package/test/addFieldSubcommand.test.js +11 -7
- package/test/buildTemplateContext.test.js +158 -3
package/package.descriptor.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export default Object.freeze({
|
|
2
2
|
packageVersion: 1,
|
|
3
3
|
packageId: "@jskit-ai/crud-ui-generator",
|
|
4
|
-
version: "0.1.
|
|
4
|
+
version: "0.1.32",
|
|
5
5
|
kind: "generator",
|
|
6
6
|
description: "Generate CRUD route trees from resource validators at an explicit route root relative to src/pages/.",
|
|
7
7
|
options: {
|
|
@@ -168,7 +168,7 @@ export default Object.freeze({
|
|
|
168
168
|
mutations: {
|
|
169
169
|
dependencies: {
|
|
170
170
|
runtime: {
|
|
171
|
-
"@jskit-ai/users-web": "0.1.
|
|
171
|
+
"@jskit-ai/users-web": "0.1.64"
|
|
172
172
|
},
|
|
173
173
|
dev: {}
|
|
174
174
|
},
|
package/package.json
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jskit-ai/crud-ui-generator",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.32",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"test": "node --test"
|
|
7
7
|
},
|
|
8
8
|
"dependencies": {
|
|
9
|
-
"@jskit-ai/crud-core": "0.1.
|
|
10
|
-
"@jskit-ai/kernel": "0.1.
|
|
9
|
+
"@jskit-ai/crud-core": "0.1.57",
|
|
10
|
+
"@jskit-ai/kernel": "0.1.49"
|
|
11
11
|
},
|
|
12
12
|
"exports": {
|
|
13
13
|
"./server/buildTemplateContext": "./src/server/buildTemplateContext.js"
|
|
@@ -258,6 +258,77 @@ function filterDisplayFields(selectedFieldKeys, fields) {
|
|
|
258
258
|
});
|
|
259
259
|
}
|
|
260
260
|
|
|
261
|
+
function rewriteGeneratedBlockIndent(source = "", { trimPrefix = "", addPrefix = "" } = {}) {
|
|
262
|
+
const text = String(source || "");
|
|
263
|
+
const trimmedPrefix = String(trimPrefix || "");
|
|
264
|
+
const extraPrefix = String(addPrefix || "");
|
|
265
|
+
if (!text) {
|
|
266
|
+
return "";
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
return text
|
|
270
|
+
.split("\n")
|
|
271
|
+
.map((line) => {
|
|
272
|
+
let nextLine = line;
|
|
273
|
+
if (trimmedPrefix && nextLine.startsWith(trimmedPrefix)) {
|
|
274
|
+
nextLine = nextLine.slice(trimmedPrefix.length);
|
|
275
|
+
}
|
|
276
|
+
return `${extraPrefix}${nextLine}`;
|
|
277
|
+
})
|
|
278
|
+
.join("\n");
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
function hasLookupFormFields(fields = []) {
|
|
282
|
+
return (Array.isArray(fields) ? fields : []).some((field) => normalizeText(field?.component).toLowerCase() === "lookup");
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
function buildLookupImportLine(fields = []) {
|
|
286
|
+
return hasLookupFormFields(fields)
|
|
287
|
+
? 'import { createCrudLookupFieldRuntime } from "@jskit-ai/users-web/client/composables/crudLookupFieldRuntime";'
|
|
288
|
+
: "";
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
function buildLookupRuntimeSetup(fields = [], {
|
|
292
|
+
formFieldsVariable = "",
|
|
293
|
+
resourceNamespace = "",
|
|
294
|
+
mode = ""
|
|
295
|
+
} = {}) {
|
|
296
|
+
if (!hasLookupFormFields(fields)) {
|
|
297
|
+
return "";
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
const normalizedFormFieldsVariable = normalizeText(formFieldsVariable) || "UI_FORM_FIELDS";
|
|
301
|
+
const normalizedResourceNamespace = normalizeText(resourceNamespace) || "resource";
|
|
302
|
+
const normalizedMode = normalizeText(mode) || "new";
|
|
303
|
+
|
|
304
|
+
return `const lookupFieldRuntime = createCrudLookupFieldRuntime({
|
|
305
|
+
formFields: ${normalizedFormFieldsVariable},
|
|
306
|
+
adapter: UI_OPERATION_ADAPTER || undefined,
|
|
307
|
+
recordIdParam: UI_RECORD_ID_PARAM,
|
|
308
|
+
lookupContainerKey: uiResource?.contract?.lookup?.containerKey,
|
|
309
|
+
queryKeyPrefix: ["ui-generator", "${normalizedResourceNamespace}", "lookup", "${normalizedMode}"],
|
|
310
|
+
placementSourcePrefix: "ui-generator.${normalizedResourceNamespace}.${normalizedMode}.lookup"
|
|
311
|
+
});
|
|
312
|
+
const {
|
|
313
|
+
resolveLookupItems,
|
|
314
|
+
resolveLookupLoading,
|
|
315
|
+
resolveLookupSearch,
|
|
316
|
+
setLookupSearch
|
|
317
|
+
} = lookupFieldRuntime;
|
|
318
|
+
`;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
function buildLookupFormProps(fields = []) {
|
|
322
|
+
if (!hasLookupFormFields(fields)) {
|
|
323
|
+
return "";
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
return ` :resolve-lookup-items="resolveLookupItems"
|
|
327
|
+
:resolve-lookup-loading="resolveLookupLoading"
|
|
328
|
+
:resolve-lookup-search="resolveLookupSearch"
|
|
329
|
+
:set-lookup-search="setLookupSearch"`;
|
|
330
|
+
}
|
|
331
|
+
|
|
261
332
|
function filterDefaultHiddenListFields(selectedFieldKeys, fields, { recordIdFieldKey = "" } = {}) {
|
|
262
333
|
const selectedFields = Array.isArray(selectedFieldKeys) ? selectedFieldKeys : [];
|
|
263
334
|
const availableFields = Array.isArray(fields) ? fields : [];
|
|
@@ -485,6 +556,8 @@ async function buildUiTemplateContext({ appRoot, options } = {}) {
|
|
|
485
556
|
const menuMarker = hasListOperation
|
|
486
557
|
? `jskit:crud-ui-generator.page.link:${pageTarget.surfaceId}:${pageTarget.routeUrlSuffix}`
|
|
487
558
|
: "";
|
|
559
|
+
const createFormColumns = buildFormColumns(createFields);
|
|
560
|
+
const editFormColumns = buildFormColumns(editFields);
|
|
488
561
|
|
|
489
562
|
return {
|
|
490
563
|
__JSKIT_UI_RESOURCE_IMPORT_PATH__: `/${normalizeRelativeAppPath(options?.["resource-file"])}`,
|
|
@@ -508,12 +581,37 @@ async function buildUiTemplateContext({ appRoot, options } = {}) {
|
|
|
508
581
|
__JSKIT_UI_HAS_VIEW_ROUTE__: hasViewOperation ? "true" : "false",
|
|
509
582
|
__JSKIT_UI_HAS_NEW_ROUTE__: hasNewOperation ? "true" : "false",
|
|
510
583
|
__JSKIT_UI_HAS_EDIT_ROUTE__: hasEditOperation ? "true" : "false",
|
|
511
|
-
|
|
512
|
-
|
|
584
|
+
__JSKIT_UI_LIST_PAGE_VIEW_URL__: JSON.stringify(hasViewOperation ? `./:${normalizeText(options?.["id-param"]) || "recordId"}` : ""),
|
|
585
|
+
__JSKIT_UI_LIST_PAGE_EDIT_URL__: JSON.stringify(hasEditOperation ? `./:${normalizeText(options?.["id-param"]) || "recordId"}/edit` : ""),
|
|
586
|
+
__JSKIT_UI_LIST_PAGE_NEW_URL__: JSON.stringify(hasNewOperation ? "./new" : ""),
|
|
587
|
+
__JSKIT_UI_NEW_PAGE_LIST_URL__: JSON.stringify(hasListOperation ? ".." : ""),
|
|
588
|
+
__JSKIT_UI_NEW_PAGE_VIEW_URL__: JSON.stringify(hasViewOperation ? `../:${normalizeText(options?.["id-param"]) || "recordId"}` : ""),
|
|
589
|
+
__JSKIT_UI_EDIT_PAGE_LIST_URL__: JSON.stringify(hasListOperation ? "../.." : ""),
|
|
590
|
+
__JSKIT_UI_EDIT_PAGE_VIEW_URL__: JSON.stringify(hasViewOperation ? ".." : ""),
|
|
591
|
+
__JSKIT_UI_VIEW_PAGE_LIST_URL__: JSON.stringify(hasListOperation ? ".." : ""),
|
|
592
|
+
__JSKIT_UI_VIEW_PAGE_EDIT_URL__: JSON.stringify(hasEditOperation ? "./edit" : ""),
|
|
593
|
+
__JSKIT_UI_CREATE_FORM_COLUMNS__: createFormColumns,
|
|
594
|
+
__JSKIT_UI_EDIT_FORM_COLUMNS__: editFormColumns,
|
|
595
|
+
__JSKIT_UI_CREATE_FORM_COLUMNS_DIRECT__: rewriteGeneratedBlockIndent(createFormColumns, { trimPrefix: " " }),
|
|
596
|
+
__JSKIT_UI_EDIT_FORM_COLUMNS_DIRECT__: rewriteGeneratedBlockIndent(editFormColumns, { trimPrefix: " " }),
|
|
513
597
|
__JSKIT_UI_CREATE_FORM_FIELDS__: JSON.stringify(createFields),
|
|
514
598
|
__JSKIT_UI_EDIT_FORM_FIELDS__: JSON.stringify(editFields),
|
|
515
599
|
__JSKIT_UI_CREATE_FORM_FIELD_PUSH_LINES__: renderObjectPushLines("UI_CREATE_FORM_FIELDS", createFields),
|
|
516
600
|
__JSKIT_UI_EDIT_FORM_FIELD_PUSH_LINES__: renderObjectPushLines("UI_EDIT_FORM_FIELDS", editFields),
|
|
601
|
+
__JSKIT_UI_CREATE_LOOKUP_IMPORT_LINE__: buildLookupImportLine(createFields),
|
|
602
|
+
__JSKIT_UI_EDIT_LOOKUP_IMPORT_LINE__: buildLookupImportLine(editFields),
|
|
603
|
+
__JSKIT_UI_CREATE_LOOKUP_RUNTIME_SETUP__: buildLookupRuntimeSetup(createFields, {
|
|
604
|
+
formFieldsVariable: "UI_CREATE_FORM_FIELDS",
|
|
605
|
+
resourceNamespace,
|
|
606
|
+
mode: "new"
|
|
607
|
+
}),
|
|
608
|
+
__JSKIT_UI_EDIT_LOOKUP_RUNTIME_SETUP__: buildLookupRuntimeSetup(editFields, {
|
|
609
|
+
formFieldsVariable: "UI_EDIT_FORM_FIELDS",
|
|
610
|
+
resourceNamespace,
|
|
611
|
+
mode: "edit"
|
|
612
|
+
}),
|
|
613
|
+
__JSKIT_UI_CREATE_LOOKUP_FORM_PROPS__: buildLookupFormProps(createFields),
|
|
614
|
+
__JSKIT_UI_EDIT_LOOKUP_FORM_PROPS__: buildLookupFormProps(editFields),
|
|
517
615
|
__JSKIT_UI_MENU_MARKER__: menuMarker,
|
|
518
616
|
__JSKIT_UI_MENU_PLACEMENT_ID__: String(pageLinkTarget?.pageTarget?.placementId || ""),
|
|
519
617
|
__JSKIT_UI_MENU_PLACEMENT_TARGET__: String(pageLinkTarget?.placementTarget?.id || ""),
|
|
@@ -728,7 +728,18 @@ function escapeHtml(value) {
|
|
|
728
728
|
}
|
|
729
729
|
|
|
730
730
|
function serializeTemplateBindingValue(value) {
|
|
731
|
-
return JSON.stringify(value)
|
|
731
|
+
return JSON.stringify(value)
|
|
732
|
+
.replaceAll("&", "&")
|
|
733
|
+
.replaceAll('"', """)
|
|
734
|
+
.replaceAll("<", "<")
|
|
735
|
+
.replaceAll(">", ">")
|
|
736
|
+
.replaceAll("'", "\\u0027");
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
function renderTemplateJsStringLiteral(value) {
|
|
740
|
+
return `'${String(value || "")
|
|
741
|
+
.replaceAll("\\", "\\\\")
|
|
742
|
+
.replaceAll("'", "\\'")}'`;
|
|
732
743
|
}
|
|
733
744
|
|
|
734
745
|
function buildListHeaderColumns(fields = []) {
|
|
@@ -822,84 +833,93 @@ function buildFormColumns(fields = []) {
|
|
|
822
833
|
}
|
|
823
834
|
|
|
824
835
|
const label = escapeHtml(field?.label || toFieldLabel(key));
|
|
825
|
-
const formAccessor = toAccessorExpression("
|
|
826
|
-
const
|
|
836
|
+
const formAccessor = toAccessorExpression("formState", key);
|
|
837
|
+
const fieldKeyLiteral = renderTemplateJsStringLiteral(key);
|
|
838
|
+
const fieldErrorExpression = `resolveFieldErrors(${fieldKeyLiteral})`;
|
|
827
839
|
const component = normalizeText(field?.component).toLowerCase();
|
|
828
840
|
if (component === "switch") {
|
|
829
|
-
return `
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
841
|
+
return ` <v-col cols="12" md="6">
|
|
842
|
+
<v-switch
|
|
843
|
+
v-model="${formAccessor}"
|
|
844
|
+
label="${label}"
|
|
845
|
+
color="primary"
|
|
846
|
+
hide-details="auto"
|
|
847
|
+
:disabled="addEdit.isFieldLocked"
|
|
848
|
+
:error-messages="${fieldErrorExpression}"
|
|
849
|
+
/>
|
|
850
|
+
</v-col>`;
|
|
839
851
|
}
|
|
840
852
|
|
|
841
853
|
if (component === "select") {
|
|
842
854
|
const selectOptions = Array.isArray(field?.options) ? field.options : [];
|
|
843
|
-
return `
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
855
|
+
return ` <v-col cols="12" md="6">
|
|
856
|
+
<v-select
|
|
857
|
+
v-model="${formAccessor}"
|
|
858
|
+
label="${label}"
|
|
859
|
+
variant="outlined"
|
|
860
|
+
density="comfortable"
|
|
861
|
+
:items="${serializeTemplateBindingValue(selectOptions)}"
|
|
862
|
+
item-title="label"
|
|
863
|
+
item-value="value"
|
|
864
|
+
:disabled="addEdit.isFieldLocked"
|
|
865
|
+
:clearable="${field.nullable === true ? "true" : "false"}"
|
|
866
|
+
:error-messages="${fieldErrorExpression}"
|
|
867
|
+
/>
|
|
868
|
+
</v-col>`;
|
|
857
869
|
}
|
|
858
870
|
|
|
859
871
|
if (component === "lookup") {
|
|
860
872
|
const lookupFormControl = field?.lookupFormControl === "select" ? "select" : "autocomplete";
|
|
861
873
|
const useAutocomplete = lookupFormControl !== "select";
|
|
862
874
|
const lookupComponentTag = useAutocomplete ? "v-autocomplete" : "v-select";
|
|
863
|
-
const
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
875
|
+
const lookupAttributeLines = [
|
|
876
|
+
` :items="resolveLookupItems(${fieldKeyLiteral}, { selectedValue: ${formAccessor}, selectedRecord: addEdit.resource.data })"`
|
|
877
|
+
];
|
|
878
|
+
if (useAutocomplete) {
|
|
879
|
+
lookupAttributeLines.push(
|
|
880
|
+
` :search="resolveLookupSearch(${fieldKeyLiteral})"`,
|
|
881
|
+
` @update:search="setLookupSearch(${fieldKeyLiteral}, $event)"`
|
|
882
|
+
);
|
|
883
|
+
}
|
|
884
|
+
lookupAttributeLines.push(
|
|
885
|
+
` item-title="label"`,
|
|
886
|
+
` item-value="value"`
|
|
887
|
+
);
|
|
888
|
+
if (useAutocomplete) {
|
|
889
|
+
lookupAttributeLines.push(" no-filter");
|
|
890
|
+
}
|
|
891
|
+
return ` <v-col cols="12" md="6">
|
|
892
|
+
<${lookupComponentTag}
|
|
893
|
+
v-model="${formAccessor}"
|
|
894
|
+
label="${label}"
|
|
895
|
+
variant="outlined"
|
|
896
|
+
density="comfortable"
|
|
897
|
+
autocomplete="off"
|
|
898
|
+
${lookupAttributeLines.join("\n")}
|
|
899
|
+
:loading="resolveLookupLoading(${fieldKeyLiteral})"
|
|
900
|
+
:disabled="addEdit.isFieldLocked"
|
|
901
|
+
:clearable="${field.nullable === true ? "true" : "false"}"
|
|
902
|
+
:error-messages="${fieldErrorExpression}"
|
|
903
|
+
/>
|
|
904
|
+
</v-col>`;
|
|
885
905
|
}
|
|
886
906
|
|
|
887
907
|
const inputType = normalizeText(field?.inputType) || "text";
|
|
888
908
|
const maxLength = Number.isInteger(field?.maxLength) && field.maxLength > 0
|
|
889
909
|
? String(field.maxLength)
|
|
890
910
|
: "undefined";
|
|
891
|
-
return `
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
911
|
+
return ` <v-col cols="12" md="6">
|
|
912
|
+
<v-text-field
|
|
913
|
+
v-model="${formAccessor}"
|
|
914
|
+
label="${label}"
|
|
915
|
+
type="${escapeHtml(inputType)}"
|
|
916
|
+
variant="outlined"
|
|
917
|
+
density="comfortable"
|
|
918
|
+
:maxlength="${maxLength}"
|
|
919
|
+
:readonly="addEdit.isFieldLocked"
|
|
920
|
+
:error-messages="${fieldErrorExpression}"
|
|
921
|
+
/>
|
|
922
|
+
</v-col>`;
|
|
903
923
|
})
|
|
904
924
|
.filter(Boolean)
|
|
905
925
|
.join("\n");
|
|
@@ -12,9 +12,9 @@
|
|
|
12
12
|
<v-btn v-if="cancelTo" variant="tonal" :to="resolveCancelTo(cancelTo)">Cancel</v-btn>
|
|
13
13
|
<v-btn
|
|
14
14
|
color="primary"
|
|
15
|
-
:loading="
|
|
16
|
-
:disabled="
|
|
17
|
-
@click="
|
|
15
|
+
:loading="addEdit.isSaving"
|
|
16
|
+
:disabled="addEdit.isSubmitDisabled"
|
|
17
|
+
@click="addEdit.submit"
|
|
18
18
|
>
|
|
19
19
|
{{ saveLabel }}
|
|
20
20
|
</v-btn>
|
|
@@ -23,14 +23,14 @@
|
|
|
23
23
|
</v-card-item>
|
|
24
24
|
|
|
25
25
|
<v-card-text class="pt-0">
|
|
26
|
-
<p v-if="
|
|
27
|
-
{{
|
|
26
|
+
<p v-if="addEdit.loadError" class="text-body-2 text-medium-emphasis mb-0">
|
|
27
|
+
{{ addEdit.loadError }}
|
|
28
28
|
</p>
|
|
29
29
|
<template v-else-if="formRuntime.showFormSkeleton">
|
|
30
30
|
<v-skeleton-loader type="heading, text@2, article" />
|
|
31
31
|
</template>
|
|
32
|
-
<v-form v-else @submit.prevent="
|
|
33
|
-
<v-progress-linear v-if="
|
|
32
|
+
<v-form v-else @submit.prevent="addEdit.submit" novalidate>
|
|
33
|
+
<v-progress-linear v-if="addEdit.isRefetching" indeterminate class="mb-4" />
|
|
34
34
|
<v-row>
|
|
35
35
|
<template v-if="mode === 'new'">
|
|
36
36
|
<!-- jskit:crud-ui-fields:new -->
|
|
@@ -91,6 +91,14 @@ const props = defineProps({
|
|
|
91
91
|
}
|
|
92
92
|
});
|
|
93
93
|
|
|
94
|
+
const formRuntime = props.formRuntime;
|
|
95
|
+
const addEdit = formRuntime.addEdit;
|
|
96
|
+
const formState = formRuntime.form;
|
|
97
|
+
|
|
98
|
+
function resolveFieldErrors(fieldKey) {
|
|
99
|
+
return formRuntime.resolveFieldErrors(fieldKey);
|
|
100
|
+
}
|
|
101
|
+
|
|
94
102
|
function resolveCancelTo(target) {
|
|
95
103
|
if (!target) {
|
|
96
104
|
return "";
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
<v-progress-linear v-if="formRuntime.addEdit.isRefetching" indeterminate class="mb-4" />
|
|
38
38
|
<v-row>
|
|
39
39
|
<!-- jskit:crud-ui-fields:edit -->
|
|
40
|
-
|
|
40
|
+
__JSKIT_UI_EDIT_FORM_COLUMNS_DIRECT__
|
|
41
41
|
</v-row>
|
|
42
42
|
</v-form>
|
|
43
43
|
</v-card-text>
|
|
@@ -49,15 +49,15 @@ __JSKIT_UI_EDIT_FORM_COLUMNS__
|
|
|
49
49
|
import { computed } from "vue";
|
|
50
50
|
import { useRoute } from "vue-router";
|
|
51
51
|
import { useCrudAddEdit } from "@jskit-ai/users-web/client/composables/useCrudAddEdit";
|
|
52
|
-
|
|
52
|
+
__JSKIT_UI_EDIT_LOOKUP_IMPORT_LINE__
|
|
53
53
|
import { resource as uiResource } from "__JSKIT_UI_RESOURCE_IMPORT_PATH__";
|
|
54
54
|
|
|
55
55
|
const UI_OPERATION_ADAPTER = null;
|
|
56
56
|
const UI_RECORD_ID_PARAM = "__JSKIT_UI_RECORD_ID_PARAM__";
|
|
57
57
|
const UI_API_BASE_URL = "__JSKIT_UI_API_BASE_URL__";
|
|
58
58
|
const UI_EDIT_API_URL = `${UI_API_BASE_URL}/:${UI_RECORD_ID_PARAM}`;
|
|
59
|
-
const UI_LIST_URL =
|
|
60
|
-
const UI_VIEW_URL =
|
|
59
|
+
const UI_LIST_URL = __JSKIT_UI_EDIT_PAGE_LIST_URL__;
|
|
60
|
+
const UI_VIEW_URL = __JSKIT_UI_EDIT_PAGE_VIEW_URL__;
|
|
61
61
|
const UI_CANCEL_URL = UI_VIEW_URL || UI_LIST_URL;
|
|
62
62
|
const UI_RECORD_CHANGED_EVENT = __JSKIT_UI_RECORD_CHANGED_EVENT__;
|
|
63
63
|
const UI_EDIT_FORM_FIELDS = [];
|
|
@@ -79,20 +79,7 @@ const routeRecordId = computed(() => {
|
|
|
79
79
|
return String(source ?? "").trim();
|
|
80
80
|
});
|
|
81
81
|
|
|
82
|
-
|
|
83
|
-
formFields: UI_EDIT_FORM_FIELDS,
|
|
84
|
-
adapter: UI_OPERATION_ADAPTER || undefined,
|
|
85
|
-
recordIdParam: UI_RECORD_ID_PARAM,
|
|
86
|
-
lookupContainerKey: uiResource?.contract?.lookup?.containerKey,
|
|
87
|
-
queryKeyPrefix: ["ui-generator", "__JSKIT_UI_RESOURCE_NAMESPACE__", "lookup", "edit"],
|
|
88
|
-
placementSourcePrefix: "ui-generator.__JSKIT_UI_RESOURCE_NAMESPACE__.edit.lookup"
|
|
89
|
-
});
|
|
90
|
-
const {
|
|
91
|
-
resolveLookupItems,
|
|
92
|
-
resolveLookupLoading,
|
|
93
|
-
resolveLookupSearch,
|
|
94
|
-
setLookupSearch
|
|
95
|
-
} = lookupFieldRuntime;
|
|
82
|
+
__JSKIT_UI_EDIT_LOOKUP_RUNTIME_SETUP__
|
|
96
83
|
|
|
97
84
|
const formRuntime = useCrudAddEdit({
|
|
98
85
|
resource: uiResource,
|
|
@@ -128,4 +115,10 @@ const formRuntime = useCrudAddEdit({
|
|
|
128
115
|
listUrlTemplate: UI_LIST_URL
|
|
129
116
|
}
|
|
130
117
|
});
|
|
118
|
+
const addEdit = formRuntime.addEdit;
|
|
119
|
+
const formState = formRuntime.form;
|
|
120
|
+
|
|
121
|
+
function resolveFieldErrors(fieldKey) {
|
|
122
|
+
return formRuntime.resolveFieldErrors(fieldKey);
|
|
123
|
+
}
|
|
131
124
|
</script>
|
|
@@ -6,10 +6,7 @@
|
|
|
6
6
|
subtitle="Update the selected __JSKIT_UI_RESOURCE_SINGULAR_TITLE__."
|
|
7
7
|
save-label="Save changes"
|
|
8
8
|
:cancel-to="cancelTo"
|
|
9
|
-
|
|
10
|
-
:resolve-lookup-loading="resolveLookupLoading"
|
|
11
|
-
:resolve-lookup-search="resolveLookupSearch"
|
|
12
|
-
:set-lookup-search="setLookupSearch"
|
|
9
|
+
__JSKIT_UI_EDIT_LOOKUP_FORM_PROPS__
|
|
13
10
|
/>
|
|
14
11
|
</template>
|
|
15
12
|
|
|
@@ -17,7 +14,7 @@
|
|
|
17
14
|
import { computed } from "vue";
|
|
18
15
|
import { useRoute } from "vue-router";
|
|
19
16
|
import { useCrudAddEdit } from "@jskit-ai/users-web/client/composables/useCrudAddEdit";
|
|
20
|
-
|
|
17
|
+
__JSKIT_UI_EDIT_LOOKUP_IMPORT_LINE__
|
|
21
18
|
import { resource as uiResource } from "__JSKIT_UI_RESOURCE_IMPORT_PATH__";
|
|
22
19
|
import CrudAddEditForm from "../_components/__JSKIT_UI_FORM_COMPONENT_FILE__";
|
|
23
20
|
import { UI_EDIT_FORM_FIELDS } from "../_components/__JSKIT_UI_FORM_FIELDS_FILE__";
|
|
@@ -26,8 +23,8 @@ const UI_OPERATION_ADAPTER = null;
|
|
|
26
23
|
const UI_RECORD_ID_PARAM = "__JSKIT_UI_RECORD_ID_PARAM__";
|
|
27
24
|
const UI_API_BASE_URL = "__JSKIT_UI_API_BASE_URL__";
|
|
28
25
|
const UI_EDIT_API_URL = `${UI_API_BASE_URL}/:${UI_RECORD_ID_PARAM}`;
|
|
29
|
-
const UI_LIST_URL =
|
|
30
|
-
const UI_VIEW_URL =
|
|
26
|
+
const UI_LIST_URL = __JSKIT_UI_EDIT_PAGE_LIST_URL__;
|
|
27
|
+
const UI_VIEW_URL = __JSKIT_UI_EDIT_PAGE_VIEW_URL__;
|
|
31
28
|
const UI_CANCEL_URL = UI_VIEW_URL || UI_LIST_URL;
|
|
32
29
|
const UI_RECORD_CHANGED_EVENT = __JSKIT_UI_RECORD_CHANGED_EVENT__;
|
|
33
30
|
const route = useRoute();
|
|
@@ -44,20 +41,7 @@ const routeRecordId = computed(() => {
|
|
|
44
41
|
return String(source ?? "").trim();
|
|
45
42
|
});
|
|
46
43
|
|
|
47
|
-
|
|
48
|
-
formFields: UI_EDIT_FORM_FIELDS,
|
|
49
|
-
adapter: UI_OPERATION_ADAPTER || undefined,
|
|
50
|
-
recordIdParam: UI_RECORD_ID_PARAM,
|
|
51
|
-
lookupContainerKey: uiResource?.contract?.lookup?.containerKey,
|
|
52
|
-
queryKeyPrefix: ["ui-generator", "__JSKIT_UI_RESOURCE_NAMESPACE__", "lookup", "edit"],
|
|
53
|
-
placementSourcePrefix: "ui-generator.__JSKIT_UI_RESOURCE_NAMESPACE__.edit.lookup"
|
|
54
|
-
});
|
|
55
|
-
const {
|
|
56
|
-
resolveLookupItems,
|
|
57
|
-
resolveLookupLoading,
|
|
58
|
-
resolveLookupSearch,
|
|
59
|
-
setLookupSearch
|
|
60
|
-
} = lookupFieldRuntime;
|
|
44
|
+
__JSKIT_UI_EDIT_LOOKUP_RUNTIME_SETUP__
|
|
61
45
|
|
|
62
46
|
const formRuntime = useCrudAddEdit({
|
|
63
47
|
resource: uiResource,
|
|
@@ -40,8 +40,8 @@
|
|
|
40
40
|
<tr>
|
|
41
41
|
__JSKIT_UI_LIST_HEADER_COLUMNS__
|
|
42
42
|
<!-- jskit:crud-ui-fields:list-header -->
|
|
43
|
-
<th v-if="UI_VIEW_URL" class="text-right"
|
|
44
|
-
<th v-if="UI_EDIT_URL" class="text-right"
|
|
43
|
+
<th v-if="UI_VIEW_URL" class="text-right" />
|
|
44
|
+
<th v-if="UI_EDIT_URL" class="text-right" />
|
|
45
45
|
</tr>
|
|
46
46
|
</thead>
|
|
47
47
|
<tbody>
|
|
@@ -90,9 +90,9 @@ import { resource as uiResource } from "__JSKIT_UI_RESOURCE_IMPORT_PATH__";
|
|
|
90
90
|
const UI_OPERATION_ADAPTER = null;
|
|
91
91
|
const UI_RECORD_ID_PARAM = "__JSKIT_UI_RECORD_ID_PARAM__";
|
|
92
92
|
const UI_LIST_API_URL = "__JSKIT_UI_API_BASE_URL__";
|
|
93
|
-
const UI_VIEW_URL =
|
|
94
|
-
const UI_EDIT_URL =
|
|
95
|
-
const UI_NEW_URL =
|
|
93
|
+
const UI_VIEW_URL = __JSKIT_UI_LIST_PAGE_VIEW_URL__;
|
|
94
|
+
const UI_EDIT_URL = __JSKIT_UI_LIST_PAGE_EDIT_URL__;
|
|
95
|
+
const UI_NEW_URL = __JSKIT_UI_LIST_PAGE_NEW_URL__;
|
|
96
96
|
const UI_RECORD_CHANGED_EVENTS = __JSKIT_UI_LIST_REALTIME_EVENTS__;
|
|
97
97
|
const UI_ROUTE_QUERY_BLACKLIST = Object.freeze(["include", "cursor", "limit"]);
|
|
98
98
|
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
<v-form v-else @submit.prevent="formRuntime.addEdit.submit" novalidate>
|
|
28
28
|
<v-row>
|
|
29
29
|
<!-- jskit:crud-ui-fields:new -->
|
|
30
|
-
|
|
30
|
+
__JSKIT_UI_CREATE_FORM_COLUMNS_DIRECT__
|
|
31
31
|
</v-row>
|
|
32
32
|
</v-form>
|
|
33
33
|
</v-card-text>
|
|
@@ -37,14 +37,14 @@ __JSKIT_UI_CREATE_FORM_COLUMNS__
|
|
|
37
37
|
|
|
38
38
|
<script setup>
|
|
39
39
|
import { useCrudAddEdit } from "@jskit-ai/users-web/client/composables/useCrudAddEdit";
|
|
40
|
-
|
|
40
|
+
__JSKIT_UI_CREATE_LOOKUP_IMPORT_LINE__
|
|
41
41
|
import { resource as uiResource } from "__JSKIT_UI_RESOURCE_IMPORT_PATH__";
|
|
42
42
|
|
|
43
43
|
const UI_OPERATION_ADAPTER = null;
|
|
44
44
|
const UI_RECORD_ID_PARAM = "__JSKIT_UI_RECORD_ID_PARAM__";
|
|
45
45
|
const UI_CREATE_API_URL = "__JSKIT_UI_API_BASE_URL__";
|
|
46
|
-
const UI_LIST_URL =
|
|
47
|
-
const UI_VIEW_URL =
|
|
46
|
+
const UI_LIST_URL = __JSKIT_UI_NEW_PAGE_LIST_URL__;
|
|
47
|
+
const UI_VIEW_URL = __JSKIT_UI_NEW_PAGE_VIEW_URL__;
|
|
48
48
|
const UI_RECORD_CHANGED_EVENT = __JSKIT_UI_RECORD_CHANGED_EVENT__;
|
|
49
49
|
const UI_CREATE_FORM_FIELDS = [];
|
|
50
50
|
|
|
@@ -54,20 +54,7 @@ void UI_CREATE_FORM_FIELDS;
|
|
|
54
54
|
__JSKIT_UI_CREATE_FORM_FIELD_PUSH_LINES__
|
|
55
55
|
Object.freeze(UI_CREATE_FORM_FIELDS);
|
|
56
56
|
|
|
57
|
-
|
|
58
|
-
formFields: UI_CREATE_FORM_FIELDS,
|
|
59
|
-
adapter: UI_OPERATION_ADAPTER || undefined,
|
|
60
|
-
recordIdParam: UI_RECORD_ID_PARAM,
|
|
61
|
-
lookupContainerKey: uiResource?.contract?.lookup?.containerKey,
|
|
62
|
-
queryKeyPrefix: ["ui-generator", "__JSKIT_UI_RESOURCE_NAMESPACE__", "lookup", "new"],
|
|
63
|
-
placementSourcePrefix: "ui-generator.__JSKIT_UI_RESOURCE_NAMESPACE__.new.lookup"
|
|
64
|
-
});
|
|
65
|
-
const {
|
|
66
|
-
resolveLookupItems,
|
|
67
|
-
resolveLookupLoading,
|
|
68
|
-
resolveLookupSearch,
|
|
69
|
-
setLookupSearch
|
|
70
|
-
} = lookupFieldRuntime;
|
|
57
|
+
__JSKIT_UI_CREATE_LOOKUP_RUNTIME_SETUP__
|
|
71
58
|
|
|
72
59
|
const formRuntime = useCrudAddEdit({
|
|
73
60
|
resource: uiResource,
|
|
@@ -101,4 +88,10 @@ const formRuntime = useCrudAddEdit({
|
|
|
101
88
|
listUrlTemplate: UI_LIST_URL
|
|
102
89
|
}
|
|
103
90
|
});
|
|
91
|
+
const addEdit = formRuntime.addEdit;
|
|
92
|
+
const formState = formRuntime.form;
|
|
93
|
+
|
|
94
|
+
function resolveFieldErrors(fieldKey) {
|
|
95
|
+
return formRuntime.resolveFieldErrors(fieldKey);
|
|
96
|
+
}
|
|
104
97
|
</script>
|
|
@@ -6,16 +6,13 @@
|
|
|
6
6
|
subtitle="Create a new __JSKIT_UI_RESOURCE_SINGULAR_TITLE__."
|
|
7
7
|
save-label="Save __JSKIT_UI_RESOURCE_SINGULAR_TITLE__"
|
|
8
8
|
:cancel-to="UI_CANCEL_URL"
|
|
9
|
-
|
|
10
|
-
:resolve-lookup-loading="resolveLookupLoading"
|
|
11
|
-
:resolve-lookup-search="resolveLookupSearch"
|
|
12
|
-
:set-lookup-search="setLookupSearch"
|
|
9
|
+
__JSKIT_UI_CREATE_LOOKUP_FORM_PROPS__
|
|
13
10
|
/>
|
|
14
11
|
</template>
|
|
15
12
|
|
|
16
13
|
<script setup>
|
|
17
14
|
import { useCrudAddEdit } from "@jskit-ai/users-web/client/composables/useCrudAddEdit";
|
|
18
|
-
|
|
15
|
+
__JSKIT_UI_CREATE_LOOKUP_IMPORT_LINE__
|
|
19
16
|
import { resource as uiResource } from "__JSKIT_UI_RESOURCE_IMPORT_PATH__";
|
|
20
17
|
import CrudAddEditForm from "./_components/__JSKIT_UI_FORM_COMPONENT_FILE__";
|
|
21
18
|
import { UI_CREATE_FORM_FIELDS } from "./_components/__JSKIT_UI_FORM_FIELDS_FILE__";
|
|
@@ -23,28 +20,15 @@ import { UI_CREATE_FORM_FIELDS } from "./_components/__JSKIT_UI_FORM_FIELDS_FILE
|
|
|
23
20
|
const UI_OPERATION_ADAPTER = null;
|
|
24
21
|
const UI_RECORD_ID_PARAM = "__JSKIT_UI_RECORD_ID_PARAM__";
|
|
25
22
|
const UI_CREATE_API_URL = "__JSKIT_UI_API_BASE_URL__";
|
|
26
|
-
const UI_LIST_URL =
|
|
27
|
-
const UI_VIEW_URL =
|
|
23
|
+
const UI_LIST_URL = __JSKIT_UI_NEW_PAGE_LIST_URL__;
|
|
24
|
+
const UI_VIEW_URL = __JSKIT_UI_NEW_PAGE_VIEW_URL__;
|
|
28
25
|
const UI_CANCEL_URL = UI_LIST_URL;
|
|
29
26
|
const UI_RECORD_CHANGED_EVENT = __JSKIT_UI_RECORD_CHANGED_EVENT__;
|
|
30
27
|
|
|
31
28
|
// jskit:crud-ui-fields-target ./_components/__JSKIT_UI_FORM_COMPONENT_FILE__
|
|
32
29
|
// jskit:crud-ui-form-fields-target ./_components/__JSKIT_UI_FORM_FIELDS_FILE__
|
|
33
30
|
|
|
34
|
-
|
|
35
|
-
formFields: UI_CREATE_FORM_FIELDS,
|
|
36
|
-
adapter: UI_OPERATION_ADAPTER || undefined,
|
|
37
|
-
recordIdParam: UI_RECORD_ID_PARAM,
|
|
38
|
-
lookupContainerKey: uiResource?.contract?.lookup?.containerKey,
|
|
39
|
-
queryKeyPrefix: ["ui-generator", "__JSKIT_UI_RESOURCE_NAMESPACE__", "lookup", "new"],
|
|
40
|
-
placementSourcePrefix: "ui-generator.__JSKIT_UI_RESOURCE_NAMESPACE__.new.lookup"
|
|
41
|
-
});
|
|
42
|
-
const {
|
|
43
|
-
resolveLookupItems,
|
|
44
|
-
resolveLookupLoading,
|
|
45
|
-
resolveLookupSearch,
|
|
46
|
-
setLookupSearch
|
|
47
|
-
} = lookupFieldRuntime;
|
|
31
|
+
__JSKIT_UI_CREATE_LOOKUP_RUNTIME_SETUP__
|
|
48
32
|
|
|
49
33
|
const formRuntime = useCrudAddEdit({
|
|
50
34
|
resource: uiResource,
|
|
@@ -61,8 +61,8 @@ const UI_OPERATION_ADAPTER = null;
|
|
|
61
61
|
const UI_RECORD_ID_PARAM = "__JSKIT_UI_RECORD_ID_PARAM__";
|
|
62
62
|
const UI_API_BASE_URL = "__JSKIT_UI_API_BASE_URL__";
|
|
63
63
|
const UI_VIEW_API_URL = `${UI_API_BASE_URL}/:${UI_RECORD_ID_PARAM}`;
|
|
64
|
-
const UI_LIST_URL =
|
|
65
|
-
const UI_EDIT_URL =
|
|
64
|
+
const UI_LIST_URL = __JSKIT_UI_VIEW_PAGE_LIST_URL__;
|
|
65
|
+
const UI_EDIT_URL = __JSKIT_UI_VIEW_PAGE_EDIT_URL__;
|
|
66
66
|
const UI_VIEW_TITLE_FALLBACK_FIELD_KEY = __JSKIT_UI_VIEW_TITLE_FALLBACK_FIELD_KEY__;
|
|
67
67
|
const UI_RECORD_CHANGED_EVENT = __JSKIT_UI_RECORD_CHANGED_EVENT__;
|
|
68
68
|
|
|
@@ -130,12 +130,16 @@ UI_EDIT_FORM_FIELDS.push({ key: "firstName", component: "text" });
|
|
|
130
130
|
const editSource = await readFile(path.join(appRoot, editFile), "utf8");
|
|
131
131
|
assert.match(
|
|
132
132
|
editSource,
|
|
133
|
-
/<v-autocomplete[\s\S]*resolveLookupItems\(
|
|
133
|
+
/<v-autocomplete[\s\S]*resolveLookupItems\('vetId', \{ selectedValue: formState\.vetId, selectedRecord: addEdit\.resource\.data \}\)/
|
|
134
134
|
);
|
|
135
|
-
assert.match(editSource, /:items=
|
|
136
|
-
assert.match(
|
|
137
|
-
|
|
138
|
-
|
|
135
|
+
assert.match(editSource, /:items="resolveLookupItems\('vetId', \{ selectedValue: formState\.vetId, selectedRecord: addEdit\.resource\.data \}\)"/);
|
|
136
|
+
assert.match(
|
|
137
|
+
editSource,
|
|
138
|
+
/\n {18}:items="resolveLookupItems\('vetId', \{ selectedValue: formState\.vetId, selectedRecord: addEdit\.resource\.data \}\)"\n {18}:search="resolveLookupSearch\('vetId'\)"\n {18}@update:search="setLookupSearch\('vetId', \$event\)"\n {18}item-title="label"\n {18}item-value="value"\n {18}no-filter/
|
|
139
|
+
);
|
|
140
|
+
assert.match(editSource, /:search="resolveLookupSearch\('vetId'\)"/);
|
|
141
|
+
assert.match(editSource, /@update:search="setLookupSearch\('vetId', \$event\)"/);
|
|
142
|
+
assert.match(editSource, /:loading="resolveLookupLoading\('vetId'\)"/);
|
|
139
143
|
assert.match(editSource, /UI_EDIT_FORM_FIELDS\.push\(\{[\s\S]*"key": "vetId"/);
|
|
140
144
|
|
|
141
145
|
const second = await runGeneratorSubcommand({
|
|
@@ -200,7 +204,7 @@ UI_EDIT_FORM_FIELDS.push({ key: "firstName", component: "text" });
|
|
|
200
204
|
const addEditFormSource = await readFile(path.join(appRoot, addEditFormFile), "utf8");
|
|
201
205
|
assert.match(
|
|
202
206
|
addEditFormSource,
|
|
203
|
-
/<v-autocomplete[\s\S]*resolveLookupItems\(
|
|
207
|
+
/<v-autocomplete[\s\S]*resolveLookupItems\('vetId', \{ selectedValue: formState\.vetId, selectedRecord: addEdit\.resource\.data \}\)/
|
|
204
208
|
);
|
|
205
209
|
|
|
206
210
|
const addEditFieldsSource = await readFile(path.join(appRoot, addEditFieldsFile), "utf8");
|
|
@@ -282,7 +286,7 @@ const UI_EDIT_FORM_FIELDS = [];
|
|
|
282
286
|
});
|
|
283
287
|
|
|
284
288
|
const addEditFormSource = await readFile(path.join(appRoot, addEditFormFile), "utf8");
|
|
285
|
-
assert.equal((addEditFormSource.match(/resolveLookupItems\(
|
|
289
|
+
assert.equal((addEditFormSource.match(/resolveLookupItems\('vetId'/g) || []).length, 2);
|
|
286
290
|
|
|
287
291
|
const addEditFieldsSource = await readFile(path.join(appRoot, addEditFieldsFile), "utf8");
|
|
288
292
|
assert.match(addEditFieldsSource, /UI_CREATE_FORM_FIELDS\.push\(\{[\s\S]*"key": "vetId"/);
|
|
@@ -190,6 +190,82 @@ const resource = {
|
|
|
190
190
|
export { resource };
|
|
191
191
|
`;
|
|
192
192
|
|
|
193
|
+
const SELECT_RESOURCE_SOURCE = `const recordSchema = {
|
|
194
|
+
type: "object",
|
|
195
|
+
properties: {
|
|
196
|
+
id: { type: "integer" },
|
|
197
|
+
type: { type: "string" }
|
|
198
|
+
},
|
|
199
|
+
additionalProperties: false
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
const bodySchema = {
|
|
203
|
+
type: "object",
|
|
204
|
+
properties: {
|
|
205
|
+
type: { type: "string", enum: ["dryer", "pallet racking", "freezer", "coolroom"] }
|
|
206
|
+
},
|
|
207
|
+
additionalProperties: false
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
const resource = {
|
|
211
|
+
namespace: "locations",
|
|
212
|
+
operations: {
|
|
213
|
+
list: {
|
|
214
|
+
outputValidator: {
|
|
215
|
+
schema: {
|
|
216
|
+
type: "object",
|
|
217
|
+
properties: {
|
|
218
|
+
items: {
|
|
219
|
+
type: "array",
|
|
220
|
+
items: recordSchema
|
|
221
|
+
},
|
|
222
|
+
nextCursor: { type: ["string", "null"] }
|
|
223
|
+
},
|
|
224
|
+
additionalProperties: false
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
},
|
|
228
|
+
view: {
|
|
229
|
+
outputValidator: {
|
|
230
|
+
schema: recordSchema
|
|
231
|
+
}
|
|
232
|
+
},
|
|
233
|
+
create: {
|
|
234
|
+
bodyValidator: {
|
|
235
|
+
schema: bodySchema
|
|
236
|
+
},
|
|
237
|
+
outputValidator: {
|
|
238
|
+
schema: recordSchema
|
|
239
|
+
}
|
|
240
|
+
},
|
|
241
|
+
patch: {
|
|
242
|
+
bodyValidator: {
|
|
243
|
+
schema: bodySchema
|
|
244
|
+
},
|
|
245
|
+
outputValidator: {
|
|
246
|
+
schema: recordSchema
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
},
|
|
250
|
+
fieldMeta: [
|
|
251
|
+
{
|
|
252
|
+
key: "type",
|
|
253
|
+
ui: {
|
|
254
|
+
formControl: "select",
|
|
255
|
+
options: [
|
|
256
|
+
{ value: "dryer", label: "Dryer" },
|
|
257
|
+
{ value: "pallet racking", label: "Pallet Racking" },
|
|
258
|
+
{ value: "freezer", label: "Freezer" },
|
|
259
|
+
{ value: "coolroom", label: "Coolroom" }
|
|
260
|
+
]
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
]
|
|
264
|
+
};
|
|
265
|
+
|
|
266
|
+
export { resource };
|
|
267
|
+
`;
|
|
268
|
+
|
|
193
269
|
const LOOKUP_RESOURCE_SOURCE = `const recordSchema = {
|
|
194
270
|
type: "object",
|
|
195
271
|
properties: {
|
|
@@ -325,11 +401,20 @@ test("buildUiTemplateContext derives CRUD placeholders from the explicit target-
|
|
|
325
401
|
assert.match(context.__JSKIT_UI_LIST_HEADER_COLUMNS__, /First Name/);
|
|
326
402
|
assert.match(context.__JSKIT_UI_LIST_ROW_COLUMNS__, /record\.firstName/);
|
|
327
403
|
assert.match(context.__JSKIT_UI_VIEW_COLUMNS__, /view\.record\?\.firstName/);
|
|
328
|
-
assert.match(context.__JSKIT_UI_CREATE_FORM_COLUMNS__, /
|
|
329
|
-
assert.match(context.__JSKIT_UI_EDIT_FORM_COLUMNS__, /
|
|
404
|
+
assert.match(context.__JSKIT_UI_CREATE_FORM_COLUMNS__, /formState\.firstName/);
|
|
405
|
+
assert.match(context.__JSKIT_UI_EDIT_FORM_COLUMNS__, /formState\.email/);
|
|
330
406
|
assert.equal(context.__JSKIT_UI_RECORD_CHANGED_EVENT__, "\"customers.record.changed\"");
|
|
331
407
|
assert.equal(context.__JSKIT_UI_LIST_RECORD_ID_EXPR__, "item.id");
|
|
332
408
|
assert.equal(context.__JSKIT_UI_VIEW_TITLE_FALLBACK_FIELD_KEY__, "\"firstName\"");
|
|
409
|
+
assert.equal(context.__JSKIT_UI_LIST_PAGE_VIEW_URL__, "\"./:customerId\"");
|
|
410
|
+
assert.equal(context.__JSKIT_UI_LIST_PAGE_EDIT_URL__, "\"./:customerId/edit\"");
|
|
411
|
+
assert.equal(context.__JSKIT_UI_LIST_PAGE_NEW_URL__, "\"./new\"");
|
|
412
|
+
assert.equal(context.__JSKIT_UI_NEW_PAGE_LIST_URL__, "\"..\"");
|
|
413
|
+
assert.equal(context.__JSKIT_UI_NEW_PAGE_VIEW_URL__, "\"../:customerId\"");
|
|
414
|
+
assert.equal(context.__JSKIT_UI_EDIT_PAGE_LIST_URL__, "\"../..\"");
|
|
415
|
+
assert.equal(context.__JSKIT_UI_EDIT_PAGE_VIEW_URL__, "\"..\"");
|
|
416
|
+
assert.equal(context.__JSKIT_UI_VIEW_PAGE_LIST_URL__, "\"..\"");
|
|
417
|
+
assert.equal(context.__JSKIT_UI_VIEW_PAGE_EDIT_URL__, "\"./edit\"");
|
|
333
418
|
});
|
|
334
419
|
});
|
|
335
420
|
|
|
@@ -343,7 +428,7 @@ test("buildUiTemplateContext keeps non-nullable booleans as switches", async ()
|
|
|
343
428
|
});
|
|
344
429
|
|
|
345
430
|
assert.match(context.__JSKIT_UI_CREATE_FORM_COLUMNS__, /<v-switch/);
|
|
346
|
-
assert.match(context.__JSKIT_UI_CREATE_FORM_COLUMNS__, /
|
|
431
|
+
assert.match(context.__JSKIT_UI_CREATE_FORM_COLUMNS__, /formState\.vip/);
|
|
347
432
|
});
|
|
348
433
|
});
|
|
349
434
|
|
|
@@ -365,6 +450,76 @@ test("buildUiTemplateContext renders nullable booleans as tri-state selects by d
|
|
|
365
450
|
});
|
|
366
451
|
});
|
|
367
452
|
|
|
453
|
+
test("buildUiTemplateContext omits lookup runtime placeholders when form fields do not include lookups", async () => {
|
|
454
|
+
await withTempApp(async (appRoot) => {
|
|
455
|
+
await writeResource(appRoot, RESOURCE_FILE, FULL_RESOURCE_SOURCE);
|
|
456
|
+
|
|
457
|
+
const context = await buildUiTemplateContext({
|
|
458
|
+
appRoot,
|
|
459
|
+
options: createOptions()
|
|
460
|
+
});
|
|
461
|
+
|
|
462
|
+
assert.equal(context.__JSKIT_UI_CREATE_LOOKUP_IMPORT_LINE__, "");
|
|
463
|
+
assert.equal(context.__JSKIT_UI_EDIT_LOOKUP_IMPORT_LINE__, "");
|
|
464
|
+
assert.equal(context.__JSKIT_UI_CREATE_LOOKUP_RUNTIME_SETUP__, "");
|
|
465
|
+
assert.equal(context.__JSKIT_UI_EDIT_LOOKUP_RUNTIME_SETUP__, "");
|
|
466
|
+
assert.equal(context.__JSKIT_UI_CREATE_LOOKUP_FORM_PROPS__, "");
|
|
467
|
+
assert.equal(context.__JSKIT_UI_EDIT_LOOKUP_FORM_PROPS__, "");
|
|
468
|
+
});
|
|
469
|
+
});
|
|
470
|
+
|
|
471
|
+
test("buildUiTemplateContext indents direct-page form columns without changing shared form columns", async () => {
|
|
472
|
+
await withTempApp(async (appRoot) => {
|
|
473
|
+
await writeResource(appRoot, RESOURCE_FILE, FULL_RESOURCE_SOURCE);
|
|
474
|
+
|
|
475
|
+
const context = await buildUiTemplateContext({
|
|
476
|
+
appRoot,
|
|
477
|
+
options: createOptions()
|
|
478
|
+
});
|
|
479
|
+
|
|
480
|
+
assert.match(context.__JSKIT_UI_CREATE_FORM_COLUMNS__, /^ {14}<v-col/m);
|
|
481
|
+
assert.match(context.__JSKIT_UI_EDIT_FORM_COLUMNS__, /^ {14}<v-col/m);
|
|
482
|
+
assert.match(context.__JSKIT_UI_CREATE_FORM_COLUMNS_DIRECT__, /^ {12}<v-col/m);
|
|
483
|
+
assert.match(context.__JSKIT_UI_EDIT_FORM_COLUMNS_DIRECT__, /^ {12}<v-col/m);
|
|
484
|
+
});
|
|
485
|
+
});
|
|
486
|
+
|
|
487
|
+
test("buildUiTemplateContext includes lookup runtime placeholders when form fields include lookups", async () => {
|
|
488
|
+
await withTempApp(async (appRoot) => {
|
|
489
|
+
await writeResource(appRoot, RESOURCE_FILE, LOOKUP_RESOURCE_SOURCE);
|
|
490
|
+
|
|
491
|
+
const context = await buildUiTemplateContext({
|
|
492
|
+
appRoot,
|
|
493
|
+
options: createOptions({
|
|
494
|
+
"display-fields": "serviceId"
|
|
495
|
+
})
|
|
496
|
+
});
|
|
497
|
+
|
|
498
|
+
assert.match(context.__JSKIT_UI_CREATE_LOOKUP_IMPORT_LINE__, /createCrudLookupFieldRuntime/);
|
|
499
|
+
assert.match(context.__JSKIT_UI_EDIT_LOOKUP_IMPORT_LINE__, /createCrudLookupFieldRuntime/);
|
|
500
|
+
assert.match(context.__JSKIT_UI_CREATE_LOOKUP_RUNTIME_SETUP__, /resolveLookupItems/);
|
|
501
|
+
assert.match(context.__JSKIT_UI_EDIT_LOOKUP_RUNTIME_SETUP__, /resolveLookupItems/);
|
|
502
|
+
assert.match(context.__JSKIT_UI_CREATE_LOOKUP_FORM_PROPS__, /resolve-lookup-items/);
|
|
503
|
+
assert.match(context.__JSKIT_UI_EDIT_LOOKUP_FORM_PROPS__, /resolve-lookup-items/);
|
|
504
|
+
});
|
|
505
|
+
});
|
|
506
|
+
|
|
507
|
+
test("buildUiTemplateContext escapes select option bindings safely for Vue attributes", async () => {
|
|
508
|
+
await withTempApp(async (appRoot) => {
|
|
509
|
+
await writeResource(appRoot, RESOURCE_FILE, SELECT_RESOURCE_SOURCE);
|
|
510
|
+
|
|
511
|
+
const context = await buildUiTemplateContext({
|
|
512
|
+
appRoot,
|
|
513
|
+
options: createOptions()
|
|
514
|
+
});
|
|
515
|
+
|
|
516
|
+
assert.match(
|
|
517
|
+
context.__JSKIT_UI_CREATE_FORM_COLUMNS__,
|
|
518
|
+
/:items="\[\{"value":"dryer","label":"Dryer"\},\{"value":"pallet racking","label":"Pallet Racking"\},\{"value":"freezer","label":"Freezer"\},\{"value":"coolroom","label":"Coolroom"\}\]"/
|
|
519
|
+
);
|
|
520
|
+
});
|
|
521
|
+
});
|
|
522
|
+
|
|
368
523
|
test("buildUiTemplateContext derives menu auth visibility from the target surface policy", async () => {
|
|
369
524
|
await withTempApp(async (appRoot) => {
|
|
370
525
|
await writeFile(
|