@jskit-ai/crud-ui-generator 0.1.26 → 0.1.28
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
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.28",
|
|
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.60"
|
|
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.28",
|
|
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.53",
|
|
10
|
+
"@jskit-ai/kernel": "0.1.45"
|
|
11
11
|
},
|
|
12
12
|
"exports": {
|
|
13
13
|
"./server/buildTemplateContext": "./src/server/buildTemplateContext.js"
|
|
@@ -570,6 +570,14 @@ function resolveFormFieldComponent(fieldType, relation = null) {
|
|
|
570
570
|
return "text";
|
|
571
571
|
}
|
|
572
572
|
|
|
573
|
+
function buildDefaultNullableBooleanOptions() {
|
|
574
|
+
return [
|
|
575
|
+
{ label: "Unset", value: null },
|
|
576
|
+
{ label: "Yes", value: true },
|
|
577
|
+
{ label: "No", value: false }
|
|
578
|
+
];
|
|
579
|
+
}
|
|
580
|
+
|
|
573
581
|
function toPositiveInteger(value) {
|
|
574
582
|
const parsed = Number(value);
|
|
575
583
|
return Number.isInteger(parsed) && parsed > 0 ? parsed : null;
|
|
@@ -638,7 +646,17 @@ function createFormFieldDefinitions(
|
|
|
638
646
|
`resource form field "${key}" defines schema enum values but is missing resource.fieldMeta["${key}"].ui.options.`
|
|
639
647
|
);
|
|
640
648
|
}
|
|
641
|
-
const selectOptions = relation
|
|
649
|
+
const selectOptions = relation
|
|
650
|
+
? []
|
|
651
|
+
: (
|
|
652
|
+
fieldUiOptions.length > 0
|
|
653
|
+
? fieldUiOptions
|
|
654
|
+
: (
|
|
655
|
+
schemaType.type === "boolean" && schemaType.nullable === true
|
|
656
|
+
? buildDefaultNullableBooleanOptions()
|
|
657
|
+
: []
|
|
658
|
+
)
|
|
659
|
+
);
|
|
642
660
|
const lookupFormControl = relation
|
|
643
661
|
? checkCrudLookupFormControl(fieldMetaMap?.[key]?.ui?.formControl, {
|
|
644
662
|
context: `resource.fieldMeta["${key}"].ui.formControl`,
|
|
@@ -126,6 +126,70 @@ const resource = {
|
|
|
126
126
|
export { resource };
|
|
127
127
|
`;
|
|
128
128
|
|
|
129
|
+
const NULLABLE_BOOLEAN_RESOURCE_SOURCE = `const customerRecordSchema = {
|
|
130
|
+
type: "object",
|
|
131
|
+
properties: {
|
|
132
|
+
id: { type: "integer" },
|
|
133
|
+
firstName: { type: "string" },
|
|
134
|
+
reviewPassed: { type: ["boolean", "null"] }
|
|
135
|
+
},
|
|
136
|
+
additionalProperties: false
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
const customerBodySchema = {
|
|
140
|
+
type: "object",
|
|
141
|
+
properties: {
|
|
142
|
+
firstName: { type: "string", maxLength: 120 },
|
|
143
|
+
reviewPassed: { type: ["boolean", "null"] }
|
|
144
|
+
},
|
|
145
|
+
additionalProperties: false
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
const resource = {
|
|
149
|
+
resource: "customers",
|
|
150
|
+
operations: {
|
|
151
|
+
list: {
|
|
152
|
+
outputValidator: {
|
|
153
|
+
schema: {
|
|
154
|
+
type: "object",
|
|
155
|
+
properties: {
|
|
156
|
+
items: {
|
|
157
|
+
type: "array",
|
|
158
|
+
items: customerRecordSchema
|
|
159
|
+
},
|
|
160
|
+
nextCursor: { type: ["string", "null"] }
|
|
161
|
+
},
|
|
162
|
+
additionalProperties: false
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
},
|
|
166
|
+
view: {
|
|
167
|
+
outputValidator: {
|
|
168
|
+
schema: customerRecordSchema
|
|
169
|
+
}
|
|
170
|
+
},
|
|
171
|
+
create: {
|
|
172
|
+
bodyValidator: {
|
|
173
|
+
schema: customerBodySchema
|
|
174
|
+
},
|
|
175
|
+
outputValidator: {
|
|
176
|
+
schema: customerRecordSchema
|
|
177
|
+
}
|
|
178
|
+
},
|
|
179
|
+
patch: {
|
|
180
|
+
bodyValidator: {
|
|
181
|
+
schema: customerBodySchema
|
|
182
|
+
},
|
|
183
|
+
outputValidator: {
|
|
184
|
+
schema: customerRecordSchema
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
export { resource };
|
|
191
|
+
`;
|
|
192
|
+
|
|
129
193
|
const LOOKUP_RESOURCE_SOURCE = `const recordSchema = {
|
|
130
194
|
type: "object",
|
|
131
195
|
properties: {
|
|
@@ -269,6 +333,38 @@ test("buildUiTemplateContext derives CRUD placeholders from the explicit target-
|
|
|
269
333
|
});
|
|
270
334
|
});
|
|
271
335
|
|
|
336
|
+
test("buildUiTemplateContext keeps non-nullable booleans as switches", async () => {
|
|
337
|
+
await withTempApp(async (appRoot) => {
|
|
338
|
+
await writeResource(appRoot, RESOURCE_FILE, FULL_RESOURCE_SOURCE);
|
|
339
|
+
|
|
340
|
+
const context = await buildUiTemplateContext({
|
|
341
|
+
appRoot,
|
|
342
|
+
options: createOptions()
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
assert.match(context.__JSKIT_UI_CREATE_FORM_COLUMNS__, /<v-switch/);
|
|
346
|
+
assert.match(context.__JSKIT_UI_CREATE_FORM_COLUMNS__, /formRuntime\.form\.vip/);
|
|
347
|
+
});
|
|
348
|
+
});
|
|
349
|
+
|
|
350
|
+
test("buildUiTemplateContext renders nullable booleans as tri-state selects by default", async () => {
|
|
351
|
+
await withTempApp(async (appRoot) => {
|
|
352
|
+
await writeResource(appRoot, RESOURCE_FILE, NULLABLE_BOOLEAN_RESOURCE_SOURCE);
|
|
353
|
+
|
|
354
|
+
const context = await buildUiTemplateContext({
|
|
355
|
+
appRoot,
|
|
356
|
+
options: createOptions()
|
|
357
|
+
});
|
|
358
|
+
|
|
359
|
+
assert.match(context.__JSKIT_UI_CREATE_FORM_FIELDS__, /"key":"reviewPassed"/);
|
|
360
|
+
assert.match(context.__JSKIT_UI_CREATE_FORM_FIELDS__, /"component":"select"/);
|
|
361
|
+
assert.match(context.__JSKIT_UI_CREATE_FORM_FIELDS__, /"nullable":true/);
|
|
362
|
+
assert.match(context.__JSKIT_UI_CREATE_FORM_FIELDS__, /"options":\[\{"label":"Unset","value":null\},\{"label":"Yes","value":true\},\{"label":"No","value":false\}\]/);
|
|
363
|
+
assert.match(context.__JSKIT_UI_CREATE_FORM_COLUMNS__, /<v-select/);
|
|
364
|
+
assert.doesNotMatch(context.__JSKIT_UI_CREATE_FORM_COLUMNS__, /<v-switch/);
|
|
365
|
+
});
|
|
366
|
+
});
|
|
367
|
+
|
|
272
368
|
test("buildUiTemplateContext derives menu auth visibility from the target surface policy", async () => {
|
|
273
369
|
await withTempApp(async (appRoot) => {
|
|
274
370
|
await writeFile(
|