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,185 @@
1
+ import { create } from 'zustand';
2
+ import { devtools, persist } from 'zustand/middleware';
3
+ import type { PluginState, PluginConfig } from './types';
4
+ import { saasConfig } from '../../saas.config';
5
+
6
+ interface PluginStore {
7
+ // State
8
+ plugins: Record<string, PluginState>;
9
+ isLoading: boolean;
10
+
11
+ // Actions
12
+ registerPlugin: (config: PluginConfig) => void;
13
+ unregisterPlugin: (name: string) => void;
14
+ enablePlugin: (name: string) => void;
15
+ disablePlugin: (name: string) => void;
16
+ updatePluginSettings: (name: string, settings: Record<string, unknown>) => void;
17
+ setPluginLoaded: (name: string, loaded: boolean) => void;
18
+ setPluginError: (name: string, error: string) => void;
19
+ initializeFromConfig: () => void;
20
+ }
21
+
22
+ /**
23
+ * Get default plugin settings from manifest
24
+ */
25
+ function getDefaultSettings(config: PluginConfig): Record<string, unknown> {
26
+ const settings: Record<string, unknown> = {};
27
+
28
+ if (config.settings) {
29
+ for (const setting of config.settings) {
30
+ if (setting.defaultValue !== undefined) {
31
+ settings[setting.key] = setting.defaultValue;
32
+ }
33
+ }
34
+ }
35
+
36
+ return settings;
37
+ }
38
+
39
+ /**
40
+ * Create plugin store
41
+ */
42
+ export const usePluginStore = create<PluginStore>()(
43
+ devtools(
44
+ persist(
45
+ (set, get) => ({
46
+ plugins: {},
47
+ isLoading: false,
48
+
49
+ registerPlugin: (config) => {
50
+ const { name } = config.manifest;
51
+ const { enabled } = config;
52
+
53
+ set((state) => ({
54
+ plugins: {
55
+ ...state.plugins,
56
+ [name]: {
57
+ id: name,
58
+ manifest: config.manifest,
59
+ enabled,
60
+ loaded: false,
61
+ settings: getDefaultSettings(config),
62
+ },
63
+ },
64
+ }));
65
+ },
66
+
67
+ unregisterPlugin: (name) => {
68
+ set((state) => {
69
+ const plugins = { ...state.plugins };
70
+ delete plugins[name];
71
+ return { plugins };
72
+ });
73
+ },
74
+
75
+ enablePlugin: (name) => {
76
+ set((state) => ({
77
+ plugins: {
78
+ ...state.plugins,
79
+ [name]: {
80
+ ...state.plugins[name],
81
+ enabled: true,
82
+ },
83
+ },
84
+ }));
85
+ },
86
+
87
+ disablePlugin: (name) => {
88
+ set((state) => ({
89
+ plugins: {
90
+ ...state.plugins,
91
+ [name]: {
92
+ ...state.plugins[name],
93
+ enabled: false,
94
+ loaded: false,
95
+ },
96
+ },
97
+ }));
98
+ },
99
+
100
+ updatePluginSettings: (name, settings) => {
101
+ set((state) => ({
102
+ plugins: {
103
+ ...state.plugins,
104
+ [name]: {
105
+ ...state.plugins[name],
106
+ settings: {
107
+ ...state.plugins[name].settings,
108
+ ...settings,
109
+ },
110
+ },
111
+ },
112
+ }));
113
+ },
114
+
115
+ setPluginLoaded: (name, loaded) => {
116
+ set((state) => ({
117
+ plugins: {
118
+ ...state.plugins,
119
+ [name]: {
120
+ ...state.plugins[name],
121
+ loaded,
122
+ },
123
+ },
124
+ }));
125
+ },
126
+
127
+ setPluginError: (name, error) => {
128
+ set((state) => ({
129
+ plugins: {
130
+ ...state.plugins,
131
+ [name]: {
132
+ ...state.plugins[name],
133
+ error,
134
+ loaded: false,
135
+ },
136
+ },
137
+ }));
138
+ },
139
+
140
+ initializeFromConfig: () => {
141
+ const { plugins } = saasConfig;
142
+
143
+ set((state) => {
144
+ const newPlugins = { ...state.plugins };
145
+
146
+ for (const plugin of plugins) {
147
+ const existing = newPlugins[plugin.name];
148
+
149
+ if (!existing) {
150
+ // Create placeholder for plugin that will be loaded
151
+ newPlugins[plugin.name] = {
152
+ id: plugin.name,
153
+ manifest: {
154
+ name: plugin.name,
155
+ version: '0.0.0',
156
+ displayName: plugin.name,
157
+ description: '',
158
+ },
159
+ enabled: plugin.enabled,
160
+ loaded: false,
161
+ settings: {},
162
+ };
163
+ } else {
164
+ // Update enabled state from config
165
+ newPlugins[plugin.name] = {
166
+ ...existing,
167
+ enabled: plugin.enabled,
168
+ };
169
+ }
170
+ }
171
+
172
+ return { plugins: newPlugins };
173
+ });
174
+ },
175
+ }),
176
+ {
177
+ name: 'lego-plugin-store',
178
+ partialize: (state) => ({
179
+ plugins: state.plugins,
180
+ }),
181
+ }
182
+ ),
183
+ { name: 'LegoPluginStore' }
184
+ )
185
+ );
@@ -0,0 +1,103 @@
1
+ import type { ReactNode } from 'react';
2
+
3
+ /**
4
+ * Slot names where plugins can inject content
5
+ */
6
+ export enum SlotName {
7
+ SIDEBAR_TOP = 'sidebar:top',
8
+ SIDEBAR_NAV = 'sidebar:nav',
9
+ SIDEBAR_BOTTOM = 'sidebar:bottom',
10
+ TOPBAR_LEFT = 'topbar:left',
11
+ TOPBAR_RIGHT = 'topbar:right',
12
+ TOPBAR_CENTER = 'topbar:center',
13
+ SETTINGS_MENU = 'settings:menu',
14
+ DASHBOARD_WIDGETS = 'dashboard:widgets',
15
+ }
16
+
17
+ /**
18
+ * Slot injection configuration
19
+ */
20
+ export interface SlotInjection {
21
+ slot: SlotName;
22
+ component: ReactNode | React.ComponentType;
23
+ order?: number; // Lower = higher priority
24
+ props?: Record<string, unknown>;
25
+ condition?: () => boolean; // Conditional rendering
26
+ }
27
+
28
+ /**
29
+ * Plugin manifest metadata
30
+ */
31
+ export interface PluginManifest {
32
+ name: string; // e.g., '@lego/plugin-dashboard'
33
+ version: string;
34
+ displayName: string;
35
+ description: string;
36
+ author?: string;
37
+ icon?: string;
38
+ permissions?: string[]; // Required permissions
39
+ }
40
+
41
+ /**
42
+ * Plugin configuration
43
+ */
44
+ export interface PluginConfig {
45
+ manifest: PluginManifest;
46
+ enabled: boolean;
47
+ slots: SlotInjection[];
48
+ routes?: RouteConfig[];
49
+ settings?: PluginSetting[];
50
+ }
51
+
52
+ /**
53
+ * Plugin route configuration
54
+ */
55
+ export interface RouteConfig {
56
+ path: string;
57
+ component: React.ComponentType;
58
+ protected?: boolean;
59
+ permissions?: string[];
60
+ }
61
+
62
+ /**
63
+ * Plugin setting for admin UI
64
+ */
65
+ export interface PluginSetting {
66
+ key: string;
67
+ type: 'boolean' | 'string' | 'number' | 'select';
68
+ label: string;
69
+ description?: string;
70
+ defaultValue?: unknown;
71
+ options?: { label: string; value: string }[];
72
+ }
73
+
74
+ /**
75
+ * Plugin state
76
+ */
77
+ export interface PluginState {
78
+ id: string;
79
+ manifest: PluginManifest;
80
+ enabled: boolean;
81
+ loaded: boolean;
82
+ error?: string;
83
+ settings: Record<string, unknown>;
84
+ }
85
+
86
+ /**
87
+ * Plugin registry
88
+ */
89
+ export interface PluginRegistry {
90
+ plugins: Map<string, PluginConfig>;
91
+ enabled: Set<string>;
92
+ slots: Map<SlotName, SlotInjection[]>;
93
+ }
94
+
95
+ /**
96
+ * Plugin info from host config
97
+ */
98
+ export interface HostPluginInfo {
99
+ name: string;
100
+ enabled: boolean;
101
+ entry: string | (() => Promise<unknown>);
102
+ activeWhen: string;
103
+ }
@@ -0,0 +1,70 @@
1
+ import { createContext, useContext, useEffect, useState } from 'react';
2
+ import PocketBase from 'pocketbase';
3
+ import { useGlobalKernelState } from '../shared-state';
4
+ import { initializeFirstOrganization } from '../rbac/utils';
5
+
6
+ const PocketBaseContext = createContext<PocketBase | null>(null);
7
+
8
+ export function PocketBaseProvider({ children }: { children: React.ReactNode }) {
9
+ const [pb, setPb] = useState<PocketBase | null>(null);
10
+ const { token, clearAuth, setOrganization } = useGlobalKernelState();
11
+
12
+ useEffect(() => {
13
+ // Initialize PocketBase client
14
+ const client = new PocketBase(import.meta.env.VITE_POCKETBASE_URL || 'http://127.0.0.1:8090');
15
+
16
+ // Load stored token
17
+ const storedToken = localStorage.getItem('pocketbase_auth');
18
+ if (storedToken) {
19
+ client.authStore.save(storedToken, null);
20
+ }
21
+
22
+ setPb(client);
23
+
24
+ // Set up auth cleanup on token invalidation
25
+ client.authStore.onChange(async (token, model) => {
26
+ if (!token) {
27
+ clearAuth();
28
+ localStorage.removeItem('pocketbase_auth');
29
+ setOrganization(null);
30
+ } else {
31
+ localStorage.setItem('pocketbase_auth', token);
32
+
33
+ // Initialize organization on login
34
+ if (model && !model.organizationId) {
35
+ const org = await initializeFirstOrganization(client, model.id);
36
+ if (org) {
37
+ setOrganization(org);
38
+ }
39
+ }
40
+ }
41
+ });
42
+
43
+ return () => {
44
+ // Cleanup on unmount
45
+ };
46
+ }, [clearAuth, setOrganization]);
47
+
48
+ // Update auth store when token changes in state
49
+ useEffect(() => {
50
+ if (pb && token && pb.authStore.token !== token) {
51
+ pb.authStore.save(token, null);
52
+ }
53
+ }, [pb, token]);
54
+
55
+ if (!pb) {
56
+ return null; // or a loading spinner
57
+ }
58
+
59
+ return (
60
+ <PocketBaseContext.Provider value={pb}>{children}</PocketBaseContext.Provider>
61
+ );
62
+ }
63
+
64
+ export function usePocketBase() {
65
+ const context = useContext(PocketBaseContext);
66
+ if (!context) {
67
+ throw new Error('usePocketBase must be used within PocketBaseProvider');
68
+ }
69
+ return context;
70
+ }
@@ -0,0 +1,28 @@
1
+ import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
2
+ import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
3
+ import { useState, type ReactNode } from 'react';
4
+
5
+ export function QueryProvider({ children }: { children: ReactNode }) {
6
+ const [queryClient] = useState(
7
+ () =>
8
+ new QueryClient({
9
+ defaultOptions: {
10
+ queries: {
11
+ staleTime: 1000 * 60 * 5, // 5 minutes
12
+ gcTime: 1000 * 60 * 30, // 30 minutes (formerly cacheTime)
13
+ retry: 1,
14
+ refetchOnWindowFocus: false,
15
+ },
16
+ },
17
+ })
18
+ );
19
+
20
+ return (
21
+ <QueryClientProvider client={queryClient}>
22
+ {children}
23
+ {import.meta.env.MODE === 'development' && (
24
+ <ReactQueryDevtools initialIsOpen={false} />
25
+ )}
26
+ </QueryClientProvider>
27
+ );
28
+ }
@@ -0,0 +1,25 @@
1
+ import { useEffect } from 'react';
2
+ import { useGlobalKernelState, type Theme } from '../shared-state';
3
+
4
+ export function ThemeProvider({ children }: { children: React.ReactNode }) {
5
+ const { theme } = useGlobalKernelState();
6
+
7
+ useEffect(() => {
8
+ const root = window.document.documentElement;
9
+ root.classList.remove('light', 'dark');
10
+
11
+ let effectiveTheme: 'light' | 'dark' = 'light';
12
+
13
+ if (theme === 'system') {
14
+ effectiveTheme = window.matchMedia('(prefers-color-scheme: dark)').matches
15
+ ? 'dark'
16
+ : 'light';
17
+ } else {
18
+ effectiveTheme = theme;
19
+ }
20
+
21
+ root.classList.add(effectiveTheme);
22
+ }, [theme]);
23
+
24
+ return <>{children}</>;
25
+ }
@@ -0,0 +1,3 @@
1
+ export * from './PocketBaseProvider';
2
+ export * from './QueryProvider';
3
+ export * from './ThemeProvider';
@@ -0,0 +1,69 @@
1
+ import { useCurrentOrganization, useOrganizations } from '../hooks';
2
+ import {
3
+ DropdownMenu,
4
+ DropdownMenuContent,
5
+ DropdownMenuItem,
6
+ DropdownMenuLabel,
7
+ DropdownMenuSeparator,
8
+ DropdownMenuTrigger,
9
+ } from '../../components/ui/dropdown-menu';
10
+ import { Button } from '../../components/ui/button';
11
+ import { Building2, ChevronDown, Loader2, Plus } from 'lucide-react';
12
+ import { useNavigate } from '@modern-js/runtime/router';
13
+
14
+ export function OrganizationSelector() {
15
+ const { organization, setCurrentOrganization } = useCurrentOrganization();
16
+ const { data: organizations, isLoading } = useOrganizations();
17
+ const navigate = useNavigate();
18
+
19
+ const handleSwitch = (orgId: string) => {
20
+ const org = organizations?.find(o => o.id === orgId);
21
+ if (org) {
22
+ setCurrentOrganization(org);
23
+ }
24
+ };
25
+
26
+ const handleCreate = () => {
27
+ navigate('/settings/organizations/new');
28
+ };
29
+
30
+ return (
31
+ <DropdownMenu>
32
+ <DropdownMenuTrigger asChild>
33
+ <Button variant="outline" className="gap-2">
34
+ <Building2 className="h-4 w-4" />
35
+ {isLoading ? (
36
+ <Loader2 className="h-4 w-4 animate-spin" />
37
+ ) : organization ? (
38
+ <span className="max-w-[150px] truncate">{organization.name}</span>
39
+ ) : (
40
+ <span>Select Organization</span>
41
+ )}
42
+ <ChevronDown className="h-4 w-4" />
43
+ </Button>
44
+ </DropdownMenuTrigger>
45
+ <DropdownMenuContent className="w-56" align="start">
46
+ <DropdownMenuLabel>Organizations</DropdownMenuLabel>
47
+ <DropdownMenuSeparator />
48
+ {organizations?.map((org) => (
49
+ <DropdownMenuItem
50
+ key={org.id}
51
+ onClick={() => handleSwitch(org.id)}
52
+ className={organization?.id === org.id ? 'bg-accent' : ''}
53
+ >
54
+ <Building2 className="mr-2 h-4 w-4" />
55
+ <span className="flex-1 truncate">{org.name}</span>
56
+ {organization?.id === org.id && (
57
+ <span className="text-xs text-muted-foreground">Current</span>
58
+ )}
59
+ </DropdownMenuItem>
60
+ ))}
61
+ <DropdownMenuSeparator />
62
+ <DropdownMenuItem onClick={handleCreate}>
63
+ <Plus className="mr-2 h-4 w-4" />
64
+ Create Organization
65
+ </DropdownMenuItem>
66
+ </DropdownMenuContent>
67
+ </DropdownMenu>
68
+ );
69
+ }
@@ -0,0 +1,43 @@
1
+ import { useRequirePermission } from '../hooks';
2
+ import type { ResourceType, ActionType } from '../types';
3
+ import { Alert, AlertDescription } from '../../components/ui/alert';
4
+ import { Lock } from 'lucide-react';
5
+
6
+ interface PermissionGateProps {
7
+ resource: ResourceType;
8
+ action: ActionType;
9
+ children: React.ReactNode;
10
+ fallback?: React.ReactNode;
11
+ }
12
+
13
+ export function PermissionGate({
14
+ resource,
15
+ action,
16
+ children,
17
+ fallback,
18
+ }: PermissionGateProps) {
19
+ const { hasPermission, isLoading } = useRequirePermission(resource, action);
20
+
21
+ if (isLoading) {
22
+ return (
23
+ <div className="flex min-h-[200px] items-center justify-center">
24
+ <div className="text-sm text-muted-foreground">Checking permissions...</div>
25
+ </div>
26
+ );
27
+ }
28
+
29
+ if (!hasPermission) {
30
+ return (
31
+ fallback || (
32
+ <Alert variant="destructive">
33
+ <Lock className="h-4 w-4" />
34
+ <AlertDescription>
35
+ You don't have permission to {action} {resource}.
36
+ </AlertDescription>
37
+ </Alert>
38
+ )
39
+ );
40
+ }
41
+
42
+ return <>{children}</>;
43
+ }