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,55 @@
1
+ import { useQuery } from '@tanstack/react-query';
2
+ import { usePocketBase } from './usePocketBase';
3
+
4
+ export interface Activity {
5
+ id: string;
6
+ action: string;
7
+ resource: string;
8
+ userId: string;
9
+ userName?: string;
10
+ createdAt: string;
11
+ }
12
+
13
+ /**
14
+ * Fetch recent activity feed
15
+ */
16
+ export function useRecentActivity(limit = 10) {
17
+ const pb = usePocketBase();
18
+
19
+ return useQuery({
20
+ queryKey: ['dashboard', 'activity', limit],
21
+ queryFn: async (): Promise<Activity[]> => {
22
+ if (!pb) {
23
+ throw new Error('PocketBase not initialized');
24
+ }
25
+
26
+ // Get current organization from host state
27
+ const kernelState = (window as any).__LEGO_KERNEL_STATE__;
28
+ const state = kernelState?.useGlobalKernelState?.getState();
29
+
30
+ if (!state?.organization?.id) {
31
+ return [];
32
+ }
33
+
34
+ const orgId = state.organization.id;
35
+
36
+ // Fetch recent audit logs
37
+ const result = await pb.collection('audit_logs').getList(1, limit, {
38
+ filter: `organizationId = "${orgId}"`,
39
+ sort: '-created',
40
+ expand: 'userId',
41
+ });
42
+
43
+ return result.items.map((item: any) => ({
44
+ id: item.id,
45
+ action: item.action,
46
+ resource: item.resource,
47
+ userId: item.userId,
48
+ userName: item.expand?.userId?.name || item.expand?.userId?.email || 'Unknown',
49
+ createdAt: item.created,
50
+ }));
51
+ },
52
+ enabled: !!pb,
53
+ refetchInterval: 30000, // Refresh every 30 seconds
54
+ });
55
+ }
@@ -0,0 +1,6 @@
1
+ import { clsx, type ClassValue } from 'clsx';
2
+ import { twMerge } from 'tailwind-merge';
3
+
4
+ export function cn(...inputs: ClassValue[]) {
5
+ return twMerge(clsx(inputs));
6
+ }
@@ -0,0 +1,105 @@
1
+ import { useEffect } from 'react';
2
+ import { useDashboardStats } from '../hooks/useDashboardStats';
3
+ import { useRecentActivity } from '../hooks/useRecentActivity';
4
+ import { StatCard } from '../components/StatCard';
5
+ import { ActivityFeed } from '../components/ActivityFeed';
6
+ import { QuickActions } from '../components/QuickActions';
7
+
8
+ /**
9
+ * Dashboard Page
10
+ *
11
+ * Main dashboard page showing:
12
+ * - Organization statistics
13
+ * - Recent activity feed
14
+ * - Quick action buttons
15
+ */
16
+ export default function DashboardPage() {
17
+ const { data: stats, isLoading: statsLoading } = useDashboardStats();
18
+ const { data: activities, isLoading: activityLoading } = useRecentActivity();
19
+
20
+ // Publish toast helper
21
+ const publishToast = (data: { type: string; title: string; description?: string }) => {
22
+ const channelBus = (window as any).__LEGO_CHANNEL_BUS__;
23
+ if (channelBus) {
24
+ channelBus.publish('lego:toast', { channel: 'lego:toast', data });
25
+ }
26
+ };
27
+
28
+ // Notify that plugin is ready
29
+ useEffect(() => {
30
+ const channelBus = (window as any).__LEGO_CHANNEL_BUS__;
31
+ if (channelBus) {
32
+ channelBus.publish('lego:plugin:ready', {
33
+ channel: 'lego:plugin:ready',
34
+ data: { name: '@lego/plugin-dashboard', version: '1.0.0' }
35
+ });
36
+ }
37
+ }, []);
38
+
39
+ useEffect(() => {
40
+ if (stats) {
41
+ console.log('[Dashboard] Stats loaded:', stats);
42
+ }
43
+ }, [stats]);
44
+
45
+ return (
46
+ <div className="space-y-6">
47
+ {/* Header */}
48
+ <div>
49
+ <h1 className="text-3xl font-bold tracking-tight">Dashboard</h1>
50
+ <p className="text-muted-foreground">
51
+ Welcome to your organization dashboard
52
+ </p>
53
+ </div>
54
+
55
+ {/* Statistics Grid */}
56
+ {statsLoading ? (
57
+ <div className="grid gap-4 md:grid-cols-3">
58
+ {[1, 2, 3].map((i) => (
59
+ <div key={i} className="h-32 animate-pulse rounded-lg bg-muted" />
60
+ ))}
61
+ </div>
62
+ ) : stats ? (
63
+ <div className="grid gap-4 md:grid-cols-3">
64
+ <StatCard
65
+ title="Total Users"
66
+ value={stats.totalUsers}
67
+ icon="Users"
68
+ description="Active users in organization"
69
+ trend={{ value: 12, direction: 'up' }}
70
+ />
71
+ <StatCard
72
+ title="Active Todos"
73
+ value={stats.activeTodos}
74
+ icon="CheckSquare"
75
+ description="Tasks in progress"
76
+ trend={{ value: 8, direction: 'up' }}
77
+ />
78
+ <StatCard
79
+ title="Completed Todos"
80
+ value={stats.completedTodos}
81
+ icon="CheckCircle"
82
+ description="Tasks completed"
83
+ trend={{ value: 5, direction: 'down' }}
84
+ />
85
+ </div>
86
+ ) : null}
87
+
88
+ <div className="grid gap-6 md:grid-cols-2">
89
+ {/* Quick Actions */}
90
+ <QuickActions onToast={publishToast} />
91
+
92
+ {/* Recent Activity */}
93
+ {activityLoading ? (
94
+ <div className="space-y-4">
95
+ {[1, 2, 3, 4, 5].map((i) => (
96
+ <div key={i} className="h-16 animate-pulse rounded-lg bg-muted" />
97
+ ))}
98
+ </div>
99
+ ) : (
100
+ <ActivityFeed activities={activities || []} />
101
+ )}
102
+ </div>
103
+ </div>
104
+ );
105
+ }
@@ -0,0 +1,121 @@
1
+ import type { ReactNode, ComponentType } from 'react';
2
+ import { SidebarWidget } from './components/SidebarWidget';
3
+ import { QuickActionSlot } from './components/QuickActionSlot';
4
+
5
+ // Local type definitions for plugin config
6
+ interface PluginManifest {
7
+ name: string;
8
+ version: string;
9
+ displayName: string;
10
+ description: string;
11
+ author: string;
12
+ permissions: string[];
13
+ }
14
+
15
+ interface SlotInjection {
16
+ slot: string;
17
+ component: ReactNode | ComponentType<any>;
18
+ order?: number;
19
+ props?: Record<string, unknown>;
20
+ condition?: () => boolean;
21
+ }
22
+
23
+ interface RouteConfig {
24
+ path: string;
25
+ component: () => Promise<{ default: ComponentType<any> }>;
26
+ protected?: boolean;
27
+ permissions?: string[];
28
+ }
29
+
30
+ interface PluginSetting {
31
+ key: string;
32
+ type: 'boolean' | 'string' | 'number' | 'select';
33
+ label: string;
34
+ description?: string;
35
+ defaultValue?: unknown;
36
+ options?: { label: string; value: string }[];
37
+ }
38
+
39
+ interface PluginConfig {
40
+ manifest: PluginManifest;
41
+ enabled: boolean;
42
+ slots: SlotInjection[];
43
+ routes?: RouteConfig[];
44
+ settings?: PluginSetting[];
45
+ }
46
+
47
+ /**
48
+ * Dashboard plugin configuration
49
+ *
50
+ * This config defines:
51
+ * - Plugin metadata (manifest)
52
+ * - Slot injections (content injected into host layout)
53
+ * - Routes (plugin pages)
54
+ * - Settings (configurable by admin)
55
+ */
56
+ export const pluginConfig: PluginConfig = {
57
+ manifest: {
58
+ name: '@lego/plugin-dashboard',
59
+ version: '1.0.0',
60
+ displayName: 'Dashboard',
61
+ description: 'Organization dashboard with stats, activity, and quick actions',
62
+ author: 'Lego-One',
63
+ permissions: ['organizations.read', 'audit_logs.read'],
64
+ },
65
+
66
+ enabled: true,
67
+
68
+ // Slot injections - add content to host layout
69
+ slots: [
70
+ {
71
+ slot: 'sidebar:nav',
72
+ component: SidebarWidget,
73
+ order: 50, // After Home, before Todos
74
+ props: {
75
+ to: '/dashboard',
76
+ icon: 'LayoutDashboard',
77
+ label: 'Dashboard',
78
+ },
79
+ },
80
+ {
81
+ slot: 'dashboard:widgets',
82
+ component: QuickActionSlot,
83
+ order: 100,
84
+ },
85
+ ],
86
+
87
+ // Plugin routes
88
+ routes: [
89
+ {
90
+ path: '/dashboard',
91
+ component: () => import('./pages/DashboardPage').then(m => ({ default: m.default })),
92
+ protected: true,
93
+ permissions: ['organizations.read'],
94
+ },
95
+ ],
96
+
97
+ // Plugin settings (shown in admin UI)
98
+ settings: [
99
+ {
100
+ key: 'showStats',
101
+ type: 'boolean',
102
+ label: 'Show Statistics',
103
+ description: 'Display organization statistics on dashboard',
104
+ defaultValue: true,
105
+ },
106
+ {
107
+ key: 'showActivity',
108
+ type: 'boolean',
109
+ label: 'Show Recent Activity',
110
+ description: 'Display recent activity feed',
111
+ defaultValue: true,
112
+ },
113
+ {
114
+ key: 'activityLimit',
115
+ type: 'number',
116
+ label: 'Activity Feed Limit',
117
+ description: 'Number of activity items to show',
118
+ defaultValue: 10,
119
+ },
120
+ ],
121
+ };
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Plugin entry point
3
+ *
4
+ * This file is the main entry point for the Dashboard plugin.
5
+ * It exports the plugin config and the root component.
6
+ */
7
+
8
+ import { pluginConfig } from './plugin.config';
9
+ import DashboardApp from './App';
10
+
11
+ export default {
12
+ config: pluginConfig,
13
+ App: DashboardApp,
14
+ };
15
+
16
+ // Also export for convenience
17
+ export { pluginConfig };
18
+ export { default as App } from './App';
@@ -0,0 +1,32 @@
1
+ /// <reference types="vite/client" />
2
+
3
+ declare global {
4
+ interface ImportMetaEnv {
5
+ readonly VITE_POCKETBASE_URL: string;
6
+ }
7
+
8
+ interface ImportMeta {
9
+ readonly env: ImportMetaEnv;
10
+ }
11
+
12
+ interface Window {
13
+ __LEGO_KERNEL_STATE__?: {
14
+ useGlobalKernelState: {
15
+ getState: () => {
16
+ token?: string;
17
+ organization?: { id: string };
18
+ user?: any;
19
+ };
20
+ subscribe: (callback: (state: any) => void) => () => void;
21
+ };
22
+ };
23
+ __LEGO_CHANNEL_BUS__?: {
24
+ publish: (channel: string, data: any) => void;
25
+ subscribe: (channel: string, callback: (data: any) => void) => () => void;
26
+ unsubscribe: (channel: string, callback: (data: any) => void) => void;
27
+ unsubscribeAll: (channel: string) => void;
28
+ };
29
+ }
30
+ }
31
+
32
+ export {};
@@ -0,0 +1,35 @@
1
+ import type { Config } from 'tailwindcss';
2
+
3
+ const config: Config = {
4
+ darkMode: ['class'],
5
+ content: [
6
+ './src/**/*.{js,ts,jsx,tsx,mdx}',
7
+ ],
8
+ theme: {
9
+ extend: {
10
+ colors: {
11
+ border: 'hsl(var(--border))',
12
+ background: 'hsl(var(--background))',
13
+ foreground: 'hsl(var(--foreground))',
14
+ primary: {
15
+ DEFAULT: 'hsl(var(--primary))',
16
+ foreground: 'hsl(var(--primary-foreground))',
17
+ },
18
+ muted: {
19
+ DEFAULT: 'hsl(var(--muted))',
20
+ foreground: 'hsl(var(--muted-foreground))',
21
+ },
22
+ card: {
23
+ DEFAULT: 'hsl(var(--card))',
24
+ foreground: 'hsl(var(--card-foreground))',
25
+ },
26
+ },
27
+ borderRadius: {
28
+ lg: 'var(--radius)',
29
+ },
30
+ },
31
+ },
32
+ plugins: [],
33
+ };
34
+
35
+ export default config;
@@ -0,0 +1,18 @@
1
+ {
2
+ "compilerOptions": {
3
+ "jsx": "react-jsx",
4
+ "strict": true,
5
+ "esModuleInterop": true,
6
+ "skipLibCheck": true,
7
+ "module": "esnext",
8
+ "moduleResolution": "bundler",
9
+ "resolveJsonModule": true,
10
+ "isolatedModules": true,
11
+ "types": ["vite/client"],
12
+ "paths": {
13
+ "@/*": ["./src/*"]
14
+ }
15
+ },
16
+ "include": ["src"],
17
+ "exclude": ["node_modules", "dist"]
18
+ }
@@ -0,0 +1,18 @@
1
+ import { appTools, defineConfig } from '@modern-js/app-tools';
2
+ import { garfishPlugin } from '@modern-js/plugin-garfish';
3
+
4
+ export default defineConfig({
5
+ dev: {
6
+ port: 3002,
7
+ },
8
+
9
+ runtime: {
10
+ router: true,
11
+ },
12
+
13
+ deploy: {
14
+ microFrontend: true,
15
+ },
16
+
17
+ plugins: [appTools(), garfishPlugin()],
18
+ });
@@ -0,0 +1,41 @@
1
+ {
2
+ "name": "@lego/plugin-todo",
3
+ "version": "2.0.13",
4
+ "private": true,
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "modern dev",
8
+ "build": "modern build",
9
+ "start": "modern start",
10
+ "lint": "eslint src --ext .ts,.tsx",
11
+ "typecheck": "tsc --noEmit"
12
+ },
13
+ "dependencies": {
14
+ "@hookform/resolvers": "^3.9.0",
15
+ "@modern-js/runtime": "^2.70.0",
16
+ "@radix-ui/react-dialog": "^1.1.2",
17
+ "@radix-ui/react-label": "^2.1.0",
18
+ "@radix-ui/react-slot": "^1.1.0",
19
+ "@tanstack/react-query": "^5.62.0",
20
+ "class-variance-authority": "^0.7.0",
21
+ "clsx": "^2.1.1",
22
+ "garfish": "^1.19.0",
23
+ "lucide-react": "^0.468.0",
24
+ "pocketbase": "^0.22.0",
25
+ "react": "^18.3.1",
26
+ "react-dom": "^18.3.1",
27
+ "react-hook-form": "^7.53.0",
28
+ "tailwind-merge": "^2.5.4",
29
+ "zod": "^3.23.8"
30
+ },
31
+ "devDependencies": {
32
+ "@modern-js/app-tools": "^2.70.0",
33
+ "@modern-js/plugin-garfish": "^2.70.0",
34
+ "@types/react": "^18.3.12",
35
+ "@types/react-dom": "^18.3.1",
36
+ "autoprefixer": "^10.4.20",
37
+ "postcss": "^8.4.49",
38
+ "tailwindcss": "^3.4.15",
39
+ "typescript": "^5.6.3"
40
+ }
41
+ }
@@ -0,0 +1,6 @@
1
+ export default {
2
+ plugins: {
3
+ tailwindcss: {},
4
+ autoprefixer: {},
5
+ },
6
+ };
@@ -0,0 +1,12 @@
1
+ import { Route, Routes } from '@modern-js/runtime/router';
2
+ import { TodoPage } from './pages/TodoPage';
3
+
4
+ export function App() {
5
+ return (
6
+ <Routes>
7
+ <Route path="/todo" element={<TodoPage />} />
8
+ </Routes>
9
+ );
10
+ }
11
+
12
+ export default App;
@@ -0,0 +1,16 @@
1
+ import { CheckSquare } from 'lucide-react';
2
+ import { useNavigate } from '@modern-js/runtime/router';
3
+
4
+ export function SidebarWidget() {
5
+ const navigate = useNavigate();
6
+
7
+ return (
8
+ <button
9
+ onClick={() => navigate('/todo')}
10
+ className="flex w-full items-center gap-3 rounded-md px-3 py-2 text-sm font-medium text-muted-foreground transition-colors hover:bg-accent hover:text-accent-foreground"
11
+ >
12
+ <CheckSquare className="h-4 w-4" />
13
+ <span>Todo</span>
14
+ </button>
15
+ );
16
+ }
@@ -0,0 +1,55 @@
1
+ import * as Dialog from '@radix-ui/react-dialog';
2
+ import { X } from 'lucide-react';
3
+ import { TodoForm } from './TodoForm';
4
+ import { TodoFormInput } from '../schemas';
5
+ import { cn } from '../lib/utils';
6
+
7
+ interface TodoDialogProps {
8
+ open: boolean;
9
+ onOpenChange: (open: boolean) => void;
10
+ onSubmit: (data: TodoFormInput) => void;
11
+ isLoading?: boolean;
12
+ }
13
+
14
+ export function TodoDialog({
15
+ open,
16
+ onOpenChange,
17
+ onSubmit,
18
+ isLoading,
19
+ }: TodoDialogProps) {
20
+ return (
21
+ <Dialog.Root open={open} onOpenChange={onOpenChange}>
22
+ <Dialog.Portal>
23
+ <Dialog.Overlay className="fixed inset-0 bg-black/50 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0" />
24
+ <Dialog.Content
25
+ className={cn(
26
+ 'fixed left-1/2 top-1/2 z-50 w-full max-w-md -translate-x-1/2 -translate-y-1/2',
27
+ 'rounded-lg border bg-background p-6 shadow-lg',
28
+ 'data-[state=open]:animate-in data-[state=closed]:animate-out',
29
+ 'data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0',
30
+ 'data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95',
31
+ 'data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%]',
32
+ 'data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%]'
33
+ )}
34
+ >
35
+ <div className="flex items-center justify-between">
36
+ <Dialog.Title className="text-lg font-semibold">
37
+ Create New Todo
38
+ </Dialog.Title>
39
+ <Dialog.Close
40
+ disabled={isLoading}
41
+ className="rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none"
42
+ >
43
+ <X className="h-4 w-4" />
44
+ <span className="sr-only">Close</span>
45
+ </Dialog.Close>
46
+ </div>
47
+
48
+ <div className="mt-4">
49
+ <TodoForm onSubmit={onSubmit} isLoading={isLoading} />
50
+ </div>
51
+ </Dialog.Content>
52
+ </Dialog.Portal>
53
+ </Dialog.Root>
54
+ );
55
+ }
@@ -0,0 +1,79 @@
1
+ import { Search, Filter } from 'lucide-react';
2
+ import { TodoFilters as TodoFiltersType } from '../types';
3
+ import { cn } from '../lib/utils';
4
+
5
+ interface TodoFiltersProps {
6
+ filters: TodoFiltersType;
7
+ onFiltersChange: (filters: TodoFiltersType) => void;
8
+ todoCount: number;
9
+ }
10
+
11
+ export function TodoFilters({
12
+ filters,
13
+ onFiltersChange,
14
+ todoCount,
15
+ }: TodoFiltersProps) {
16
+ return (
17
+ <div className="space-y-4">
18
+ {/* Search */}
19
+ <div className="relative">
20
+ <Search className="absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground" />
21
+ <input
22
+ type="text"
23
+ placeholder="Search todos..."
24
+ value={filters.search || ''}
25
+ onChange={(e) =>
26
+ onFiltersChange({ ...filters, search: e.target.value })
27
+ }
28
+ className="w-full rounded-md border border-input bg-background pl-10 pr-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2"
29
+ />
30
+ </div>
31
+
32
+ {/* Filter Controls */}
33
+ <div className="flex flex-wrap gap-2">
34
+ {/* Status Filter */}
35
+ <div className="flex items-center gap-2 rounded-md border bg-background p-1">
36
+ <Filter className="h-4 w-4 text-muted-foreground" />
37
+ <select
38
+ value={filters.status || 'all'}
39
+ onChange={(e) =>
40
+ onFiltersChange({
41
+ ...filters,
42
+ status: e.target.value as TodoFiltersType['status'],
43
+ })
44
+ }
45
+ className="bg-transparent text-sm focus:outline-none"
46
+ >
47
+ <option value="all">All Status</option>
48
+ <option value="active">Active</option>
49
+ <option value="completed">Completed</option>
50
+ </select>
51
+ </div>
52
+
53
+ {/* Priority Filter */}
54
+ <div className="flex items-center gap-2 rounded-md border bg-background p-1">
55
+ <select
56
+ value={filters.priority || 'all'}
57
+ onChange={(e) =>
58
+ onFiltersChange({
59
+ ...filters,
60
+ priority: e.target.value as TodoFiltersType['priority'],
61
+ })
62
+ }
63
+ className="bg-transparent text-sm focus:outline-none"
64
+ >
65
+ <option value="all">All Priority</option>
66
+ <option value="low">Low</option>
67
+ <option value="medium">Medium</option>
68
+ <option value="high">High</option>
69
+ </select>
70
+ </div>
71
+
72
+ {/* Results Count */}
73
+ <div className="ml-auto flex items-center text-sm text-muted-foreground">
74
+ {todoCount} {todoCount === 1 ? 'todo' : 'todos'}
75
+ </div>
76
+ </div>
77
+ </div>
78
+ );
79
+ }