@kematjaya/crud-ui-generator 0.1.0 → 0.1.1
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/README.md +1 -2
- package/dist/cli.js +1 -1
- package/dist/spec.js +2 -0
- package/dist/templates/apiShapes.js +2 -1
- package/dist/templates/bffRoutes.js +9 -2
- package/dist/templates/table.js +2 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -55,8 +55,7 @@ Appended to (multi-entity, idempotent — each entity gets one marker-guarded bl
|
|
|
55
55
|
|
|
56
56
|
## Assumptions / caveats
|
|
57
57
|
|
|
58
|
-
- **
|
|
59
|
-
hex-with-dashes pattern. Adjust by hand if an entity uses a different id type.
|
|
58
|
+
- **Id type comes from the spec's `idType`** (`uuid`/`int`/`string`, written by `ApiCrudRenderer::detectIdType()` off the entity's actual id column) — `validId()` in the generated `[id]/route.ts` and the `is{Entity}` type guard in `api-shapes.ts` are generated to match. Older spec files without `idType` default to `uuid`.
|
|
60
59
|
- **The list search box only searches one field.** If more than one field is marked
|
|
61
60
|
`searchable` in the spec, the list view (backed by ApiPlatform's `SearchFilter`, one query
|
|
62
61
|
param per property) only wires up the first one. The export endpoint ORs across all of them.
|
package/dist/cli.js
CHANGED
|
@@ -100,7 +100,7 @@ function main() {
|
|
|
100
100
|
if (searchableFields(spec).length > 1) {
|
|
101
101
|
console.log(` 4. Note: multiple searchable fields were configured (${searchableFields(spec).join(', ')}); the list view's single search box only queries by "${searchField(spec)}" (ApiPlatform SearchFilter's per-property param convention doesn't support one box matching several properties). The export endpoint does OR across all of them.`);
|
|
102
102
|
}
|
|
103
|
-
console.log(`
|
|
103
|
+
console.log(` Id type: "${spec.idType}" (from the spec's "idType") — validId() in the generated app/api/${names.entitiesKebab}/[id]/route.ts was generated to match.`);
|
|
104
104
|
console.log(` Run "npm run format" afterwards — generated files aren't pre-formatted to this project's Prettier config.`);
|
|
105
105
|
}
|
|
106
106
|
main();
|
package/dist/spec.js
CHANGED
|
@@ -37,11 +37,13 @@ export function loadSpec(specPath) {
|
|
|
37
37
|
if (!Array.isArray(spec.fields) || !spec.fields.every(isFieldSpec)) {
|
|
38
38
|
throw new Error(`Spec file "fields" is missing or malformed: ${specPath}`);
|
|
39
39
|
}
|
|
40
|
+
const idType = spec.idType === 'int' || spec.idType === 'string' || spec.idType === 'uuid' ? spec.idType : 'uuid';
|
|
40
41
|
return {
|
|
41
42
|
entity: spec.entity,
|
|
42
43
|
permissionPrefix: spec.permissionPrefix,
|
|
43
44
|
ownerProperty: typeof spec.ownerProperty === 'string' ? spec.ownerProperty : null,
|
|
44
45
|
timestampField: typeof spec.timestampField === 'string' ? spec.timestampField : null,
|
|
46
|
+
idType,
|
|
45
47
|
fields: spec.fields,
|
|
46
48
|
};
|
|
47
49
|
}
|
|
@@ -18,7 +18,8 @@ export function apiShapesImport(names) {
|
|
|
18
18
|
}
|
|
19
19
|
export function apiShapesBlock(spec, names) {
|
|
20
20
|
const { entityPascal, entitiesPascal } = names;
|
|
21
|
-
const
|
|
21
|
+
const idJsType = 'int' === spec.idType ? 'number' : 'string';
|
|
22
|
+
const checks = ['isRecord(value)', `typeof value.id === '${idJsType}'`];
|
|
22
23
|
for (const field of spec.fields) {
|
|
23
24
|
checks.push(`typeof value.${field.name} === '${tsType(field)}'`);
|
|
24
25
|
}
|
|
@@ -35,7 +35,14 @@ export const dynamic = 'force-dynamic';
|
|
|
35
35
|
export const revalidate = 0;
|
|
36
36
|
`;
|
|
37
37
|
}
|
|
38
|
-
|
|
38
|
+
function validIdCheck(idType) {
|
|
39
|
+
if (idType === 'int')
|
|
40
|
+
return 'return /^[1-9][0-9]*$/.test(id);';
|
|
41
|
+
if (idType === 'string')
|
|
42
|
+
return "return id.length > 0;";
|
|
43
|
+
return "return /^[0-9a-fA-F-]{36}$/.test(id);";
|
|
44
|
+
}
|
|
45
|
+
export function itemRoute(spec, names) {
|
|
39
46
|
const { entityCamel, entitiesKebab } = names;
|
|
40
47
|
return `import type { NextRequest } from 'next/server';
|
|
41
48
|
import { authedBackend } from '@/lib/bff';
|
|
@@ -45,7 +52,7 @@ import { ${entityCamel}Schema } from '@/lib/schemas';
|
|
|
45
52
|
type Params = { params: Promise<{ id: string }> };
|
|
46
53
|
|
|
47
54
|
function validId(id: string) {
|
|
48
|
-
|
|
55
|
+
${validIdCheck(spec.idType)}
|
|
49
56
|
}
|
|
50
57
|
|
|
51
58
|
export async function GET(_request: NextRequest, context: Params) {
|
package/dist/templates/table.js
CHANGED
|
@@ -79,7 +79,7 @@ export function ${entityPascal}Table() {
|
|
|
79
79
|
const [error, setError] = useState('');
|
|
80
80
|
const [deleteTarget, setDeleteTarget] = useState<${entityPascal} | null>(null);
|
|
81
81
|
const [deleting, setDeleting] = useState(false);
|
|
82
|
-
const [selectedIds, setSelectedIds] = useState<Set
|
|
82
|
+
const [selectedIds, setSelectedIds] = useState<Set<${entityPascal}['id']>>(new Set());
|
|
83
83
|
const [bulkDeleteConfirming, setBulkDeleteConfirming] = useState(false);
|
|
84
84
|
const [bulkDeleting, setBulkDeleting] = useState(false);
|
|
85
85
|
const [toastMessage, setToastMessage] = useState(
|
|
@@ -188,7 +188,7 @@ ${hasTitleFilterLine} const trueEmpty = !loading && !error && !hasSearch && t
|
|
|
188
188
|
}
|
|
189
189
|
}, [someOnPageSelected, allOnPageSelected]);
|
|
190
190
|
|
|
191
|
-
function toggleSelect(id:
|
|
191
|
+
function toggleSelect(id: ${entityPascal}['id']) {
|
|
192
192
|
setSelectedIds((prev) => {
|
|
193
193
|
const next = new Set(prev);
|
|
194
194
|
if (next.has(id)) next.delete(id);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kematjaya/crud-ui-generator",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "Next.js CRUD frontend generator that reads crud-specs/{Entity}.json sidecars written by kematjaya/crud-maker-bundle's make:kmj-api-crud and generates pages, components, and BFF routes matching the boilerplate's hand-written Notes feature.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|