@jskit-ai/crud-ui-generator 0.1.48 → 0.1.50
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 +30 -14
- package/package.json +4 -4
- package/src/server/buildTemplateContext.js +178 -11
- package/src/server/resourceSupport.js +78 -62
- package/templates/src/pages/admin/ui-generator/AddEditForm.vue +17 -86
- package/templates/src/pages/admin/ui-generator/EditElement.vue +18 -53
- package/templates/src/pages/admin/ui-generator/EditWrapperElement.vue +9 -20
- package/templates/src/pages/admin/ui-generator/ListElement.vue +38 -116
- package/templates/src/pages/admin/ui-generator/NewElement.vue +17 -44
- package/templates/src/pages/admin/ui-generator/NewWrapperElement.vue +8 -8
- package/templates/src/pages/admin/ui-generator/ViewElement.vue +18 -72
- package/templates/src/pages/admin/ui-generator/listBulkActions.js +5 -0
- package/templates/src/pages/admin/ui-generator/listFilters.js +5 -0
- package/test/addFieldSubcommand.test.js +8 -8
- package/test/buildTemplateContext.test.js +273 -22
- package/test/packageDescriptor.test.js +47 -2
package/package.descriptor.mjs
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
import { GENERATED_UI_NAVIGATION_ROLE_OPTION } from "@jskit-ai/kernel/shared/support/generatedUiContract";
|
|
2
|
+
|
|
1
3
|
export default Object.freeze({
|
|
2
4
|
packageVersion: 1,
|
|
3
5
|
packageId: "@jskit-ai/crud-ui-generator",
|
|
4
|
-
version: "0.1.
|
|
6
|
+
version: "0.1.50",
|
|
5
7
|
kind: "generator",
|
|
6
8
|
description: "Generate CRUD route trees from resource validators at an explicit route root relative to src/pages/.",
|
|
7
9
|
options: {
|
|
@@ -64,16 +66,9 @@ export default Object.freeze({
|
|
|
64
66
|
inputType: "text",
|
|
65
67
|
defaultValue: "",
|
|
66
68
|
promptLabel: "Link placement",
|
|
67
|
-
promptHint: "Optional target override for the generated list-page link placement (format:
|
|
68
|
-
},
|
|
69
|
-
"link-component-token": {
|
|
70
|
-
required: false,
|
|
71
|
-
inputType: "text",
|
|
72
|
-
defaultValue: "",
|
|
73
|
-
promptLabel: "Link component token",
|
|
74
|
-
promptHint:
|
|
75
|
-
"Optional component token override for the generated list-page link placement (example: local.main.ui.tab-link-item)."
|
|
69
|
+
promptHint: "Optional semantic target override for the generated list-page link placement (format: area.slot)."
|
|
76
70
|
},
|
|
71
|
+
"navigation-role": GENERATED_UI_NAVIGATION_ROLE_OPTION,
|
|
77
72
|
namespace: {
|
|
78
73
|
required: false,
|
|
79
74
|
inputType: "text",
|
|
@@ -118,8 +113,8 @@ export default Object.freeze({
|
|
|
118
113
|
"display-fields",
|
|
119
114
|
"id-param",
|
|
120
115
|
"parent-title",
|
|
116
|
+
"navigation-role",
|
|
121
117
|
"link-placement",
|
|
122
|
-
"link-component-token",
|
|
123
118
|
"namespace",
|
|
124
119
|
"force"
|
|
125
120
|
],
|
|
@@ -180,7 +175,7 @@ export default Object.freeze({
|
|
|
180
175
|
mutations: {
|
|
181
176
|
dependencies: {
|
|
182
177
|
runtime: {
|
|
183
|
-
"@jskit-ai/users-web": "0.1.
|
|
178
|
+
"@jskit-ai/users-web": "0.1.82"
|
|
184
179
|
},
|
|
185
180
|
dev: {}
|
|
186
181
|
},
|
|
@@ -204,6 +199,28 @@ export default Object.freeze({
|
|
|
204
199
|
in: ["list"]
|
|
205
200
|
}
|
|
206
201
|
},
|
|
202
|
+
{
|
|
203
|
+
from: "templates/src/pages/admin/ui-generator/listFilters.js",
|
|
204
|
+
to: "src/pages/${option:target-root|trim}/listFilters.js",
|
|
205
|
+
reason: "Install generated client-side list filter definitions.",
|
|
206
|
+
category: "crud-ui-generator",
|
|
207
|
+
id: "crud-ui-page-list-filters-${option:target-root|snake}",
|
|
208
|
+
when: {
|
|
209
|
+
option: "operations",
|
|
210
|
+
in: ["list"]
|
|
211
|
+
}
|
|
212
|
+
},
|
|
213
|
+
{
|
|
214
|
+
from: "templates/src/pages/admin/ui-generator/listBulkActions.js",
|
|
215
|
+
to: "src/pages/${option:target-root|trim}/listBulkActions.js",
|
|
216
|
+
reason: "Install generated client-side list bulk action definitions.",
|
|
217
|
+
category: "crud-ui-generator",
|
|
218
|
+
id: "crud-ui-page-list-bulk-actions-${option:target-root|snake}",
|
|
219
|
+
when: {
|
|
220
|
+
option: "operations",
|
|
221
|
+
in: ["list"]
|
|
222
|
+
}
|
|
223
|
+
},
|
|
207
224
|
{
|
|
208
225
|
from: "templates/src/pages/admin/ui-generator/ViewElement.vue",
|
|
209
226
|
to: "src/pages/${option:target-root|trim}/[${option:id-param|trim}]/index.vue",
|
|
@@ -364,8 +381,7 @@ export default Object.freeze({
|
|
|
364
381
|
file: "src/placement.js",
|
|
365
382
|
position: "bottom",
|
|
366
383
|
skipIfContains: "__JSKIT_UI_MENU_MARKER__",
|
|
367
|
-
value:
|
|
368
|
-
"\n// __JSKIT_UI_MENU_MARKER__\n{\n addPlacement({\n id: \"__JSKIT_UI_MENU_PLACEMENT_ID__\",\n target: \"__JSKIT_UI_MENU_PLACEMENT_TARGET__\",\n surfaces: [\"__JSKIT_UI_SURFACE_ID__\"],\n order: 155,\n componentToken: \"__JSKIT_UI_MENU_COMPONENT_TOKEN__\",\n props: {\n label: \"__JSKIT_UI_MENU_LABEL__\",\n icon: \"__JSKIT_UI_MENU_ICON__\",\n surface: \"__JSKIT_UI_SURFACE_ID__\",\n scopedSuffix: \"__JSKIT_UI_MENU_WORKSPACE_SUFFIX__\",\n unscopedSuffix: \"__JSKIT_UI_MENU_NON_WORKSPACE_SUFFIX__\",\n__JSKIT_UI_MENU_TO_PROP_LINE__ },\n__JSKIT_UI_MENU_WHEN_LINE__ });\n}\n",
|
|
384
|
+
value: "__JSKIT_UI_MENU_APPEND_BLOCK__",
|
|
369
385
|
reason: "Append generated CRUD list-page placement.",
|
|
370
386
|
category: "crud-ui-generator",
|
|
371
387
|
id: "crud-ui-placement-menu",
|
package/package.json
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jskit-ai/crud-ui-generator",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.50",
|
|
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.
|
|
11
|
-
"@jskit-ai/resource-crud-core": "0.1.
|
|
9
|
+
"@jskit-ai/crud-core": "0.1.75",
|
|
10
|
+
"@jskit-ai/kernel": "0.1.67",
|
|
11
|
+
"@jskit-ai/resource-crud-core": "0.1.12"
|
|
12
12
|
},
|
|
13
13
|
"exports": {
|
|
14
14
|
"./server/buildTemplateContext": "./src/server/buildTemplateContext.js"
|
|
@@ -7,6 +7,10 @@ import {
|
|
|
7
7
|
resolvePageTargetDetails
|
|
8
8
|
} from "@jskit-ai/kernel/server/support";
|
|
9
9
|
import { normalizeText } from "@jskit-ai/kernel/shared/support/normalize";
|
|
10
|
+
import {
|
|
11
|
+
resolveGeneratedUiNavigationRoleLinkPlacement,
|
|
12
|
+
shouldCreateGeneratedUiNavigationLink
|
|
13
|
+
} from "@jskit-ai/kernel/shared/support/generatedUiContract";
|
|
10
14
|
import {
|
|
11
15
|
loadResourceDefinition,
|
|
12
16
|
requireOperation,
|
|
@@ -22,6 +26,7 @@ import {
|
|
|
22
26
|
resolveNearestParentRouteParamKey,
|
|
23
27
|
buildListHeaderColumns,
|
|
24
28
|
buildListRowColumns,
|
|
29
|
+
buildListCardFields,
|
|
25
30
|
buildViewColumns,
|
|
26
31
|
buildFormColumns,
|
|
27
32
|
resolveRecordIdFieldKey,
|
|
@@ -157,6 +162,18 @@ function toTitleFromKebab(value = "", fallback = "") {
|
|
|
157
162
|
return wordsToTitle(words);
|
|
158
163
|
}
|
|
159
164
|
|
|
165
|
+
function buildCrudListCopy(resourceLabels = {}) {
|
|
166
|
+
const pluralTitle = normalizeText(resourceLabels.pluralTitle) || "Records";
|
|
167
|
+
const singularTitle = normalizeText(resourceLabels.singularTitle) || "Record";
|
|
168
|
+
return Object.freeze({
|
|
169
|
+
loadErrorTitle: `Unable to load ${pluralTitle}`,
|
|
170
|
+
loadErrorBody: "Check the connection and try again.",
|
|
171
|
+
emptyTitle: `No ${pluralTitle} yet`,
|
|
172
|
+
emptyBody: `Create the first ${singularTitle} to start using this workflow.`,
|
|
173
|
+
createLabel: `New ${singularTitle}`
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
|
|
160
177
|
function normalizeRelativeAppPath(value = "") {
|
|
161
178
|
return String(value || "")
|
|
162
179
|
.replaceAll("\\", "/")
|
|
@@ -242,6 +259,20 @@ function parseParentTitleOption(options) {
|
|
|
242
259
|
return parentTitleMode;
|
|
243
260
|
}
|
|
244
261
|
|
|
262
|
+
function shouldCreateNavigationLink(options = {}, inferenceContext = {}) {
|
|
263
|
+
return shouldCreateGeneratedUiNavigationLink(options, {
|
|
264
|
+
dynamicRoutePolicy: "any",
|
|
265
|
+
routePath: inferenceContext?.routePath
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
function resolveNavigationRoleLinkPlacement(options = {}, inferenceContext = {}) {
|
|
270
|
+
return resolveGeneratedUiNavigationRoleLinkPlacement(options, {
|
|
271
|
+
dynamicRoutePolicy: "any",
|
|
272
|
+
routePath: inferenceContext?.routePath
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
|
|
245
276
|
function validateDisplayFieldsForOperation(selectedFieldKeys, fields, operationName) {
|
|
246
277
|
const selectedFields = Array.isArray(selectedFieldKeys) ? selectedFieldKeys : [];
|
|
247
278
|
if (selectedFields.length < 1) {
|
|
@@ -303,6 +334,58 @@ function hasLookupFormFields(fields = []) {
|
|
|
303
334
|
return (Array.isArray(fields) ? fields : []).some((field) => normalizeText(field?.component).toLowerCase() === "lookup");
|
|
304
335
|
}
|
|
305
336
|
|
|
337
|
+
function hasLookupDisplayFields(fields = []) {
|
|
338
|
+
return (Array.isArray(fields) ? fields : []).some((field) => normalizeText(field?.relation?.kind).toLowerCase() === "lookup");
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
function buildListCardSlotProps(fields = []) {
|
|
342
|
+
const normalizedFields = Array.isArray(fields) ? fields : [];
|
|
343
|
+
if (normalizedFields.length < 1) {
|
|
344
|
+
return "{}";
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
const props = ["record"];
|
|
348
|
+
if (hasLookupDisplayFields(normalizedFields)) {
|
|
349
|
+
props.push("records");
|
|
350
|
+
}
|
|
351
|
+
props.push("formatListCardValue");
|
|
352
|
+
return `{ ${props.join(", ")} }`;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
function buildListRowSlotProps(fields = []) {
|
|
356
|
+
const normalizedFields = Array.isArray(fields) ? fields : [];
|
|
357
|
+
if (normalizedFields.length < 1) {
|
|
358
|
+
return "{}";
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
const props = ["record"];
|
|
362
|
+
if (hasLookupDisplayFields(normalizedFields)) {
|
|
363
|
+
props.push("records");
|
|
364
|
+
}
|
|
365
|
+
return `{ ${props.join(", ")} }`;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
function buildCrudFieldsSlotProps(fields = [], { includeMode = false } = {}) {
|
|
369
|
+
const props = [];
|
|
370
|
+
if (includeMode) {
|
|
371
|
+
props.push("mode");
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
props.push("formState", "addEdit", "resolveFieldErrors");
|
|
375
|
+
if (hasLookupFormFields(fields)) {
|
|
376
|
+
props.push(
|
|
377
|
+
"resolveLookupItems: fieldLookupItems",
|
|
378
|
+
"resolveLookupLoading: fieldLookupLoading",
|
|
379
|
+
"resolveLookupSearch: fieldLookupSearch",
|
|
380
|
+
"setLookupSearch: setFieldLookupSearch"
|
|
381
|
+
);
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
return `{
|
|
385
|
+
${props.join(",\n ")}
|
|
386
|
+
}`;
|
|
387
|
+
}
|
|
388
|
+
|
|
306
389
|
function buildLookupImportLine(fields = []) {
|
|
307
390
|
return hasLookupFormFields(fields)
|
|
308
391
|
? 'import { createCrudLookupFieldRuntime } from "@jskit-ai/users-web/client/composables/crudLookupFieldRuntime";'
|
|
@@ -339,16 +422,17 @@ const {
|
|
|
339
422
|
`;
|
|
340
423
|
}
|
|
341
424
|
|
|
342
|
-
function buildLookupFormProps(fields = []) {
|
|
425
|
+
function buildLookupFormProps(fields = [], { sourcePrefix = "" } = {}) {
|
|
343
426
|
if (!hasLookupFormFields(fields)) {
|
|
344
427
|
return "";
|
|
345
428
|
}
|
|
346
429
|
|
|
430
|
+
const prefix = String(sourcePrefix || "");
|
|
347
431
|
return `
|
|
348
|
-
:resolve-lookup-items="resolveLookupItems"
|
|
349
|
-
:resolve-lookup-loading="resolveLookupLoading"
|
|
350
|
-
:resolve-lookup-search="resolveLookupSearch"
|
|
351
|
-
:set-lookup-search="setLookupSearch"`;
|
|
432
|
+
:resolve-lookup-items="${prefix}resolveLookupItems"
|
|
433
|
+
:resolve-lookup-loading="${prefix}resolveLookupLoading"
|
|
434
|
+
:resolve-lookup-search="${prefix}resolveLookupSearch"
|
|
435
|
+
:set-lookup-search="${prefix}setLookupSearch"`;
|
|
352
436
|
}
|
|
353
437
|
|
|
354
438
|
function buildLookupFormPropDefinitions({ createFields = [], editFields = [] } = {}) {
|
|
@@ -468,6 +552,16 @@ function resolveTargetRootRelativeRoutePath(pageTarget = {}) {
|
|
|
468
552
|
return visibleRouteSegments.length > 0 ? `/${visibleRouteSegments.join("/")}` : "/";
|
|
469
553
|
}
|
|
470
554
|
|
|
555
|
+
function resolveNavigationInferenceRoutePath(pageTarget = {}) {
|
|
556
|
+
const visibleRouteSegments = Array.isArray(pageTarget?.visibleRouteSegments)
|
|
557
|
+
? pageTarget.visibleRouteSegments.map((entry) => normalizeText(entry)).filter(Boolean)
|
|
558
|
+
: [];
|
|
559
|
+
if (visibleRouteSegments.length > 0) {
|
|
560
|
+
return `/${visibleRouteSegments.join("/")}`;
|
|
561
|
+
}
|
|
562
|
+
return String(pageTarget?.routeUrlSuffix || "");
|
|
563
|
+
}
|
|
564
|
+
|
|
471
565
|
function resolveMenuToPropLine(linkTo = "") {
|
|
472
566
|
if (!linkTo) {
|
|
473
567
|
return "";
|
|
@@ -475,6 +569,52 @@ function resolveMenuToPropLine(linkTo = "") {
|
|
|
475
569
|
return ` to: ${JSON.stringify(linkTo)},\n`;
|
|
476
570
|
}
|
|
477
571
|
|
|
572
|
+
function resolveMenuOwnerLine(owner = "") {
|
|
573
|
+
const normalizedOwner = normalizeText(owner);
|
|
574
|
+
if (!normalizedOwner) {
|
|
575
|
+
return "";
|
|
576
|
+
}
|
|
577
|
+
return ` owner: ${JSON.stringify(normalizedOwner)},\n`;
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
function buildMenuAppendBlock({
|
|
581
|
+
hasListOperation = false,
|
|
582
|
+
menuMarker = "",
|
|
583
|
+
pageLinkTarget = null,
|
|
584
|
+
pageTarget = {}
|
|
585
|
+
} = {}) {
|
|
586
|
+
const placementId = String(pageLinkTarget?.pageTarget?.placementId || "");
|
|
587
|
+
const placementTarget = String(pageLinkTarget?.placementTarget?.id || "");
|
|
588
|
+
if (!hasListOperation || !placementId || !placementTarget) {
|
|
589
|
+
return "";
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
const surfaceId = String(pageTarget?.surfaceId || "");
|
|
593
|
+
const routeUrlSuffix = String(pageLinkTarget?.pageTarget?.routeUrlSuffix || "");
|
|
594
|
+
const menuToPropLine = resolveMenuToPropLine(pageLinkTarget?.linkTo || "");
|
|
595
|
+
const whenLine = String(pageLinkTarget?.whenLine || "");
|
|
596
|
+
|
|
597
|
+
return `
|
|
598
|
+
// ${menuMarker}
|
|
599
|
+
{
|
|
600
|
+
addPlacement({
|
|
601
|
+
id: ${JSON.stringify(placementId)},
|
|
602
|
+
target: ${JSON.stringify(placementTarget)},
|
|
603
|
+
${resolveMenuOwnerLine(pageLinkTarget?.placementTarget?.owner || "")} kind: "link",
|
|
604
|
+
surfaces: [${JSON.stringify(surfaceId)}],
|
|
605
|
+
order: 155,
|
|
606
|
+
props: {
|
|
607
|
+
label: ${JSON.stringify(pageTarget?.defaultName || "")},
|
|
608
|
+
icon: ${JSON.stringify(DEFAULT_GENERATED_LINK_ICON)},
|
|
609
|
+
surface: ${JSON.stringify(surfaceId)},
|
|
610
|
+
scopedSuffix: ${JSON.stringify(routeUrlSuffix)},
|
|
611
|
+
unscopedSuffix: ${JSON.stringify(routeUrlSuffix)},
|
|
612
|
+
${menuToPropLine} },
|
|
613
|
+
${whenLine} });
|
|
614
|
+
}
|
|
615
|
+
`;
|
|
616
|
+
}
|
|
617
|
+
|
|
478
618
|
function resolveCrudRelativePath(namespace = "") {
|
|
479
619
|
return `/${requireCrudNamespace(namespace, {
|
|
480
620
|
context: "crud-ui-generator resource namespace"
|
|
@@ -501,7 +641,7 @@ function buildListHeadingTitleSetup({
|
|
|
501
641
|
}
|
|
502
642
|
|
|
503
643
|
return `const parentTitle = useCrudListParentTitle({
|
|
504
|
-
listRuntime
|
|
644
|
+
listRuntime,
|
|
505
645
|
resource: uiResource,
|
|
506
646
|
adapter: UI_OPERATION_ADAPTER || undefined,
|
|
507
647
|
recordIdParam: UI_RECORD_ID_PARAM,
|
|
@@ -643,14 +783,20 @@ async function buildUiTemplateContext({ appRoot, options } = {}) {
|
|
|
643
783
|
const viewTitleFallbackFieldKey = hasViewOperation
|
|
644
784
|
? resolveViewTitleFallbackFieldKey(viewFieldsAll, { recordIdFieldKey })
|
|
645
785
|
: "";
|
|
786
|
+
const listTitleFallbackFieldKey = hasListOperation
|
|
787
|
+
? resolveViewTitleFallbackFieldKey(listFieldsAll, { recordIdFieldKey: listRecordIdFieldKey })
|
|
788
|
+
: "";
|
|
646
789
|
|
|
647
|
-
const pageLinkTarget = hasListOperation
|
|
790
|
+
const pageLinkTarget = hasListOperation && shouldCreateNavigationLink(options, {
|
|
791
|
+
routePath: resolveNavigationInferenceRoutePath(pageTarget)
|
|
792
|
+
})
|
|
648
793
|
? await resolvePageLinkTargetDetails({
|
|
649
794
|
appRoot,
|
|
650
795
|
pageTarget,
|
|
651
796
|
targetFile: listTargetFile,
|
|
652
|
-
placement: options
|
|
653
|
-
|
|
797
|
+
placement: resolveNavigationRoleLinkPlacement(options, {
|
|
798
|
+
routePath: resolveNavigationInferenceRoutePath(pageTarget)
|
|
799
|
+
}),
|
|
654
800
|
context: "crud-ui-generator"
|
|
655
801
|
})
|
|
656
802
|
: null;
|
|
@@ -659,6 +805,8 @@ async function buildUiTemplateContext({ appRoot, options } = {}) {
|
|
|
659
805
|
: "";
|
|
660
806
|
const createFormColumns = buildFormColumns(createFields);
|
|
661
807
|
const editFormColumns = buildFormColumns(editFields);
|
|
808
|
+
const sharedFormFields = Object.freeze([...createFields, ...editFields]);
|
|
809
|
+
const listCopy = buildCrudListCopy(resourceLabels);
|
|
662
810
|
|
|
663
811
|
return {
|
|
664
812
|
__JSKIT_UI_RESOURCE_IMPORT_PATH__: `/${normalizeRelativeAppPath(options?.["resource-file"])}`,
|
|
@@ -667,6 +815,11 @@ async function buildUiTemplateContext({ appRoot, options } = {}) {
|
|
|
667
815
|
__JSKIT_UI_RESOURCE_NAMESPACE__: resourceNamespace,
|
|
668
816
|
__JSKIT_UI_RESOURCE_SINGULAR_TITLE__: resourceLabels.singularTitle,
|
|
669
817
|
__JSKIT_UI_RESOURCE_PLURAL_TITLE__: resourceLabels.pluralTitle,
|
|
818
|
+
__JSKIT_UI_LIST_LOAD_ERROR_TITLE__: listCopy.loadErrorTitle,
|
|
819
|
+
__JSKIT_UI_LIST_LOAD_ERROR_BODY__: listCopy.loadErrorBody,
|
|
820
|
+
__JSKIT_UI_LIST_EMPTY_TITLE__: listCopy.emptyTitle,
|
|
821
|
+
__JSKIT_UI_LIST_EMPTY_BODY__: listCopy.emptyBody,
|
|
822
|
+
__JSKIT_UI_LIST_CREATE_LABEL__: listCopy.createLabel,
|
|
670
823
|
__JSKIT_UI_ROUTE_TITLE__: pageTarget.defaultName,
|
|
671
824
|
__JSKIT_UI_PARENT_TITLE_MODE__: parentTitleMode,
|
|
672
825
|
__JSKIT_UI_LIST_PARENT_TITLE_IMPORT_LINE__: buildListParentTitleImportLine(parentTitleMode),
|
|
@@ -680,6 +833,10 @@ async function buildUiTemplateContext({ appRoot, options } = {}) {
|
|
|
680
833
|
__JSKIT_UI_SURFACE_ID__: pageTarget.surfaceId,
|
|
681
834
|
__JSKIT_UI_LIST_HEADER_COLUMNS__: buildListHeaderColumns(listFields),
|
|
682
835
|
__JSKIT_UI_LIST_ROW_COLUMNS__: buildListRowColumns(listFields),
|
|
836
|
+
__JSKIT_UI_LIST_CARD_FIELDS__: buildListCardFields(listFields),
|
|
837
|
+
__JSKIT_UI_LIST_CARD_SLOT_PROPS__: buildListCardSlotProps(listFields),
|
|
838
|
+
__JSKIT_UI_LIST_ROW_SLOT_PROPS__: buildListRowSlotProps(listFields),
|
|
839
|
+
__JSKIT_UI_LIST_TITLE_FALLBACK_FIELD_KEY__: JSON.stringify(listTitleFallbackFieldKey),
|
|
683
840
|
__JSKIT_UI_LIST_REALTIME_EVENTS__: JSON.stringify(listRealtimeEvents),
|
|
684
841
|
__JSKIT_UI_LIST_RECORD_ID_EXPR__: resolveRecordIdExpression(recordIdFields),
|
|
685
842
|
__JSKIT_UI_VIEW_COLUMNS__: buildViewColumns(viewFields),
|
|
@@ -702,6 +859,9 @@ async function buildUiTemplateContext({ appRoot, options } = {}) {
|
|
|
702
859
|
__JSKIT_UI_EDIT_FORM_COLUMNS__: editFormColumns,
|
|
703
860
|
__JSKIT_UI_CREATE_FORM_COLUMNS_DIRECT__: rewriteGeneratedBlockIndent(createFormColumns, { trimPrefix: " " }),
|
|
704
861
|
__JSKIT_UI_EDIT_FORM_COLUMNS_DIRECT__: rewriteGeneratedBlockIndent(editFormColumns, { trimPrefix: " " }),
|
|
862
|
+
__JSKIT_UI_CREATE_FORM_SLOT_PROPS__: buildCrudFieldsSlotProps(createFields),
|
|
863
|
+
__JSKIT_UI_EDIT_FORM_SLOT_PROPS__: buildCrudFieldsSlotProps(editFields),
|
|
864
|
+
__JSKIT_UI_SHARED_FORM_SLOT_PROPS__: buildCrudFieldsSlotProps(sharedFormFields, { includeMode: true }),
|
|
705
865
|
__JSKIT_UI_CREATE_FORM_FIELDS__: JSON.stringify(createFields),
|
|
706
866
|
__JSKIT_UI_EDIT_FORM_FIELDS__: JSON.stringify(editFields),
|
|
707
867
|
__JSKIT_UI_CREATE_FORM_FIELD_PUSH_LINES__: renderObjectPushLines("UI_CREATE_FORM_FIELDS", createFields),
|
|
@@ -720,6 +880,7 @@ async function buildUiTemplateContext({ appRoot, options } = {}) {
|
|
|
720
880
|
}),
|
|
721
881
|
__JSKIT_UI_CREATE_LOOKUP_FORM_PROPS__: buildLookupFormProps(createFields),
|
|
722
882
|
__JSKIT_UI_EDIT_LOOKUP_FORM_PROPS__: buildLookupFormProps(editFields),
|
|
883
|
+
__JSKIT_UI_SHARED_LOOKUP_FORM_PROPS__: buildLookupFormProps(sharedFormFields, { sourcePrefix: "props." }),
|
|
723
884
|
__JSKIT_UI_FORM_LOOKUP_PROP_DEFS__: buildLookupFormPropDefinitions({
|
|
724
885
|
createFields,
|
|
725
886
|
editFields
|
|
@@ -727,13 +888,19 @@ async function buildUiTemplateContext({ appRoot, options } = {}) {
|
|
|
727
888
|
__JSKIT_UI_MENU_MARKER__: menuMarker,
|
|
728
889
|
__JSKIT_UI_MENU_PLACEMENT_ID__: String(pageLinkTarget?.pageTarget?.placementId || ""),
|
|
729
890
|
__JSKIT_UI_MENU_PLACEMENT_TARGET__: String(pageLinkTarget?.placementTarget?.id || ""),
|
|
730
|
-
|
|
891
|
+
__JSKIT_UI_MENU_OWNER_LINE__: resolveMenuOwnerLine(pageLinkTarget?.placementTarget?.owner || ""),
|
|
731
892
|
__JSKIT_UI_MENU_ICON__: DEFAULT_GENERATED_LINK_ICON,
|
|
732
893
|
__JSKIT_UI_MENU_WORKSPACE_SUFFIX__: String(pageLinkTarget?.pageTarget?.routeUrlSuffix || ""),
|
|
733
894
|
__JSKIT_UI_MENU_NON_WORKSPACE_SUFFIX__: String(pageLinkTarget?.pageTarget?.routeUrlSuffix || ""),
|
|
734
895
|
__JSKIT_UI_MENU_WHEN_LINE__: String(pageLinkTarget?.whenLine || ""),
|
|
735
896
|
__JSKIT_UI_MENU_TO_PROP_LINE__: resolveMenuToPropLine(pageLinkTarget?.linkTo || ""),
|
|
736
|
-
__JSKIT_UI_MENU_LABEL__: pageTarget.defaultName
|
|
897
|
+
__JSKIT_UI_MENU_LABEL__: pageTarget.defaultName,
|
|
898
|
+
__JSKIT_UI_MENU_APPEND_BLOCK__: buildMenuAppendBlock({
|
|
899
|
+
hasListOperation,
|
|
900
|
+
menuMarker,
|
|
901
|
+
pageLinkTarget,
|
|
902
|
+
pageTarget
|
|
903
|
+
})
|
|
737
904
|
};
|
|
738
905
|
}
|
|
739
906
|
|
|
@@ -835,7 +835,7 @@ function renderTemplateJsStringLiteral(value) {
|
|
|
835
835
|
|
|
836
836
|
function buildListHeaderColumns(fields = []) {
|
|
837
837
|
return (Array.isArray(fields) ? fields : [])
|
|
838
|
-
.map((field) => `
|
|
838
|
+
.map((field) => ` <th>${escapeHtml(field.label)}</th>`)
|
|
839
839
|
.join("\n");
|
|
840
840
|
}
|
|
841
841
|
|
|
@@ -888,10 +888,25 @@ function buildListRowColumns(fields = []) {
|
|
|
888
888
|
return (Array.isArray(fields) ? fields : [])
|
|
889
889
|
.map((field) => {
|
|
890
890
|
if (isLookupField(field)) {
|
|
891
|
-
return `
|
|
891
|
+
return ` <td>{{ records.resolveFieldDisplay(record, ${toLookupDisplayFieldExpression(field)}) }}</td>`;
|
|
892
892
|
}
|
|
893
893
|
|
|
894
|
-
return `
|
|
894
|
+
return ` <td>{{ ${toAccessorExpression("record", field.key)} }}</td>`;
|
|
895
|
+
})
|
|
896
|
+
.join("\n");
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
function buildListCardFields(fields = []) {
|
|
900
|
+
return (Array.isArray(fields) ? fields : [])
|
|
901
|
+
.map((field) => {
|
|
902
|
+
const valueExpression = isLookupField(field)
|
|
903
|
+
? `records.resolveFieldDisplay(record, ${toLookupDisplayFieldExpression(field)})`
|
|
904
|
+
: toAccessorExpression("record", field.key);
|
|
905
|
+
|
|
906
|
+
return ` <div class="ui-generator-list-card__field">
|
|
907
|
+
<span class="ui-generator-list-card__field-label">${escapeHtml(field.label)}</span>
|
|
908
|
+
<span class="ui-generator-list-card__field-value">{{ formatListCardValue(${valueExpression}) }}</span>
|
|
909
|
+
</div>`;
|
|
895
910
|
})
|
|
896
911
|
.join("\n");
|
|
897
912
|
}
|
|
@@ -903,10 +918,10 @@ function buildViewColumns(fields = []) {
|
|
|
903
918
|
? `view.resolveFieldDisplay(view.record, ${toLookupDisplayFieldExpression(field)})`
|
|
904
919
|
: toOptionalAccessorExpression("view.record", field.key);
|
|
905
920
|
|
|
906
|
-
return `
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
921
|
+
return ` <v-col cols="12" md="6">
|
|
922
|
+
<div class="text-caption text-medium-emphasis">${escapeHtml(field.label)}</div>
|
|
923
|
+
<div class="text-body-1">{{ ${valueExpression} }}</div>
|
|
924
|
+
</v-col>`;
|
|
910
925
|
})
|
|
911
926
|
.join("\n");
|
|
912
927
|
}
|
|
@@ -929,34 +944,34 @@ function buildFormColumns(fields = []) {
|
|
|
929
944
|
const fieldErrorExpression = `resolveFieldErrors(${fieldKeyLiteral})`;
|
|
930
945
|
const component = normalizeText(field?.component).toLowerCase();
|
|
931
946
|
if (component === "switch") {
|
|
932
|
-
return `
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
947
|
+
return ` <v-col cols="12" md="6">
|
|
948
|
+
<v-switch
|
|
949
|
+
v-model="${formAccessor}"
|
|
950
|
+
label="${label}"
|
|
951
|
+
color="primary"
|
|
952
|
+
hide-details="auto"
|
|
953
|
+
:disabled="addEdit.isFieldLocked"
|
|
954
|
+
:error-messages="${fieldErrorExpression}"
|
|
955
|
+
/>
|
|
956
|
+
</v-col>`;
|
|
942
957
|
}
|
|
943
958
|
|
|
944
959
|
if (component === "select") {
|
|
945
960
|
const selectOptions = Array.isArray(field?.options) ? field.options : [];
|
|
946
|
-
return `
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
961
|
+
return ` <v-col cols="12" md="6">
|
|
962
|
+
<v-select
|
|
963
|
+
v-model="${formAccessor}"
|
|
964
|
+
label="${label}"
|
|
965
|
+
variant="outlined"
|
|
966
|
+
density="comfortable"
|
|
967
|
+
:items="${serializeTemplateBindingValue(selectOptions)}"
|
|
968
|
+
item-title="label"
|
|
969
|
+
item-value="value"
|
|
970
|
+
:disabled="addEdit.isFieldLocked"
|
|
971
|
+
:clearable="${field.nullable === true ? "true" : "false"}"
|
|
972
|
+
:error-messages="${fieldErrorExpression}"
|
|
973
|
+
/>
|
|
974
|
+
</v-col>`;
|
|
960
975
|
}
|
|
961
976
|
|
|
962
977
|
if (component === "lookup") {
|
|
@@ -964,53 +979,53 @@ function buildFormColumns(fields = []) {
|
|
|
964
979
|
const useAutocomplete = lookupFormControl !== "select";
|
|
965
980
|
const lookupComponentTag = useAutocomplete ? "v-autocomplete" : "v-select";
|
|
966
981
|
const lookupAttributeLines = [
|
|
967
|
-
`
|
|
982
|
+
` :items="fieldLookupItems(${fieldKeyLiteral}, { selectedValue: ${formAccessor}, selectedRecord: addEdit.resource.data })"`
|
|
968
983
|
];
|
|
969
984
|
if (useAutocomplete) {
|
|
970
985
|
lookupAttributeLines.push(
|
|
971
|
-
`
|
|
972
|
-
`
|
|
986
|
+
` :search="fieldLookupSearch(${fieldKeyLiteral})"`,
|
|
987
|
+
` @update:search="setFieldLookupSearch(${fieldKeyLiteral}, $event)"`
|
|
973
988
|
);
|
|
974
989
|
}
|
|
975
990
|
lookupAttributeLines.push(
|
|
976
|
-
`
|
|
977
|
-
`
|
|
991
|
+
` item-title="label"`,
|
|
992
|
+
` item-value="value"`
|
|
978
993
|
);
|
|
979
994
|
if (useAutocomplete) {
|
|
980
|
-
lookupAttributeLines.push("
|
|
995
|
+
lookupAttributeLines.push(" no-filter");
|
|
981
996
|
}
|
|
982
|
-
return `
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
997
|
+
return ` <v-col cols="12" md="6">
|
|
998
|
+
<${lookupComponentTag}
|
|
999
|
+
v-model="${formAccessor}"
|
|
1000
|
+
label="${label}"
|
|
1001
|
+
variant="outlined"
|
|
1002
|
+
density="comfortable"
|
|
1003
|
+
autocomplete="off"
|
|
989
1004
|
${lookupAttributeLines.join("\n")}
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
1005
|
+
:loading="fieldLookupLoading(${fieldKeyLiteral})"
|
|
1006
|
+
:disabled="addEdit.isFieldLocked"
|
|
1007
|
+
:clearable="${field.nullable === true ? "true" : "false"}"
|
|
1008
|
+
:error-messages="${fieldErrorExpression}"
|
|
1009
|
+
/>
|
|
1010
|
+
</v-col>`;
|
|
996
1011
|
}
|
|
997
1012
|
|
|
998
1013
|
const inputType = normalizeText(field?.inputType) || "text";
|
|
999
1014
|
const maxLength = Number.isInteger(field?.maxLength) && field.maxLength > 0
|
|
1000
1015
|
? String(field.maxLength)
|
|
1001
1016
|
: "undefined";
|
|
1002
|
-
return `
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1017
|
+
return ` <v-col cols="12" md="6">
|
|
1018
|
+
<v-text-field
|
|
1019
|
+
v-model="${formAccessor}"
|
|
1020
|
+
label="${label}"
|
|
1021
|
+
type="${escapeHtml(inputType)}"
|
|
1022
|
+
variant="outlined"
|
|
1023
|
+
density="comfortable"
|
|
1024
|
+
:maxlength="${maxLength}"
|
|
1025
|
+
:readonly="addEdit.isFieldLocked"
|
|
1026
|
+
:error-messages="${fieldErrorExpression}"
|
|
1027
|
+
/>
|
|
1028
|
+
</v-col>`;
|
|
1014
1029
|
})
|
|
1015
1030
|
.filter(Boolean)
|
|
1016
1031
|
.join("\n");
|
|
@@ -1069,6 +1084,7 @@ export {
|
|
|
1069
1084
|
resolveNearestParentRouteParamKey,
|
|
1070
1085
|
buildListHeaderColumns,
|
|
1071
1086
|
buildListRowColumns,
|
|
1087
|
+
buildListCardFields,
|
|
1072
1088
|
buildViewColumns,
|
|
1073
1089
|
buildFormColumns,
|
|
1074
1090
|
resolveRecordIdFieldKey,
|