@ghentcdh/crouton-cli 0.0.1-alpha.81 → 0.0.1-alpha.89
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 +87 -0
- package/agent-files/.claude/skills/crouton/SKILL.md +59 -0
- package/agent-files/.claude/skills/crouton/references/common-mistakes.md +89 -0
- package/agent-files/.claude/skills/crouton/references/data-sources.md +47 -0
- package/agent-files/.claude/skills/crouton/references/field-inputs.md +52 -0
- package/agent-files/.claude/skills/crouton/references/recipes.md +222 -0
- package/agent-files/.claude/skills/crouton/references/relations.md +150 -0
- package/agent-files/.claude/skills/crouton/references/resource-json.md +326 -0
- package/agent-files/.cursor/rules/crouton.mdc +39 -0
- package/{create-datasource → dist/create-datasource}/index.js +7440 -4094
- package/dist/create-datasource/index.js.map +1 -0
- package/{index.js → dist/index.js} +10172 -6341
- package/dist/index.js.map +1 -0
- package/dist/package.json +22 -0
- package/package.json +38 -8
- package/create-datasource/index.js.map +0 -1
- package/index.js.map +0 -1
package/README.md
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# @ghentcdh/crouton-cli
|
|
2
|
+
|
|
3
|
+
CLI for managing Crouton projects — update resources, datasources, and more.
|
|
4
|
+
|
|
5
|
+
## Local Development
|
|
6
|
+
|
|
7
|
+
Three ways to test your changes, ordered from fastest to most thorough.
|
|
8
|
+
|
|
9
|
+
### 1. Run the built CLI directly (quickest)
|
|
10
|
+
|
|
11
|
+
Best for: changes to `update resources` and other CLI commands.
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
# in the crouton repo
|
|
15
|
+
pnpm nx build crouton-cli
|
|
16
|
+
# or watch mode:
|
|
17
|
+
cd packages/crouton-cli && npx tsup --watch
|
|
18
|
+
|
|
19
|
+
# in your test project
|
|
20
|
+
node ~/path/to/crouton/packages/crouton-cli/dist/index.js update resources
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Tip — add an alias so you don't retype the path:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
alias crouton-dev="node ~/path/to/crouton/packages/crouton-cli/dist/index.js"
|
|
27
|
+
crouton-dev update resources
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
### 2. Run `create-crouton` from your checkout
|
|
33
|
+
|
|
34
|
+
Best for: changes to the scaffold flow (prompts, templates, next-steps output).
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
pnpm nx build create-crouton
|
|
38
|
+
cd ~/project/workshop_crouton
|
|
39
|
+
node ~/path/to/crouton/packages/create-crouton/dist/index.js my-app
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Local templates are bundled into `dist`, so you get your edits. The generated `package.json` still references `@ghentcdh/*@<version>`, so `pnpm install` pulls from npm — fine for testing prompts and templates. Pass `--no-install` to skip the install step.
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
### 3. Local npm registry via Verdaccio (most complete)
|
|
47
|
+
|
|
48
|
+
Best for: testing the full `npx @ghentcdh/create-crouton` flow, including the install step, an unpublished `crouton-cli`, and `crouton-prisma`. Run this at least once before cutting a release.
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
# start the registry (http://localhost:4873)
|
|
52
|
+
npx verdaccio
|
|
53
|
+
|
|
54
|
+
# build everything and publish each package to the local registry
|
|
55
|
+
pnpm nx run-many -t build
|
|
56
|
+
(cd packages/crouton-cli/dist && npm publish --registry http://localhost:4873)
|
|
57
|
+
(cd packages/create-crouton && npm publish --registry http://localhost:4873)
|
|
58
|
+
# repeat for crouton-core / crouton-api / crouton-vue / crouton-prisma
|
|
59
|
+
|
|
60
|
+
# scaffold using only the local registry
|
|
61
|
+
cd ~/project/workshop_crouton
|
|
62
|
+
npm_config_registry=http://localhost:4873 npx @ghentcdh/create-crouton@latest my-app
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Before republishing the same version, either bump it (e.g. `0.0.1-local.2`) or unpublish first:
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
npm unpublish --force --registry http://localhost:4873 @ghentcdh/crouton-cli
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Verdaccio proxies anything it doesn't have through to the public npm registry. You can also let Nx manage the registry setup:
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
pnpm nx g @nx/js:setup-verdaccio
|
|
75
|
+
# then use:
|
|
76
|
+
pnpm nx local-registry
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
### Which option to use?
|
|
82
|
+
|
|
83
|
+
| Situation | Option |
|
|
84
|
+
|-----------|--------|
|
|
85
|
+
| Fixing a CLI command (`.env`, schema, etc.) | **1** |
|
|
86
|
+
| Changing scaffold prompts or templates | **2** |
|
|
87
|
+
| Pre-release smoke test (e.g. before alpha.83) | **3** |
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: crouton
|
|
3
|
+
description: Add or change crouton resources, relations, field inputs, layouts, operations and data sources (resource.json, datasource.json, crouton.json). Use for any change to these files.
|
|
4
|
+
crouton-version: 0.0.1-alpha.82
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
> If `crouton --version` differs from `crouton-version` above, the skill may be stale — check the installed version's docs in `node_modules/@ghentcdh/crouton-core/agent-docs/`.
|
|
8
|
+
|
|
9
|
+
# Crouton skill
|
|
10
|
+
|
|
11
|
+
Crouton is a resource-driven admin UI framework. This skill teaches the correct workflow for adding and changing
|
|
12
|
+
resources.
|
|
13
|
+
|
|
14
|
+
## What crouton is
|
|
15
|
+
|
|
16
|
+
- Resources live in `<resourcesDir>` (see `crouton.json`). Each is a folder with `resource.json`.
|
|
17
|
+
- Data sources live in `<dataSourcesDir>`. Each is a folder with `datasource.json`.
|
|
18
|
+
- The Prisma schema (`schema.prisma`) is the source of truth for models and columns. Never hand-edit column lists that
|
|
19
|
+
`crouton update` owns.
|
|
20
|
+
|
|
21
|
+
## The workflow loop
|
|
22
|
+
|
|
23
|
+
1. Change `schema.prisma` (add/modify models or fields).
|
|
24
|
+
2. Run `crouton update` — regenerates column lists, migrations, and type stubs.
|
|
25
|
+
3. Edit `resource.json` (UI/behaviour: labels, field inputs, relations, layout).
|
|
26
|
+
4. Run `crouton validate` — fix any reported issues before proceeding.
|
|
27
|
+
5. Typecheck/build to confirm no TS errors.
|
|
28
|
+
|
|
29
|
+
## Key rules
|
|
30
|
+
|
|
31
|
+
- Never hand-edit auto-generated sections inside `resource.json` (marked with comments).
|
|
32
|
+
- Use `manyToOne` for autocomplete lookups; `oneToMany` for embedded tables.
|
|
33
|
+
- `kind: "custom"` only when you have a `repository.ts` (config-only resource). For non-Prisma data, use `adapter` in
|
|
34
|
+
`datasource.json`.
|
|
35
|
+
- After any `resource.json` or `crouton.json` change, run `crouton validate`.
|
|
36
|
+
- Layout nodes use `items` only — there is no `controls` list. Items render in declared order.
|
|
37
|
+
- Table settings (`rowActions`, `tableActions`, `searchFields`, `filterFields`) go on the `type: "table"` node inside
|
|
38
|
+
`layout.table`, never on an outer wrapper node.
|
|
39
|
+
- When `layout.<ctx>` is defined, it is the **full list** for that context — only listed columns render. Unlisted
|
|
40
|
+
columns are hidden regardless of `hiddenIn*` flags.
|
|
41
|
+
- After editing layouts, run `crouton validate` to confirm no migration is needed.
|
|
42
|
+
|
|
43
|
+
## Project-specific notes
|
|
44
|
+
|
|
45
|
+
If `.claude/skills/crouton/project.md` exists, read it first — it holds project conventions that override these
|
|
46
|
+
defaults.
|
|
47
|
+
|
|
48
|
+
## References
|
|
49
|
+
|
|
50
|
+
- `references/resource-json.md` — annotated full example + key table
|
|
51
|
+
- `references/relations.md` — relation recipes
|
|
52
|
+
- `references/field-inputs.md` — which input for which column type
|
|
53
|
+
- `references/data-sources.md` — prisma vs custom adapter
|
|
54
|
+
- `references/recipes.md` — task recipes
|
|
55
|
+
- `references/common-mistakes.md` — common mistakes
|
|
56
|
+
|
|
57
|
+
## Docs for the installed version
|
|
58
|
+
|
|
59
|
+
`node_modules/@ghentcdh/crouton-core/agent-docs/README.md`
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# Common mistakes
|
|
2
|
+
|
|
3
|
+
## 1. Using removed root keys
|
|
4
|
+
|
|
5
|
+
Many root keys moved. Run `crouton validate` to detect them — it reports the correct replacement path for each key.
|
|
6
|
+
|
|
7
|
+
| Wrong (old) | Right (new) |
|
|
8
|
+
|-----------------------------------------|------------------------------------------------------------------------------------------------|
|
|
9
|
+
| `"model": "Book"` at root | `"source": { "model": "Book" }` |
|
|
10
|
+
| `"database": "main"` at root | `"source": { "datasource": "main" }` |
|
|
11
|
+
| `"table": "books"` at root | `"source": { "dbTable": "books" }` |
|
|
12
|
+
| `"idType": "number"` at root | `"source": { "idType": "number" }` |
|
|
13
|
+
| `"display": { "mode": "page" }` at root | `"layout": { "view": { "container": { "mode": "page" } } }` |
|
|
14
|
+
| `"modalSize": "lg"` at root | `"layout": { "form": { "container": { "size": "lg" } } }` |
|
|
15
|
+
| `"include": ["author"]` at root | `"operations": { "findAll": { "include": ["author"] }, "findOne": { "include": ["author"] } }` |
|
|
16
|
+
| `"calculatedColumns": [...]` | `"columns": { "myCol": { "computed": { "sql": "..." } } }` |
|
|
17
|
+
| `column.showInLookup: true` | `"lookup": { "labels": ["colId"] }` on resource root |
|
|
18
|
+
|
|
19
|
+
## 2. Putting relation config in fieldInput instead of column.relation
|
|
20
|
+
|
|
21
|
+
`fieldInput` only configures the UI widget. Relation data model belongs in `column.relation`.
|
|
22
|
+
|
|
23
|
+
```json
|
|
24
|
+
// WRONG
|
|
25
|
+
"authorId": {
|
|
26
|
+
"fieldInput": {
|
|
27
|
+
"type": "autocomplete",
|
|
28
|
+
"relationType": "manyToOne",
|
|
29
|
+
"resource": "./author/resource.json",
|
|
30
|
+
"foreignKey": "authorId"
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// RIGHT
|
|
35
|
+
"authorId": {
|
|
36
|
+
"relation": {
|
|
37
|
+
"resource": "./author/resource.json",
|
|
38
|
+
"type": "manyToOne",
|
|
39
|
+
"foreignKey": "authorId",
|
|
40
|
+
"displayKey": "name"
|
|
41
|
+
},
|
|
42
|
+
"fieldInput": { "type": "autocomplete" }
|
|
43
|
+
}
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## 3. Using `format: "relation"` to detect relations
|
|
47
|
+
|
|
48
|
+
`fieldInput.format` is removed. Use `isRelation(col)` from `@ghentcdh/crouton-core` or check `!!col.relation`.
|
|
49
|
+
|
|
50
|
+
## 4. Setting both `enum` and `fieldInput.options.values`
|
|
51
|
+
|
|
52
|
+
`enum` resolves a shared list from `crouton.enums.json` at load time. Setting both causes a validation error.
|
|
53
|
+
Use `enum` for shared lists, `fieldInput.options.values` for local inline lists — never both.
|
|
54
|
+
|
|
55
|
+
## 5. Hand-editing auto-generated columns
|
|
56
|
+
|
|
57
|
+
`crouton update` owns columns derived from Prisma. Edits to column names or types are overwritten on the next
|
|
58
|
+
`crouton update`.
|
|
59
|
+
Only edit `fieldInput`, `label`, `hidden`, `required`, `relation` inside each column entry.
|
|
60
|
+
|
|
61
|
+
## 6. `kind: "custom"` when you need `adapter: "custom"`
|
|
62
|
+
|
|
63
|
+
`kind: "custom"` = this resource has a custom `repository.ts`.
|
|
64
|
+
If your data source is external (not Prisma), set `adapter: "custom"` in `datasource.json` instead.
|
|
65
|
+
|
|
66
|
+
## 7. Typos in fieldInput type
|
|
67
|
+
|
|
68
|
+
The `type` value is case-sensitive: `autocomplete` not `Autocomplete`; `select` not `Select`.
|
|
69
|
+
Run `crouton validate` — it reports unknown fieldInput types.
|
|
70
|
+
|
|
71
|
+
## 8. Missing `crouton validate` after edits
|
|
72
|
+
|
|
73
|
+
Schema violations, unknown resources in relations, and broken references are only caught by the validator.
|
|
74
|
+
Always run `crouton validate` after any change to `resource.json` or `crouton.json`.
|
|
75
|
+
|
|
76
|
+
## 9. Wrong relation direction
|
|
77
|
+
|
|
78
|
+
`manyToOne`: FK is on **this** resource. `oneToMany`: FK is on the **target** resource.
|
|
79
|
+
Swapping them causes the relation panel to query the wrong table.
|
|
80
|
+
|
|
81
|
+
## 10. `colspan`/`rowspan`/`hideLabel` on table items
|
|
82
|
+
|
|
83
|
+
These form-layout options have no effect on table items and are rejected with a validation error.
|
|
84
|
+
Remove them from `layout.table` items — use `width`, `align`, `truncate` for table-specific display.
|
|
85
|
+
|
|
86
|
+
## 11. Table/column settings on the wrapper instead of the table node
|
|
87
|
+
|
|
88
|
+
In a wrapper table layout, `rowActions`, `tableActions`, `searchFields`, `filterFields`, `pageSize` etc.
|
|
89
|
+
belong on the **`type: "table"` node**, not on the outer wrapper.
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# Data sources — decision table
|
|
2
|
+
|
|
3
|
+
## Prisma-backed (default)
|
|
4
|
+
|
|
5
|
+
Use when: data lives in the relational DB and a Prisma model exists.
|
|
6
|
+
|
|
7
|
+
`datasource.json`:
|
|
8
|
+
```json
|
|
9
|
+
{
|
|
10
|
+
"name": "default",
|
|
11
|
+
"prismaSchema": "prisma/schema.prisma",
|
|
12
|
+
"prismaConfig": "prisma/prisma.config.ts",
|
|
13
|
+
"urlEnv": "DATABASE_URL",
|
|
14
|
+
"zodOutput": "generated/default/types/src",
|
|
15
|
+
"clientOutput": "generated/default/client",
|
|
16
|
+
"default": true
|
|
17
|
+
}
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
- `crouton update` introspects this datasource and manages column lists.
|
|
21
|
+
- Resources reference it implicitly when `kind` is `"default"`.
|
|
22
|
+
|
|
23
|
+
## Custom adapter
|
|
24
|
+
|
|
25
|
+
Use when: data comes from an external API, file, or non-Prisma store.
|
|
26
|
+
|
|
27
|
+
`datasource.json`:
|
|
28
|
+
```json
|
|
29
|
+
{
|
|
30
|
+
"name": "external-api",
|
|
31
|
+
"adapter": "custom"
|
|
32
|
+
}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
- `crouton update` skips Prisma introspection for this datasource.
|
|
36
|
+
- Resources using it must supply a `repository.ts` that implements the crouton repository interface.
|
|
37
|
+
- Set `kind: "custom"` in `resource.json` and point `datasource` to this name.
|
|
38
|
+
|
|
39
|
+
## kind vs adapter
|
|
40
|
+
|
|
41
|
+
| Scenario | resource.json `kind` | datasource.json `adapter` |
|
|
42
|
+
|----------|----------------------|---------------------------|
|
|
43
|
+
| Prisma-backed resource | `"default"` (or omit) | omit (Prisma) |
|
|
44
|
+
| Config-only resource with custom repo | `"custom"` | omit (Prisma still used for other resources) |
|
|
45
|
+
| Fully external data source | `"custom"` | `"custom"` |
|
|
46
|
+
|
|
47
|
+
**Common mistake**: using `kind: "custom"` when you only need a custom adapter. If your data is in the DB but you want custom read logic, keep `kind: "default"` and override the repository at the NestJS module level.
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# Field inputs — column type → input type
|
|
2
|
+
|
|
3
|
+
| Column type (Prisma) | Recommended fieldInput type | Notes |
|
|
4
|
+
|---------------------|-----------------------------|-------|
|
|
5
|
+
| `String` | `string` | Default for text |
|
|
6
|
+
| `String` (long text) | `textarea` | Use when content is multi-line prose |
|
|
7
|
+
| `String` (rich text) | `markdown` | Renders a Markdown editor |
|
|
8
|
+
| `Int` / `Float` / `Decimal` | `number` | |
|
|
9
|
+
| `Boolean` | `toggle` | Renders a toggle switch |
|
|
10
|
+
| `DateTime` | `date` | Date picker; use `date-range` for range filters |
|
|
11
|
+
| `String[]` / `Int[]` | `array` | Multi-value field |
|
|
12
|
+
| Enum | `select` | Pass `options` from the enum values |
|
|
13
|
+
| FK column (relation) | `autocomplete` | Requires `resource` + `labelField` |
|
|
14
|
+
| PK / internal | `hidden` | Suppresses the field from forms |
|
|
15
|
+
| Computed / read-only | `string` + `readonly: true` | Shows value, disallows editing |
|
|
16
|
+
|
|
17
|
+
## autocomplete options
|
|
18
|
+
|
|
19
|
+
```json
|
|
20
|
+
{
|
|
21
|
+
"type": "autocomplete",
|
|
22
|
+
"resource": "author",
|
|
23
|
+
"labelField": "name"
|
|
24
|
+
}
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
`resource` — the target resource name (folder name under `resourcesDir`).
|
|
28
|
+
`labelField` — the column from the target resource to show as the label.
|
|
29
|
+
|
|
30
|
+
## select options
|
|
31
|
+
|
|
32
|
+
```json
|
|
33
|
+
{
|
|
34
|
+
"type": "select",
|
|
35
|
+
"options": [
|
|
36
|
+
{ "value": "draft", "label": "Draft" },
|
|
37
|
+
{ "value": "published", "label": "Published" }
|
|
38
|
+
]
|
|
39
|
+
}
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Options can also be sourced from `crouton.enums.json` via `enumKey`.
|
|
43
|
+
|
|
44
|
+
## custom field input
|
|
45
|
+
|
|
46
|
+
Register a custom Vue component, then reference it:
|
|
47
|
+
|
|
48
|
+
```json
|
|
49
|
+
{ "type": "custom", "component": "MyCustomInput" }
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
The component must be registered globally or via `useCrouton` before the form mounts.
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
# Recipes — common tasks
|
|
2
|
+
|
|
3
|
+
## Add a resource for an existing Prisma model
|
|
4
|
+
|
|
5
|
+
1. Confirm the model exists in `schema.prisma`.
|
|
6
|
+
2. Run `crouton update` and select the model when prompted.
|
|
7
|
+
3. Crouton writes `<resourcesDir>/<name>/resource.json`.
|
|
8
|
+
4. Edit `resource.json`: set labels, choose fieldInput types, add relations.
|
|
9
|
+
5. Run `crouton validate`.
|
|
10
|
+
|
|
11
|
+
Minimal resource:
|
|
12
|
+
|
|
13
|
+
```json
|
|
14
|
+
{
|
|
15
|
+
"schemaVersion": 1,
|
|
16
|
+
"name": "book",
|
|
17
|
+
"title": "Books",
|
|
18
|
+
"source": { "model": "Book" },
|
|
19
|
+
"columns": {
|
|
20
|
+
"id": { "idField": true, "hiddenInForm": true },
|
|
21
|
+
"title": { "label": "Title" }
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Add a manyToOne relation (autocomplete)
|
|
27
|
+
|
|
28
|
+
In `resource.json` of the resource holding the FK:
|
|
29
|
+
|
|
30
|
+
```json
|
|
31
|
+
{
|
|
32
|
+
"columns": {
|
|
33
|
+
"authorId": {
|
|
34
|
+
"label": "Author",
|
|
35
|
+
"relation": {
|
|
36
|
+
"resource": "./author/resource.json",
|
|
37
|
+
"type": "manyToOne",
|
|
38
|
+
"foreignKey": "authorId",
|
|
39
|
+
"displayKey": "name"
|
|
40
|
+
},
|
|
41
|
+
"fieldInput": { "type": "autocomplete" }
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
"operations": {
|
|
45
|
+
"findAll": { "include": ["author"] },
|
|
46
|
+
"findOne": { "include": ["author"] }
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Add a oneToMany relation (embedded panel)
|
|
52
|
+
|
|
53
|
+
In `resource.json` of the parent resource:
|
|
54
|
+
|
|
55
|
+
```json
|
|
56
|
+
{
|
|
57
|
+
"columns": {
|
|
58
|
+
"reviews": {
|
|
59
|
+
"label": "Reviews",
|
|
60
|
+
"hiddenInTable": true,
|
|
61
|
+
"hiddenInForm": true,
|
|
62
|
+
"relation": {
|
|
63
|
+
"resource": "./review/resource.json",
|
|
64
|
+
"type": "oneToMany",
|
|
65
|
+
"foreignKey": "bookId",
|
|
66
|
+
"sort": "createdAt",
|
|
67
|
+
"sortDir": "desc"
|
|
68
|
+
},
|
|
69
|
+
"fieldInput": { "type": "relation" }
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
"operations": {
|
|
73
|
+
"findOne": { "include": ["reviews"] }
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Add an enum select
|
|
79
|
+
|
|
80
|
+
1. Add the enum to `crouton.enums.json` (or let `crouton update` create it).
|
|
81
|
+
2. In `resource.json` use `enum` to reference it:
|
|
82
|
+
|
|
83
|
+
```json
|
|
84
|
+
{
|
|
85
|
+
"columns": {
|
|
86
|
+
"status": {
|
|
87
|
+
"enum": "BookStatus",
|
|
88
|
+
"fieldInput": { "type": "select" }
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Or use inline `values` for a local list (not shared):
|
|
95
|
+
|
|
96
|
+
```json
|
|
97
|
+
{
|
|
98
|
+
"columns": {
|
|
99
|
+
"genre": {
|
|
100
|
+
"fieldInput": {
|
|
101
|
+
"type": "select",
|
|
102
|
+
"options": { "values": [
|
|
103
|
+
{ "value": "fiction", "label": "Fiction" },
|
|
104
|
+
{ "value": "non-fiction", "label": "Non-fiction" }
|
|
105
|
+
]}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Never set both `enum` and `fieldInput.options.values` — validation error.
|
|
113
|
+
|
|
114
|
+
## Right-align numbers, newest-first, 50 per page
|
|
115
|
+
|
|
116
|
+
```json
|
|
117
|
+
{
|
|
118
|
+
"layout": {
|
|
119
|
+
"table": {
|
|
120
|
+
"pageSize": 50,
|
|
121
|
+
"pageSizes": [20, 50, 100],
|
|
122
|
+
"items": [
|
|
123
|
+
{ "id": "title", "sortable": true },
|
|
124
|
+
{ "id": "price", "align": "right", "width": "xs", "sortable": true },
|
|
125
|
+
{ "id": "createdAt", "sortable": true, "defaultSort": "desc" }
|
|
126
|
+
]
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
`defaultSort: "desc"` sets newest-first as the initial sort direction.
|
|
133
|
+
|
|
134
|
+
## Truncate long text in table
|
|
135
|
+
|
|
136
|
+
```json
|
|
137
|
+
{ "id": "description", "truncate": 3 }
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
`truncate: 3` = max 3 lines before clamp. `truncate: true` = single-line ellipsis.
|
|
141
|
+
|
|
142
|
+
## Pin a column sticky left
|
|
143
|
+
|
|
144
|
+
```json
|
|
145
|
+
{ "id": "name", "pin": "left", "width": "md" }
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
## Open view as page, form as large modal
|
|
149
|
+
|
|
150
|
+
```json
|
|
151
|
+
{
|
|
152
|
+
"layout": {
|
|
153
|
+
"form": { "container": { "mode": "modal", "size": "xl" } },
|
|
154
|
+
"view": { "container": { "mode": "page" } }
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
## Load a relation only on detail, not in list
|
|
160
|
+
|
|
161
|
+
```json
|
|
162
|
+
{
|
|
163
|
+
"operations": {
|
|
164
|
+
"findAll": { "include": [] },
|
|
165
|
+
"findOne": { "include": ["author", "chapters"] }
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
## Add a computed column (SQL subquery)
|
|
171
|
+
|
|
172
|
+
```json
|
|
173
|
+
{
|
|
174
|
+
"columns": {
|
|
175
|
+
"chapterCount": {
|
|
176
|
+
"label": "Chapters",
|
|
177
|
+
"type": "number",
|
|
178
|
+
"computed": {
|
|
179
|
+
"sql": "SELECT COUNT(*) FROM chapter WHERE chapter.book_id = main.id"
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
Use `main` as the alias for the parent table row.
|
|
187
|
+
|
|
188
|
+
## Set dropdown labels for this resource
|
|
189
|
+
|
|
190
|
+
Control how this resource's records appear in other resources' autocompletes:
|
|
191
|
+
|
|
192
|
+
```json
|
|
193
|
+
{
|
|
194
|
+
"lookup": {
|
|
195
|
+
"labels": ["title", "authorId"],
|
|
196
|
+
"separator": " · "
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
Replaces `showInLookup: true` on individual columns.
|
|
202
|
+
|
|
203
|
+
## Custom field input + renderer registration
|
|
204
|
+
|
|
205
|
+
1. Create a Vue component `MyInput.vue`.
|
|
206
|
+
2. Register it via `useCrouton`:
|
|
207
|
+
```ts
|
|
208
|
+
crouton.registerRenderer('MyInput', MyInput);
|
|
209
|
+
```
|
|
210
|
+
3. Reference in `resource.json`:
|
|
211
|
+
```json
|
|
212
|
+
{ "fieldInput": { "type": "custom", "component": "MyInput" } }
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
## Config-only resource (kind: custom)
|
|
216
|
+
|
|
217
|
+
Use when the resource has no Prisma model or uses fully custom data logic.
|
|
218
|
+
|
|
219
|
+
1. Create `<resourcesDir>/<name>/resource.json` with `"kind": "custom"`.
|
|
220
|
+
2. Create `<resourcesDir>/<name>/repository.ts` implementing the crouton repository interface.
|
|
221
|
+
3. Register the repository in the NestJS module.
|
|
222
|
+
4. Run `crouton validate`.
|