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.
Files changed (242) hide show
  1. package/dist/index.cjs +179 -0
  2. package/dist/index.cjs.map +1 -1
  3. package/package.json +5 -3
  4. package/template/.cursor/rules/rules.mdc +639 -0
  5. package/template/.dockerignore +58 -0
  6. package/template/.env.example +18 -0
  7. package/template/.eslintignore +5 -0
  8. package/template/.eslintrc.js +28 -0
  9. package/template/.prettierignore +6 -0
  10. package/template/.prettierrc +11 -0
  11. package/template/CLAUDE.md +634 -0
  12. package/template/Dockerfile +67 -0
  13. package/template/PROMPT.md +457 -0
  14. package/template/README.md +325 -0
  15. package/template/docker-compose.yml +48 -0
  16. package/template/docker-entrypoint.sh +23 -0
  17. package/template/docs/checkpoints/.template.md +64 -0
  18. package/template/docs/checkpoints/framework/01-infrastructure-setup.md +132 -0
  19. package/template/docs/checkpoints/framework/02-pocketbase-setup.md +155 -0
  20. package/template/docs/checkpoints/framework/03-host-kernel.md +170 -0
  21. package/template/docs/checkpoints/framework/04-auth-system.md +163 -0
  22. package/template/docs/checkpoints/framework/phase-05-multitenancy-rbac.md +223 -0
  23. package/template/docs/checkpoints/framework/phase-06-ui-components.md +260 -0
  24. package/template/docs/checkpoints/framework/phase-07-communication-system.md +276 -0
  25. package/template/docs/checkpoints/framework/phase-08-plugin-system.md +91 -0
  26. package/template/docs/checkpoints/framework/phase-09-dashboard-plugin.md +111 -0
  27. package/template/docs/checkpoints/framework/phase-10-todo-plugin.md +169 -0
  28. package/template/docs/checkpoints/framework/phase-11-testing.md +264 -0
  29. package/template/docs/checkpoints/framework/phase-12-deployment.md +294 -0
  30. package/template/docs/checkpoints/framework/phase-13-documentation.md +312 -0
  31. package/template/docs/framework/plans/00-index.md +164 -0
  32. package/template/docs/framework/plans/01-infrastructure-setup.md +855 -0
  33. package/template/docs/framework/plans/02-pocketbase-setup.md +1374 -0
  34. package/template/docs/framework/plans/03-host-kernel.md +1518 -0
  35. package/template/docs/framework/plans/04-auth-system.md +1466 -0
  36. package/template/docs/framework/plans/05-multitenancy-rbac.md +1527 -0
  37. package/template/docs/framework/plans/06-ui-components.md +1478 -0
  38. package/template/docs/framework/plans/07-communication-system.md +1106 -0
  39. package/template/docs/framework/plans/08-plugin-system.md +1179 -0
  40. package/template/docs/framework/plans/09-dashboard-plugin.md +1137 -0
  41. package/template/docs/framework/plans/10-todo-plugin.md +1343 -0
  42. package/template/docs/framework/plans/11-testing.md +935 -0
  43. package/template/docs/framework/plans/12-deployment.md +896 -0
  44. package/template/docs/framework/prompts/0-boilerplate-modernjs.md +151 -0
  45. package/template/docs/framework/research/00-modernjs-audit.md +488 -0
  46. package/template/docs/framework/research/01-system-blueprint.md +721 -0
  47. package/template/docs/framework/research/02-data-migration-protocol.md +699 -0
  48. package/template/docs/framework/research/03-host-setup.md +714 -0
  49. package/template/docs/framework/research/04-plugin-architecture.md +645 -0
  50. package/template/docs/framework/research/05-slot-injection-pattern.md +671 -0
  51. package/template/docs/framework/research/06-cli-strategy.md +615 -0
  52. package/template/docs/framework/research/07-deployment.md +629 -0
  53. package/template/docs/framework/research/README.md +282 -0
  54. package/template/docs/framework/setup/00-index.md +210 -0
  55. package/template/docs/framework/setup/01-framework-structure.md +308 -0
  56. package/template/docs/framework/setup/02-development-workflow.md +405 -0
  57. package/template/docs/framework/setup/03-environment-setup.md +215 -0
  58. package/template/docs/framework/setup/04-kernel-architecture.md +499 -0
  59. package/template/docs/framework/setup/05-plugin-system.md +620 -0
  60. package/template/docs/framework/setup/06-communication-patterns.md +451 -0
  61. package/template/docs/framework/setup/07-plugin-development.md +582 -0
  62. package/template/docs/framework/setup/08-component-library.md +658 -0
  63. package/template/docs/framework/setup/09-data-integration.md +609 -0
  64. package/template/docs/framework/setup/10-auth-rbac.md +497 -0
  65. package/template/docs/framework/setup/11-hooks-api.md +393 -0
  66. package/template/docs/framework/setup/12-components-api.md +665 -0
  67. package/template/docs/framework/setup/13-deployment-guide.md +566 -0
  68. package/template/docs/framework/setup/README.md +548 -0
  69. package/template/host/e2e/auth.spec.ts +38 -0
  70. package/template/host/e2e/layout.spec.ts +38 -0
  71. package/template/host/modern.config.ts +19 -0
  72. package/template/host/package.json +71 -0
  73. package/template/host/playwright.config.ts +34 -0
  74. package/template/host/postcss.config.mjs +6 -0
  75. package/template/host/src/App.tsx +6 -0
  76. package/template/host/src/bootstrap.tsx +74 -0
  77. package/template/host/src/global.css +59 -0
  78. package/template/host/src/index.ts +2 -0
  79. package/template/host/src/kernel/__tests__/lib-utils.test.ts +32 -0
  80. package/template/host/src/kernel/__tests__/rbac-hooks.test.tsx +114 -0
  81. package/template/host/src/kernel/__tests__/rbac-utils.test.ts +108 -0
  82. package/template/host/src/kernel/auth/ProtectedRoute.tsx +41 -0
  83. package/template/host/src/kernel/auth/components/LoginForm.tsx +97 -0
  84. package/template/host/src/kernel/auth/components/LogoutButton.tsx +79 -0
  85. package/template/host/src/kernel/auth/hooks.ts +174 -0
  86. package/template/host/src/kernel/auth/index.ts +5 -0
  87. package/template/host/src/kernel/auth/schemas.ts +27 -0
  88. package/template/host/src/kernel/auth/service.ts +197 -0
  89. package/template/host/src/kernel/auth/types.ts +36 -0
  90. package/template/host/src/kernel/channels/ChannelBus.ts +181 -0
  91. package/template/host/src/kernel/channels/ChannelProvider.tsx +57 -0
  92. package/template/host/src/kernel/channels/events.ts +27 -0
  93. package/template/host/src/kernel/channels/hooks.ts +168 -0
  94. package/template/host/src/kernel/channels/index.ts +6 -0
  95. package/template/host/src/kernel/channels/integrations/ToastIntegration.tsx +60 -0
  96. package/template/host/src/kernel/channels/plugin-hooks.ts +72 -0
  97. package/template/host/src/kernel/channels/types.ts +112 -0
  98. package/template/host/src/kernel/components/__tests__/Badge.test.tsx +35 -0
  99. package/template/host/src/kernel/components/__tests__/Button.test.tsx +63 -0
  100. package/template/host/src/kernel/components/__tests__/Input.test.tsx +64 -0
  101. package/template/host/src/kernel/components/index.ts +32 -0
  102. package/template/host/src/kernel/components/ui/alert.tsx +58 -0
  103. package/template/host/src/kernel/components/ui/avatar.tsx +47 -0
  104. package/template/host/src/kernel/components/ui/badge.tsx +35 -0
  105. package/template/host/src/kernel/components/ui/button.tsx +50 -0
  106. package/template/host/src/kernel/components/ui/card.tsx +78 -0
  107. package/template/host/src/kernel/components/ui/dialog.tsx +116 -0
  108. package/template/host/src/kernel/components/ui/dropdown-menu.tsx +192 -0
  109. package/template/host/src/kernel/components/ui/index.ts +7 -0
  110. package/template/host/src/kernel/components/ui/input.tsx +24 -0
  111. package/template/host/src/kernel/components/ui/label.tsx +21 -0
  112. package/template/host/src/kernel/components/ui/popover.tsx +28 -0
  113. package/template/host/src/kernel/components/ui/progress.tsx +25 -0
  114. package/template/host/src/kernel/components/ui/scroll-area.tsx +45 -0
  115. package/template/host/src/kernel/components/ui/select.tsx +155 -0
  116. package/template/host/src/kernel/components/ui/separator.tsx +28 -0
  117. package/template/host/src/kernel/components/ui/skeleton.tsx +15 -0
  118. package/template/host/src/kernel/components/ui/switch.tsx +26 -0
  119. package/template/host/src/kernel/components/ui/table.tsx +116 -0
  120. package/template/host/src/kernel/components/ui/tabs.tsx +52 -0
  121. package/template/host/src/kernel/components/ui/toast.tsx +126 -0
  122. package/template/host/src/kernel/components/ui/toaster.tsx +34 -0
  123. package/template/host/src/kernel/components/ui/tooltip.tsx +27 -0
  124. package/template/host/src/kernel/components/ui/use-toast.ts +183 -0
  125. package/template/host/src/kernel/index.ts +48 -0
  126. package/template/host/src/kernel/lib/cn.ts +1 -0
  127. package/template/host/src/kernel/lib/utils.ts +36 -0
  128. package/template/host/src/kernel/plugins/Slot.tsx +41 -0
  129. package/template/host/src/kernel/plugins/SlotProvider.tsx +88 -0
  130. package/template/host/src/kernel/plugins/index.ts +23 -0
  131. package/template/host/src/kernel/plugins/loader.ts +122 -0
  132. package/template/host/src/kernel/plugins/schemas.ts +54 -0
  133. package/template/host/src/kernel/plugins/store.ts +185 -0
  134. package/template/host/src/kernel/plugins/types.ts +103 -0
  135. package/template/host/src/kernel/providers/PocketBaseProvider.tsx +70 -0
  136. package/template/host/src/kernel/providers/QueryProvider.tsx +28 -0
  137. package/template/host/src/kernel/providers/ThemeProvider.tsx +25 -0
  138. package/template/host/src/kernel/providers/index.ts +3 -0
  139. package/template/host/src/kernel/rbac/components/OrganizationSelector.tsx +69 -0
  140. package/template/host/src/kernel/rbac/components/PermissionGate.tsx +43 -0
  141. package/template/host/src/kernel/rbac/hooks.ts +379 -0
  142. package/template/host/src/kernel/rbac/index.ts +6 -0
  143. package/template/host/src/kernel/rbac/service.ts +504 -0
  144. package/template/host/src/kernel/rbac/types.ts +164 -0
  145. package/template/host/src/kernel/rbac/utils.ts +34 -0
  146. package/template/host/src/kernel/shared-state/bridge.ts +31 -0
  147. package/template/host/src/kernel/shared-state/index.ts +3 -0
  148. package/template/host/src/kernel/shared-state/store.ts +62 -0
  149. package/template/host/src/kernel/shared-state/types.ts +60 -0
  150. package/template/host/src/kernel/use-migrations.ts +72 -0
  151. package/template/host/src/layout/MobileMenu.tsx +61 -0
  152. package/template/host/src/layout/Shell.tsx +42 -0
  153. package/template/host/src/layout/Sidebar.tsx +178 -0
  154. package/template/host/src/layout/Topbar.tsx +50 -0
  155. package/template/host/src/layout/index.ts +4 -0
  156. package/template/host/src/lib/pocketbase/client.ts +38 -0
  157. package/template/host/src/lib/pocketbase/collections/audit_logs.ts +87 -0
  158. package/template/host/src/lib/pocketbase/collections/index.ts +19 -0
  159. package/template/host/src/lib/pocketbase/collections/organizations.ts +63 -0
  160. package/template/host/src/lib/pocketbase/collections/permissions.ts +57 -0
  161. package/template/host/src/lib/pocketbase/collections/roles.ts +55 -0
  162. package/template/host/src/lib/pocketbase/collections/todos.ts +74 -0
  163. package/template/host/src/lib/pocketbase/collections/user_roles.ts +57 -0
  164. package/template/host/src/lib/pocketbase/collections/users.ts +43 -0
  165. package/template/host/src/lib/pocketbase/index.ts +5 -0
  166. package/template/host/src/lib/pocketbase/migrations.ts +44 -0
  167. package/template/host/src/lib/pocketbase/seed/permissions.ts +8 -0
  168. package/template/host/src/lib/pocketbase/seed/roles.ts +22 -0
  169. package/template/host/src/lib/pocketbase/seed.ts +113 -0
  170. package/template/host/src/lib/pocketbase/types.ts +102 -0
  171. package/template/host/src/modern.runtime.ts +26 -0
  172. package/template/host/src/plugins.d.ts +9 -0
  173. package/template/host/src/providers/PocketBaseProvider.tsx +30 -0
  174. package/template/host/src/routes/_.tsx +6 -0
  175. package/template/host/src/routes/dashboard._.tsx +41 -0
  176. package/template/host/src/routes/index.tsx +93 -0
  177. package/template/host/src/routes/login.tsx +36 -0
  178. package/template/host/src/saas.config.ts +52 -0
  179. package/template/host/src/test/setup.ts +65 -0
  180. package/template/host/src/test/utils.tsx +69 -0
  181. package/template/host/src/test/vitest-globals.d.ts +19 -0
  182. package/template/host/src/vite-env.d.ts +16 -0
  183. package/template/host/tailwind.config.ts +77 -0
  184. package/template/host/tsconfig.json +19 -0
  185. package/template/host/vitest.config.ts +30 -0
  186. package/template/nginx.conf +72 -0
  187. package/template/package.json +44 -0
  188. package/template/packages/plugins/@lego/plugin-dashboard/modern.config.ts +19 -0
  189. package/template/packages/plugins/@lego/plugin-dashboard/package.json +35 -0
  190. package/template/packages/plugins/@lego/plugin-dashboard/postcss.config.mjs +6 -0
  191. package/template/packages/plugins/@lego/plugin-dashboard/src/App.tsx +27 -0
  192. package/template/packages/plugins/@lego/plugin-dashboard/src/components/ActivityFeed.tsx +63 -0
  193. package/template/packages/plugins/@lego/plugin-dashboard/src/components/QuickActionSlot.tsx +11 -0
  194. package/template/packages/plugins/@lego/plugin-dashboard/src/components/QuickActions.tsx +68 -0
  195. package/template/packages/plugins/@lego/plugin-dashboard/src/components/SidebarWidget.tsx +35 -0
  196. package/template/packages/plugins/@lego/plugin-dashboard/src/components/StatCard.tsx +47 -0
  197. package/template/packages/plugins/@lego/plugin-dashboard/src/global.css +24 -0
  198. package/template/packages/plugins/@lego/plugin-dashboard/src/hooks/useChannelIntegration.ts +43 -0
  199. package/template/packages/plugins/@lego/plugin-dashboard/src/hooks/useDashboardStats.ts +65 -0
  200. package/template/packages/plugins/@lego/plugin-dashboard/src/hooks/usePocketBase.ts +47 -0
  201. package/template/packages/plugins/@lego/plugin-dashboard/src/hooks/useRecentActivity.ts +55 -0
  202. package/template/packages/plugins/@lego/plugin-dashboard/src/lib/utils.ts +6 -0
  203. package/template/packages/plugins/@lego/plugin-dashboard/src/pages/DashboardPage.tsx +105 -0
  204. package/template/packages/plugins/@lego/plugin-dashboard/src/plugin.config.ts +121 -0
  205. package/template/packages/plugins/@lego/plugin-dashboard/src/plugin.ts +18 -0
  206. package/template/packages/plugins/@lego/plugin-dashboard/src/vite-env.d.ts +32 -0
  207. package/template/packages/plugins/@lego/plugin-dashboard/tailwind.config.ts +35 -0
  208. package/template/packages/plugins/@lego/plugin-dashboard/tsconfig.json +18 -0
  209. package/template/packages/plugins/@lego/plugin-todo/modern.config.ts +18 -0
  210. package/template/packages/plugins/@lego/plugin-todo/package.json +41 -0
  211. package/template/packages/plugins/@lego/plugin-todo/postcss.config.mjs +6 -0
  212. package/template/packages/plugins/@lego/plugin-todo/src/App.tsx +12 -0
  213. package/template/packages/plugins/@lego/plugin-todo/src/components/SidebarWidget.tsx +16 -0
  214. package/template/packages/plugins/@lego/plugin-todo/src/components/TodoDialog.tsx +55 -0
  215. package/template/packages/plugins/@lego/plugin-todo/src/components/TodoFilters.tsx +79 -0
  216. package/template/packages/plugins/@lego/plugin-todo/src/components/TodoForm.tsx +94 -0
  217. package/template/packages/plugins/@lego/plugin-todo/src/components/TodoItem.tsx +121 -0
  218. package/template/packages/plugins/@lego/plugin-todo/src/components/TodoList.tsx +41 -0
  219. package/template/packages/plugins/@lego/plugin-todo/src/components/index.ts +6 -0
  220. package/template/packages/plugins/@lego/plugin-todo/src/global.css +59 -0
  221. package/template/packages/plugins/@lego/plugin-todo/src/hooks/useCreateTodo.ts +62 -0
  222. package/template/packages/plugins/@lego/plugin-todo/src/hooks/useDeleteTodo.ts +46 -0
  223. package/template/packages/plugins/@lego/plugin-todo/src/hooks/usePocketBase.ts +38 -0
  224. package/template/packages/plugins/@lego/plugin-todo/src/hooks/useTodos.ts +64 -0
  225. package/template/packages/plugins/@lego/plugin-todo/src/hooks/useUpdateTodo.ts +35 -0
  226. package/template/packages/plugins/@lego/plugin-todo/src/index.tsx +5 -0
  227. package/template/packages/plugins/@lego/plugin-todo/src/lib/utils.ts +20 -0
  228. package/template/packages/plugins/@lego/plugin-todo/src/pages/TodoPage.tsx +89 -0
  229. package/template/packages/plugins/@lego/plugin-todo/src/plugin.config.ts +104 -0
  230. package/template/packages/plugins/@lego/plugin-todo/src/plugin.ts +13 -0
  231. package/template/packages/plugins/@lego/plugin-todo/src/schemas.ts +37 -0
  232. package/template/packages/plugins/@lego/plugin-todo/src/types.ts +42 -0
  233. package/template/packages/plugins/@lego/plugin-todo/src/vite-env.d.ts +31 -0
  234. package/template/packages/plugins/@lego/plugin-todo/tailwind.config.ts +51 -0
  235. package/template/packages/plugins/@lego/plugin-todo/tsconfig.json +18 -0
  236. package/template/pnpm-workspace.yaml +4 -0
  237. package/template/pocketbase/CHANGELOG.md +911 -0
  238. package/template/pocketbase/LICENSE.md +17 -0
  239. package/template/scripts/create-plugin.js +221 -0
  240. package/template/scripts/deploy.sh +56 -0
  241. package/template/tsconfig.base.json +26 -0
  242. package/template/tsconfig.json +8 -0
