Initial commit
This commit is contained in:
@@ -0,0 +1,32 @@
|
||||
# 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 root:
|
||||
|
||||
```txt
|
||||
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,78 @@
|
||||
# Module Generator
|
||||
|
||||
The module generator creates a backend module and a matching admin frontend module from an existing PostgreSQL table. It combines database metadata with frontend code builders and Handlebars templates.
|
||||
|
||||
The generator is intended to produce a maintainable starting point. Generated authorization rules, validation, relationships, API behavior, labels, translations, and field controls must still be reviewed for the product domain.
|
||||
|
||||
## Source Ownership
|
||||
|
||||
| Part | Source |
|
||||
| ------------------------------- | ---------------------------------------------- |
|
||||
| Generator page and form | `apps/admin/src/devtools/` |
|
||||
| Copied frontend module template | `apps/admin/src/modules/_template/` |
|
||||
| Generator HTTP endpoints | `backend/start/routes.ts` |
|
||||
| Generator controller | `backend/app/controllers/module_controller.ts` |
|
||||
| Database inspection | `backend/app/services/database_inspector.ts` |
|
||||
| Backend Handlebars templates | `backend/app/templates/backend/` |
|
||||
| Frontend Handlebars templates | `backend/app/templates/frontend/` |
|
||||
|
||||
See [frontend.md](./frontend.md) for the UI, payload builders, and generated frontend architecture. See [backend.md](./backend.md) for endpoints, database inspection, templates, and generated backend files. If generated files are not loaded or the module is missing from the menu, use the [troubleshooting guide](./troubleshooting.md).
|
||||
|
||||
## Prerequisites
|
||||
|
||||
1. Run the backend from the `backend/` workspace. The controller resolves backend and frontend destinations relative to `process.cwd()`.
|
||||
2. Run the admin app with dev mode enabled so the devtools module is registered.
|
||||
3. Authenticate in the admin app. All generator endpoints use authentication middleware.
|
||||
4. Create and migrate the source database table before opening the generator. The form reads tables and columns from PostgreSQL `information_schema`.
|
||||
5. Use a module name compatible with TypeScript identifiers, route segments, permission slugs, and folder names.
|
||||
|
||||
## 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"]
|
||||
```
|
||||
|
||||
The generator first copies the complete frontend template, then overwrites generated frontend files with the payload content and database-derived entity metadata. Backend files are rendered according to the selected assets.
|
||||
|
||||
## Generated Locations
|
||||
|
||||
```txt
|
||||
backend/modules/<ModuleName>/
|
||||
backend/tests/modules/<ModuleName>/
|
||||
apps/admin/src/modules/<name>/
|
||||
```
|
||||
|
||||
Frontend modules are discovered automatically through their `config/module.ts` manifest. Backend module routes and API routes are loaded automatically from `backend/modules/` during application startup.
|
||||
|
||||
## Important Operational Behavior
|
||||
|
||||
- Generation writes directly into both workspaces; it does not provide a preview, transaction, or rollback.
|
||||
- Existing destination files may be overwritten. Generate into a clean name and inspect the Git diff before keeping the result.
|
||||
- File writes are synchronous, but schema inspection and Swagger model generation are asynchronous.
|
||||
- API generation imports the generated model to build its Swagger schema. Select `model` when selecting `api`.
|
||||
- The frontend module template is always copied. Frontend form and datatable files are populated only when those assets are selected.
|
||||
- Generated code is not automatically formatted or committed.
|
||||
|
||||
## Verification Checklist
|
||||
|
||||
After generating a module:
|
||||
|
||||
1. Inspect all generated files and the Git diff.
|
||||
2. Confirm route paths, API prefixes, permission slugs, and module naming.
|
||||
3. Review nullable fields, dates, passwords, foreign keys, validation, and relationship names.
|
||||
4. Replace generic labels and translation values.
|
||||
5. Verify list, create, update, delete, authorization, and validation-error behavior.
|
||||
6. Run `npm run build:backend` and `npm run build:admin` from the repository root.
|
||||
7. Run the generated backend tests and add domain-specific coverage.
|
||||
|
||||
For restart, module-discovery, permission, and menu problems, see [Troubleshooting](./troubleshooting.md).
|
||||
|
||||
The generated frontend structure follows the [React frontend architecture](../../apps-architecture/react-frontend/Readme.md). Backend modules should be reviewed against the [backend architecture](../../apps-architecture/backend/Readme.md).
|
||||
@@ -0,0 +1,134 @@
|
||||
# Module Generator Backend
|
||||
|
||||
The backend generator exposes authenticated schema-inspection and generation endpoints. It renders backend files from Handlebars templates and writes frontend files using a copied template plus source strings supplied by the admin app.
|
||||
|
||||
## Endpoints
|
||||
|
||||
Routes are registered in `backend/start/routes.ts`.
|
||||
|
||||
| Method | Path | Controller method | Purpose |
|
||||
| ------ | ---------------------------- | ----------------- | -------------------------------------------------- |
|
||||
| `GET` | `/makeModule/tables` | `tables` | List eligible public PostgreSQL tables |
|
||||
| `GET` | `/makeModule/columns/:table` | `columns` | Return columns used to configure form/table fields |
|
||||
| `POST` | `/makeModule/generate` | `generate` | Generate selected frontend and backend assets |
|
||||
|
||||
All three endpoints use `middleware.auth()`.
|
||||
|
||||
## Database Inspection
|
||||
|
||||
`backend/app/services/database_inspector.ts` reads PostgreSQL `information_schema`.
|
||||
|
||||
- Table discovery is limited to the `public` schema.
|
||||
- framework, authentication, access-control, migration, and PostgreSQL system tables are excluded.
|
||||
- columns provide `column_name`, `data_type`, and `is_nullable`.
|
||||
- columns ending in `_id` are treated as belongs-to relationship candidates.
|
||||
- tables containing a `<table>_id` column are treated as has-many relationship candidates.
|
||||
- searchable string and numeric columns are derived for generated resource controllers.
|
||||
|
||||
Relationship inference is heuristic. Review generated model imports, related class names, property names, and module availability.
|
||||
|
||||
## Type Mapping
|
||||
|
||||
The controller maps database types separately for Lucid models, Vine validators, and frontend entities.
|
||||
|
||||
| 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 database columns receive `| null` in generated model/entity types and optional validation where applicable. `created_at`, `updated_at`, and `deleted_at` receive special handling.
|
||||
|
||||
## Backend Output
|
||||
|
||||
For module name `<name>`, table `<table>`, and derived module class `<ModuleName>`, selected assets are written under:
|
||||
|
||||
```txt
|
||||
backend/modules/<ModuleName>/
|
||||
├── api/
|
||||
│ ├── controllers/<entity>_api_controller.ts
|
||||
│ ├── validators/<entity>_create_api_validator.ts
|
||||
│ ├── validators/<entity>_update_api_validator.ts
|
||||
│ └── routes.ts
|
||||
├── config/permissions.ts
|
||||
├── controller/<entity>_controller.ts
|
||||
├── model/<entity>.ts
|
||||
├── policy/<entity>_policy.ts
|
||||
├── validators/<entity>_create_validator.ts
|
||||
├── validators/<entity>_update_validator.ts
|
||||
└── routes.ts
|
||||
```
|
||||
|
||||
Tests are written under:
|
||||
|
||||
```txt
|
||||
backend/tests/modules/<ModuleName>/
|
||||
├── <entity>.spec.ts
|
||||
└── <entity>.api.spec.ts
|
||||
```
|
||||
|
||||
`<entity>` is derived by removing the final character from the selected table name. This assumes a plural table name and must be reviewed for irregular plurals.
|
||||
|
||||
## Asset Behavior
|
||||
|
||||
### Model
|
||||
|
||||
Renders `backend/app/templates/backend/model.hbs` with columns, inferred relationships, searchable fields, and optional password hashing.
|
||||
|
||||
### Controller and routes
|
||||
|
||||
Renders an authenticated resource controller with list/search/sort/pagination and CRUD actions. `routes.hbs` registers an `apiOnly` resource route and applies authentication.
|
||||
|
||||
### Validators
|
||||
|
||||
Creates separate create and update Vine validators. Primary keys and timestamp/deletion fields are excluded according to controller rules. Email, password, nullable, and string trimming behavior is inferred from column names and database metadata.
|
||||
|
||||
### Policy and permissions
|
||||
|
||||
Creates a Bouncer policy with view/create/update/delete checks, creates `config/permissions.ts`, and adds a lazy policy import to `backend/app/policies/main.ts` when missing.
|
||||
|
||||
Permission slugs are derived from the module name, for example:
|
||||
|
||||
```txt
|
||||
<name>s.view
|
||||
<name>s.create
|
||||
<name>s.update
|
||||
<name>s.delete
|
||||
```
|
||||
|
||||
Review names that are already plural because policy and frontend metadata derivation are not a general-purpose pluralization system.
|
||||
|
||||
### API subtree
|
||||
|
||||
Creates a second CRUD controller and route group under `/api/<entity>`, plus API-specific validators and a test. The controller imports the generated model and passes it to `modelToSchema` for Swagger response metadata.
|
||||
|
||||
API routes currently do not add authentication middleware in the generated API template. Add the required authentication and authorization explicitly before exposing the route.
|
||||
|
||||
## Frontend Writes Performed by the Backend
|
||||
|
||||
The backend copies `apps/admin/src/modules/_template` to `apps/admin/src/modules/<name>` and then writes:
|
||||
|
||||
- configured form component and form definition;
|
||||
- configured datatable and list page;
|
||||
- configured manage trigger, menu, and module manifest;
|
||||
- generated `config/meta.ts`;
|
||||
- database-derived `entities/Entity.ts`.
|
||||
|
||||
See [frontend.md](./frontend.md) for the complete frontend structure and payload ownership.
|
||||
|
||||
## Template Helpers
|
||||
|
||||
The controller registers Handlebars helpers for `eq`, `not`, `or`, and `and`. New templates should use the existing render function and receive an explicit context object from the controller.
|
||||
|
||||
## Extending the Backend Generator
|
||||
|
||||
1. Add or update a Handlebars template under `backend/app/templates/`.
|
||||
2. Add an explicit destination path in the controller `paths` object.
|
||||
3. Create its parent directory through the existing path initialization.
|
||||
4. Render only when the corresponding asset is selected.
|
||||
5. Pass normalized metadata rather than querying the database inside templates.
|
||||
6. Add generation tests for nullable values, dates, numeric types, passwords, and irregular names.
|
||||
7. Verify both backend and frontend builds after generation.
|
||||
|
||||
Avoid adding hidden writes. Every generated or modified destination, including shared registries such as `app/policies/main.ts`, should remain visible in the controller flow and documented here.
|
||||
@@ -0,0 +1,137 @@
|
||||
# Module Generator Frontend
|
||||
|
||||
The frontend generator UI collects module metadata, selected assets, form layout, and datatable configuration. It converts the configuration into TypeScript/TSX source strings and sends them to the backend generator.
|
||||
|
||||
## Main Flow
|
||||
|
||||
| Responsibility | Source |
|
||||
| ------------------------------------------------------ | ------------------------------------------------------------------------------------- |
|
||||
| Page submission, notifications, and mutation callbacks | `apps/admin/src/devtools/pages/ModuleGeneratorPage/index.tsx` |
|
||||
| Payload assembly | `apps/admin/src/devtools/pages/ModuleGeneratorPage/lib/buildGenerateModulePayload.ts` |
|
||||
| Form state and initial values | `apps/admin/src/devtools/view/forms/ModuleGeneratorForm/form.ts` |
|
||||
| Metadata, form, and datatable sections | `apps/admin/src/devtools/view/forms/ModuleGeneratorForm/sections/` |
|
||||
| Payload DTOs | `apps/admin/src/devtools/data/api/dto.ts` |
|
||||
| HTTP client | `apps/admin/src/devtools/data/api/index.ts` |
|
||||
| React Query hooks | `apps/admin/src/devtools/data/queries/` |
|
||||
| Source-code builders | `apps/admin/src/devtools/data/builders/` |
|
||||
|
||||
The page submits through `useGenerateModule`. On success it resets the form and shows a success alert; backend or network errors are normalized through the shared React Query error helpers.
|
||||
|
||||
## Metadata and Assets
|
||||
|
||||
The required metadata is:
|
||||
|
||||
- `name`: frontend folder name, backend module identity, route segment, and permission-slug base.
|
||||
- `table`: existing PostgreSQL source table.
|
||||
- `generate`: selected backend and frontend assets.
|
||||
|
||||
Supported asset values are defined by `ModuleAsset`:
|
||||
|
||||
| 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 |
|
||||
|
||||
The default selection includes all backend assets. Form and datatable generation are opt-in.
|
||||
|
||||
## Schema Queries
|
||||
|
||||
The frontend uses two authenticated read endpoints before generation:
|
||||
|
||||
- `GET /makeModule/tables` populates the table selector.
|
||||
- `GET /makeModule/columns/:table` populates form and datatable field choices.
|
||||
|
||||
Table and column requests are cached through React Query. Column queries remain disabled until a table is selected.
|
||||
|
||||
## Payload Builders
|
||||
|
||||
`buildGenerateModulePayload` delegates source generation to focused builders:
|
||||
|
||||
| Builder | Payload field | Generated destination |
|
||||
| ------------------------------------- | ---------------- | ----------------------------------- |
|
||||
| `FormTemplateBuilder.buildComponent` | `form` | `view/forms/MainForm/Component.tsx` |
|
||||
| `FormTemplateBuilder.buildDefinition` | `formDefinition` | `view/forms/MainForm/form.ts` |
|
||||
| `FormDialogTemplateBuilder` | `formDialog` | `view/widgets/ManageTrigger.tsx` |
|
||||
| `DataTableTemplateBuilder` | `dataTable` | `view/widgets/DataTable.tsx` |
|
||||
| `ListPageTemplateBuilder` | `list` | `pages/ListPage/index.tsx` |
|
||||
| `MenuTemplateBuilder` | `menu` | `config/menu.ts` |
|
||||
| `ModuleTemplateBuilder` | `module` | `config/module.ts` |
|
||||
|
||||
If form generation is disabled, `form`, `formDefinition`, and `formDialog` are `null`. If datatable generation is disabled, `dataTable` and `menu` are `null`, while 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
|
||||
├── pages/
|
||||
│ └── ListPage/
|
||||
│ └── index.tsx
|
||||
└── view/
|
||||
├── forms/
|
||||
│ └── MainForm/
|
||||
│ ├── Component.tsx
|
||||
│ ├── constants.ts
|
||||
│ ├── form.ts
|
||||
│ ├── index.ts
|
||||
│ └── submit.ts
|
||||
├── layouts/
|
||||
│ └── Layout.tsx
|
||||
└── widgets/
|
||||
├── DataTable.tsx
|
||||
├── DeleteTrigger.tsx
|
||||
└── ManageTrigger.tsx
|
||||
```
|
||||
|
||||
The `api` directory intentionally has no barrel file. Consumers import each operation directly, for example:
|
||||
|
||||
```ts
|
||||
import { useCreate } from "../../../api/create";
|
||||
import { invalidateDetailed } from "../../../api/detailed";
|
||||
import { invalidateList } from "../../../api/list";
|
||||
import { useUpdate } from "../../../api/update";
|
||||
```
|
||||
|
||||
Each CRUD operation remains isolated in its own file. `keys.ts` owns React Query key builders, while `list.ts` and `detailed.ts` own their cache invalidation helpers.
|
||||
|
||||
## Layer Responsibilities
|
||||
|
||||
- `entities/Entity.ts` is generated from database columns and is shared by API hooks, forms, and widgets.
|
||||
- `api/` owns Axios calls, React Query hooks, cache keys, and mutation notifications.
|
||||
- `pages/` owns route-level composition and permission page gates.
|
||||
- `view/forms/` owns schema, form values, UI integration, and submission orchestration.
|
||||
- `view/widgets/` owns stateful table, delete, and manage interactions.
|
||||
- `config/` integrates routes, menu, translations, metadata, and permissions with the app module registry.
|
||||
|
||||
## Extending the Frontend Generator
|
||||
|
||||
When adding a new generated frontend artifact:
|
||||
|
||||
1. Add its configuration type under `apps/admin/src/devtools/entities/`.
|
||||
2. Add UI controls to `ModuleGeneratorForm`.
|
||||
3. Add a focused builder under `data/builders/` if the artifact contains dynamic source.
|
||||
4. Add the payload field to `GenerateModulePayload` and `buildGenerateModulePayload`.
|
||||
5. Accept and write the field in `backend/app/controllers/module_controller.ts`.
|
||||
6. Add the static structure to `apps/admin/src/modules/_template/` when every module needs it.
|
||||
7. Verify generation with and without the new asset selected.
|
||||
@@ -0,0 +1,122 @@
|
||||
# Module Generator Troubleshooting
|
||||
|
||||
## Generated Files Exist but the Module Is Not Loaded
|
||||
|
||||
Docker file watching does not always restart the backend or rebuild the admin module registry after the generator creates new directories. Existing file changes may be detected while newly generated module files are not.
|
||||
|
||||
Restart both application services manually from the repository root:
|
||||
|
||||
```bash
|
||||
docker compose restart
|
||||
```
|
||||
|
||||
Then reload the admin app. If the module still is not loaded, inspect service logs:
|
||||
|
||||
```bash
|
||||
docker compose logs --tail=200 backend admin
|
||||
```
|
||||
|
||||
Check for TypeScript errors, invalid generated imports, route-loading failures, or module-manifest errors. If the containers are not running through Compose, restart the corresponding backend and admin development processes manually.
|
||||
|
||||
The restart is necessary because:
|
||||
|
||||
- the backend discovers generated route files under `backend/modules/` during startup;
|
||||
- the admin app discovers `apps/admin/src/modules/**/config/module.ts` through Vite's module registry;
|
||||
- adding an entirely new directory may not trigger the same reload behavior as editing an existing file.
|
||||
|
||||
## Module Loads but Is Missing from the Menu
|
||||
|
||||
If generation succeeded and both services restarted, a missing menu item usually indicates a permission mismatch or an invalid menu/module configuration.
|
||||
|
||||
### 1. Compare permission slugs
|
||||
|
||||
Check the generated backend permissions:
|
||||
|
||||
```txt
|
||||
backend/modules/<ModuleName>/config/permissions.ts
|
||||
```
|
||||
|
||||
Check the frontend permission constants:
|
||||
|
||||
```txt
|
||||
apps/admin/src/modules/<name>/config/meta.ts
|
||||
```
|
||||
|
||||
The view slug must match exactly. For example:
|
||||
|
||||
```ts
|
||||
// backend permissions.ts
|
||||
{ slug: 'products.view', name: 'Product view' }
|
||||
|
||||
// frontend meta.ts
|
||||
export const MODULE_PERMISSION_VIEW = 'products.view';
|
||||
```
|
||||
|
||||
Also compare the create, update, and delete slugs. Module-name pluralization is generated heuristically, so names that are already plural or have irregular plurals require manual correction.
|
||||
|
||||
### 2. Verify the current role owns the permission
|
||||
|
||||
The menu filters items using `MODULE_PERMISSION_VIEW`. Confirm that:
|
||||
|
||||
- the generated permission is available to the access-control system;
|
||||
- the current admin role has the generated view permission;
|
||||
- the role-to-permission assignment uses the same exact slug;
|
||||
- the session or permission data has been refreshed after changing the role.
|
||||
|
||||
### 3. Check menu configuration
|
||||
|
||||
Inspect:
|
||||
|
||||
```txt
|
||||
apps/admin/src/modules/<name>/config/menu.ts
|
||||
```
|
||||
|
||||
Confirm that:
|
||||
|
||||
- the exported menu factory is valid;
|
||||
- `path` uses `MODULE_ROOT_PATH`;
|
||||
- `permissions` contains `MODULE_PERMISSION_VIEW`;
|
||||
- the file is not empty;
|
||||
- the configured route exists.
|
||||
|
||||
The generator creates the menu content with the Datatable frontend asset. If Datatable was not selected, add a menu implementation manually or keep the module intentionally hidden from navigation.
|
||||
|
||||
### 4. Check the module manifest
|
||||
|
||||
Inspect:
|
||||
|
||||
```txt
|
||||
apps/admin/src/modules/<name>/config/module.ts
|
||||
```
|
||||
|
||||
The manifest must export a valid `AppModuleConfig`, must not set `enabled: false`, and must expose the intended `menu`, `routes`, and `messages` values. Its module ID should be unique.
|
||||
|
||||
### 5. Check route configuration
|
||||
|
||||
Inspect:
|
||||
|
||||
```txt
|
||||
apps/admin/src/modules/<name>/config/routing.tsx
|
||||
```
|
||||
|
||||
Confirm that `MODULE_ROOT_PATH`, the menu path, and the generated route all point to the same URL. A visible menu item with a broken route and an invisible menu item are separate problems; verify both configuration files.
|
||||
|
||||
## Final Checks
|
||||
|
||||
Run both builds to expose generated TypeScript or import errors:
|
||||
|
||||
```bash
|
||||
npm run build:backend
|
||||
npm run build:admin
|
||||
```
|
||||
|
||||
Then verify:
|
||||
|
||||
1. `backend/modules/<ModuleName>/` exists and contains the selected backend assets.
|
||||
2. `apps/admin/src/modules/<name>/` exists and contains a valid module manifest.
|
||||
3. Docker services or local development processes were restarted.
|
||||
4. Backend and frontend permission slugs match.
|
||||
5. The current role owns the view permission.
|
||||
6. The menu path matches the frontend route.
|
||||
|
||||
If all checks pass, review the backend and admin logs during startup. Module discovery errors are normally reported there.
|
||||
@@ -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