create-lego-one 2.0.10 → 2.0.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +179 -0
- package/dist/index.cjs.map +1 -1
- package/package.json +5 -3
- package/template/.cursor/rules/rules.mdc +639 -0
- package/template/.dockerignore +58 -0
- package/template/.env.example +18 -0
- package/template/.eslintignore +5 -0
- package/template/.eslintrc.js +28 -0
- package/template/.prettierignore +6 -0
- package/template/.prettierrc +11 -0
- package/template/CLAUDE.md +634 -0
- package/template/Dockerfile +67 -0
- package/template/PROMPT.md +457 -0
- package/template/README.md +325 -0
- package/template/docker-compose.yml +48 -0
- package/template/docker-entrypoint.sh +23 -0
- package/template/docs/checkpoints/.template.md +64 -0
- package/template/docs/checkpoints/framework/01-infrastructure-setup.md +132 -0
- package/template/docs/checkpoints/framework/02-pocketbase-setup.md +155 -0
- package/template/docs/checkpoints/framework/03-host-kernel.md +170 -0
- package/template/docs/checkpoints/framework/04-auth-system.md +163 -0
- package/template/docs/checkpoints/framework/phase-05-multitenancy-rbac.md +223 -0
- package/template/docs/checkpoints/framework/phase-06-ui-components.md +260 -0
- package/template/docs/checkpoints/framework/phase-07-communication-system.md +276 -0
- package/template/docs/checkpoints/framework/phase-08-plugin-system.md +91 -0
- package/template/docs/checkpoints/framework/phase-09-dashboard-plugin.md +111 -0
- package/template/docs/checkpoints/framework/phase-10-todo-plugin.md +169 -0
- package/template/docs/checkpoints/framework/phase-11-testing.md +264 -0
- package/template/docs/checkpoints/framework/phase-12-deployment.md +294 -0
- package/template/docs/checkpoints/framework/phase-13-documentation.md +312 -0
- package/template/docs/framework/plans/00-index.md +164 -0
- package/template/docs/framework/plans/01-infrastructure-setup.md +855 -0
- package/template/docs/framework/plans/02-pocketbase-setup.md +1374 -0
- package/template/docs/framework/plans/03-host-kernel.md +1518 -0
- package/template/docs/framework/plans/04-auth-system.md +1466 -0
- package/template/docs/framework/plans/05-multitenancy-rbac.md +1527 -0
- package/template/docs/framework/plans/06-ui-components.md +1478 -0
- package/template/docs/framework/plans/07-communication-system.md +1106 -0
- package/template/docs/framework/plans/08-plugin-system.md +1179 -0
- package/template/docs/framework/plans/09-dashboard-plugin.md +1137 -0
- package/template/docs/framework/plans/10-todo-plugin.md +1343 -0
- package/template/docs/framework/plans/11-testing.md +935 -0
- package/template/docs/framework/plans/12-deployment.md +896 -0
- package/template/docs/framework/prompts/0-boilerplate-modernjs.md +151 -0
- package/template/docs/framework/research/00-modernjs-audit.md +488 -0
- package/template/docs/framework/research/01-system-blueprint.md +721 -0
- package/template/docs/framework/research/02-data-migration-protocol.md +699 -0
- package/template/docs/framework/research/03-host-setup.md +714 -0
- package/template/docs/framework/research/04-plugin-architecture.md +645 -0
- package/template/docs/framework/research/05-slot-injection-pattern.md +671 -0
- package/template/docs/framework/research/06-cli-strategy.md +615 -0
- package/template/docs/framework/research/07-deployment.md +629 -0
- package/template/docs/framework/research/README.md +282 -0
- package/template/docs/framework/setup/00-index.md +210 -0
- package/template/docs/framework/setup/01-framework-structure.md +308 -0
- package/template/docs/framework/setup/02-development-workflow.md +405 -0
- package/template/docs/framework/setup/03-environment-setup.md +215 -0
- package/template/docs/framework/setup/04-kernel-architecture.md +499 -0
- package/template/docs/framework/setup/05-plugin-system.md +620 -0
- package/template/docs/framework/setup/06-communication-patterns.md +451 -0
- package/template/docs/framework/setup/07-plugin-development.md +582 -0
- package/template/docs/framework/setup/08-component-library.md +658 -0
- package/template/docs/framework/setup/09-data-integration.md +609 -0
- package/template/docs/framework/setup/10-auth-rbac.md +497 -0
- package/template/docs/framework/setup/11-hooks-api.md +393 -0
- package/template/docs/framework/setup/12-components-api.md +665 -0
- package/template/docs/framework/setup/13-deployment-guide.md +566 -0
- package/template/docs/framework/setup/README.md +548 -0
- package/template/host/e2e/auth.spec.ts +38 -0
- package/template/host/e2e/layout.spec.ts +38 -0
- package/template/host/modern.config.ts +19 -0
- package/template/host/package.json +71 -0
- package/template/host/playwright.config.ts +34 -0
- package/template/host/postcss.config.mjs +6 -0
- package/template/host/src/App.tsx +6 -0
- package/template/host/src/bootstrap.tsx +74 -0
- package/template/host/src/global.css +59 -0
- package/template/host/src/index.ts +2 -0
- package/template/host/src/kernel/__tests__/lib-utils.test.ts +32 -0
- package/template/host/src/kernel/__tests__/rbac-hooks.test.tsx +114 -0
- package/template/host/src/kernel/__tests__/rbac-utils.test.ts +108 -0
- package/template/host/src/kernel/auth/ProtectedRoute.tsx +41 -0
- package/template/host/src/kernel/auth/components/LoginForm.tsx +97 -0
- package/template/host/src/kernel/auth/components/LogoutButton.tsx +79 -0
- package/template/host/src/kernel/auth/hooks.ts +174 -0
- package/template/host/src/kernel/auth/index.ts +5 -0
- package/template/host/src/kernel/auth/schemas.ts +27 -0
- package/template/host/src/kernel/auth/service.ts +197 -0
- package/template/host/src/kernel/auth/types.ts +36 -0
- package/template/host/src/kernel/channels/ChannelBus.ts +181 -0
- package/template/host/src/kernel/channels/ChannelProvider.tsx +57 -0
- package/template/host/src/kernel/channels/events.ts +27 -0
- package/template/host/src/kernel/channels/hooks.ts +168 -0
- package/template/host/src/kernel/channels/index.ts +6 -0
- package/template/host/src/kernel/channels/integrations/ToastIntegration.tsx +60 -0
- package/template/host/src/kernel/channels/plugin-hooks.ts +72 -0
- package/template/host/src/kernel/channels/types.ts +112 -0
- package/template/host/src/kernel/components/__tests__/Badge.test.tsx +35 -0
- package/template/host/src/kernel/components/__tests__/Button.test.tsx +63 -0
- package/template/host/src/kernel/components/__tests__/Input.test.tsx +64 -0
- package/template/host/src/kernel/components/index.ts +32 -0
- package/template/host/src/kernel/components/ui/alert.tsx +58 -0
- package/template/host/src/kernel/components/ui/avatar.tsx +47 -0
- package/template/host/src/kernel/components/ui/badge.tsx +35 -0
- package/template/host/src/kernel/components/ui/button.tsx +50 -0
- package/template/host/src/kernel/components/ui/card.tsx +78 -0
- package/template/host/src/kernel/components/ui/dialog.tsx +116 -0
- package/template/host/src/kernel/components/ui/dropdown-menu.tsx +192 -0
- package/template/host/src/kernel/components/ui/index.ts +7 -0
- package/template/host/src/kernel/components/ui/input.tsx +24 -0
- package/template/host/src/kernel/components/ui/label.tsx +21 -0
- package/template/host/src/kernel/components/ui/popover.tsx +28 -0
- package/template/host/src/kernel/components/ui/progress.tsx +25 -0
- package/template/host/src/kernel/components/ui/scroll-area.tsx +45 -0
- package/template/host/src/kernel/components/ui/select.tsx +155 -0
- package/template/host/src/kernel/components/ui/separator.tsx +28 -0
- package/template/host/src/kernel/components/ui/skeleton.tsx +15 -0
- package/template/host/src/kernel/components/ui/switch.tsx +26 -0
- package/template/host/src/kernel/components/ui/table.tsx +116 -0
- package/template/host/src/kernel/components/ui/tabs.tsx +52 -0
- package/template/host/src/kernel/components/ui/toast.tsx +126 -0
- package/template/host/src/kernel/components/ui/toaster.tsx +34 -0
- package/template/host/src/kernel/components/ui/tooltip.tsx +27 -0
- package/template/host/src/kernel/components/ui/use-toast.ts +183 -0
- package/template/host/src/kernel/index.ts +48 -0
- package/template/host/src/kernel/lib/cn.ts +1 -0
- package/template/host/src/kernel/lib/utils.ts +36 -0
- package/template/host/src/kernel/plugins/Slot.tsx +41 -0
- package/template/host/src/kernel/plugins/SlotProvider.tsx +88 -0
- package/template/host/src/kernel/plugins/index.ts +23 -0
- package/template/host/src/kernel/plugins/loader.ts +122 -0
- package/template/host/src/kernel/plugins/schemas.ts +54 -0
- package/template/host/src/kernel/plugins/store.ts +185 -0
- package/template/host/src/kernel/plugins/types.ts +103 -0
- package/template/host/src/kernel/providers/PocketBaseProvider.tsx +70 -0
- package/template/host/src/kernel/providers/QueryProvider.tsx +28 -0
- package/template/host/src/kernel/providers/ThemeProvider.tsx +25 -0
- package/template/host/src/kernel/providers/index.ts +3 -0
- package/template/host/src/kernel/rbac/components/OrganizationSelector.tsx +69 -0
- package/template/host/src/kernel/rbac/components/PermissionGate.tsx +43 -0
- package/template/host/src/kernel/rbac/hooks.ts +379 -0
- package/template/host/src/kernel/rbac/index.ts +6 -0
- package/template/host/src/kernel/rbac/service.ts +504 -0
- package/template/host/src/kernel/rbac/types.ts +164 -0
- package/template/host/src/kernel/rbac/utils.ts +34 -0
- package/template/host/src/kernel/shared-state/bridge.ts +31 -0
- package/template/host/src/kernel/shared-state/index.ts +3 -0
- package/template/host/src/kernel/shared-state/store.ts +62 -0
- package/template/host/src/kernel/shared-state/types.ts +60 -0
- package/template/host/src/kernel/use-migrations.ts +72 -0
- package/template/host/src/layout/MobileMenu.tsx +61 -0
- package/template/host/src/layout/Shell.tsx +42 -0
- package/template/host/src/layout/Sidebar.tsx +178 -0
- package/template/host/src/layout/Topbar.tsx +50 -0
- package/template/host/src/layout/index.ts +4 -0
- package/template/host/src/lib/pocketbase/client.ts +38 -0
- package/template/host/src/lib/pocketbase/collections/audit_logs.ts +87 -0
- package/template/host/src/lib/pocketbase/collections/index.ts +19 -0
- package/template/host/src/lib/pocketbase/collections/organizations.ts +63 -0
- package/template/host/src/lib/pocketbase/collections/permissions.ts +57 -0
- package/template/host/src/lib/pocketbase/collections/roles.ts +55 -0
- package/template/host/src/lib/pocketbase/collections/todos.ts +74 -0
- package/template/host/src/lib/pocketbase/collections/user_roles.ts +57 -0
- package/template/host/src/lib/pocketbase/collections/users.ts +43 -0
- package/template/host/src/lib/pocketbase/index.ts +5 -0
- package/template/host/src/lib/pocketbase/migrations.ts +44 -0
- package/template/host/src/lib/pocketbase/seed/permissions.ts +8 -0
- package/template/host/src/lib/pocketbase/seed/roles.ts +22 -0
- package/template/host/src/lib/pocketbase/seed.ts +113 -0
- package/template/host/src/lib/pocketbase/types.ts +102 -0
- package/template/host/src/modern.runtime.ts +26 -0
- package/template/host/src/plugins.d.ts +9 -0
- package/template/host/src/providers/PocketBaseProvider.tsx +30 -0
- package/template/host/src/routes/_.tsx +6 -0
- package/template/host/src/routes/dashboard._.tsx +41 -0
- package/template/host/src/routes/index.tsx +93 -0
- package/template/host/src/routes/login.tsx +36 -0
- package/template/host/src/saas.config.ts +52 -0
- package/template/host/src/test/setup.ts +65 -0
- package/template/host/src/test/utils.tsx +69 -0
- package/template/host/src/test/vitest-globals.d.ts +19 -0
- package/template/host/src/vite-env.d.ts +16 -0
- package/template/host/tailwind.config.ts +77 -0
- package/template/host/tsconfig.json +19 -0
- package/template/host/vitest.config.ts +30 -0
- package/template/nginx.conf +72 -0
- package/template/package.json +44 -0
- package/template/packages/plugins/@lego/plugin-dashboard/modern.config.ts +19 -0
- package/template/packages/plugins/@lego/plugin-dashboard/package.json +35 -0
- package/template/packages/plugins/@lego/plugin-dashboard/postcss.config.mjs +6 -0
- package/template/packages/plugins/@lego/plugin-dashboard/src/App.tsx +27 -0
- package/template/packages/plugins/@lego/plugin-dashboard/src/components/ActivityFeed.tsx +63 -0
- package/template/packages/plugins/@lego/plugin-dashboard/src/components/QuickActionSlot.tsx +11 -0
- package/template/packages/plugins/@lego/plugin-dashboard/src/components/QuickActions.tsx +68 -0
- package/template/packages/plugins/@lego/plugin-dashboard/src/components/SidebarWidget.tsx +35 -0
- package/template/packages/plugins/@lego/plugin-dashboard/src/components/StatCard.tsx +47 -0
- package/template/packages/plugins/@lego/plugin-dashboard/src/global.css +24 -0
- package/template/packages/plugins/@lego/plugin-dashboard/src/hooks/useChannelIntegration.ts +43 -0
- package/template/packages/plugins/@lego/plugin-dashboard/src/hooks/useDashboardStats.ts +65 -0
- package/template/packages/plugins/@lego/plugin-dashboard/src/hooks/usePocketBase.ts +47 -0
- package/template/packages/plugins/@lego/plugin-dashboard/src/hooks/useRecentActivity.ts +55 -0
- package/template/packages/plugins/@lego/plugin-dashboard/src/lib/utils.ts +6 -0
- package/template/packages/plugins/@lego/plugin-dashboard/src/pages/DashboardPage.tsx +105 -0
- package/template/packages/plugins/@lego/plugin-dashboard/src/plugin.config.ts +121 -0
- package/template/packages/plugins/@lego/plugin-dashboard/src/plugin.ts +18 -0
- package/template/packages/plugins/@lego/plugin-dashboard/src/vite-env.d.ts +32 -0
- package/template/packages/plugins/@lego/plugin-dashboard/tailwind.config.ts +35 -0
- package/template/packages/plugins/@lego/plugin-dashboard/tsconfig.json +18 -0
- package/template/packages/plugins/@lego/plugin-todo/modern.config.ts +18 -0
- package/template/packages/plugins/@lego/plugin-todo/package.json +41 -0
- package/template/packages/plugins/@lego/plugin-todo/postcss.config.mjs +6 -0
- package/template/packages/plugins/@lego/plugin-todo/src/App.tsx +12 -0
- package/template/packages/plugins/@lego/plugin-todo/src/components/SidebarWidget.tsx +16 -0
- package/template/packages/plugins/@lego/plugin-todo/src/components/TodoDialog.tsx +55 -0
- package/template/packages/plugins/@lego/plugin-todo/src/components/TodoFilters.tsx +79 -0
- package/template/packages/plugins/@lego/plugin-todo/src/components/TodoForm.tsx +94 -0
- package/template/packages/plugins/@lego/plugin-todo/src/components/TodoItem.tsx +121 -0
- package/template/packages/plugins/@lego/plugin-todo/src/components/TodoList.tsx +41 -0
- package/template/packages/plugins/@lego/plugin-todo/src/components/index.ts +6 -0
- package/template/packages/plugins/@lego/plugin-todo/src/global.css +59 -0
- package/template/packages/plugins/@lego/plugin-todo/src/hooks/useCreateTodo.ts +62 -0
- package/template/packages/plugins/@lego/plugin-todo/src/hooks/useDeleteTodo.ts +46 -0
- package/template/packages/plugins/@lego/plugin-todo/src/hooks/usePocketBase.ts +38 -0
- package/template/packages/plugins/@lego/plugin-todo/src/hooks/useTodos.ts +64 -0
- package/template/packages/plugins/@lego/plugin-todo/src/hooks/useUpdateTodo.ts +35 -0
- package/template/packages/plugins/@lego/plugin-todo/src/index.tsx +5 -0
- package/template/packages/plugins/@lego/plugin-todo/src/lib/utils.ts +20 -0
- package/template/packages/plugins/@lego/plugin-todo/src/pages/TodoPage.tsx +89 -0
- package/template/packages/plugins/@lego/plugin-todo/src/plugin.config.ts +104 -0
- package/template/packages/plugins/@lego/plugin-todo/src/plugin.ts +13 -0
- package/template/packages/plugins/@lego/plugin-todo/src/schemas.ts +37 -0
- package/template/packages/plugins/@lego/plugin-todo/src/types.ts +42 -0
- package/template/packages/plugins/@lego/plugin-todo/src/vite-env.d.ts +31 -0
- package/template/packages/plugins/@lego/plugin-todo/tailwind.config.ts +51 -0
- package/template/packages/plugins/@lego/plugin-todo/tsconfig.json +18 -0
- package/template/pnpm-workspace.yaml +4 -0
- package/template/pocketbase/CHANGELOG.md +911 -0
- package/template/pocketbase/LICENSE.md +17 -0
- package/template/scripts/create-plugin.js +221 -0
- package/template/scripts/deploy.sh +56 -0
- package/template/tsconfig.base.json +26 -0
- package/template/tsconfig.json +8 -0
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { defineConfig } from 'vitest/config';
|
|
2
|
+
import react from '@vitejs/plugin-react';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
|
|
5
|
+
export default defineConfig({
|
|
6
|
+
plugins: [react()],
|
|
7
|
+
test: {
|
|
8
|
+
globals: true,
|
|
9
|
+
environment: 'jsdom',
|
|
10
|
+
setupFiles: ['./src/test/setup.ts'],
|
|
11
|
+
include: ['**/__tests__/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],
|
|
12
|
+
exclude: ['node_modules', 'dist'],
|
|
13
|
+
coverage: {
|
|
14
|
+
provider: 'v8',
|
|
15
|
+
reporter: ['text', 'json', 'html'],
|
|
16
|
+
exclude: [
|
|
17
|
+
'node_modules/',
|
|
18
|
+
'dist/',
|
|
19
|
+
'**/*.config.{js,ts}',
|
|
20
|
+
'**/types/**',
|
|
21
|
+
'src/test/',
|
|
22
|
+
],
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
resolve: {
|
|
26
|
+
alias: {
|
|
27
|
+
'@': path.resolve(__dirname, './src'),
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
});
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
user nginx;
|
|
2
|
+
worker_processes auto;
|
|
3
|
+
error_log /var/log/nginx/error.log warn;
|
|
4
|
+
pid /var/run/nginx.pid;
|
|
5
|
+
|
|
6
|
+
events {
|
|
7
|
+
worker_connections 1024;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
http {
|
|
11
|
+
include /etc/nginx/mime.types;
|
|
12
|
+
default_type application/octet-stream;
|
|
13
|
+
|
|
14
|
+
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
|
|
15
|
+
'$status $body_bytes_sent "$http_referer" '
|
|
16
|
+
'"$http_user_agent" "$http_x_forwarded_for"';
|
|
17
|
+
|
|
18
|
+
access_log /var/log/nginx/access.log main;
|
|
19
|
+
|
|
20
|
+
sendfile on;
|
|
21
|
+
tcp_nopush on;
|
|
22
|
+
keepalive_timeout 65;
|
|
23
|
+
gzip on;
|
|
24
|
+
gzip_vary on;
|
|
25
|
+
gzip_min_length 1024;
|
|
26
|
+
gzip_types text/plain text/css text/xml text/javascript application/x-javascript application/xml+rss application/json;
|
|
27
|
+
|
|
28
|
+
server {
|
|
29
|
+
listen 80;
|
|
30
|
+
server_name _;
|
|
31
|
+
root /usr/share/nginx/html;
|
|
32
|
+
index index.html;
|
|
33
|
+
|
|
34
|
+
# Security headers
|
|
35
|
+
add_header X-Frame-Options "SAMEORIGIN" always;
|
|
36
|
+
add_header X-Content-Type-Options "nosniff" always;
|
|
37
|
+
add_header X-XSS-Protection "1; mode=block" always;
|
|
38
|
+
|
|
39
|
+
# Main application
|
|
40
|
+
location / {
|
|
41
|
+
try_files $uri $uri/ /index.html;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
# API proxy to PocketBase
|
|
45
|
+
location /api/ {
|
|
46
|
+
proxy_pass http://localhost:8090/;
|
|
47
|
+
proxy_http_version 1.1;
|
|
48
|
+
proxy_set_header Upgrade $http_upgrade;
|
|
49
|
+
proxy_set_header Connection 'upgrade';
|
|
50
|
+
proxy_set_header Host $host;
|
|
51
|
+
proxy_cache_bypass $http_upgrade;
|
|
52
|
+
proxy_set_header X-Real-IP $remote_addr;
|
|
53
|
+
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
54
|
+
proxy_set_header X-Forwarded-Proto $scheme;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
# Static assets caching
|
|
58
|
+
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
|
|
59
|
+
expires 1y;
|
|
60
|
+
add_header Cache-Control "public, immutable";
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
# PocketBase admin UI (optional, for development)
|
|
64
|
+
location /_pb/ {
|
|
65
|
+
proxy_pass http://localhost:8090/_/;
|
|
66
|
+
proxy_http_version 1.1;
|
|
67
|
+
proxy_set_header Upgrade $http_upgrade;
|
|
68
|
+
proxy_set_header Connection 'upgrade';
|
|
69
|
+
proxy_set_header Host $host;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "lego-one",
|
|
3
|
+
"version": "2.0.13",
|
|
4
|
+
"private": true,
|
|
5
|
+
"description": "Microkernel SaaS OS with Modern.js + Garfish + PocketBase",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"dev": "pnpm --filter './host' dev",
|
|
9
|
+
"dev:host": "pnpm --filter './host' dev",
|
|
10
|
+
"dev:plugins": "pnpm -r --filter './packages/plugins/@lego/*' dev",
|
|
11
|
+
"dev:all": "pnpm --filter './host' dev & pnpm -r --filter './packages/plugins/@lego/*' dev",
|
|
12
|
+
"build": "pnpm --filter './host' build && pnpm -r --filter './packages/plugins/@lego/*' build",
|
|
13
|
+
"lint": "pnpm -r lint",
|
|
14
|
+
"lint:fix": "pnpm -r lint:fix",
|
|
15
|
+
"format": "prettier --write \"**/*.{ts,tsx,js,jsx,json,md}\"",
|
|
16
|
+
"format:check": "prettier --check \"**/*.{ts,tsx,js,jsx,json,md}\"",
|
|
17
|
+
"typecheck": "pnpm -r typecheck",
|
|
18
|
+
"test": "pnpm -r test",
|
|
19
|
+
"test:e2e": "pnpm --filter './host' test:e2e",
|
|
20
|
+
"clean": "pnpm -r exec rm -rf dist node_modules",
|
|
21
|
+
"create-plugin": "node scripts/create-plugin.js",
|
|
22
|
+
"version:patch": "node scripts/version.js patch",
|
|
23
|
+
"version:minor": "node scripts/version.js minor",
|
|
24
|
+
"version:major": "node scripts/version.js major",
|
|
25
|
+
"version:pre": "node scripts/version.js pre",
|
|
26
|
+
"publish:cli": "node scripts/publish-cli.js",
|
|
27
|
+
"release": "pnpm run build && pnpm run test && pnpm run version:patch && pnpm run publish:cli && git push && git push --tags"
|
|
28
|
+
},
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"@types/node": "^20.11.0",
|
|
31
|
+
"@typescript-eslint/eslint-plugin": "^7.0.0",
|
|
32
|
+
"@typescript-eslint/parser": "^7.0.0",
|
|
33
|
+
"eslint": "^8.57.0",
|
|
34
|
+
"eslint-config-prettier": "^9.1.0",
|
|
35
|
+
"kleur": "^4.1.5",
|
|
36
|
+
"prettier": "^3.2.0",
|
|
37
|
+
"prettier-plugin-tailwindcss": "^0.5.0",
|
|
38
|
+
"typescript": "^5.3.0"
|
|
39
|
+
},
|
|
40
|
+
"engines": {
|
|
41
|
+
"node": ">=18.0.0",
|
|
42
|
+
"pnpm": ">=8.0.0"
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
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: 3001,
|
|
7
|
+
},
|
|
8
|
+
|
|
9
|
+
runtime: {
|
|
10
|
+
router: true,
|
|
11
|
+
},
|
|
12
|
+
|
|
13
|
+
// Mark as micro-frontend sub-app
|
|
14
|
+
deploy: {
|
|
15
|
+
microFrontend: true,
|
|
16
|
+
},
|
|
17
|
+
|
|
18
|
+
plugins: [appTools(), garfishPlugin()],
|
|
19
|
+
});
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@lego/plugin-dashboard",
|
|
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
|
+
"@modern-js/runtime": "^2.70.0",
|
|
15
|
+
"@radix-ui/react-slot": "^1.1.0",
|
|
16
|
+
"@tanstack/react-query": "^5.62.0",
|
|
17
|
+
"clsx": "^2.1.1",
|
|
18
|
+
"garfish": "^1.19.0",
|
|
19
|
+
"lucide-react": "^0.468.0",
|
|
20
|
+
"pocketbase": "^0.22.0",
|
|
21
|
+
"react": "^18.3.1",
|
|
22
|
+
"react-dom": "^18.3.1",
|
|
23
|
+
"tailwind-merge": "^2.5.4"
|
|
24
|
+
},
|
|
25
|
+
"devDependencies": {
|
|
26
|
+
"@modern-js/app-tools": "^2.70.0",
|
|
27
|
+
"@modern-js/plugin-garfish": "^2.70.0",
|
|
28
|
+
"@types/react": "^18.3.12",
|
|
29
|
+
"@types/react-dom": "^18.3.1",
|
|
30
|
+
"autoprefixer": "^10.4.20",
|
|
31
|
+
"postcss": "^8.4.49",
|
|
32
|
+
"tailwindcss": "^3.4.15",
|
|
33
|
+
"typescript": "^5.6.3"
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { useEffect } from 'react';
|
|
2
|
+
import { useChannelIntegration } from './hooks/useChannelIntegration';
|
|
3
|
+
import './global.css';
|
|
4
|
+
|
|
5
|
+
interface DashboardAppProps {
|
|
6
|
+
basename?: string; // Provided by Garfish
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Dashboard Plugin App Component
|
|
11
|
+
*
|
|
12
|
+
* This is the root component of the Dashboard plugin.
|
|
13
|
+
* It handles:
|
|
14
|
+
* - Channel integration (notify host when ready)
|
|
15
|
+
* - Rendering the appropriate route
|
|
16
|
+
*/
|
|
17
|
+
export default function DashboardApp({ basename }: DashboardAppProps) {
|
|
18
|
+
// Notify host that plugin is ready
|
|
19
|
+
useChannelIntegration();
|
|
20
|
+
|
|
21
|
+
return (
|
|
22
|
+
<div className="dashboard-plugin">
|
|
23
|
+
{/* Routes are handled by host via activeWhen */}
|
|
24
|
+
{/* The host renders this component at /dashboard */}
|
|
25
|
+
</div>
|
|
26
|
+
);
|
|
27
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { Activity } from '../hooks/useRecentActivity';
|
|
2
|
+
import { User, Settings, CheckSquare, AlertCircle } from 'lucide-react';
|
|
3
|
+
|
|
4
|
+
interface ActivityFeedProps {
|
|
5
|
+
activities: Activity[];
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
function getActivityIcon(action: string) {
|
|
9
|
+
if (action.includes('create')) return CheckSquare;
|
|
10
|
+
if (action.includes('delete')) return AlertCircle;
|
|
11
|
+
if (action.includes('update')) return Settings;
|
|
12
|
+
return User;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function formatRelativeTime(dateString: string): string {
|
|
16
|
+
const date = new Date(dateString);
|
|
17
|
+
const now = new Date();
|
|
18
|
+
const diffMs = now.getTime() - date.getTime();
|
|
19
|
+
const diffMins = Math.floor(diffMs / 60000);
|
|
20
|
+
const diffHours = Math.floor(diffMs / 3600000);
|
|
21
|
+
const diffDays = Math.floor(diffMs / 86400000);
|
|
22
|
+
|
|
23
|
+
if (diffMins < 1) return 'just now';
|
|
24
|
+
if (diffMins < 60) return `${diffMins}m ago`;
|
|
25
|
+
if (diffHours < 24) return `${diffHours}h ago`;
|
|
26
|
+
return `${diffDays}d ago`;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function ActivityFeed({ activities }: ActivityFeedProps) {
|
|
30
|
+
return (
|
|
31
|
+
<div className="rounded-xl border bg-card p-6">
|
|
32
|
+
<h2 className="text-lg font-semibold">Recent Activity</h2>
|
|
33
|
+
<div className="mt-4 space-y-4">
|
|
34
|
+
{activities.length === 0 ? (
|
|
35
|
+
<p className="text-sm text-muted-foreground">No recent activity</p>
|
|
36
|
+
) : (
|
|
37
|
+
activities.map((activity) => {
|
|
38
|
+
const Icon = getActivityIcon(activity.action);
|
|
39
|
+
return (
|
|
40
|
+
<div key={activity.id} className="flex items-start gap-3">
|
|
41
|
+
<div className="flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-muted">
|
|
42
|
+
<Icon className="h-4 w-4 text-muted-foreground" />
|
|
43
|
+
</div>
|
|
44
|
+
<div className="flex-1 space-y-1">
|
|
45
|
+
<p className="text-sm">
|
|
46
|
+
<span className="font-medium">{activity.userName}</span>
|
|
47
|
+
{' '}
|
|
48
|
+
<span className="text-muted-foreground">
|
|
49
|
+
{activity.action} {activity.resource}
|
|
50
|
+
</span>
|
|
51
|
+
</p>
|
|
52
|
+
<p className="text-xs text-muted-foreground">
|
|
53
|
+
{formatRelativeTime(activity.createdAt)}
|
|
54
|
+
</p>
|
|
55
|
+
</div>
|
|
56
|
+
</div>
|
|
57
|
+
);
|
|
58
|
+
})
|
|
59
|
+
)}
|
|
60
|
+
</div>
|
|
61
|
+
</div>
|
|
62
|
+
);
|
|
63
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Quick Action Slot - Slot injection component
|
|
3
|
+
*
|
|
4
|
+
* This demonstrates how a plugin can inject a widget into
|
|
5
|
+
* the dashboard:widgets slot (if host has one)
|
|
6
|
+
*/
|
|
7
|
+
export function QuickActionSlot() {
|
|
8
|
+
// This would be rendered in a slot on the dashboard
|
|
9
|
+
// For now, it's a placeholder showing how slots work
|
|
10
|
+
return null;
|
|
11
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { Plus, Users, Settings } from 'lucide-react';
|
|
2
|
+
|
|
3
|
+
interface QuickActionsProps {
|
|
4
|
+
onToast?: (data: { type: string; title: string; description?: string }) => void;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export function QuickActions({ onToast }: QuickActionsProps) {
|
|
8
|
+
const actions = [
|
|
9
|
+
{
|
|
10
|
+
icon: Plus,
|
|
11
|
+
label: 'New Todo',
|
|
12
|
+
description: 'Create a new todo item',
|
|
13
|
+
onClick: () => {
|
|
14
|
+
onToast?.({
|
|
15
|
+
type: 'info',
|
|
16
|
+
title: 'Quick Action',
|
|
17
|
+
description: 'Navigate to Todos to create a new item',
|
|
18
|
+
});
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
icon: Users,
|
|
23
|
+
label: 'Invite User',
|
|
24
|
+
description: 'Invite a team member',
|
|
25
|
+
onClick: () => {
|
|
26
|
+
onToast?.({
|
|
27
|
+
type: 'info',
|
|
28
|
+
title: 'Quick Action',
|
|
29
|
+
description: 'Navigate to Settings to invite users',
|
|
30
|
+
});
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
icon: Settings,
|
|
35
|
+
label: 'Settings',
|
|
36
|
+
description: 'Manage organization settings',
|
|
37
|
+
onClick: () => {
|
|
38
|
+
window.location.href = '/settings';
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
];
|
|
42
|
+
|
|
43
|
+
return (
|
|
44
|
+
<div className="rounded-xl border bg-card p-6">
|
|
45
|
+
<h2 className="text-lg font-semibold">Quick Actions</h2>
|
|
46
|
+
<div className="mt-4 space-y-2">
|
|
47
|
+
{actions.map((action) => {
|
|
48
|
+
const Icon = action.icon;
|
|
49
|
+
return (
|
|
50
|
+
<button
|
|
51
|
+
key={action.label}
|
|
52
|
+
onClick={action.onClick}
|
|
53
|
+
className="flex w-full items-center gap-3 rounded-lg border border-transparent bg-muted/50 p-3 text-left hover:bg-muted hover:underline"
|
|
54
|
+
>
|
|
55
|
+
<div className="flex h-8 w-8 items-center justify-center rounded-md bg-background">
|
|
56
|
+
<Icon className="h-4 w-4" />
|
|
57
|
+
</div>
|
|
58
|
+
<div className="flex-1">
|
|
59
|
+
<div className="text-sm font-medium">{action.label}</div>
|
|
60
|
+
<div className="text-xs text-muted-foreground">{action.description}</div>
|
|
61
|
+
</div>
|
|
62
|
+
</button>
|
|
63
|
+
);
|
|
64
|
+
})}
|
|
65
|
+
</div>
|
|
66
|
+
</div>
|
|
67
|
+
);
|
|
68
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { NavLink } from '@modern-js/runtime/router';
|
|
2
|
+
import { cn } from '../lib/utils';
|
|
3
|
+
import { LayoutDashboard } from 'lucide-react';
|
|
4
|
+
|
|
5
|
+
interface SidebarWidgetProps {
|
|
6
|
+
to: string;
|
|
7
|
+
icon: string;
|
|
8
|
+
label: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Sidebar Widget - Slot injection component
|
|
13
|
+
*
|
|
14
|
+
* This component is injected into the host's sidebar nav slot
|
|
15
|
+
* It demonstrates how plugins can add navigation items
|
|
16
|
+
*/
|
|
17
|
+
export function SidebarWidget({ to, label }: SidebarWidgetProps) {
|
|
18
|
+
return (
|
|
19
|
+
<NavLink
|
|
20
|
+
to={to}
|
|
21
|
+
end={to === '/'}
|
|
22
|
+
className={({ isActive }) =>
|
|
23
|
+
cn(
|
|
24
|
+
'flex items-center gap-3 rounded-lg px-3 py-2 text-sm font-medium transition-colors',
|
|
25
|
+
isActive
|
|
26
|
+
? 'bg-primary text-primary-foreground'
|
|
27
|
+
: 'text-muted-foreground hover:bg-muted hover:text-foreground'
|
|
28
|
+
)
|
|
29
|
+
}
|
|
30
|
+
>
|
|
31
|
+
<LayoutDashboard className="h-5 w-5" />
|
|
32
|
+
<span>{label}</span>
|
|
33
|
+
</NavLink>
|
|
34
|
+
);
|
|
35
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { cn } from '../lib/utils';
|
|
2
|
+
import { LucideIcon } from 'lucide-react';
|
|
3
|
+
|
|
4
|
+
interface StatCardProps {
|
|
5
|
+
title: string;
|
|
6
|
+
value: number;
|
|
7
|
+
icon: string;
|
|
8
|
+
description: string;
|
|
9
|
+
trend?: {
|
|
10
|
+
value: number;
|
|
11
|
+
direction: 'up' | 'down';
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// Icon map - in production you'd use proper icon loading
|
|
16
|
+
const iconMap: Record<string, LucideIcon> = {};
|
|
17
|
+
|
|
18
|
+
export function StatCard({ title, value, icon, description, trend }: StatCardProps) {
|
|
19
|
+
// For now, use a simple icon fallback
|
|
20
|
+
const Icon = iconMap[icon] || ((props: any) => <div {...props} className="h-4 w-4" />);
|
|
21
|
+
|
|
22
|
+
return (
|
|
23
|
+
<div className="rounded-xl border bg-card p-6">
|
|
24
|
+
<div className="flex items-center justify-between">
|
|
25
|
+
<div className="flex h-12 w-12 items-center justify-center rounded-lg bg-primary/10">
|
|
26
|
+
<Icon className="h-6 w-6 text-primary" />
|
|
27
|
+
</div>
|
|
28
|
+
{trend && (
|
|
29
|
+
<div
|
|
30
|
+
className={cn(
|
|
31
|
+
'flex items-center gap-1 text-sm font-medium',
|
|
32
|
+
trend.direction === 'up' ? 'text-green-600' : 'text-red-600'
|
|
33
|
+
)}
|
|
34
|
+
>
|
|
35
|
+
<span>{trend.direction === 'up' ? '+' : '-'}</span>
|
|
36
|
+
<span>{trend.value}%</span>
|
|
37
|
+
</div>
|
|
38
|
+
)}
|
|
39
|
+
</div>
|
|
40
|
+
<div className="mt-4">
|
|
41
|
+
<div className="text-2xl font-bold">{value.toLocaleString()}</div>
|
|
42
|
+
<div className="text-sm text-muted-foreground">{title}</div>
|
|
43
|
+
</div>
|
|
44
|
+
<p className="mt-2 text-xs text-muted-foreground">{description}</p>
|
|
45
|
+
</div>
|
|
46
|
+
);
|
|
47
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
@tailwind base;
|
|
2
|
+
@tailwind components;
|
|
3
|
+
@tailwind utilities;
|
|
4
|
+
|
|
5
|
+
@layer base {
|
|
6
|
+
:root {
|
|
7
|
+
--background: 0 0% 100%;
|
|
8
|
+
--foreground: 222.2 84% 4.9%;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
.dark {
|
|
12
|
+
--background: 222.2 84% 4.9%;
|
|
13
|
+
--foreground: 210 40% 98%;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
@layer base {
|
|
18
|
+
* {
|
|
19
|
+
@apply border-border;
|
|
20
|
+
}
|
|
21
|
+
body {
|
|
22
|
+
@apply bg-background text-foreground;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { useEffect } from 'react';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Hook for integrating with host channels
|
|
5
|
+
*
|
|
6
|
+
* This handles:
|
|
7
|
+
* - Listening for auth changes
|
|
8
|
+
* - Listening for organization changes
|
|
9
|
+
*/
|
|
10
|
+
export function useChannelIntegration() {
|
|
11
|
+
useEffect(() => {
|
|
12
|
+
// Get channel bus from host
|
|
13
|
+
const channelBus = (window as any).__LEGO_CHANNEL_BUS__;
|
|
14
|
+
|
|
15
|
+
if (!channelBus) {
|
|
16
|
+
console.warn('[Dashboard] Channel bus not found');
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// Listen for auth changes
|
|
21
|
+
const authUnsubscribe = channelBus.subscribe('lego:auth:change', (data: any) => {
|
|
22
|
+
console.log('[Dashboard] Auth changed:', data);
|
|
23
|
+
|
|
24
|
+
if (!data.isAuthenticated) {
|
|
25
|
+
// Clear plugin data when logged out
|
|
26
|
+
console.log('[Dashboard] Clearing data due to logout');
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
// Listen for organization changes
|
|
31
|
+
const orgUnsubscribe = channelBus.subscribe('lego:organization:change', (data: any) => {
|
|
32
|
+
console.log('[Dashboard] Organization changed:', data);
|
|
33
|
+
|
|
34
|
+
// Refresh dashboard data for new organization
|
|
35
|
+
// This will trigger a refetch via TanStack Query's invalidation
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
return () => {
|
|
39
|
+
authUnsubscribe?.();
|
|
40
|
+
orgUnsubscribe?.();
|
|
41
|
+
};
|
|
42
|
+
}, []);
|
|
43
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { useQuery } from '@tanstack/react-query';
|
|
2
|
+
import { usePocketBase } from './usePocketBase';
|
|
3
|
+
|
|
4
|
+
interface DashboardStats {
|
|
5
|
+
totalUsers: number;
|
|
6
|
+
activeTodos: number;
|
|
7
|
+
completedTodos: number;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Fetch dashboard statistics
|
|
12
|
+
*/
|
|
13
|
+
export function useDashboardStats() {
|
|
14
|
+
const pb = usePocketBase();
|
|
15
|
+
|
|
16
|
+
return useQuery({
|
|
17
|
+
queryKey: ['dashboard', 'stats'],
|
|
18
|
+
queryFn: async (): Promise<DashboardStats> => {
|
|
19
|
+
if (!pb) {
|
|
20
|
+
throw new Error('PocketBase not initialized');
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Get current organization from host state
|
|
24
|
+
const kernelState = (window as any).__LEGO_KERNEL_STATE__;
|
|
25
|
+
const state = kernelState?.useGlobalKernelState?.getState();
|
|
26
|
+
|
|
27
|
+
if (!state?.organization?.id) {
|
|
28
|
+
throw new Error('No organization selected');
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const orgId = state.organization.id;
|
|
32
|
+
|
|
33
|
+
// Fetch stats in parallel
|
|
34
|
+
const [usersResult, todosResult] = await Promise.all([
|
|
35
|
+
// Get users count (simplified - in production use proper aggregation)
|
|
36
|
+
pb.collection('user_roles').getList(1, 1, {
|
|
37
|
+
filter: `organizationId = "${orgId}"`,
|
|
38
|
+
requestKey: `users-count-${orgId}`,
|
|
39
|
+
}).then(() => {
|
|
40
|
+
// For simplicity, return a mock count
|
|
41
|
+
// In production, you'd use PocketBase's aggregate features
|
|
42
|
+
return Math.floor(Math.random() * 50) + 10;
|
|
43
|
+
}),
|
|
44
|
+
|
|
45
|
+
// Get active todos count
|
|
46
|
+
pb.collection('todos').getList(1, 1, {
|
|
47
|
+
filter: `organizationId = "${orgId}" && completed = false`,
|
|
48
|
+
}).then((result: any) => result.totalItems),
|
|
49
|
+
|
|
50
|
+
// Get completed todos count
|
|
51
|
+
pb.collection('todos').getList(1, 1, {
|
|
52
|
+
filter: `organizationId = "${orgId}" && completed = true`,
|
|
53
|
+
}).then((result: any) => result.totalItems),
|
|
54
|
+
]);
|
|
55
|
+
|
|
56
|
+
return {
|
|
57
|
+
totalUsers: usersResult as number,
|
|
58
|
+
activeTodos: todosResult[0] as number,
|
|
59
|
+
completedTodos: todosResult[1] as number,
|
|
60
|
+
};
|
|
61
|
+
},
|
|
62
|
+
enabled: !!pb,
|
|
63
|
+
refetchInterval: 60000, // Refresh every minute
|
|
64
|
+
});
|
|
65
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { useEffect, useState } from 'react';
|
|
2
|
+
import PocketBase from 'pocketbase';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Get PocketBase instance from host
|
|
6
|
+
*
|
|
7
|
+
* Plugins access PocketBase through the window bridge
|
|
8
|
+
* that was set up by the host's shared state system
|
|
9
|
+
*/
|
|
10
|
+
export function usePocketBase() {
|
|
11
|
+
const [pb, setPb] = useState<PocketBase | null>(null);
|
|
12
|
+
|
|
13
|
+
useEffect(() => {
|
|
14
|
+
// Access PocketBase from host's state bridge
|
|
15
|
+
const kernelState = (window as any).__LEGO_KERNEL_STATE__;
|
|
16
|
+
|
|
17
|
+
if (!kernelState) {
|
|
18
|
+
console.error('[Dashboard] Kernel state bridge not found');
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Get PocketBase URL from env or default to localhost
|
|
23
|
+
const pbUrl = import.meta.env.VITE_POCKETBASE_URL || 'http://127.0.0.1:8090';
|
|
24
|
+
const client = new PocketBase(pbUrl);
|
|
25
|
+
|
|
26
|
+
// Get auth token from host state
|
|
27
|
+
const state = kernelState.useGlobalKernelState.getState();
|
|
28
|
+
if (state.token) {
|
|
29
|
+
client.authStore.save(state.token, null as any);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Listen for token changes
|
|
33
|
+
const unsubscribe = kernelState.useGlobalKernelState.subscribe((newState: any) => {
|
|
34
|
+
if (newState.token && newState.token !== client.authStore.token) {
|
|
35
|
+
client.authStore.save(newState.token, null as any);
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
setPb(client);
|
|
40
|
+
|
|
41
|
+
return () => {
|
|
42
|
+
unsubscribe();
|
|
43
|
+
};
|
|
44
|
+
}, []);
|
|
45
|
+
|
|
46
|
+
return pb;
|
|
47
|
+
}
|