@@ -0,0 +1,609 @@
1
+ # Data Integration
2
+
3
+ **PocketBase, Multi-Tenancy, and Data Fetching Patterns**
4
+
5
+ ---
6
+
7
+ ## Overview
8
+
9
+ Lego-One uses **PocketBase** as the backend, providing an embedded database with auto-generated APIs. All data is scoped to organizations for multi-tenancy, and accessed through TanStack Query for optimal caching.
10
+
11
+ ---
12
+
13
+ ## PocketBase Setup
14
+
15
+ ### Collections
16
+
17
+ **Standard Collections:**
18
+
19
+ | Collection | Purpose | Multi-Tenancy |
20
+ |-----------|---------|---------------|
21
+ | `users` | User accounts | Via memberships |
22
+ | `organizations` | Tenant organizations | N/A |
23
+ | `roles` | System and custom roles | Per org |
24
+ | `permissions` | Permission definitions | N/A |
25
+ | `user_roles` | User-role assignments | Required |
26
+ | `audit_logs` | Activity tracking | Required |
27
+
28
+ **Plugin Collections:**
29
+ Plugins create their own collections, e.g.:
30
+ - `todos` - Todo plugin
31
+ - `dashboard_widgets` - Dashboard plugin
32
+
33
+ ---
34
+
35
+ ### Collection Schema Pattern
36
+
37
+ All plugin collections MUST follow this pattern:
38
+
39
+ ```javascript
40
+ {
41
+ name: 'my_plugin_items',
42
+ type: 'base',
43
+
44
+ // Multi-tenancy required fields
45
+ fields: [
46
+ {
47
+ name: 'organizationId',
48
+ type: 'relation',
49
+ required: true,
50
+ options: {
51
+ collectionId: 'organizations',
52
+ displayFields: ['name']
53
+ }
54
+ },
55
+ {
56
+ name: 'ownerId',
57
+ type: 'relation',
58
+ required: true,
59
+ options: {
60
+ collectionId: 'users',
61
+ displayFields: ['name', 'email']
62
+ }
63
+ },
64
+
65
+ // Your plugin-specific fields
66
+ { name: 'title', type: 'text', required: true },
67
+ { name: 'description', type: 'text' },
68
+ { name: 'status', type: 'select', options: { values: ['active', 'inactive'] } },
69
+ { name: 'priority', type: 'select', options: { values: ['low', 'medium', 'high'] } },
70
+ ],
71
+
72
+ // Multi-tenancy API rules
73
+ listRule: '@request.auth.id != "" && organizationId = @request.auth.membership.organizations',
74
+ viewRule: '@request.auth.id != "" && organizationId = @request.auth.membership.organizations',
75
+ createRule: '@request.auth.id != "" && organizationId = @request.auth.membership.organizations',
76
+ updateRule: '@request.auth.id != "" && organizationId = @request.auth.membership.organizations && ownerId = @request.auth.id',
77
+ deleteRule: '@request.auth.id != "" && organizationId = @request.auth.membership.organizations && ownerId = @request.auth.id',
78
+ }
79
+ ```
80
+
81
+ ---
82
+
83
+ ## PocketBase Client
84
+
85
+ ### Accessing PocketBase
86
+
87
+ **In Host Components:**
88
+ ```typescript
89
+ import { usePocketBase } from '@lego/kernel/providers';
90
+
91
+ function MyComponent() {
92
+ const pb = usePocketBase();
93
+
94
+ useEffect(() => {
95
+ if (!pb) return;
96
+
97
+ async function fetchData() {
98
+ const result = await pb.collection('todos').getList(1, 20);
99
+ console.log(result.items);
100
+ }
101
+
102
+ fetchData();
103
+ }, [pb]);
104
+
105
+ return <div>...</div>;
106
+ }
107
+ ```
108
+
109
+ **In Plugins:**
110
+ ```typescript
111
+ // Create usePocketBase hook for plugin
112
+ import { useEffect, useState } from 'react';
113
+
114
+ export function usePocketBase() {
115
+ const [pb, setPb] = useState(null);
116
+
117
+ useEffect(() => {
118
+ // Get kernel state from window bridge
119
+ const kernelState = window.__LEGO_KERNEL_STATE__;
120
+
121
+ // Get PocketBase URL from environment
122
+ const pbUrl = import.meta.env.VITE_POCKETBASE_URL || 'http://127.0.0.1:8090';
123
+
124
+ // Create PocketBase client
125
+ const PocketBase = require('pocketbase').default;
126
+ const client = new PocketBase(pbUrl);
127
+
128
+ // Restore auth token from kernel state
129
+ const state = kernelState?.useGlobalKernelState?.getState();
130
+ if (state?.token) {
131
+ client.authStore.save(state.token);
132
+ }
133
+
134
+ setPb(client);
135
+ }, []);
136
+
137
+ return pb;
138
+ }
139
+ ```
140
+
141
+ ---
142
+
143
+ ## TanStack Query Integration
144
+
145
+ ### Creating Data Hooks
146
+
147
+ **Use TanStack Query for all server state:**
148
+
149
+ ```typescript
150
+ import { useQuery } from '@tanstack/react-query';
151
+ import { usePocketBase } from './usePocketBase';
152
+
153
+ export function useTodos() {
154
+ const pb = usePocketBase();
155
+
156
+ return useQuery({
157
+ queryKey: ['todos'],
158
+ queryFn: async () => {
159
+ if (!pb) throw new Error('PB not initialized');
160
+
161
+ const kernelState = window.__LEGO_KERNEL_STATE__;
162
+ const state = kernelState?.useGlobalKernelState?.getState();
163
+ const orgId = state?.organization?.id;
164
+
165
+ const result = await pb.collection('todos').getList(1, 50, {
166
+ filter: `organizationId = "${orgId}"`,
167
+ sort: '-created',
168
+ });
169
+
170
+ return result.items;
171
+ },
172
+ enabled: !!pb && !!orgId,
173
+ });
174
+ }
175
+ ```
176
+
177
+ ### Creating Mutation Hooks
178
+
179
+ ```typescript
180
+ import { useMutation, useQueryClient } from '@tanstack/react-query';
181
+ import { usePocketBase } from './usePocketBase';
182
+ import { useToastChannel } from './useToastChannel';
183
+
184
+ export function useCreateTodo() {
185
+ const pb = usePocketBase();
186
+ const queryClient = useQueryClient();
187
+ const showToast = useToastChannel();
188
+
189
+ return useMutation({
190
+ mutationFn: async (data: { title: string; description?: string }) => {
191
+ if (!pb) throw new Error('PB not initialized');
192
+
193
+ const kernelState = window.__LEGO_KERNEL_STATE__;
194
+ const state = kernelState?.useGlobalKernelState?.getState();
195
+
196
+ return await pb.collection('todos').create({
197
+ ...data,
198
+ organizationId: state?.organization?.id,
199
+ ownerId: state?.user?.id,
200
+ });
201
+ },
202
+ onSuccess: () => {
203
+ // Invalidate and refetch
204
+ queryClient.invalidateQueries({ queryKey: ['todos'] });
205
+
206
+ // Show success toast
207
+ showToast({
208
+ type: 'success',
209
+ title: 'Todo Created',
210
+ description: 'Your todo was created successfully',
211
+ });
212
+ },
213
+ onError: (error) => {
214
+ showToast({
215
+ type: 'error',
216
+ title: 'Error',
217
+ description: error.message,
218
+ });
219
+ },
220
+ });
221
+ }
222
+ ```
223
+
224
+ ---
225
+
226
+ ## Multi-Tenancy Patterns
227
+
228
+ ### Data Isolation
229
+
230
+ **CRITICAL:** All data queries MUST be scoped to organization:
231
+
232
+ ```typescript
233
+ // ✅ CORRECT - Filter by organization
234
+ const result = await pb.collection('items').getList(1, 50, {
235
+ filter: `organizationId = "${orgId}"`,
236
+ });
237
+
238
+ // ❌ WRONG - No organization filter
239
+ const result = await pb.collection('items').getList(1, 50);
240
+ ```
241
+
242
+ ### Getting Current Organization
243
+
244
+ ```typescript
245
+ import { useEffect, useState } from 'react';
246
+
247
+ export function useCurrentOrganization() {
248
+ const [org, setOrg] = useState(null);
249
+
250
+ useEffect(() => {
251
+ // Get from kernel state
252
+ const kernelState = window.__LEGO_KERNEL_STATE__;
253
+ const state = kernelState?.useGlobalKernelState?.getState();
254
+
255
+ setOrg(state?.organization || null);
256
+
257
+ // Subscribe to organization changes
258
+ const channelBus = window.__LEGO_CHANNEL_BUS__;
259
+ if (!channelBus) return;
260
+
261
+ const unsubscribe = channelBus.subscribe('lego:organization:change', () => {
262
+ const newState = kernelState?.useGlobalKernelState?.getState();
263
+ setOrg(newState?.organization || null);
264
+ });
265
+
266
+ return unsubscribe;
267
+ }, []);
268
+
269
+ return org;
270
+ }
271
+ ```
272
+
273
+ ---
274
+
275
+ ## CRUD Patterns
276
+
277
+ ### Create
278
+
279
+ ```typescript
280
+ export function useCreateItem() {
281
+ const pb = usePocketBase();
282
+ const queryClient = useQueryClient();
283
+
284
+ return useMutation({
285
+ mutationFn: async (data: CreateItemData) => {
286
+ if (!pb) throw new Error('PB not initialized');
287
+
288
+ const state = window.__LEGO_KERNEL_STATE__?.useGlobalKernelState?.getState();
289
+
290
+ return await pb.collection('items').create({
291
+ ...data,
292
+ organizationId: state?.organization?.id,
293
+ ownerId: state?.user?.id,
294
+ });
295
+ },
296
+ onSuccess: () => {
297
+ queryClient.invalidateQueries({ queryKey: ['items'] });
298
+ },
299
+ });
300
+ }
301
+ ```
302
+
303
+ ### Read
304
+
305
+ ```typescript
306
+ export function useItems() {
307
+ const pb = usePocketBase();
308
+
309
+ return useQuery({
310
+ queryKey: ['items'],
311
+ queryFn: async () => {
312
+ if (!pb) throw new Error('PB not initialized');
313
+
314
+ const state = window.__LEGO_KERNEL_STATE__?.useGlobalKernelState?.getState();
315
+ const orgId = state?.organization?.id;
316
+
317
+ const result = await pb.collection('items').getList(1, 50, {
318
+ filter: `organizationId = "${orgId}"`,
319
+ sort: '-created',
320
+ });
321
+
322
+ return result.items;
323
+ },
324
+ enabled: !!pb && !!orgId,
325
+ });
326
+ }
327
+ ```
328
+
329
+ ### Update
330
+
331
+ ```typescript
332
+ export function useUpdateItem() {
333
+ const pb = usePocketBase();
334
+ const queryClient = useQueryClient();
335
+
336
+ return useMutation({
337
+ mutationFn: async ({ id, data }: { id: string; data: UpdateItemData }) => {
338
+ if (!pb) throw new Error('PB not initialized');
339
+
340
+ return await pb.collection('items').update(id, data);
341
+ },
342
+ onSuccess: (_, { id }) => {
343
+ queryClient.invalidateQueries({ queryKey: ['items'] });
344
+ queryClient.invalidateQueries({ queryKey: ['item', id] });
345
+ },
346
+ });
347
+ }
348
+ ```
349
+
350
+ ### Delete
351
+
352
+ ```typescript
353
+ export function useDeleteItem() {
354
+ const pb = usePocketBase();
355
+ const queryClient = useQueryClient();
356
+
357
+ return useMutation({
358
+ mutationFn: async (id: string) => {
359
+ if (!pb) throw new Error('PB not initialized');
360
+
361
+ return await pb.collection('items').delete(id);
362
+ },
363
+ onSuccess: () => {
364
+ queryClient.invalidateQueries({ queryKey: ['items'] });
365
+ },
366
+ });
367
+ }
368
+ ```
369
+
370
+ ---
371
+
372
+ ## Advanced Queries
373
+
374
+ ### Filtering
375
+
376
+ ```typescript
377
+ export function useTodos(filter: 'all' | 'active' | 'completed') {
378
+ const pb = usePocketBase();
379
+
380
+ return useQuery({
381
+ queryKey: ['todos', filter],
382
+ queryFn: async () => {
383
+ if (!pb) throw new Error('PB not initialized');
384
+
385
+ const state = window.__LEGO_KERNEL_STATE__?.useGlobalKernelState?.getState();
386
+ const orgId = state?.organization?.id;
387
+
388
+ let filterStr = `organizationId = "${orgId}"`;
389
+
390
+ if (filter === 'active') {
391
+ filterStr += ' && status = "active"';
392
+ } else if (filter === 'completed') {
393
+ filterStr += ' && status = "completed"';
394
+ }
395
+
396
+ const result = await pb.collection('todos').getList(1, 50, {
397
+ filter: filterStr,
398
+ sort: '-created',
399
+ });
400
+
401
+ return result.items;
402
+ },
403
+ enabled: !!pb && !!orgId,
404
+ });
405
+ }
406
+ ```
407
+
408
+ ### Searching
409
+
410
+ ```typescript
411
+ export function useSearchTodos(search: string) {
412
+ const pb = usePocketBase();
413
+
414
+ return useQuery({
415
+ queryKey: ['todos', 'search', search],
416
+ queryFn: async () => {
417
+ if (!pb || !search) return [];
418
+
419
+ const state = window.__LEGO_KERNEL_STATE__?.useGlobalKernelState?.getState();
420
+ const orgId = state?.organization?.id;
421
+
422
+ const result = await pb.collection('todos').getList(1, 50, {
423
+ filter: `organizationId = "${orgId}" && title ~ "${search}"`,
424
+ sort: '-created',
425
+ });
426
+
427
+ return result.items;
428
+ },
429
+ enabled: !!pb && !!orgId && search.length > 0,
430
+ });
431
+ }
432
+ ```
433
+
434
+ ### Pagination
435
+
436
+ ```typescript
437
+ export function useTodosPaginated(page: number, perPage: number = 20) {
438
+ const pb = usePocketBase();
439
+
440
+ return useQuery({
441
+ queryKey: ['todos', 'paginated', page, perPage],
442
+ queryFn: async () => {
443
+ if (!pb) throw new Error('PB not initialized');
444
+
445
+ const state = window.__LEGO_KERNEL_STATE__?.useGlobalKernelState?.getState();
446
+ const orgId = state?.organization?.id;
447
+
448
+ const result = await pb.collection('todos').getList(page, perPage, {
449
+ filter: `organizationId = "${orgId}"`,
450
+ sort: '-created',
451
+ });
452
+
453
+ return {
454
+ items: result.items,
455
+ totalPages: result.totalPages,
456
+ totalItems: result.totalItems,
457
+ };
458
+ },
459
+ enabled: !!pb && !!orgId,
460
+ });
461
+ }
462
+ ```
463
+
464
+ ---
465
+
466
+ ## Real-time Subscriptions
467
+
468
+ PocketBase supports real-time subscriptions:
469
+
470
+ ```typescript
471
+ useEffect(() => {
472
+ const pb = usePocketBase();
473
+ if (!pb) return;
474
+
475
+ // Subscribe to todo changes
476
+ const unsubscribe = pb.collection('todos').subscribe('*', (e) => {
477
+ console.log('Todo changed:', e);
478
+
479
+ // Refresh data on changes
480
+ queryClient.invalidateQueries({ queryKey: ['todos'] });
481
+ });
482
+
483
+ return () => {
484
+ unsubscribe(); // Cleanup
485
+ };
486
+ }, [pb]);
487
+ ```
488
+
489
+ ---
490
+
491
+ ## Validation with Zod
492
+
493
+ ### Define Schema
494
+
495
+ ```typescript
496
+ import { z } from 'zod';
497
+
498
+ export const todoFormSchema = z.object({
499
+ title: z.string().min(1, 'Title is required').max(200, 'Title too long'),
500
+ description: z.string().max(1000).optional(),
501
+ priority: z.enum(['low', 'medium', 'high']).default('medium'),
502
+ dueDate: z.string().optional(),
503
+ });
504
+
505
+ export type TodoFormData = z.infer<typeof todoFormSchema>;
506
+ ```
507
+
508
+ ### Use with Form Library
509
+
510
+ ```typescript
511
+ import { useForm } from 'react-hook-form';
512
+ import { zodResolver } from '@hookform/resolvers/zod';
513
+ import { todoFormSchema } from './schemas';
514
+
515
+ function TodoForm() {
516
+ const { register, handleSubmit, formState: { errors } } = useForm({
517
+ resolver: zodResolver(todoFormSchema),
518
+ });
519
+
520
+ const { mutate: createTodo } = useCreateTodo();
521
+
522
+ const onSubmit = (data: TodoFormData) => {
523
+ createTodo(data);
524
+ };
525
+
526
+ return (
527
+ <form onSubmit={handleSubmit(onSubmit)}>
528
+ <input {...register('title')} />
529
+ {errors.title && <span>{errors.title.message}</span>}
530
+
531
+ <textarea {...register('description')} />
532
+
533
+ <select {...register('priority')}>
534
+ <option value="low">Low</option>
535
+ <option value="medium">Medium</option>
536
+ <option value="high">High</option>
537
+ </select>
538
+
539
+ <button type="submit">Create</button>
540
+ </form>
541
+ );
542
+ }
543
+ ```
544
+
545
+ ---
546
+
547
+ ## Error Handling
548
+
549
+ ### Handling PocketBase Errors
550
+
551
+ ```typescript
552
+ try {
553
+ const result = await pb.collection('todos').create(data);
554
+ } catch (error) {
555
+ // PocketBase errors
556
+ if (error?.data) {
557
+ // Validation error
558
+ console.error('Validation failed:', error.data);
559
+ } else if (error?.status === 404) {
560
+ console.error('Collection not found');
561
+ } else if (error?.status === 403) {
562
+ console.error('Permission denied');
563
+ } else {
564
+ console.error('Unknown error:', error);
565
+ }
566
+ }
567
+ ```
568
+
569
+ ### User-Friendly Error Messages
570
+
571
+ ```typescript
572
+ function getErrorMessage(error: any): string {
573
+ if (error?.data?.title) {
574
+ return error.data.title; // PocketBase validation error
575
+ }
576
+
577
+ if (error?.message) {
578
+ return error.message;
579
+ }
580
+
581
+ return 'An unexpected error occurred';
582
+ }
583
+ ```
584
+
585
+ ---
586
+
587
+ ## Best Practices
588
+
589
+ ### DO ✅
590
+
591
+ 1. **Scope all queries to organization** - Always filter by `organizationId`
592
+ 2. **Use TanStack Query** - Automatic caching, refetching, loading states
593
+ 3. **Show loading states** - Use Skeleton, Spinners during fetch
594
+ 4. **Handle errors gracefully** - Show user-friendly error messages
595
+ 5. **Invalidate queries** - After mutations, invalidate related queries
596
+ 6. **Use toast notifications** - For user feedback on actions
597
+ 7. **Validate with Zod** - Type-safe validation
598
+
599
+ ### DON'T ❌
600
+
601
+ 1. **Don't skip organization filtering** - Data isolation is critical
602
+ 2. **Don't fetch directly in components** - Use custom hooks
603
+ 3. **Don't ignore loading/error states** - Always handle these
604
+ 4. **Don't forget cleanup** - Unsubscribe from events
605
+ 5. **Don't hardcode org IDs** - Always get from kernel state
606
+
607
+ ---
608
+
609
+ **Next:** Read [`10-auth-rbac.md`](./10-auth-rbac.md) for authentication and authorization patterns.