Where to start
Use How to when you already know the goal and need the shortest correct path. Use Getting started for first-time project setup. Use Documentation when you need complete API surface, options, and edge cases.
- Prefer these recipes when the outcome is clear and you need steps + code
- Getting started — new project, CLI, folder layout, first plugin
- Documentation — full reference for backend / frontend APIs
- Troubleshooting — boot, DI, hot reload, Tailwind, 403 / missing UI
Creating a plugin
Scaffold with @quan-erp/cli (or copy sample-es), align folder / metadata / package names and @quan-erp/* versions, add a first feature, run quan-erp watch, then Install from the Modules UI.
- When — greenfield plugin under plugins/
- Outcome — menus/routes load in the shell after Install
How to create a public page
Register a blank-layout page outside the ERP chrome with AppRegistry.rootRoute.add() — not route.add(). Use for customer portals, kitchen boards, and QR menus.
- When — UI must not show sidebar / admin shell
- Key API — rootRoute.add with /${metadata.name} path prefix
How to make mobile responsive
Pair desktop tables with mobile card lists, use useMediaQuery / SCREENS, and keep bottom-nav and FAB spacing consistent with shared UI helpers.
- When — pages must work on phone and desktop
- Key ideas — isMobile gates, card list vs dense table
Adding Floating action button
Add a mobile-only primary action with FloatingActionButton from @quan-erp/shared-ui. Gate with isMobile and shift position when the bottom nav is visible.
- When — create / primary action on mobile lists
- Key APIs — FloatingActionButton, useIsContainInBottomNavBar
Adding new Database source
Prefer the shared default DataSource (plugin: "default"). Open a custom TypeORM connection with @Database on the root @Module only when you need a separate host, schema, reporting DB, or replica.
- When — isolation beyond the shared Postgres
- Key APIs — @Database, entities with matching plugin + name, @InjectDatabaseSource
Adding Caching
Declare caches with @Cache on the root module (in-memory or Redis from env), inject with @CacheClient, and optionally cache HTTP routes or method results with @CacheRoute / @CacheFn.
- When — repeated reads, session/token stores, expensive computations
- Key APIs — @Cache, @CacheClient, route/method helpers, cross-plugin reuse
How to create a DB migration for plugin
Ship schema changes with IDatabaseMigration classes (getName, getSource, up, down) returned from IPlugin.getMigrations(). Do not rely on synchronize for production upgrades.
- When — additive/destructive schema changes or one-shot backfills
- Key APIs — IDatabaseMigration, getMigrations(), optional onMigrate()
Plugins frontend and backend assets
Ship static files with the plugin: backend assets under backend/assets/ via AppFolder; frontend URLs via PluginAssets.network. Never hardcode APP_DATA_FOLDER; never write into the asset folder at runtime.
- When — templates, seed JSON, logos, other bundled static files
- Key APIs — AppFolder.getPluginAssetFolder / getPluginDataFolder, PluginAssets.network