Add initial project files
This commit is contained in:
@@ -0,0 +1,28 @@
|
||||
# Devtools
|
||||
|
||||
The admin devtools module contains development-only interfaces for generating business modules, reviewing UI and form components in the sandbox, and managing the local application version.
|
||||
|
||||
Source: `apps/admin/src/devtools/`
|
||||
|
||||
The frontend module is registered through `apps/admin/src/devtools/config/module.ts`. Its routes, menu, and translations are exposed only when `IS_DEV_MODE` is enabled. All devtools frontend routes are protected routes.
|
||||
|
||||
## Documentation
|
||||
|
||||
| Area | Purpose | Documentation |
|
||||
| ---------------- | --------------------------------------------------------------------------------------- | ------------------------------------------------ |
|
||||
| Module generator | Generates backend and admin frontend modules from a database table and UI configuration | [Module generator](./module-generator/Readme.md) |
|
||||
| Sandbox | Interactive examples and copyable source snippets for shared UI and form controls | [Sandbox](./sandbox/Readme.md) |
|
||||
|
||||
The version manager is part of the same source module but is outside the scope of these guides.
|
||||
|
||||
## Frontend Routes
|
||||
|
||||
| Route | Page |
|
||||
| ------------------------------------ | ------------------------------ |
|
||||
| `/devtools/module-generator` | Module generator |
|
||||
| `/devtools/module-generator/docs` | Module generator documentation |
|
||||
| `/devtools/sandbox/ui` | Shared UI component sandbox |
|
||||
| `/devtools/sandbox/forms` | Composed form-control sandbox |
|
||||
| `/devtools/module-generator/version` | Version manager |
|
||||
|
||||
Devtools module is enabled only in dev mode!
|
||||
@@ -0,0 +1,53 @@
|
||||
# Module Generator
|
||||
|
||||
Creates a backend module and a matching admin frontend module from an existing PostgreSQL table, combining database metadata with frontend code builders and Handlebars templates. The output is a maintainable starting point — generated authorization, validation, relationships, API behavior, labels, and translations must still be reviewed for the product domain.
|
||||
|
||||
| Part | Source |
|
||||
| ------------------------------- | --------------------------------------------------- |
|
||||
| Generator page and form | `apps/admin/src/devtools/` |
|
||||
| Copied frontend module template | `apps/admin/src/modules/_template/` |
|
||||
| Generator HTTP endpoints | `apps/backend/start/routes.ts` |
|
||||
| Generator controller | `apps/backend/app/controllers/module_controller.ts` |
|
||||
| Database inspection | `apps/backend/app/services/database_inspector.ts` |
|
||||
| Backend Handlebars templates | `apps/backend/app/templates/backend/` |
|
||||
| Frontend Handlebars templates | `apps/backend/app/templates/frontend/` |
|
||||
|
||||
Details: [frontend.md](./frontend.md) (UI, payload builders, generated frontend), [backend.md](./backend.md) (endpoints, inspection, templates, generated backend), [troubleshooting.md](./troubleshooting.md) (module not loaded / missing from menu).
|
||||
|
||||
## Prerequisites
|
||||
|
||||
1. Backend running from `apps/backend/` (destinations resolve relative to `process.cwd()`); admin app in dev mode; authenticated session (all endpoints use auth middleware).
|
||||
2. The source table created and migrated — the form reads PostgreSQL `information_schema`.
|
||||
3. A module name valid as a TypeScript identifier, route segment, permission slug, and folder name.
|
||||
|
||||
## Generation Flow
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
A["Select table and assets"] --> B["Configure form and table"]
|
||||
B --> C["Frontend builders create source strings"]
|
||||
C --> D["POST /makeModule/generate"]
|
||||
D --> E["Inspect database metadata"]
|
||||
E --> F["Copy frontend _template"]
|
||||
E --> G["Render backend templates"]
|
||||
F --> H["Write configured frontend files"]
|
||||
G --> I["Write backend module and tests"]
|
||||
```
|
||||
|
||||
Output goes to `apps/backend/modules/<ModuleName>/`, `apps/backend/tests/modules/<ModuleName>/`, and `apps/admin/src/modules/<name>/`. Frontend modules are discovered through their `config/module.ts` manifest; backend routes load from `apps/backend/modules/` at startup.
|
||||
|
||||
## Operational Behavior
|
||||
|
||||
- Generation writes directly into both workspaces — no preview, transaction, or rollback; existing files may be overwritten. Generate into a clean name and inspect the Git diff.
|
||||
- Select `model` whenever you select `api` — API generation imports the model for its Swagger schema.
|
||||
- The frontend template is always copied; form and datatable files are populated only when selected.
|
||||
- Generated code is not formatted or committed automatically.
|
||||
|
||||
## Verification Checklist
|
||||
|
||||
1. Inspect the Git diff; confirm route paths, API prefixes, permission slugs, and naming.
|
||||
2. Review nullable fields, dates, passwords, foreign keys, validation, relationship names; replace generic labels and translations.
|
||||
3. Verify list/create/update/delete, authorization, and validation-error behavior.
|
||||
4. Run `npm run build:backend` and `npm run build:admin`; run the generated backend tests and add domain coverage.
|
||||
|
||||
Generated frontend follows the [React frontend architecture](../../../architecture/react-frontend/Readme.md); review backend modules against the [backend architecture](../../../architecture/backend/Readme.md).
|
||||
@@ -0,0 +1,61 @@
|
||||
# Module Generator Backend
|
||||
|
||||
Authenticated schema-inspection and generation endpoints; backend files rendered from Handlebars templates, frontend files written from a copied template plus source strings supplied by the admin app.
|
||||
|
||||
## Endpoints
|
||||
|
||||
Registered in `apps/backend/start/routes.ts`, all behind `middleware.auth()`:
|
||||
|
||||
| Method | Path | Purpose |
|
||||
| ------ | ---------------------------- | -------------------------------------------------- |
|
||||
| `GET` | `/makeModule/tables` | List eligible public PostgreSQL tables |
|
||||
| `GET` | `/makeModule/columns/:table` | Return columns used to configure form/table fields |
|
||||
| `POST` | `/makeModule/generate` | Generate selected frontend and backend assets |
|
||||
|
||||
## Database Inspection
|
||||
|
||||
`database_inspector.ts` reads `information_schema`: only the `public` schema, excluding framework/auth/access-control/migration/system tables. Columns ending in `_id` become belongs-to candidates; tables containing a `<table>_id` column become has-many candidates; searchable string/numeric columns are derived for resource controllers. **Relationship inference is heuristic** — review generated model imports, class and property names.
|
||||
|
||||
## Type Mapping
|
||||
|
||||
| Database category | Lucid TypeScript type | Vine type | Frontend type |
|
||||
| --------------------- | --------------------- | --------- | --------------- |
|
||||
| integer/numeric types | `number` | `number` | `number` |
|
||||
| boolean | `boolean` | `boolean` | `boolean` |
|
||||
| date/timestamp | `DateTime` | `date` | `IsoDateString` |
|
||||
| other values | `string` | `string` | `string` |
|
||||
|
||||
Nullable columns get `| null` and optional validation; `created_at`, `updated_at`, `deleted_at` receive special handling.
|
||||
|
||||
## Backend Output
|
||||
|
||||
```txt
|
||||
apps/backend/modules/<ModuleName>/
|
||||
├── api/ # controller, create/update validators, routes.ts
|
||||
├── config/permissions.ts
|
||||
├── controller/<entity>_controller.ts
|
||||
├── model/<entity>.ts
|
||||
├── policy/<entity>_policy.ts
|
||||
├── validators/ # <entity>_create/_update_validator.ts
|
||||
└── routes.ts
|
||||
|
||||
apps/backend/tests/modules/<ModuleName>/ # <entity>.spec.ts, <entity>.api.spec.ts
|
||||
```
|
||||
|
||||
`<entity>` is the table name minus its final character — assumes a plural table name; review irregular plurals.
|
||||
|
||||
## Asset Behavior
|
||||
|
||||
- **Model** — `model.hbs` with columns, inferred relationships, searchable fields, optional password hashing.
|
||||
- **Controller and routes** — authenticated resource controller with list/search/sort/pagination and CRUD; `routes.hbs` registers an `apiOnly` resource route with authentication.
|
||||
- **Validators** — separate create/update Vine validators; primary keys and timestamp/deletion fields excluded; email, password, nullable, and trimming behavior inferred from names and metadata.
|
||||
- **Policy and permissions** — Bouncer policy with view/create/update/delete, `config/permissions.ts`, and a lazy policy import added to `app/policies/main.ts` when missing. Slugs derive from the module name (`<name>s.view`, `.create`, `.update`, `.delete`) — review already-plural names, this is not a general pluralization system.
|
||||
- **API subtree** — a second CRUD controller and routes under `/api/<entity>` with validators and a test; imports the model for `modelToSchema` Swagger metadata. **The generated API template adds no authentication middleware — add auth and authorization before exposing the route.**
|
||||
|
||||
## Frontend Writes
|
||||
|
||||
The backend copies `apps/admin/src/modules/_template` to `apps/admin/src/modules/<name>`, then writes the configured form (component + definition), datatable and list page, manage trigger, menu, module manifest, generated `config/meta.ts`, and database-derived `entities/Entity.ts`. See [frontend.md](./frontend.md).
|
||||
|
||||
## Extending
|
||||
|
||||
Handlebars helpers available: `eq`, `not`, `or`, `and`. To add a generated artifact: add the template under `apps/backend/app/templates/`, an explicit destination in the controller `paths` object (parent dir via existing path initialization), render only when its asset is selected, and pass normalized metadata — never query the database inside templates. Add generation tests for nullable values, dates, numerics, passwords, and irregular names; verify both builds. No hidden writes — every destination, including shared registries like `app/policies/main.ts`, stays visible in the controller flow and documented here.
|
||||
@@ -0,0 +1,69 @@
|
||||
# Module Generator Frontend
|
||||
|
||||
The generator UI collects module metadata, selected assets, form layout, and datatable configuration, converts it into TypeScript/TSX source strings, and sends them to the backend generator.
|
||||
|
||||
| Responsibility | Source (under `apps/admin/src/devtools/`) |
|
||||
| ------------------------------------------------------ | ------------------------------------------------------------- |
|
||||
| Page submission, notifications, and mutation callbacks | `pages/ModuleGeneratorPage/index.tsx` |
|
||||
| Payload assembly | `pages/ModuleGeneratorPage/lib/buildGenerateModulePayload.ts` |
|
||||
| Form state, initial values, and sections | `view/forms/ModuleGeneratorForm/` |
|
||||
| Payload DTOs and HTTP client | `data/api/` |
|
||||
| React Query hooks | `data/queries/` |
|
||||
| Source-code builders | `data/builders/` |
|
||||
|
||||
The page submits through `useGenerateModule`; success resets the form and shows an alert, errors go through the shared React Query error helpers.
|
||||
|
||||
## Metadata and Assets
|
||||
|
||||
Required metadata: `name` (frontend folder, backend module identity, route segment, permission-slug base), `table` (existing PostgreSQL table), `generate` (selected assets). Backend assets are all selected by default; form and datatable are opt-in.
|
||||
|
||||
| Asset | Workspace | Output |
|
||||
| ------------ | --------- | --------------------------------------------------- |
|
||||
| `model` | Backend | Lucid model |
|
||||
| `controller` | Backend | Authenticated resource controller and test |
|
||||
| `routes` | Backend | Resource route registration |
|
||||
| `validator` | Backend | Create and update Vine validators |
|
||||
| `policy` | Backend | Bouncer policy and permission configuration |
|
||||
| `api` | Backend | API controller, validators, routes, and test |
|
||||
| `form` | Frontend | Form definition, component, and modal/sheet trigger |
|
||||
| `datatable` | Frontend | List page table and menu entry |
|
||||
|
||||
Before generation the UI reads `GET /makeModule/tables` (table selector) and `GET /makeModule/columns/:table` (field choices) — cached via React Query, columns disabled until a table is selected.
|
||||
|
||||
## Payload Builders
|
||||
|
||||
`buildGenerateModulePayload` delegates to focused builders:
|
||||
|
||||
| Builder | Payload field | Generated destination |
|
||||
| ----------------------------------- | ---------------- | ----------------------------------- |
|
||||
| `FormSourceBuilder.buildComponent` | `form` | `view/forms/MainForm/Component.tsx` |
|
||||
| `FormSourceBuilder.buildDefinition` | `formDefinition` | `view/forms/MainForm/form.ts` |
|
||||
| `ManageTriggerSourceBuilder` | `formDialog` | `view/widgets/ManageTrigger.tsx` |
|
||||
| `DataTableSourceBuilder` | `dataTable` | `view/widgets/DataTable.tsx` |
|
||||
| `ListPageSourceBuilder` | `list` | `pages/ListPage/index.tsx` |
|
||||
| `MenuSourceBuilder` | `menu` | `config/menu.ts` |
|
||||
| `ModuleConfigSourceBuilder` | `module` | `config/module.ts` |
|
||||
|
||||
Form disabled → `form`, `formDefinition`, `formDialog` are `null`. Datatable disabled → `dataTable` and `menu` are `null`, and the list-page builder emits a page without a table.
|
||||
|
||||
## Generated Frontend Architecture
|
||||
|
||||
```txt
|
||||
apps/admin/src/modules/<name>/
|
||||
├── api/ # create.ts, delete.ts, detailed.ts, keys.ts, list.ts, update.ts
|
||||
├── config/ # translation/, menu.ts, messages.ts, meta.ts, module.ts, routing.tsx
|
||||
├── entities/ # Entity.ts (from database columns)
|
||||
├── pages/ListPage/
|
||||
└── view/
|
||||
├── forms/MainForm/ # Component.tsx, constants.ts, form.ts, index.ts, submit.ts
|
||||
├── layouts/Layout.tsx
|
||||
└── widgets/ # DataTable.tsx, DeleteTrigger.tsx, ManageTrigger.tsx
|
||||
```
|
||||
|
||||
`api/` deliberately has no barrel file — each CRUD operation is its own file, imported directly (`import { useCreate } from "../../../api/create"`); `keys.ts` owns query key builders, `list.ts`/`detailed.ts` their invalidation helpers.
|
||||
|
||||
Layer ownership: `entities/Entity.ts` is shared by API hooks, forms, and widgets; `api/` owns Axios calls, hooks, keys, and notifications; `pages/` owns route composition and permission gates; `view/forms/` owns schema, values, UI, and submission; `view/widgets/` owns stateful interactions; `config/` integrates routes, menu, translations, and permissions with the module registry.
|
||||
|
||||
## Extending
|
||||
|
||||
To add a generated frontend artifact: add its configuration type under `entities/`, UI controls to `ModuleGeneratorForm`, a focused builder under `data/builders/` (for dynamic source), the payload field to `GenerateModulePayload` + `buildGenerateModulePayload`, accept and write it in `module_controller.ts`, and add static structure to `_template/` when every module needs it. Verify generation with and without the asset selected.
|
||||
@@ -0,0 +1,31 @@
|
||||
# Module Generator Troubleshooting
|
||||
|
||||
## Generated Files Exist but the Module Is Not Loaded
|
||||
|
||||
Docker file watching does not reliably pick up entirely new directories: the backend discovers `apps/backend/modules/` routes at startup, and the admin app discovers `apps/admin/src/modules/**/config/module.ts` through Vite's module registry. Restart both services and reload the admin app:
|
||||
|
||||
```bash
|
||||
docker compose restart
|
||||
```
|
||||
|
||||
Still not loaded? Check logs for TypeScript errors, invalid generated imports, route-loading or manifest errors:
|
||||
|
||||
```bash
|
||||
docker compose logs --tail=200 backend admin
|
||||
```
|
||||
|
||||
(Outside Compose, restart the backend and admin dev processes manually.)
|
||||
|
||||
## Module Loads but Is Missing from the Menu
|
||||
|
||||
Usually a permission mismatch or invalid menu/module configuration. Check in order:
|
||||
|
||||
1. **Permission slugs match exactly** — backend `apps/backend/modules/<ModuleName>/config/permissions.ts` (e.g. `{ slug: 'products.view' }`) vs frontend `apps/admin/src/modules/<name>/config/meta.ts` (`MODULE_PERMISSION_VIEW = 'products.view'`), for view/create/update/delete. Pluralization is heuristic — already-plural or irregular names need manual correction.
|
||||
2. **The current role owns the view permission** — the menu filters on `MODULE_PERMISSION_VIEW`: the permission exists in access control, the role has it with the exact slug, and the session was refreshed after role changes.
|
||||
3. **`config/menu.ts`** — valid exported menu factory, `path` uses `MODULE_ROOT_PATH`, `permissions` contains `MODULE_PERMISSION_VIEW`, the route exists. The menu is generated with the Datatable asset — without it, add a menu manually or keep the module hidden.
|
||||
4. **`config/module.ts`** — exports a valid `AppModuleConfig`, unique module ID, not `enabled: false`, exposes `menu`, `routes`, `messages`.
|
||||
5. **`config/routing.tsx`** — `MODULE_ROOT_PATH`, the menu path, and the generated route all point to the same URL (a broken route and an invisible menu item are separate problems).
|
||||
|
||||
## Final Checks
|
||||
|
||||
Run `npm run build:backend` and `npm run build:admin` to expose generated TypeScript or import errors, then verify: both module directories exist with the selected assets, services were restarted, slugs match, the role has the view permission, and the menu path matches the route. Module discovery errors are reported in backend and admin startup logs.
|
||||
@@ -0,0 +1,19 @@
|
||||
# Devtools Sandbox
|
||||
|
||||
The sandbox is an interactive development surface for reviewing shared admin UI components and composed form controls. Each example renders the real component and exposes a copyable usage snippet.
|
||||
|
||||
## Routes and Source
|
||||
|
||||
| Route | Source | Coverage |
|
||||
| ------------------------- | ------------------------------------------------- | --------------------------------------------- |
|
||||
| `/devtools/sandbox/ui` | `apps/admin/src/devtools/pages/SandboxUIPage/` | Shared UI primitives and composed UI behavior |
|
||||
| `/devtools/sandbox/forms` | `apps/admin/src/devtools/pages/SandboxFormsPage/` | Shared composed form controls |
|
||||
|
||||
## Maintenance Rules
|
||||
|
||||
- Use real shared components; do not duplicate their implementation in devtools.
|
||||
- Keep examples small enough to show one API or behavior clearly.
|
||||
- Keep rendered examples and source snippets synchronized.
|
||||
- Prefer adding edge-state examples when a component has loading, empty, error, disabled, or destructive behavior.
|
||||
- Do not add backend dependencies to the sandbox unless the example explicitly documents and isolates them.
|
||||
- Remove sandbox examples when the underlying shared API is removed.
|
||||
Reference in New Issue
Block a user