@jskit-ai/agent-docs 0.1.27 → 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/DISTR_AGENT.md +1 -0
- package/guide/agent/app-extras/assistant.md +2 -2
- package/guide/agent/app-extras/mobile-capacitor.md +10 -3
- package/guide/agent/app-extras/realtime.md +11 -11
- package/guide/agent/app-setup/a-more-interesting-shell.md +52 -44
- package/guide/agent/app-setup/authentication.md +40 -22
- package/guide/agent/app-setup/console.md +5 -5
- package/guide/agent/app-setup/database-layer.md +15 -15
- package/guide/agent/app-setup/initial-scaffolding.md +37 -5
- package/guide/agent/app-setup/multi-homing.md +28 -28
- package/guide/agent/app-setup/users.md +22 -22
- package/guide/agent/app-setup/working-with-the-jskit-cli.md +13 -6
- package/guide/agent/generators/advanced-cruds.md +111 -68
- package/guide/agent/generators/crud-generators.md +5 -5
- package/guide/agent/generators/ui-generators.md +15 -13
- package/guide/agent/index.md +1 -1
- package/package.json +1 -1
- package/patterns/INDEX.md +4 -1
- package/patterns/client-requests.md +23 -6
- package/patterns/crud-repository-mapping.md +1 -1
- package/patterns/crud-scaffolding.md +10 -1
- package/patterns/filters.md +28 -10
- package/patterns/generated-ui-contract-tracking.md +62 -0
- package/patterns/page-scaffolding.md +17 -0
- package/patterns/placements.md +60 -0
- package/patterns/surfaces.md +5 -1
- package/patterns/ui-testing.md +3 -0
- package/reference/autogen/KERNEL_MAP.md +30 -1
- package/reference/autogen/packages/assistant.md +1 -1
- package/reference/autogen/packages/crud-ui-generator.md +19 -8
- package/reference/autogen/packages/kernel.md +38 -6
- package/reference/autogen/packages/realtime.md +1 -0
- package/reference/autogen/packages/shell-web.md +35 -3
- package/reference/autogen/packages/ui-generator.md +26 -5
- package/reference/autogen/packages/users-core.md +2 -0
- package/reference/autogen/packages/users-web.md +92 -3
- package/reference/autogen/packages/workspaces-web.md +3 -1
- package/reference/autogen/tooling/jskit-cli.md +24 -8
- package/templates/app/AGENTS.md +1 -0
- package/workflow/bootstrap.md +1 -0
|
@@ -249,6 +249,8 @@ packages/contacts/
|
|
|
249
249
|
|
|
250
250
|
src/pages/w/[workspaceSlug]/admin/contacts/
|
|
251
251
|
index.vue
|
|
252
|
+
listBulkActions.js
|
|
253
|
+
listFilters.js
|
|
252
254
|
new.vue
|
|
253
255
|
[contactId]/index.vue
|
|
254
256
|
[contactId]/edit.vue
|
|
@@ -439,6 +441,8 @@ src/pages/w/[workspaceSlug]/admin/contacts/
|
|
|
439
441
|
new.vue
|
|
440
442
|
[contactId]/index.vue
|
|
441
443
|
[contactId]/edit.vue
|
|
444
|
+
listBulkActions.js
|
|
445
|
+
listFilters.js
|
|
442
446
|
_components/CrudAddEditForm.vue
|
|
443
447
|
_components/CrudAddEditFormFields.js
|
|
444
448
|
```
|
|
@@ -449,13 +453,13 @@ This is the list-page container.
|
|
|
449
453
|
|
|
450
454
|
Its job is usually to:
|
|
451
455
|
|
|
452
|
-
- call `
|
|
453
|
-
-
|
|
454
|
-
- render
|
|
456
|
+
- call `useCrudListScreen()`
|
|
457
|
+
- pass page-local `listFilters` and `listBulkActions`
|
|
458
|
+
- render the shared `CrudListScreen`
|
|
455
459
|
- resolve list/view/edit/new URLs
|
|
456
460
|
- pass route query state through when navigating deeper
|
|
457
461
|
|
|
458
|
-
The actual list machinery lives
|
|
462
|
+
The actual list machinery lives in `users-web` shared screen composables and the shared resource contract.
|
|
459
463
|
|
|
460
464
|
### `[contactId]/index.vue`
|
|
461
465
|
|
|
@@ -463,8 +467,8 @@ This is the view-page container.
|
|
|
463
467
|
|
|
464
468
|
Its job is usually to:
|
|
465
469
|
|
|
466
|
-
- call `
|
|
467
|
-
- render the
|
|
470
|
+
- call `useCrudViewScreen()`
|
|
471
|
+
- render the shared `CrudViewScreen`
|
|
468
472
|
- resolve "back" and "edit" navigation
|
|
469
473
|
|
|
470
474
|
Again, the runtime behavior is mostly uphill. The page is a route-level composition layer.
|
|
@@ -475,9 +479,9 @@ These are add/edit route wrappers.
|
|
|
475
479
|
|
|
476
480
|
They usually:
|
|
477
481
|
|
|
478
|
-
- call `
|
|
482
|
+
- call `useCrudAddEditScreen()`
|
|
479
483
|
- wire lookup runtime for lookup-backed fields
|
|
480
|
-
- hand the form runtime into the shared
|
|
484
|
+
- hand the form runtime into the shared `CrudAddEditScreen`
|
|
481
485
|
|
|
482
486
|
These files are mostly containers. That is deliberate.
|
|
483
487
|
|
|
@@ -735,15 +739,14 @@ The safe mental model is:
|
|
|
735
739
|
|
|
736
740
|
### `_components/CrudAddEditForm.vue`
|
|
737
741
|
|
|
738
|
-
This is the
|
|
742
|
+
This is the generated field bridge for the shared add/edit screen.
|
|
739
743
|
|
|
740
744
|
It owns:
|
|
741
745
|
|
|
742
|
-
- the card/surface layout
|
|
743
|
-
- save/cancel buttons
|
|
744
746
|
- which set of generated form fields is rendered in `new` vs `edit`
|
|
747
|
+
- lookup field prop forwarding into those fields
|
|
745
748
|
|
|
746
|
-
It does **not** own persistence logic.
|
|
749
|
+
It does **not** own persistence logic or the shared screen chrome. `CrudAddEditScreen` from `users-web` owns the common title, load state, retry action, save/cancel action row, and form surface.
|
|
747
750
|
|
|
748
751
|
### `_components/CrudAddEditFormFields.js`
|
|
749
752
|
|
|
@@ -766,8 +769,8 @@ That is navigation wiring, not CRUD logic.
|
|
|
766
769
|
|
|
767
770
|
A generated CRUD works because several layers cooperate:
|
|
768
771
|
|
|
769
|
-
1. the route page calls `
|
|
770
|
-
2. those composables
|
|
772
|
+
1. the route page calls `useCrudListScreen()`, `useCrudViewScreen()`, or `useCrudAddEditScreen()`
|
|
773
|
+
2. those screen composables configure the lower-level list/view/add-edit runtimes from `users-web`
|
|
771
774
|
3. the request hits the HTTP route from `registerRoutes.js`
|
|
772
775
|
4. the route executes an action from `actions.js`
|
|
773
776
|
5. the action delegates to the service in `service.js`
|
|
@@ -789,8 +792,9 @@ Use this rule of thumb when deciding where to edit:
|
|
|
789
792
|
| Change default ordering, searchable fields, or ownership autofilter | `contactResource.js` | The shared resource is the single source of truth for both CRUD contract and internal JSON:API resource config |
|
|
790
793
|
| Change SQL, joins, parent filters, or advanced search | `repository.js` | This is the data-access layer |
|
|
791
794
|
| Add cross-record or domain rules on save/delete | `service.js` | This is business logic |
|
|
792
|
-
| Change
|
|
793
|
-
| Change
|
|
795
|
+
| Change shared CRUD screen chrome, load states, or retry behavior | `users-web` shared screen components | Generated pages consume the shared screen contract |
|
|
796
|
+
| Change page-specific display behavior | the route pages, generated slots, and app-owned composables | This is presentation |
|
|
797
|
+
| Change form field layout and inputs | `_components/CrudAddEditForm.vue` and `CrudAddEditFormFields.js` | This is the generated form field layer |
|
|
794
798
|
|
|
795
799
|
## How mature CRUDs grow
|
|
796
800
|
|
|
@@ -824,10 +828,10 @@ This is the default generated list-page pattern for the `contacts` resource from
|
|
|
824
828
|
|
|
825
829
|
#### Client side
|
|
826
830
|
|
|
827
|
-
|
|
831
|
+
The generated list page delegates search wiring to the shared list screen:
|
|
828
832
|
|
|
829
833
|
```js
|
|
830
|
-
const
|
|
834
|
+
const screen = useCrudListScreen({
|
|
831
835
|
resource: uiResource,
|
|
832
836
|
apiSuffix: "/contacts",
|
|
833
837
|
search: {
|
|
@@ -841,15 +845,14 @@ const records = useCrudList({
|
|
|
841
845
|
});
|
|
842
846
|
```
|
|
843
847
|
|
|
844
|
-
Then
|
|
848
|
+
Then render the shared screen:
|
|
845
849
|
|
|
846
850
|
```vue
|
|
847
|
-
<
|
|
848
|
-
v-model="records.searchQuery"
|
|
849
|
-
:loading="records.isSearchDebouncing"
|
|
850
|
-
/>
|
|
851
|
+
<CrudListScreen :screen="screen" />
|
|
851
852
|
```
|
|
852
853
|
|
|
854
|
+
The shared screen binds the search input and passes the search query into the list request.
|
|
855
|
+
|
|
853
856
|
The client runtime debounces the search input, writes the query string to `q`, and trims the list back to the first page when search changes.
|
|
854
857
|
|
|
855
858
|
#### Server side
|
|
@@ -1006,27 +1009,33 @@ Important limits:
|
|
|
1006
1009
|
|
|
1007
1010
|
For the agent-facing quick rule, see `patterns/crud-repository-mapping.md`.
|
|
1008
1011
|
|
|
1009
|
-
### Pattern 3: structured list filters
|
|
1012
|
+
### Pattern 3: client-side structured list filters
|
|
1013
|
+
|
|
1014
|
+
Generated CRUD list pages include a client-side filter seam by default. The generated page imports `./listFilters.js` and passes `listFilters` into `useCrudListScreen(...)`. The shared list screen builds the filter runtime, passes the resulting query params into the list request, and renders `CrudListFilterSurface`.
|
|
1015
|
+
|
|
1016
|
+
If `listFilters.js` is empty, the filter surface renders nothing.
|
|
1010
1017
|
|
|
1011
|
-
|
|
1018
|
+
Use this first when adding filters. The server still needs explicit support for any query params you declare; JSKIT does not infer server filter semantics from the UI.
|
|
1012
1019
|
|
|
1013
1020
|
Do not hand-build:
|
|
1014
1021
|
|
|
1015
1022
|
- one filter shape in the page
|
|
1016
|
-
-
|
|
1017
|
-
-
|
|
1023
|
+
- custom chip/reset/query-param state
|
|
1024
|
+
- a second copy of the same client filter definitions
|
|
1018
1025
|
|
|
1019
1026
|
Instead:
|
|
1020
1027
|
|
|
1021
|
-
1.
|
|
1022
|
-
2.
|
|
1023
|
-
3.
|
|
1024
|
-
4.
|
|
1028
|
+
1. edit the generated page-local `listFilters.js`
|
|
1029
|
+
2. let `useCrudListScreen(...)` wire the filter query params into the list request
|
|
1030
|
+
3. let `CrudListFilterSurface` render controls, chips, clear-one, and clear-all behavior
|
|
1031
|
+
4. add explicit server support separately for the same query params
|
|
1025
1032
|
|
|
1026
|
-
For example, a
|
|
1033
|
+
For example, a generated page-local filter-definition module can look like this:
|
|
1027
1034
|
|
|
1028
1035
|
```js
|
|
1029
|
-
|
|
1036
|
+
import { defineCrudListFilters } from "@jskit-ai/users-web/client/filters";
|
|
1037
|
+
|
|
1038
|
+
const listFilters = defineCrudListFilters({
|
|
1030
1039
|
onlyStaff: {
|
|
1031
1040
|
type: "flag",
|
|
1032
1041
|
label: "Staff"
|
|
@@ -1040,60 +1049,94 @@ export const CONTACTS_LIST_FILTER_DEFINITIONS = Object.freeze({
|
|
|
1040
1049
|
label: "Archived"
|
|
1041
1050
|
}
|
|
1042
1051
|
});
|
|
1052
|
+
|
|
1053
|
+
export { listFilters };
|
|
1054
|
+
```
|
|
1055
|
+
|
|
1056
|
+
### Pattern 3B: client-side bulk list actions
|
|
1057
|
+
|
|
1058
|
+
Generated CRUD list pages also include a client-side bulk-action seam by default. The generated page imports `./listBulkActions.js` and passes `listBulkActions` into `useCrudListScreen(...)`. The shared list screen builds the bulk-action runtime and renders `CrudListBulkActionSurface`.
|
|
1059
|
+
|
|
1060
|
+
If `listBulkActions.js` is empty, selection controls and the bulk action bar stay hidden.
|
|
1061
|
+
|
|
1062
|
+
Use this first when adding selected-record actions. JSKIT does not invent server operations such as delete, archive, approve, or export; the action definition owns that behavior.
|
|
1063
|
+
|
|
1064
|
+
For example:
|
|
1065
|
+
|
|
1066
|
+
```js
|
|
1067
|
+
import { defineCrudListBulkActions } from "@jskit-ai/users-web/client/bulkActions";
|
|
1068
|
+
|
|
1069
|
+
const listBulkActions = defineCrudListBulkActions([
|
|
1070
|
+
{
|
|
1071
|
+
key: "archive",
|
|
1072
|
+
label: "Archive",
|
|
1073
|
+
async run({ selectedIds, clearSelection, reload }) {
|
|
1074
|
+
await archiveContacts(selectedIds);
|
|
1075
|
+
clearSelection();
|
|
1076
|
+
await reload();
|
|
1077
|
+
}
|
|
1078
|
+
}
|
|
1079
|
+
]);
|
|
1080
|
+
|
|
1081
|
+
export { listBulkActions };
|
|
1043
1082
|
```
|
|
1044
1083
|
|
|
1084
|
+
The generated runtime passes action handlers:
|
|
1085
|
+
|
|
1086
|
+
- `selectedIds`
|
|
1087
|
+
- `ids` as an alias for `selectedIds`
|
|
1088
|
+
- `selectedRecords`
|
|
1089
|
+
- `clearSelection()`
|
|
1090
|
+
- `records`
|
|
1091
|
+
- `reload`
|
|
1092
|
+
|
|
1093
|
+
Keep bulk action definitions page-local unless another page needs to share them.
|
|
1094
|
+
|
|
1045
1095
|
#### Exact file checklist
|
|
1046
1096
|
|
|
1047
1097
|
For a generated CRUD, treat this as the concrete file plan:
|
|
1048
1098
|
|
|
1099
|
+
- edit `src/pages/.../contacts/listFilters.js`
|
|
1100
|
+
- make sure the matching server route/action/repository code already accepts and applies the declared query params
|
|
1101
|
+
|
|
1102
|
+
If the filter contract should be shared with server code, promote it into a CRUD package module and import it from both sides:
|
|
1103
|
+
|
|
1049
1104
|
- create `packages/contacts/src/shared/contactListFilters.js`
|
|
1050
1105
|
- update `packages/contacts/src/server/registerRoutes.js` so the list route query validator lists structured filter params explicitly with `createCrudListFilterQueryField(...)`, unless you already extracted list-query composition into `packages/contacts/src/server/listQueryValidators.js`
|
|
1051
1106
|
- update `packages/contacts/src/server/actions.js` so the list action input validator includes that same explicit contract choice
|
|
1052
1107
|
- update `packages/contacts/src/server/repository.js` so the list query path builds the `createCrudListFilters(...)` runtime and calls `applyQuery(...)`
|
|
1053
|
-
- update the app-owned list page or list-runtime composable, usually under `src/pages/.../contacts/` or `src/composables/...`, so it builds `useCrudListFilters(...)`, passes `queryParams` into `useCrudList(...)`, and renders chips / reset behavior from that runtime
|
|
1054
|
-
|
|
1055
|
-
The only file in that list that is normally **new** is the shared module:
|
|
1056
|
-
|
|
1057
|
-
- `packages/contacts/src/shared/contactListFilters.js`
|
|
1058
|
-
|
|
1059
|
-
The others are normally edits to the generated CRUD package and the app-owned page layer that already exist.
|
|
1060
1108
|
|
|
1061
1109
|
#### Client side
|
|
1062
1110
|
|
|
1063
|
-
|
|
1111
|
+
Generated CRUD list pages pass the page-local filter definitions into the shared list screen:
|
|
1064
1112
|
|
|
1065
1113
|
```js
|
|
1066
|
-
|
|
1114
|
+
import { listFilters } from "./listFilters.js";
|
|
1067
1115
|
|
|
1068
|
-
const
|
|
1116
|
+
const screen = useCrudListScreen({
|
|
1069
1117
|
resource: uiResource,
|
|
1070
1118
|
apiSuffix: "/contacts?include=pets",
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
mode: "query"
|
|
1074
|
-
},
|
|
1075
|
-
queryParams: listFilters.queryParams,
|
|
1076
|
-
syncToRoute: {
|
|
1077
|
-
enabled: true,
|
|
1078
|
-
search: true,
|
|
1079
|
-
queryParams: true,
|
|
1080
|
-
queryParamBlacklist: ["include", "cursor", "limit"]
|
|
1081
|
-
}
|
|
1119
|
+
listFilters,
|
|
1120
|
+
routeQueryBlacklist: Object.freeze(["include", "cursor", "limit"])
|
|
1082
1121
|
});
|
|
1083
1122
|
```
|
|
1084
1123
|
|
|
1085
|
-
|
|
1124
|
+
```vue
|
|
1125
|
+
<CrudListScreen :screen="screen" />
|
|
1126
|
+
```
|
|
1127
|
+
|
|
1128
|
+
Inside that shared screen runtime, `useCrudListFilters(...)` gives the list:
|
|
1086
1129
|
|
|
1087
|
-
- `
|
|
1088
|
-
- `
|
|
1089
|
-
- `
|
|
1090
|
-
- `
|
|
1091
|
-
- `
|
|
1092
|
-
- `
|
|
1093
|
-
- `
|
|
1094
|
-
- `
|
|
1095
|
-
- `
|
|
1096
|
-
- `
|
|
1130
|
+
- `filterRuntime.values`
|
|
1131
|
+
- `filterRuntime.queryParams`
|
|
1132
|
+
- `filterRuntime.presets`
|
|
1133
|
+
- `filterRuntime.activeChips`
|
|
1134
|
+
- `filterRuntime.hasActiveFilters`
|
|
1135
|
+
- `filterRuntime.clearChip(...)`
|
|
1136
|
+
- `filterRuntime.clearFilters()`
|
|
1137
|
+
- `filterRuntime.toggle(...)` for flag filters
|
|
1138
|
+
- `filterRuntime.applyPreset(...)`
|
|
1139
|
+
- `filterRuntime.matchesPreset(...)`
|
|
1097
1140
|
|
|
1098
1141
|
So the same runtime owns:
|
|
1099
1142
|
|
|
@@ -1247,13 +1290,13 @@ async function list(query = {}, callOptions = {}) {
|
|
|
1247
1290
|
|
|
1248
1291
|
#### Best practices
|
|
1249
1292
|
|
|
1250
|
-
-
|
|
1293
|
+
- Keep client-only filters in the generated page-local `listFilters.js`. Move definitions into a CRUD package only when server code or another page needs to share them.
|
|
1251
1294
|
- Keep the filter keys identical all the way through: definition key, query param key, and repository meaning.
|
|
1252
1295
|
- Prefer `createCrudListFilterQueryField(...)` plus `createCrudListFilterQuerySchema(...)` at route/action boundaries so accepted structured-filter params stay visible in app code.
|
|
1253
1296
|
- Use `type: "presence"` for null/not-null filters such as assigned vs unassigned storage. Do not model those as custom enums plus `applyQuery(...)` overrides unless the SQL semantics are genuinely different from `whereNotNull(...)` / `whereNull(...)`.
|
|
1254
1297
|
- Use `createCrudListFilters(...)` unless the list semantics are truly unusual.
|
|
1255
1298
|
- Use `q` for free-text and explicit query params for structured filters.
|
|
1256
|
-
- Run `jskit doctor` after wiring filters. It flags inline
|
|
1299
|
+
- Run `jskit doctor` after wiring filters. It flags inline filter-definition objects passed into `useCrudListFilters(...)` or `createCrudListFilters(...)`.
|
|
1257
1300
|
|
|
1258
1301
|
### Pattern 4: lookup-backed structured filters
|
|
1259
1302
|
|
|
@@ -1265,7 +1308,7 @@ This is the next real-world step: filters like `supplierContactId`, `locationId`
|
|
|
1265
1308
|
|
|
1266
1309
|
The right pattern is:
|
|
1267
1310
|
|
|
1268
|
-
1. keep the lookup filter in
|
|
1311
|
+
1. keep the lookup filter in `listFilters.js` or in shared definitions when server code also imports it
|
|
1269
1312
|
2. use `useCrudListFilters(...)` for state, chips, and query params
|
|
1270
1313
|
3. use `useCrudListFilterLookups(...)` for option loading and label resolution
|
|
1271
1314
|
|
|
@@ -262,7 +262,7 @@ npx jskit generate crud-server-generator scaffold \
|
|
|
262
262
|
|
|
263
263
|
This creates an app-local package under `packages/contacts/`.
|
|
264
264
|
|
|
265
|
-
If the table should be CRUD-owned
|
|
265
|
+
If the table should already be CRUD-owned but should **not** expose public HTTP CRUD routes yet, add:
|
|
266
266
|
|
|
267
267
|
```bash
|
|
268
268
|
--internal
|
|
@@ -322,7 +322,7 @@ npm run devlinks
|
|
|
322
322
|
|
|
323
323
|
The same rule applies after later server scaffolds such as `addresses` and `comments`. The UI generator can still read the generated resource file directly, but the app runtime needs the local package install boundary to be completed before the CRUD can boot normally.
|
|
324
324
|
|
|
325
|
-
For standard CRUDs, that file is
|
|
325
|
+
For standard CRUDs, that file is intentionally compact. It uses `defineCrudResource(...)` from `@jskit-ai/resource-crud-core`, authors the canonical `schema` / `searchSchema` / `defaultSort` / `autofilter` shape once, and lets JSKIT derive the standard CRUD operation contracts from it.
|
|
326
326
|
|
|
327
327
|
### Step 3: scaffold the UI
|
|
328
328
|
|
|
@@ -498,7 +498,7 @@ It will be the block added for the `w/[workspaceSlug]/admin/contacts/[contactId]
|
|
|
498
498
|
|
|
499
499
|
Depending on what outlets already exist in the app, that block may try to place `Addresses` in the shell menu or in the contact subpages outlet. For this example, remove it either way and keep navigation manual from the contact view.
|
|
500
500
|
|
|
501
|
-
This
|
|
501
|
+
This workflow has one intentional manual cleanup:
|
|
502
502
|
|
|
503
503
|
- keep the routed pages
|
|
504
504
|
- remove the auto menu link
|
|
@@ -657,7 +657,7 @@ If the contact view page should stay visible while child comment routes render u
|
|
|
657
657
|
npx jskit generate ui-generator add-subpages \
|
|
658
658
|
w/[workspaceSlug]/admin/contacts/[contactId]/index.vue \
|
|
659
659
|
--title "Contact" \
|
|
660
|
-
--subtitle "
|
|
660
|
+
--subtitle "Contact activity and notes."
|
|
661
661
|
```
|
|
662
662
|
|
|
663
663
|
That is the point where the comments example deliberately overlaps with the `ui-generator` chapter. This pattern needs both:
|
|
@@ -772,4 +772,4 @@ They become one workflow:
|
|
|
772
772
|
2. scaffold the server/resource package
|
|
773
773
|
3. scaffold only the UI shape that the feature actually needs
|
|
774
774
|
|
|
775
|
-
The next chapter is [Advanced CRUDs](/guide/generators/advanced-cruds). Read it as the structural follow-on to this one: this chapter teaches how to generate the CRUD, and the next one teaches how to reason about the files
|
|
775
|
+
The next chapter is [Advanced CRUDs](/guide/generators/advanced-cruds). Read it as the structural follow-on to this one: this chapter teaches how to generate the CRUD, and the next one teaches how to reason about the generated files the app owns.
|
|
@@ -80,7 +80,7 @@ The important default is this:
|
|
|
80
80
|
- if JSKIT sees no nearer routed host, the new page gets a normal shell/menu placement
|
|
81
81
|
- if JSKIT does see a nearer routed host, the new page gets linked into that host instead
|
|
82
82
|
|
|
83
|
-
Open the app
|
|
83
|
+
Open the app to see a real `/reports` page plus a real shell link for it.
|
|
84
84
|
|
|
85
85
|
### Customizing the generated menu link
|
|
86
86
|
|
|
@@ -217,7 +217,7 @@ After the command, the page gains three important pieces:
|
|
|
217
217
|
- a `ShellOutlet` for the child-page tabs
|
|
218
218
|
- `RouterView`
|
|
219
219
|
|
|
220
|
-
That
|
|
220
|
+
That lets the page keep rendering shared content while child routes render underneath it.
|
|
221
221
|
|
|
222
222
|
This is the first big distinction in this chapter:
|
|
223
223
|
|
|
@@ -247,7 +247,7 @@ The route page is still a normal page file. What changes is the inferred placeme
|
|
|
247
247
|
- no parent host found -> shell/menu entry
|
|
248
248
|
- nearest parent host found -> child link inside that host
|
|
249
249
|
|
|
250
|
-
So JSKIT is not switching to a different generator.
|
|
250
|
+
So JSKIT is not switching to a different generator. The route tree has a routed host above the new page, so the inferred placement behavior follows that host.
|
|
251
251
|
|
|
252
252
|
### Making a child page the default landing route
|
|
253
253
|
|
|
@@ -358,7 +358,7 @@ Then upgrade that page into a host:
|
|
|
358
358
|
npx jskit generate ui-generator add-subpages \
|
|
359
359
|
home/contacts/[contactId].vue \
|
|
360
360
|
--title "Contact" \
|
|
361
|
-
--subtitle "
|
|
361
|
+
--subtitle "Contact activity and notes."
|
|
362
362
|
```
|
|
363
363
|
|
|
364
364
|
This time the parent is a file route, not an `index.vue` route.
|
|
@@ -436,13 +436,13 @@ This is different from `page` in a very important way:
|
|
|
436
436
|
|
|
437
437
|
So if the thing you are adding should live *inside* an existing shell region, not at its own route, `placed-element` is the right command.
|
|
438
438
|
|
|
439
|
-
By default, the element
|
|
439
|
+
By default, the element targets the semantic status placement:
|
|
440
440
|
|
|
441
441
|
```text
|
|
442
|
-
shell
|
|
442
|
+
shell.status
|
|
443
443
|
```
|
|
444
444
|
|
|
445
|
-
That is why this is such a good command for widgets, status panels, and compact shell extensions.
|
|
445
|
+
That is why this is such a good command for widgets, status panels, and compact shell extensions. The default shell topology maps `shell.status` to the concrete shell status outlet for each layout class.
|
|
446
446
|
|
|
447
447
|
### When `--surface` matters
|
|
448
448
|
|
|
@@ -469,19 +469,19 @@ A practical example is:
|
|
|
469
469
|
npx jskit generate ui-generator placed-element \
|
|
470
470
|
--name "Ops Panel" \
|
|
471
471
|
--surface admin \
|
|
472
|
-
--placement shell
|
|
472
|
+
--placement shell.status
|
|
473
473
|
```
|
|
474
474
|
|
|
475
|
-
`shell
|
|
475
|
+
`shell.status` can be global across several surfaces. If your app has several enabled surfaces, `--surface admin` tells JSKIT which one this element is actually meant for.
|
|
476
476
|
|
|
477
477
|
So the rule of thumb is:
|
|
478
478
|
|
|
479
|
-
- page-owned
|
|
480
|
-
- shared shell
|
|
479
|
+
- page-owned semantic placement target -> surface is often inferable
|
|
480
|
+
- shared shell semantic placement in a multi-surface app -> pass `--surface`
|
|
481
481
|
|
|
482
482
|
### `--placement`
|
|
483
483
|
|
|
484
|
-
Use `--placement` when the default target `shell
|
|
484
|
+
Use `--placement` when the default target `shell.status` is not what you want.
|
|
485
485
|
|
|
486
486
|
This is the option that answers:
|
|
487
487
|
|
|
@@ -489,6 +489,8 @@ This is the option that answers:
|
|
|
489
489
|
|
|
490
490
|
In practice, it is the first override you will use for `placed-element`.
|
|
491
491
|
|
|
492
|
+
`--placement` expects a semantic placement id such as `shell.status`, `shell.global-actions`, or `settings.sections`. Concrete `host:position` outlets are exposed through topology, not used as normal placed-element authoring targets.
|
|
493
|
+
|
|
492
494
|
### `--path`
|
|
493
495
|
|
|
494
496
|
Use `--path` when the component file should live somewhere other than `src/components`.
|
|
@@ -682,4 +684,4 @@ That is why the commands fit together cleanly:
|
|
|
682
684
|
- `page` can then create nested child routes under those hosts
|
|
683
685
|
- `placed-element` and `outlet` handle the non-routed side of the same UI system
|
|
684
686
|
|
|
685
|
-
Once the UI you want
|
|
687
|
+
Once the UI you want needs real database-backed list/view/new/edit behavior instead of only page structure, move to the CRUD generators chapter.
|
package/guide/agent/index.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
This guide is the main hands-on path through JSKIT.
|
|
6
6
|
|
|
7
|
-
It
|
|
7
|
+
It starts with a fast reproducible Quickstart, then steps back to the smaller scaffold-first chapters that explain how the app is assembled piece by piece. After that, it introduces the database-backed users layer, expands into console and workspace-aware app structure, and adds a small `App extras` section for optional runtime packages such as the Android Capacitor shell, realtime, and assistant. Finally, the guide breaks out generator-specific workflows into their own section.
|
|
8
8
|
|
|
9
9
|
## Table of Contents
|
|
10
10
|
|
package/package.json
CHANGED
package/patterns/INDEX.md
CHANGED
|
@@ -26,10 +26,12 @@ How to use it:
|
|
|
26
26
|
- `page-redirects.md`
|
|
27
27
|
- live actions, checkbox, toggle, patch button, inline action, `useCommand()`
|
|
28
28
|
- `live-actions.md`
|
|
29
|
-
- ajax, fetch, API call, request, endpoint, HTTP client, `useList()`, `useView()`, `useAddEdit()`, `useEndpointResource()`, `usersWebHttpClient`
|
|
29
|
+
- ajax, fetch, API call, request, endpoint, HTTP client, `useCrudListScreen()`, `useCrudViewScreen()`, `useCrudAddEditScreen()`, `useList()`, `useView()`, `useAddEdit()`, `useEndpointResource()`, `usersWebHttpClient`
|
|
30
30
|
- `client-requests.md`
|
|
31
31
|
- playwright, browser test, e2e, ui verification, authenticated ui test, test auth, dev login as, dev auth bypass
|
|
32
32
|
- `ui-testing.md`
|
|
33
|
+
- generated UI contract, design contract, navigation roles, density, placeholder copy, card shells, shared CRUD screens
|
|
34
|
+
- `generated-ui-contract-tracking.md`
|
|
33
35
|
- filter, filters, search facets, chips, date range, enum filter, lookup filter, `useCrudListFilters`, `createCrudListFilters`
|
|
34
36
|
- `filters.md`
|
|
35
37
|
- `searchSchema`, `search: true`, `applyFilter`, server search, query validators, backend filters, internal JSON REST filters
|
|
@@ -49,6 +51,7 @@ How to use it:
|
|
|
49
51
|
- [live-actions.md](./live-actions.md)
|
|
50
52
|
- [client-requests.md](./client-requests.md)
|
|
51
53
|
- [ui-testing.md](./ui-testing.md)
|
|
54
|
+
- [generated-ui-contract-tracking.md](./generated-ui-contract-tracking.md)
|
|
52
55
|
- [filters.md](./filters.md)
|
|
53
56
|
- [server-search.md](./server-search.md)
|
|
54
57
|
- [crud-repository-mapping.md](./crud-repository-mapping.md)
|
|
@@ -6,7 +6,7 @@ Use when:
|
|
|
6
6
|
- custom endpoint reads or writes
|
|
7
7
|
- AJAX questions
|
|
8
8
|
- replacing raw `fetch(...)` calls
|
|
9
|
-
- choosing between `useCommand()`, `useList()`, `useView()`, `useAddEdit()`, and `useEndpointResource()`
|
|
9
|
+
- choosing between shared CRUD screens, `useCommand()`, `useList()`, `useView()`, `useAddEdit()`, and `useEndpointResource()`
|
|
10
10
|
|
|
11
11
|
Rules:
|
|
12
12
|
|
|
@@ -19,22 +19,31 @@ Rules:
|
|
|
19
19
|
Choose the function like this:
|
|
20
20
|
|
|
21
21
|
```js
|
|
22
|
-
// 1.
|
|
22
|
+
// 1. Generated CRUD route screen
|
|
23
|
+
const screen = useCrudListScreen({ ... });
|
|
24
|
+
|
|
25
|
+
// 2. Button/toggle/small mutation
|
|
23
26
|
const command = useCommand({ ... });
|
|
24
27
|
|
|
25
|
-
//
|
|
28
|
+
// 3. List endpoint
|
|
26
29
|
const list = useList({ ... });
|
|
27
30
|
|
|
28
|
-
//
|
|
31
|
+
// 4. Single-record endpoint
|
|
29
32
|
const view = useView({ ... });
|
|
30
33
|
|
|
31
|
-
//
|
|
34
|
+
// 5. Form save flow
|
|
32
35
|
const form = useAddEdit({ ... });
|
|
33
36
|
|
|
34
|
-
//
|
|
37
|
+
// 6. Truly custom endpoint
|
|
35
38
|
const resource = useEndpointResource({ ... });
|
|
36
39
|
```
|
|
37
40
|
|
|
41
|
+
Use the shared CRUD screen wrappers when the route is a generated CRUD page:
|
|
42
|
+
|
|
43
|
+
- `useCrudListScreen()` plus `CrudListScreen` for list route pages
|
|
44
|
+
- `useCrudViewScreen()` plus `CrudViewScreen` for record view route pages
|
|
45
|
+
- `useCrudAddEditScreen()` plus `CrudAddEditScreen` for generated new/edit route pages
|
|
46
|
+
|
|
38
47
|
Use the CRUD wrappers when they fit:
|
|
39
48
|
|
|
40
49
|
- `useCrudList()` for routed CRUD lists
|
|
@@ -55,6 +64,13 @@ Why this is the standard JSKIT shape:
|
|
|
55
64
|
- Use `requestQueryParams` for endpoint query strings on list, view, and add/edit runtimes.
|
|
56
65
|
- Keep `apiUrlTemplate` path-only. Do not put `?include=...` or other query strings in URL templates.
|
|
57
66
|
|
|
67
|
+
Error presentation rules:
|
|
68
|
+
|
|
69
|
+
- Resource load failures should stay local to the screen with the runtime's `loadError` state and a retry action.
|
|
70
|
+
- Do not force global banners for ordinary list/view/add-edit load failures; the shell error policy treats `resource-load` as `silent` by default.
|
|
71
|
+
- Use `useUiFeedback()` or `useCommand()` for user-triggered action feedback. Those flows report `action-feedback`, and the app-owned shell error policy decides the channel.
|
|
72
|
+
- Use explicit shell error intents only for exceptional app-level cases: `app-recoverable` for recoverable shell/runtime failures and `blocking` for failures that require user attention.
|
|
73
|
+
|
|
58
74
|
Avoid:
|
|
59
75
|
|
|
60
76
|
- raw `fetch(...)` for standard page or component work
|
|
@@ -62,3 +78,4 @@ Avoid:
|
|
|
62
78
|
- manually concatenating scoped route params into API URLs
|
|
63
79
|
- using a lower-level seam when a higher-level routed CRUD or command runtime already fits
|
|
64
80
|
- smuggling query params into `apiUrlTemplate`
|
|
81
|
+
- reporting routine resource load errors through hand-written global snackbar/banner calls
|
|
@@ -80,7 +80,7 @@ const repositoryRuntime = createCrudResourceRuntime(resource, {
|
|
|
80
80
|
});
|
|
81
81
|
```
|
|
82
82
|
|
|
83
|
-
What CRUD core
|
|
83
|
+
What CRUD core does for you:
|
|
84
84
|
- default select columns include only column-backed output fields
|
|
85
85
|
- create/update write payloads serialize standard writable `date-time` fields centrally
|
|
86
86
|
- create/update write payloads also apply any explicit field write serializers centrally
|
|
@@ -19,12 +19,20 @@ Rules:
|
|
|
19
19
|
- For a CRUD-backed entity, start with `jskit generate crud-server-generator scaffold ...`.
|
|
20
20
|
- Unless the table is already owned by a JSKIT baseline package or is an explicit narrow exception recorded in `.jskit/table-ownership.json`, every persisted app-owned table must go through that server CRUD step first.
|
|
21
21
|
- That server scaffold is the crucial first step even if no CRUD UI will be created yet.
|
|
22
|
-
- If the table should be CRUD-owned
|
|
22
|
+
- If the table should already be CRUD-owned but should not expose public CRUD HTTP routes yet, scaffold it with `jskit generate crud-server-generator scaffold ... --internal` instead of dropping to direct knex or a hand-built pseudo-repository.
|
|
23
23
|
- Create the real table directly in the database before scaffolding. `crud-server-generator` reads the live table shape.
|
|
24
24
|
- If `crud-server-generator` is going to own the CRUD, do not hand-write a separate CRUD migration for that table. The generator installs and manages the CRUD migration scaffold itself.
|
|
25
25
|
- Do not scaffold CRUD UI, hand-build CRUD routes, or hand-build CRUD endpoints before the server CRUD package and shared resource file exist.
|
|
26
26
|
- Treat the generated shared resource file as the canonical CRUD contract for later UI scaffolding and CRUD behavior changes.
|
|
27
27
|
- `feature-server-generator` is not the default lane for ordinary persisted entities. Use it for workflows or orchestration that sit on top of CRUD-owned tables, or for rare explicit non-CRUD exceptions.
|
|
28
|
+
- Generated CRUD UI must be compact-first. Lists need searchable cards on compact widths and tables only for medium/expanded layouts.
|
|
29
|
+
- Generated CRUD list screens need real loading, empty, and error states. Empty copy should name the resource, such as "No customers yet", and offer the create action when available.
|
|
30
|
+
- Generated CRUD view/new/edit screens should use page headers plus direct sheet panels. Do not use generic card shells as the page architecture.
|
|
31
|
+
- Compact CRUD actions should be reachable without a drawer. Use a mobile-visible primary action or FAB for create flows.
|
|
32
|
+
- Row actions should collapse into an overflow/action menu on compact widths and can be inline on wider layouts.
|
|
33
|
+
- Bulk actions should be declared in the generated page-local `listBulkActions.js`. The generated list owns selection state, keeps selection controls hidden until actions exist, and exposes selected ids/records to action handlers.
|
|
34
|
+
- Structured filters should use shared filter definitions and collapse to compact filter controls/sheets when they outgrow simple search. Do not stack dense desktop filter bars on phone widths.
|
|
35
|
+
- Use `--navigation-role` for CRUD list placement intent. Main resources can stay `primary`; nested/detail/workflow CRUD routes should usually be `secondary`, `workflow`, or `none`.
|
|
28
36
|
|
|
29
37
|
Meaning of `--internal`:
|
|
30
38
|
|
|
@@ -38,3 +46,4 @@ Avoid:
|
|
|
38
46
|
- starting with `crud-ui-generator` before the server scaffold exists
|
|
39
47
|
- hand-building CRUD routes or validators that duplicate the generated server resource contract
|
|
40
48
|
- letting a new app-owned table exist in the live database without either generated CRUD ownership or a documented `.jskit/table-ownership.json` exception
|
|
49
|
+
- accepting table-only CRUD UI as "done" when the route is visible on phone widths
|