create-lego-one 2.0.10 → 2.0.13
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/dist/index.cjs +179 -0
- package/dist/index.cjs.map +1 -1
- package/package.json +5 -3
- package/template/.cursor/rules/rules.mdc +639 -0
- package/template/.dockerignore +58 -0
- package/template/.env.example +18 -0
- package/template/.eslintignore +5 -0
- package/template/.eslintrc.js +28 -0
- package/template/.prettierignore +6 -0
- package/template/.prettierrc +11 -0
- package/template/CLAUDE.md +634 -0
- package/template/Dockerfile +67 -0
- package/template/PROMPT.md +457 -0
- package/template/README.md +325 -0
- package/template/docker-compose.yml +48 -0
- package/template/docker-entrypoint.sh +23 -0
- package/template/docs/checkpoints/.template.md +64 -0
- package/template/docs/checkpoints/framework/01-infrastructure-setup.md +132 -0
- package/template/docs/checkpoints/framework/02-pocketbase-setup.md +155 -0
- package/template/docs/checkpoints/framework/03-host-kernel.md +170 -0
- package/template/docs/checkpoints/framework/04-auth-system.md +163 -0
- package/template/docs/checkpoints/framework/phase-05-multitenancy-rbac.md +223 -0
- package/template/docs/checkpoints/framework/phase-06-ui-components.md +260 -0
- package/template/docs/checkpoints/framework/phase-07-communication-system.md +276 -0
- package/template/docs/checkpoints/framework/phase-08-plugin-system.md +91 -0
- package/template/docs/checkpoints/framework/phase-09-dashboard-plugin.md +111 -0
- package/template/docs/checkpoints/framework/phase-10-todo-plugin.md +169 -0
- package/template/docs/checkpoints/framework/phase-11-testing.md +264 -0
- package/template/docs/checkpoints/framework/phase-12-deployment.md +294 -0
- package/template/docs/checkpoints/framework/phase-13-documentation.md +312 -0
- package/template/docs/framework/plans/00-index.md +164 -0
- package/template/docs/framework/plans/01-infrastructure-setup.md +855 -0
- package/template/docs/framework/plans/02-pocketbase-setup.md +1374 -0
- package/template/docs/framework/plans/03-host-kernel.md +1518 -0
- package/template/docs/framework/plans/04-auth-system.md +1466 -0
- package/template/docs/framework/plans/05-multitenancy-rbac.md +1527 -0
- package/template/docs/framework/plans/06-ui-components.md +1478 -0
- package/template/docs/framework/plans/07-communication-system.md +1106 -0
- package/template/docs/framework/plans/08-plugin-system.md +1179 -0
- package/template/docs/framework/plans/09-dashboard-plugin.md +1137 -0
- package/template/docs/framework/plans/10-todo-plugin.md +1343 -0
- package/template/docs/framework/plans/11-testing.md +935 -0
- package/template/docs/framework/plans/12-deployment.md +896 -0
- package/template/docs/framework/prompts/0-boilerplate-modernjs.md +151 -0
- package/template/docs/framework/research/00-modernjs-audit.md +488 -0
- package/template/docs/framework/research/01-system-blueprint.md +721 -0
- package/template/docs/framework/research/02-data-migration-protocol.md +699 -0
- package/template/docs/framework/research/03-host-setup.md +714 -0
- package/template/docs/framework/research/04-plugin-architecture.md +645 -0
- package/template/docs/framework/research/05-slot-injection-pattern.md +671 -0
- package/template/docs/framework/research/06-cli-strategy.md +615 -0
- package/template/docs/framework/research/07-deployment.md +629 -0
- package/template/docs/framework/research/README.md +282 -0
- package/template/docs/framework/setup/00-index.md +210 -0
- package/template/docs/framework/setup/01-framework-structure.md +308 -0
- package/template/docs/framework/setup/02-development-workflow.md +405 -0
- package/template/docs/framework/setup/03-environment-setup.md +215 -0
- package/template/docs/framework/setup/04-kernel-architecture.md +499 -0
- package/template/docs/framework/setup/05-plugin-system.md +620 -0
- package/template/docs/framework/setup/06-communication-patterns.md +451 -0
- package/template/docs/framework/setup/07-plugin-development.md +582 -0
- package/template/docs/framework/setup/08-component-library.md +658 -0
- package/template/docs/framework/setup/09-data-integration.md +609 -0
- package/template/docs/framework/setup/10-auth-rbac.md +497 -0
- package/template/docs/framework/setup/11-hooks-api.md +393 -0
- package/template/docs/framework/setup/12-components-api.md +665 -0
- package/template/docs/framework/setup/13-deployment-guide.md +566 -0
- package/template/docs/framework/setup/README.md +548 -0
- package/template/host/e2e/auth.spec.ts +38 -0
- package/template/host/e2e/layout.spec.ts +38 -0
- package/template/host/modern.config.ts +19 -0
- package/template/host/package.json +71 -0
- package/template/host/playwright.config.ts +34 -0
- package/template/host/postcss.config.mjs +6 -0
- package/template/host/src/App.tsx +6 -0
- package/template/host/src/bootstrap.tsx +74 -0
- package/template/host/src/global.css +59 -0
- package/template/host/src/index.ts +2 -0
- package/template/host/src/kernel/__tests__/lib-utils.test.ts +32 -0
- package/template/host/src/kernel/__tests__/rbac-hooks.test.tsx +114 -0
- package/template/host/src/kernel/__tests__/rbac-utils.test.ts +108 -0
- package/template/host/src/kernel/auth/ProtectedRoute.tsx +41 -0
- package/template/host/src/kernel/auth/components/LoginForm.tsx +97 -0
- package/template/host/src/kernel/auth/components/LogoutButton.tsx +79 -0
- package/template/host/src/kernel/auth/hooks.ts +174 -0
- package/template/host/src/kernel/auth/index.ts +5 -0
- package/template/host/src/kernel/auth/schemas.ts +27 -0
- package/template/host/src/kernel/auth/service.ts +197 -0
- package/template/host/src/kernel/auth/types.ts +36 -0
- package/template/host/src/kernel/channels/ChannelBus.ts +181 -0
- package/template/host/src/kernel/channels/ChannelProvider.tsx +57 -0
- package/template/host/src/kernel/channels/events.ts +27 -0
- package/template/host/src/kernel/channels/hooks.ts +168 -0
- package/template/host/src/kernel/channels/index.ts +6 -0
- package/template/host/src/kernel/channels/integrations/ToastIntegration.tsx +60 -0
- package/template/host/src/kernel/channels/plugin-hooks.ts +72 -0
- package/template/host/src/kernel/channels/types.ts +112 -0
- package/template/host/src/kernel/components/__tests__/Badge.test.tsx +35 -0
- package/template/host/src/kernel/components/__tests__/Button.test.tsx +63 -0
- package/template/host/src/kernel/components/__tests__/Input.test.tsx +64 -0
- package/template/host/src/kernel/components/index.ts +32 -0
- package/template/host/src/kernel/components/ui/alert.tsx +58 -0
- package/template/host/src/kernel/components/ui/avatar.tsx +47 -0
- package/template/host/src/kernel/components/ui/badge.tsx +35 -0
- package/template/host/src/kernel/components/ui/button.tsx +50 -0
- package/template/host/src/kernel/components/ui/card.tsx +78 -0
- package/template/host/src/kernel/components/ui/dialog.tsx +116 -0
- package/template/host/src/kernel/components/ui/dropdown-menu.tsx +192 -0
- package/template/host/src/kernel/components/ui/index.ts +7 -0
- package/template/host/src/kernel/components/ui/input.tsx +24 -0
- package/template/host/src/kernel/components/ui/label.tsx +21 -0
- package/template/host/src/kernel/components/ui/popover.tsx +28 -0
- package/template/host/src/kernel/components/ui/progress.tsx +25 -0
- package/template/host/src/kernel/components/ui/scroll-area.tsx +45 -0
- package/template/host/src/kernel/components/ui/select.tsx +155 -0
- package/template/host/src/kernel/components/ui/separator.tsx +28 -0
- package/template/host/src/kernel/components/ui/skeleton.tsx +15 -0
- package/template/host/src/kernel/components/ui/switch.tsx +26 -0
- package/template/host/src/kernel/components/ui/table.tsx +116 -0
- package/template/host/src/kernel/components/ui/tabs.tsx +52 -0
- package/template/host/src/kernel/components/ui/toast.tsx +126 -0
- package/template/host/src/kernel/components/ui/toaster.tsx +34 -0
- package/template/host/src/kernel/components/ui/tooltip.tsx +27 -0
- package/template/host/src/kernel/components/ui/use-toast.ts +183 -0
- package/template/host/src/kernel/index.ts +48 -0
- package/template/host/src/kernel/lib/cn.ts +1 -0
- package/template/host/src/kernel/lib/utils.ts +36 -0
- package/template/host/src/kernel/plugins/Slot.tsx +41 -0
- package/template/host/src/kernel/plugins/SlotProvider.tsx +88 -0
- package/template/host/src/kernel/plugins/index.ts +23 -0
- package/template/host/src/kernel/plugins/loader.ts +122 -0
- package/template/host/src/kernel/plugins/schemas.ts +54 -0
- package/template/host/src/kernel/plugins/store.ts +185 -0
- package/template/host/src/kernel/plugins/types.ts +103 -0
- package/template/host/src/kernel/providers/PocketBaseProvider.tsx +70 -0
- package/template/host/src/kernel/providers/QueryProvider.tsx +28 -0
- package/template/host/src/kernel/providers/ThemeProvider.tsx +25 -0
- package/template/host/src/kernel/providers/index.ts +3 -0
- package/template/host/src/kernel/rbac/components/OrganizationSelector.tsx +69 -0
- package/template/host/src/kernel/rbac/components/PermissionGate.tsx +43 -0
- package/template/host/src/kernel/rbac/hooks.ts +379 -0
- package/template/host/src/kernel/rbac/index.ts +6 -0
- package/template/host/src/kernel/rbac/service.ts +504 -0
- package/template/host/src/kernel/rbac/types.ts +164 -0
- package/template/host/src/kernel/rbac/utils.ts +34 -0
- package/template/host/src/kernel/shared-state/bridge.ts +31 -0
- package/template/host/src/kernel/shared-state/index.ts +3 -0
- package/template/host/src/kernel/shared-state/store.ts +62 -0
- package/template/host/src/kernel/shared-state/types.ts +60 -0
- package/template/host/src/kernel/use-migrations.ts +72 -0
- package/template/host/src/layout/MobileMenu.tsx +61 -0
- package/template/host/src/layout/Shell.tsx +42 -0
- package/template/host/src/layout/Sidebar.tsx +178 -0
- package/template/host/src/layout/Topbar.tsx +50 -0
- package/template/host/src/layout/index.ts +4 -0
- package/template/host/src/lib/pocketbase/client.ts +38 -0
- package/template/host/src/lib/pocketbase/collections/audit_logs.ts +87 -0
- package/template/host/src/lib/pocketbase/collections/index.ts +19 -0
- package/template/host/src/lib/pocketbase/collections/organizations.ts +63 -0
- package/template/host/src/lib/pocketbase/collections/permissions.ts +57 -0
- package/template/host/src/lib/pocketbase/collections/roles.ts +55 -0
- package/template/host/src/lib/pocketbase/collections/todos.ts +74 -0
- package/template/host/src/lib/pocketbase/collections/user_roles.ts +57 -0
- package/template/host/src/lib/pocketbase/collections/users.ts +43 -0
- package/template/host/src/lib/pocketbase/index.ts +5 -0
- package/template/host/src/lib/pocketbase/migrations.ts +44 -0
- package/template/host/src/lib/pocketbase/seed/permissions.ts +8 -0
- package/template/host/src/lib/pocketbase/seed/roles.ts +22 -0
- package/template/host/src/lib/pocketbase/seed.ts +113 -0
- package/template/host/src/lib/pocketbase/types.ts +102 -0
- package/template/host/src/modern.runtime.ts +26 -0
- package/template/host/src/plugins.d.ts +9 -0
- package/template/host/src/providers/PocketBaseProvider.tsx +30 -0
- package/template/host/src/routes/_.tsx +6 -0
- package/template/host/src/routes/dashboard._.tsx +41 -0
- package/template/host/src/routes/index.tsx +93 -0
- package/template/host/src/routes/login.tsx +36 -0
- package/template/host/src/saas.config.ts +52 -0
- package/template/host/src/test/setup.ts +65 -0
- package/template/host/src/test/utils.tsx +69 -0
- package/template/host/src/test/vitest-globals.d.ts +19 -0
- package/template/host/src/vite-env.d.ts +16 -0
- package/template/host/tailwind.config.ts +77 -0
- package/template/host/tsconfig.json +19 -0
- package/template/host/vitest.config.ts +30 -0
- package/template/nginx.conf +72 -0
- package/template/package.json +44 -0
- package/template/packages/plugins/@lego/plugin-dashboard/modern.config.ts +19 -0
- package/template/packages/plugins/@lego/plugin-dashboard/package.json +35 -0
- package/template/packages/plugins/@lego/plugin-dashboard/postcss.config.mjs +6 -0
- package/template/packages/plugins/@lego/plugin-dashboard/src/App.tsx +27 -0
- package/template/packages/plugins/@lego/plugin-dashboard/src/components/ActivityFeed.tsx +63 -0
- package/template/packages/plugins/@lego/plugin-dashboard/src/components/QuickActionSlot.tsx +11 -0
- package/template/packages/plugins/@lego/plugin-dashboard/src/components/QuickActions.tsx +68 -0
- package/template/packages/plugins/@lego/plugin-dashboard/src/components/SidebarWidget.tsx +35 -0
- package/template/packages/plugins/@lego/plugin-dashboard/src/components/StatCard.tsx +47 -0
- package/template/packages/plugins/@lego/plugin-dashboard/src/global.css +24 -0
- package/template/packages/plugins/@lego/plugin-dashboard/src/hooks/useChannelIntegration.ts +43 -0
- package/template/packages/plugins/@lego/plugin-dashboard/src/hooks/useDashboardStats.ts +65 -0
- package/template/packages/plugins/@lego/plugin-dashboard/src/hooks/usePocketBase.ts +47 -0
- package/template/packages/plugins/@lego/plugin-dashboard/src/hooks/useRecentActivity.ts +55 -0
- package/template/packages/plugins/@lego/plugin-dashboard/src/lib/utils.ts +6 -0
- package/template/packages/plugins/@lego/plugin-dashboard/src/pages/DashboardPage.tsx +105 -0
- package/template/packages/plugins/@lego/plugin-dashboard/src/plugin.config.ts +121 -0
- package/template/packages/plugins/@lego/plugin-dashboard/src/plugin.ts +18 -0
- package/template/packages/plugins/@lego/plugin-dashboard/src/vite-env.d.ts +32 -0
- package/template/packages/plugins/@lego/plugin-dashboard/tailwind.config.ts +35 -0
- package/template/packages/plugins/@lego/plugin-dashboard/tsconfig.json +18 -0
- package/template/packages/plugins/@lego/plugin-todo/modern.config.ts +18 -0
- package/template/packages/plugins/@lego/plugin-todo/package.json +41 -0
- package/template/packages/plugins/@lego/plugin-todo/postcss.config.mjs +6 -0
- package/template/packages/plugins/@lego/plugin-todo/src/App.tsx +12 -0
- package/template/packages/plugins/@lego/plugin-todo/src/components/SidebarWidget.tsx +16 -0
- package/template/packages/plugins/@lego/plugin-todo/src/components/TodoDialog.tsx +55 -0
- package/template/packages/plugins/@lego/plugin-todo/src/components/TodoFilters.tsx +79 -0
- package/template/packages/plugins/@lego/plugin-todo/src/components/TodoForm.tsx +94 -0
- package/template/packages/plugins/@lego/plugin-todo/src/components/TodoItem.tsx +121 -0
- package/template/packages/plugins/@lego/plugin-todo/src/components/TodoList.tsx +41 -0
- package/template/packages/plugins/@lego/plugin-todo/src/components/index.ts +6 -0
- package/template/packages/plugins/@lego/plugin-todo/src/global.css +59 -0
- package/template/packages/plugins/@lego/plugin-todo/src/hooks/useCreateTodo.ts +62 -0
- package/template/packages/plugins/@lego/plugin-todo/src/hooks/useDeleteTodo.ts +46 -0
- package/template/packages/plugins/@lego/plugin-todo/src/hooks/usePocketBase.ts +38 -0
- package/template/packages/plugins/@lego/plugin-todo/src/hooks/useTodos.ts +64 -0
- package/template/packages/plugins/@lego/plugin-todo/src/hooks/useUpdateTodo.ts +35 -0
- package/template/packages/plugins/@lego/plugin-todo/src/index.tsx +5 -0
- package/template/packages/plugins/@lego/plugin-todo/src/lib/utils.ts +20 -0
- package/template/packages/plugins/@lego/plugin-todo/src/pages/TodoPage.tsx +89 -0
- package/template/packages/plugins/@lego/plugin-todo/src/plugin.config.ts +104 -0
- package/template/packages/plugins/@lego/plugin-todo/src/plugin.ts +13 -0
- package/template/packages/plugins/@lego/plugin-todo/src/schemas.ts +37 -0
- package/template/packages/plugins/@lego/plugin-todo/src/types.ts +42 -0
- package/template/packages/plugins/@lego/plugin-todo/src/vite-env.d.ts +31 -0
- package/template/packages/plugins/@lego/plugin-todo/tailwind.config.ts +51 -0
- package/template/packages/plugins/@lego/plugin-todo/tsconfig.json +18 -0
- package/template/pnpm-workspace.yaml +4 -0
- package/template/pocketbase/CHANGELOG.md +911 -0
- package/template/pocketbase/LICENSE.md +17 -0
- package/template/scripts/create-plugin.js +221 -0
- package/template/scripts/deploy.sh +56 -0
- package/template/tsconfig.base.json +26 -0
- package/template/tsconfig.json +8 -0
|
@@ -0,0 +1,582 @@
|
|
|
1
|
+
# Plugin Development
|
|
2
|
+
|
|
3
|
+
**Complete Guide to Creating Lego-One Plugins**
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Overview
|
|
8
|
+
|
|
9
|
+
Lego-One plugins are Modern.js micro-frontends that integrate seamlessly with the host application. This guide provides complete instructions for creating plugins.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## Plugin Structure
|
|
14
|
+
|
|
15
|
+
### Standard Plugin File Structure
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
packages/plugins/@lego/plugin-<name>/
|
|
19
|
+
├── package.json # Package configuration
|
|
20
|
+
├── tsconfig.json # TypeScript configuration
|
|
21
|
+
├── modern.config.ts # Modern.js configuration
|
|
22
|
+
├── tailwind.config.ts # Tailwind CSS configuration
|
|
23
|
+
├── postcss.config.mjs # PostCSS configuration
|
|
24
|
+
└── src/
|
|
25
|
+
├── global.css # Global styles
|
|
26
|
+
├── plugin.config.ts # Plugin manifest and configuration
|
|
27
|
+
├── plugin.ts # Plugin exports for Garfish
|
|
28
|
+
├── App.tsx # Root component with routing
|
|
29
|
+
├── types.ts # TypeScript types
|
|
30
|
+
├── schemas.ts # Zod validation schemas
|
|
31
|
+
├── pages/ # Route pages
|
|
32
|
+
│ └── <Plugin>Page.tsx
|
|
33
|
+
├── components/ # React components
|
|
34
|
+
│ └── slots/ # Slot injection components
|
|
35
|
+
│ └── SidebarWidget.tsx
|
|
36
|
+
├── hooks/ # Custom React hooks
|
|
37
|
+
│ ├── usePocketBase.ts
|
|
38
|
+
│ └── use<Feature>Data.ts
|
|
39
|
+
└── vite-env.d.ts # Vite type declarations
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## Step-by-Step Plugin Creation
|
|
45
|
+
|
|
46
|
+
### Step 1: Create Plugin Directory
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
mkdir -p packages/plugins/@lego/plugin-my-plugin
|
|
50
|
+
cd packages/plugins/@lego/plugin-my-plugin
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### Step 2: Create package.json
|
|
54
|
+
|
|
55
|
+
```json
|
|
56
|
+
{
|
|
57
|
+
"name": "@lego/plugin-my-plugin",
|
|
58
|
+
"version": "1.0.0",
|
|
59
|
+
"private": true,
|
|
60
|
+
"type": "module",
|
|
61
|
+
"scripts": {
|
|
62
|
+
"dev": "modern dev",
|
|
63
|
+
"build": "modern build",
|
|
64
|
+
"start": "modern start"
|
|
65
|
+
},
|
|
66
|
+
"dependencies": {
|
|
67
|
+
"@garfish/hooks": "^1.22.0",
|
|
68
|
+
"@modern-js/runtime": "^2.60.0",
|
|
69
|
+
"@modern-js/runtime/garfish": "^2.60.0",
|
|
70
|
+
"@tanstack/react-query": "^5.59.0",
|
|
71
|
+
"lucide-react": "^0.454.0",
|
|
72
|
+
"pocketbase": "^0.21.5",
|
|
73
|
+
"react": "^18.3.1",
|
|
74
|
+
"react-dom": "^18.3.1",
|
|
75
|
+
"zod": "^3.23.8"
|
|
76
|
+
},
|
|
77
|
+
"devDependencies": {
|
|
78
|
+
"@modern-js/app-tools": "^2.60.0",
|
|
79
|
+
"@modern-js/plugin-garfish": "^2.60.0",
|
|
80
|
+
"tailwindcss": "^3.4.0",
|
|
81
|
+
"typescript": "^5.3.0"
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### Step 3: Create modern.config.ts
|
|
87
|
+
|
|
88
|
+
```typescript
|
|
89
|
+
import { appTools, defineConfig } from '@modern-js/app-tools';
|
|
90
|
+
import { garfishPlugin } from '@modern-js/plugin-garfish';
|
|
91
|
+
|
|
92
|
+
export default defineConfig({
|
|
93
|
+
dev: {
|
|
94
|
+
port: 3003, // Unique port for each plugin
|
|
95
|
+
},
|
|
96
|
+
runtime: {
|
|
97
|
+
router: true,
|
|
98
|
+
state: true,
|
|
99
|
+
},
|
|
100
|
+
deploy: {
|
|
101
|
+
microFrontend: true,
|
|
102
|
+
},
|
|
103
|
+
plugins: [appTools(), garfishPlugin()],
|
|
104
|
+
});
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### Step 4: Create types.ts
|
|
108
|
+
|
|
109
|
+
```typescript
|
|
110
|
+
export interface MyEntity {
|
|
111
|
+
id: string;
|
|
112
|
+
name: string;
|
|
113
|
+
description?: string;
|
|
114
|
+
organizationId: string;
|
|
115
|
+
ownerId: string;
|
|
116
|
+
created: string;
|
|
117
|
+
updated: string;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export interface MyEntityFormData {
|
|
121
|
+
name: string;
|
|
122
|
+
description?: string;
|
|
123
|
+
}
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### Step 5: Create schemas.ts
|
|
127
|
+
|
|
128
|
+
```typescript
|
|
129
|
+
import { z } from 'zod';
|
|
130
|
+
|
|
131
|
+
export const myEntityFormSchema = z.object({
|
|
132
|
+
name: z.string().min(1, 'Name is required').max(200, 'Name too long'),
|
|
133
|
+
description: z.string().max(1000).optional(),
|
|
134
|
+
});
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
### Step 6: Create hooks/usePocketBase.ts
|
|
138
|
+
|
|
139
|
+
```typescript
|
|
140
|
+
import { useEffect, useState } from 'react';
|
|
141
|
+
|
|
142
|
+
export function usePocketBase() {
|
|
143
|
+
const [pb, setPb] = useState(null);
|
|
144
|
+
|
|
145
|
+
useEffect(() => {
|
|
146
|
+
const kernelState = window.__LEGO_KERNEL_STATE__;
|
|
147
|
+
const pbUrl = import.meta.env.VITE_POCKETBASE_URL || 'http://127.0.0.1:8090';
|
|
148
|
+
const PocketBase = require('pocketbase').default;
|
|
149
|
+
const client = new PocketBase(pbUrl);
|
|
150
|
+
|
|
151
|
+
const state = kernelState?.useGlobalKernelState?.getState();
|
|
152
|
+
if (state?.token) {
|
|
153
|
+
client.authStore.save(state.token);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
setPb(client);
|
|
157
|
+
}, []);
|
|
158
|
+
|
|
159
|
+
return pb;
|
|
160
|
+
}
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
### Step 7: Create hooks/useMyEntities.ts
|
|
164
|
+
|
|
165
|
+
```typescript
|
|
166
|
+
import { useQuery } from '@tanstack/react-query';
|
|
167
|
+
import { usePocketBase } from './usePocketBase';
|
|
168
|
+
|
|
169
|
+
export function useMyEntities() {
|
|
170
|
+
const pb = usePocketBase();
|
|
171
|
+
|
|
172
|
+
return useQuery({
|
|
173
|
+
queryKey: ['my-entities'],
|
|
174
|
+
queryFn: async () => {
|
|
175
|
+
if (!pb) throw new Error('PB not initialized');
|
|
176
|
+
|
|
177
|
+
const kernelState = window.__LEGO_KERNEL_STATE__;
|
|
178
|
+
const state = kernelState?.useGlobalKernelState?.getState();
|
|
179
|
+
const orgId = state?.organization?.id;
|
|
180
|
+
|
|
181
|
+
const result = await pb.collection('my_entities').getList(1, 50, {
|
|
182
|
+
filter: `organizationId = "${orgId}"`,
|
|
183
|
+
sort: '-created',
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
return result.items;
|
|
187
|
+
},
|
|
188
|
+
enabled: !!pb,
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
### Step 8: Create plugin.config.ts
|
|
194
|
+
|
|
195
|
+
```typescript
|
|
196
|
+
import type { PluginConfig } from '@lego/kernel/plugins';
|
|
197
|
+
import { SidebarWidget } from './components/slots/SidebarWidget';
|
|
198
|
+
|
|
199
|
+
export const pluginConfig: PluginConfig = {
|
|
200
|
+
manifest: {
|
|
201
|
+
name: '@lego/plugin-my-plugin',
|
|
202
|
+
version: '1.0.0',
|
|
203
|
+
displayName: 'My Plugin',
|
|
204
|
+
description: 'Description of what this plugin does',
|
|
205
|
+
permissions: ['myplugin.read', 'myplugin.write'],
|
|
206
|
+
},
|
|
207
|
+
enabled: true,
|
|
208
|
+
slots: [
|
|
209
|
+
{
|
|
210
|
+
slot: 'sidebar:nav',
|
|
211
|
+
component: () => import('./components/slots/SidebarWidget').then(m => m.default),
|
|
212
|
+
order: 100,
|
|
213
|
+
},
|
|
214
|
+
],
|
|
215
|
+
routes: [
|
|
216
|
+
{
|
|
217
|
+
path: '/my-plugin',
|
|
218
|
+
component: () => import('./pages/MyPluginPage').then(m => m.default),
|
|
219
|
+
protected: true,
|
|
220
|
+
permissions: ['myplugin.read'],
|
|
221
|
+
},
|
|
222
|
+
],
|
|
223
|
+
};
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
### Step 9: Create plugin.ts
|
|
227
|
+
|
|
228
|
+
```typescript
|
|
229
|
+
import { pluginConfig } from './plugin.config';
|
|
230
|
+
import App from './App';
|
|
231
|
+
|
|
232
|
+
export default {
|
|
233
|
+
config: pluginConfig,
|
|
234
|
+
App,
|
|
235
|
+
};
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
### Step 10: Create App.tsx
|
|
239
|
+
|
|
240
|
+
```typescript
|
|
241
|
+
import { Route, Routes } from '@modern-js/runtime/router';
|
|
242
|
+
import { MyPluginPage } from './pages/MyPluginPage';
|
|
243
|
+
|
|
244
|
+
export function App() {
|
|
245
|
+
return (
|
|
246
|
+
<Routes>
|
|
247
|
+
<Route path="/my-plugin" element={<MyPluginPage />} />
|
|
248
|
+
</Routes>
|
|
249
|
+
);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
export default App;
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
### Step 11: Create components/slots/SidebarWidget.tsx
|
|
256
|
+
|
|
257
|
+
```typescript
|
|
258
|
+
import { NavLink } from '@modern-js/runtime/router';
|
|
259
|
+
import { MyIcon } from 'lucide-react';
|
|
260
|
+
|
|
261
|
+
export function SidebarWidget() {
|
|
262
|
+
return (
|
|
263
|
+
<NavLink
|
|
264
|
+
to="/my-plugin"
|
|
265
|
+
className={({ isActive }) =>
|
|
266
|
+
`flex items-center gap-3 rounded-lg px-3 py-2 text-sm font-medium transition-colors ${
|
|
267
|
+
isActive
|
|
268
|
+
? 'bg-primary text-primary-foreground'
|
|
269
|
+
: 'text-muted-foreground hover:bg-muted hover:text-foreground'
|
|
270
|
+
}`
|
|
271
|
+
}
|
|
272
|
+
>
|
|
273
|
+
<MyIcon className="h-5 w-5" />
|
|
274
|
+
<span>My Plugin</span>
|
|
275
|
+
</NavLink>
|
|
276
|
+
);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
export default SidebarWidget;
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
### Step 12: Create pages/MyPluginPage.tsx
|
|
283
|
+
|
|
284
|
+
```typescript
|
|
285
|
+
import { useMyEntities } from '../hooks/useMyEntities';
|
|
286
|
+
|
|
287
|
+
export function MyPluginPage() {
|
|
288
|
+
const { data: entities, isLoading } = useMyEntities();
|
|
289
|
+
|
|
290
|
+
if (isLoading) {
|
|
291
|
+
return <div className="p-8">Loading...</div>;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
return (
|
|
295
|
+
<div className="container mx-auto p-8">
|
|
296
|
+
<h1 className="text-3xl font-bold">My Plugin</h1>
|
|
297
|
+
<div className="mt-6 space-y-4">
|
|
298
|
+
{entities?.map((entity) => (
|
|
299
|
+
<div key={entity.id} className="rounded border p-4">
|
|
300
|
+
<h3 className="font-semibold">{entity.name}</h3>
|
|
301
|
+
<p className="text-muted-foreground">{entity.description}</p>
|
|
302
|
+
</div>
|
|
303
|
+
))}
|
|
304
|
+
</div>
|
|
305
|
+
</div>
|
|
306
|
+
);
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
export default MyPluginPage;
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
### Step 13: Create global.css
|
|
313
|
+
|
|
314
|
+
```css
|
|
315
|
+
@tailwind base;
|
|
316
|
+
@tailwind components;
|
|
317
|
+
@tailwind utilities;
|
|
318
|
+
|
|
319
|
+
@layer base {
|
|
320
|
+
:root {
|
|
321
|
+
--background: 0 0% 100%;
|
|
322
|
+
--foreground: 222.2 84% 4.9%;
|
|
323
|
+
/* ... more variables */
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
### Step 14: Create tailwind.config.ts
|
|
329
|
+
|
|
330
|
+
```typescript
|
|
331
|
+
import type { Config } from 'tailwindcss';
|
|
332
|
+
|
|
333
|
+
export default {
|
|
334
|
+
darkMode: ['class'],
|
|
335
|
+
content: ['./src/**/*.{ts,tsx}'],
|
|
336
|
+
theme: {
|
|
337
|
+
extend: {
|
|
338
|
+
colors: {
|
|
339
|
+
border: 'hsl(var(--border))',
|
|
340
|
+
input: 'hsl(var(--input))',
|
|
341
|
+
ring: 'hsl(var(--ring))',
|
|
342
|
+
background: 'hsl(var(--background))',
|
|
343
|
+
foreground: 'hsl(var(--foreground))',
|
|
344
|
+
primary: {
|
|
345
|
+
DEFAULT: 'hsl(var(--primary))',
|
|
346
|
+
foreground: 'hsl(var(--primary-foreground))',
|
|
347
|
+
},
|
|
348
|
+
/* ... more colors */
|
|
349
|
+
},
|
|
350
|
+
},
|
|
351
|
+
},
|
|
352
|
+
plugins: [],
|
|
353
|
+
} satisfies Config;
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
### Step 15: Create postcss.config.mjs
|
|
357
|
+
|
|
358
|
+
```javascript
|
|
359
|
+
export default {
|
|
360
|
+
plugins: {
|
|
361
|
+
tailwindcss: {},
|
|
362
|
+
autoprefixer: {},
|
|
363
|
+
},
|
|
364
|
+
};
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
### Step 16: Create tsconfig.json
|
|
368
|
+
|
|
369
|
+
```json
|
|
370
|
+
{
|
|
371
|
+
"compilerOptions": {
|
|
372
|
+
"target": "ES2022",
|
|
373
|
+
"module": "ESNext",
|
|
374
|
+
"lib": ["ES2022", "DOM", "DOM.Iterable"],
|
|
375
|
+
"jsx": "react-jsx",
|
|
376
|
+
"moduleResolution": "bundler",
|
|
377
|
+
"resolveJsonModule": true,
|
|
378
|
+
"allowJs": true,
|
|
379
|
+
"strict": true,
|
|
380
|
+
"esModuleInterop": true,
|
|
381
|
+
"skipLibCheck": true,
|
|
382
|
+
"forceConsistentCasingInFileNames": true,
|
|
383
|
+
"isolatedModules": true,
|
|
384
|
+
"noEmit": true
|
|
385
|
+
},
|
|
386
|
+
"include": ["src/**/*"],
|
|
387
|
+
"exclude": ["node_modules", "dist"]
|
|
388
|
+
}
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
### Step 17: Create vite-env.d.ts
|
|
392
|
+
|
|
393
|
+
```typescript
|
|
394
|
+
declare global {
|
|
395
|
+
interface Window {
|
|
396
|
+
__LEGO_KERNEL_STATE__?: {
|
|
397
|
+
user?: { id: string; email: string; name?: string } | null;
|
|
398
|
+
organization?: { id: string; name: string; slug: string } | null;
|
|
399
|
+
isAuthenticated: boolean;
|
|
400
|
+
useGlobalKernelState?: any;
|
|
401
|
+
};
|
|
402
|
+
__LEGO_CHANNEL_BUS__?: {
|
|
403
|
+
publish: (channel: string, message: any) => void;
|
|
404
|
+
subscribe: (channel: string, callback: (data: any) => void) => () => void;
|
|
405
|
+
};
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
export {};
|
|
410
|
+
```
|
|
411
|
+
|
|
412
|
+
---
|
|
413
|
+
|
|
414
|
+
## Register Plugin with Host
|
|
415
|
+
|
|
416
|
+
### Step 1: Update host/src/modern.runtime.ts
|
|
417
|
+
|
|
418
|
+
```typescript
|
|
419
|
+
export default defineRuntimeConfig({
|
|
420
|
+
masterApp: {
|
|
421
|
+
apps: [
|
|
422
|
+
// ... existing apps
|
|
423
|
+
{
|
|
424
|
+
name: '@lego/plugin-my-plugin',
|
|
425
|
+
entry: isDev
|
|
426
|
+
? 'http://localhost:3003'
|
|
427
|
+
: () => import('@lego/plugin-my-plugin'),
|
|
428
|
+
activeWhen: '/my-plugin',
|
|
429
|
+
},
|
|
430
|
+
],
|
|
431
|
+
},
|
|
432
|
+
});
|
|
433
|
+
```
|
|
434
|
+
|
|
435
|
+
### Step 2: Update host/src/saas.config.ts
|
|
436
|
+
|
|
437
|
+
```typescript
|
|
438
|
+
export const saasConfig = {
|
|
439
|
+
plugins: [
|
|
440
|
+
// ... existing plugins
|
|
441
|
+
{ name: '@lego/plugin-my-plugin', enabled: true },
|
|
442
|
+
],
|
|
443
|
+
};
|
|
444
|
+
```
|
|
445
|
+
|
|
446
|
+
---
|
|
447
|
+
|
|
448
|
+
## Plugin Patterns
|
|
449
|
+
|
|
450
|
+
### CRUD Pattern (Like Todo Plugin)
|
|
451
|
+
|
|
452
|
+
1. **Types** → Define data interfaces
|
|
453
|
+
2. **Schemas** → Zod validation
|
|
454
|
+
3. **Hooks** → usePocketBase, useItems, useCreate, useUpdate, useDelete
|
|
455
|
+
4. **Components** → List, Item, Form, Dialog
|
|
456
|
+
5. **Pages** → Main page with filters
|
|
457
|
+
|
|
458
|
+
### Display Pattern (Like Dashboard Plugin)
|
|
459
|
+
|
|
460
|
+
1. **Hooks** → Stats queries, activity feed
|
|
461
|
+
2. **Components** → Stat cards, activity feed, quick actions
|
|
462
|
+
3. **Pages** → Dashboard overview
|
|
463
|
+
|
|
464
|
+
### Calculation Pattern
|
|
465
|
+
|
|
466
|
+
1. **Types** → Input/Output types
|
|
467
|
+
2. **Hooks** → Pure calculation functions
|
|
468
|
+
3. **Components** → Input form, result display
|
|
469
|
+
4. **Pages** → Calculator page
|
|
470
|
+
|
|
471
|
+
---
|
|
472
|
+
|
|
473
|
+
## Multi-Tenancy for Plugins
|
|
474
|
+
|
|
475
|
+
### Data Isolation
|
|
476
|
+
|
|
477
|
+
All plugin data MUST be scoped to organization:
|
|
478
|
+
|
|
479
|
+
```typescript
|
|
480
|
+
const result = await pb.collection('my_entities').getList(1, 50, {
|
|
481
|
+
filter: `organizationId = "${orgId}"`,
|
|
482
|
+
});
|
|
483
|
+
```
|
|
484
|
+
|
|
485
|
+
### PocketBase Collection Pattern
|
|
486
|
+
|
|
487
|
+
```javascript
|
|
488
|
+
{
|
|
489
|
+
name: 'my_entities',
|
|
490
|
+
type: 'base',
|
|
491
|
+
fields: [
|
|
492
|
+
// Multi-tenancy required fields
|
|
493
|
+
{ name: 'organizationId', type: 'relation', required: true, options: {
|
|
494
|
+
collectionId: 'organizations',
|
|
495
|
+
displayFields: ['name']
|
|
496
|
+
}},
|
|
497
|
+
{ name: 'ownerId', type: 'relation', required: true, options: {
|
|
498
|
+
collectionId: 'users',
|
|
499
|
+
displayFields: ['name', 'email']
|
|
500
|
+
}},
|
|
501
|
+
// Your fields here
|
|
502
|
+
{ name: 'name', type: 'text', required: true },
|
|
503
|
+
{ name: 'description', type: 'text' },
|
|
504
|
+
],
|
|
505
|
+
// Multi-tenancy API rules
|
|
506
|
+
listRule: '@request.auth.id != "" && organizationId = @request.auth.membership.organizations',
|
|
507
|
+
viewRule: '@request.auth.id != "" && organizationId = @request.auth.membership.organizations',
|
|
508
|
+
createRule: '@request.auth.id != "" && organizationId = @request.auth.membership.organizations',
|
|
509
|
+
updateRule: '@request.auth.id != "" && organizationId = @request.auth.membership.organizations && ownerId = @request.auth.id',
|
|
510
|
+
deleteRule: '@request.auth.id != "" && organizationId = @request.auth.membership.organizations && ownerId = @request.auth.id',
|
|
511
|
+
}
|
|
512
|
+
```
|
|
513
|
+
|
|
514
|
+
---
|
|
515
|
+
|
|
516
|
+
## Testing Your Plugin
|
|
517
|
+
|
|
518
|
+
### Local Development
|
|
519
|
+
|
|
520
|
+
1. **Start plugin dev server:**
|
|
521
|
+
```bash
|
|
522
|
+
cd packages/plugins/@lego/plugin-my-plugin
|
|
523
|
+
pnpm run dev
|
|
524
|
+
```
|
|
525
|
+
|
|
526
|
+
2. **Start host:**
|
|
527
|
+
```bash
|
|
528
|
+
cd host
|
|
529
|
+
pnpm run dev
|
|
530
|
+
```
|
|
531
|
+
|
|
532
|
+
3. **Navigate to:** http://localhost:8080
|
|
533
|
+
|
|
534
|
+
4. **Plugin should appear** in sidebar navigation
|
|
535
|
+
|
|
536
|
+
### Production Build Test
|
|
537
|
+
|
|
538
|
+
```bash
|
|
539
|
+
# Build all
|
|
540
|
+
pnpm run build
|
|
541
|
+
|
|
542
|
+
# Start production server
|
|
543
|
+
cd host && pnpm run start
|
|
544
|
+
|
|
545
|
+
# Navigate to http://localhost:8080
|
|
546
|
+
# Plugin should still work
|
|
547
|
+
```
|
|
548
|
+
|
|
549
|
+
---
|
|
550
|
+
|
|
551
|
+
## Common Issues
|
|
552
|
+
|
|
553
|
+
### Plugin Not Loading
|
|
554
|
+
|
|
555
|
+
**Checklist:**
|
|
556
|
+
- [ ] Plugin enabled in `saas.config.ts`
|
|
557
|
+
- [ ] Plugin registered in `modern.runtime.ts`
|
|
558
|
+
- [ ] Plugin dev server running (dev mode)
|
|
559
|
+
- [ ] Correct `activeWhen` path
|
|
560
|
+
- [ ] No console errors
|
|
561
|
+
|
|
562
|
+
### Window Bridge Undefined
|
|
563
|
+
|
|
564
|
+
**Solution:** Add type definitions in `vite-env.d.ts`
|
|
565
|
+
|
|
566
|
+
### Styles Not Loading
|
|
567
|
+
|
|
568
|
+
**Solutions:**
|
|
569
|
+
- [ ] `global.css` imported in `App.tsx`
|
|
570
|
+
- [ ] Tailwind content paths correct
|
|
571
|
+
- [ ] PostCSS config present
|
|
572
|
+
|
|
573
|
+
### Route Not Working
|
|
574
|
+
|
|
575
|
+
**Solutions:**
|
|
576
|
+
- [ ] Route defined in `plugin.config.ts`
|
|
577
|
+
- [ ] Component export matches
|
|
578
|
+
- [ ] Path doesn't conflict with existing routes
|
|
579
|
+
|
|
580
|
+
---
|
|
581
|
+
|
|
582
|
+
**Next:** Read [`08-component-library.md`](./08-component-library.md) for UI components reference.
|