@lumerahq/cli 0.10.0 → 0.10.1
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/chunk-WTDV3MTG.js +104 -0
- package/dist/index.js +5 -1
- package/dist/{init-EDSRR3YM.js → init-OH433IPH.js} +72 -55
- package/dist/templates-67O6PVFK.js +70 -0
- package/package.json +1 -1
- package/templates/default/ARCHITECTURE.md +0 -80
- package/templates/default/CLAUDE.md +0 -238
- package/templates/default/README.md +0 -59
- package/templates/default/biome.json +0 -38
- package/templates/default/gitignore +0 -9
- package/templates/default/index.html +0 -13
- package/templates/default/package.json.hbs +0 -47
- package/templates/default/platform/automations/.gitkeep +0 -0
- package/templates/default/platform/collections/example_items.json +0 -26
- package/templates/default/platform/hooks/.gitkeep +0 -0
- package/templates/default/pyproject.toml.hbs +0 -14
- package/templates/default/scripts/seed-demo.py +0 -35
- package/templates/default/src/components/Sidebar.tsx +0 -82
- package/templates/default/src/components/StatCard.tsx +0 -25
- package/templates/default/src/components/layout.tsx +0 -13
- package/templates/default/src/lib/queries.ts +0 -27
- package/templates/default/src/main.tsx +0 -131
- package/templates/default/src/routes/__root.tsx +0 -10
- package/templates/default/src/routes/index.tsx +0 -88
- package/templates/default/src/routes/settings.tsx +0 -21
- package/templates/default/src/styles.css +0 -44
- package/templates/default/tsconfig.json +0 -23
- package/templates/default/vite.config.ts +0 -28
|
@@ -1,238 +0,0 @@
|
|
|
1
|
-
# {{projectTitle}} - Claude Code Instructions
|
|
2
|
-
|
|
3
|
-
**Full Architecture**: See `ARCHITECTURE.md`
|
|
4
|
-
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
## AI Agent Skills
|
|
8
|
-
|
|
9
|
-
This project includes Lumera skills for AI coding agents in `.claude/skills/`. Read the relevant skill file when you need detailed API docs and usage patterns for that capability.
|
|
10
|
-
|
|
11
|
-
<!-- LUMERA_SKILLS_START -->
|
|
12
|
-
_Run `lumera skills install` to populate skill descriptions._
|
|
13
|
-
<!-- LUMERA_SKILLS_END -->
|
|
14
|
-
|
|
15
|
-
### Managing Skills
|
|
16
|
-
|
|
17
|
-
```bash
|
|
18
|
-
lumera skills update # Update all skills to latest
|
|
19
|
-
lumera skills install --force # Re-install from scratch
|
|
20
|
-
```
|
|
21
|
-
|
|
22
|
-
---
|
|
23
|
-
|
|
24
|
-
## Quick Reference
|
|
25
|
-
|
|
26
|
-
### Lumera CLI
|
|
27
|
-
|
|
28
|
-
All `lumera` commands are run via `pnpm dlx`:
|
|
29
|
-
|
|
30
|
-
```bash
|
|
31
|
-
# Shortcuts
|
|
32
|
-
pnpm dev # Start dev server
|
|
33
|
-
pnpm deploy # Deploy frontend
|
|
34
|
-
|
|
35
|
-
# All other commands
|
|
36
|
-
pnpm dlx @lumerahq/cli plan
|
|
37
|
-
pnpm dlx @lumerahq/cli apply
|
|
38
|
-
pnpm dlx @lumerahq/cli destroy
|
|
39
|
-
pnpm dlx @lumerahq/cli run scripts/seed-demo.py
|
|
40
|
-
pnpm dlx @lumerahq/cli status
|
|
41
|
-
```
|
|
42
|
-
|
|
43
|
-
### Running the Frontend
|
|
44
|
-
|
|
45
|
-
```bash
|
|
46
|
-
# Login first (stores credentials in .lumera/credentials.json)
|
|
47
|
-
lumera login
|
|
48
|
-
|
|
49
|
-
# Start dev server
|
|
50
|
-
pnpm dev
|
|
51
|
-
|
|
52
|
-
# With custom port
|
|
53
|
-
lumera dev --port 3000
|
|
54
|
-
|
|
55
|
-
# With ngrok tunnel
|
|
56
|
-
lumera dev --url https://my-tunnel.ngrok.io
|
|
57
|
-
|
|
58
|
-
# Plain vite (no Lumera registration)
|
|
59
|
-
pnpm dev:vite
|
|
60
|
-
```
|
|
61
|
-
|
|
62
|
-
### Linting & Formatting
|
|
63
|
-
|
|
64
|
-
```bash
|
|
65
|
-
# Type check without emitting
|
|
66
|
-
pnpm typecheck
|
|
67
|
-
|
|
68
|
-
# Lint and auto-fix
|
|
69
|
-
pnpm lint
|
|
70
|
-
|
|
71
|
-
# Format code
|
|
72
|
-
pnpm format
|
|
73
|
-
|
|
74
|
-
# Run all checks (lint + format + typecheck) - use in CI
|
|
75
|
-
pnpm check:ci
|
|
76
|
-
```
|
|
77
|
-
|
|
78
|
-
### Deploying
|
|
79
|
-
|
|
80
|
-
```bash
|
|
81
|
-
# Deploy frontend
|
|
82
|
-
lumera apply app
|
|
83
|
-
|
|
84
|
-
# Apply all resources (collections, automations, hooks, app)
|
|
85
|
-
lumera apply
|
|
86
|
-
|
|
87
|
-
# Preview changes first
|
|
88
|
-
lumera plan
|
|
89
|
-
```
|
|
90
|
-
|
|
91
|
-
### Running Scripts
|
|
92
|
-
|
|
93
|
-
All scripts are **idempotent** - safe to run multiple times during iterative development.
|
|
94
|
-
|
|
95
|
-
```bash
|
|
96
|
-
# Run a script locally
|
|
97
|
-
lumera run scripts/seed-demo.py
|
|
98
|
-
|
|
99
|
-
# Dependencies can be declared inline (PEP 723)
|
|
100
|
-
```
|
|
101
|
-
|
|
102
|
-
### Running Automations via External ID
|
|
103
|
-
|
|
104
|
-
Always run automations using their `external_id` (not the internal Lumera ID which changes per tenant).
|
|
105
|
-
|
|
106
|
-
```python
|
|
107
|
-
uv run python << 'EOF'
|
|
108
|
-
from lumera import automations
|
|
109
|
-
|
|
110
|
-
# Run automation by external_id (returns Run object immediately)
|
|
111
|
-
run = automations.run_by_external_id(
|
|
112
|
-
"{{projectName}}:my_automation",
|
|
113
|
-
inputs={"param": "value"}
|
|
114
|
-
)
|
|
115
|
-
print(f"Run ID: {run.id}")
|
|
116
|
-
print(f"Status: {run.status}")
|
|
117
|
-
|
|
118
|
-
# Wait for completion (blocks until done or timeout)
|
|
119
|
-
result = run.wait(timeout=600) # 10 min timeout
|
|
120
|
-
print(f"Result: {result}")
|
|
121
|
-
EOF
|
|
122
|
-
```
|
|
123
|
-
|
|
124
|
-
**Run object properties**: `id`, `status`, `result`, `error`, `inputs`, `is_terminal`
|
|
125
|
-
**Run object methods**: `wait(timeout)`, `refresh()`, `cancel()`
|
|
126
|
-
**Status values**: `queued`, `running`, `succeeded`, `failed`, `cancelled`, `timeout`
|
|
127
|
-
|
|
128
|
-
---
|
|
129
|
-
|
|
130
|
-
## Important Rules
|
|
131
|
-
|
|
132
|
-
1. **Authenticate first** - Before running any CLI or SDK commands, ensure the user has run `lumera login`. This stores credentials in `.lumera/credentials.json` which the SDK reads automatically.
|
|
133
|
-
|
|
134
|
-
2. **Source of truth is code** - `platform/` contains all schemas, automations, hooks. Update local code first, then deploy.
|
|
135
|
-
|
|
136
|
-
3. **Never edit Lumera directly** - Don't change data/schema in Lumera UI without explicit user approval.
|
|
137
|
-
|
|
138
|
-
4. **Offer to deploy** - When schemas, hooks, or automations change, offer to deploy to Lumera.
|
|
139
|
-
|
|
140
|
-
5. **Use lumera-sdk skill for Python** - When writing scripts or automations, refer to the **lumera-sdk** skill (`.claude/skills/lumera-sdk.md`) for available SDK functions and usage patterns. The SDK source code is also available in `.venv/lib/python*/site-packages/lumera/` for detailed implementation reference. Key modules:
|
|
141
|
-
- `lumera.pb` - Record and collection operations (search, get, create, update, delete)
|
|
142
|
-
- `lumera.storage` - File uploads and downloads
|
|
143
|
-
- `lumera.llm` - LLM completions
|
|
144
|
-
- `lumera.automations` - Running and managing automations
|
|
145
|
-
- `lumera.integrations` - Third-party integrations (Google, Slack, etc.)
|
|
146
|
-
|
|
147
|
-
6. **Use Python SDK for ad-hoc investigation** - When you need to quickly query data, inspect records, or debug issues, use the Python SDK with `uv run python` (uses `.venv` automatically via `pyproject.toml`):
|
|
148
|
-
```bash
|
|
149
|
-
uv run python -c "from lumera import pb; print(pb.search('collection_name'))"
|
|
150
|
-
```
|
|
151
|
-
|
|
152
|
-
---
|
|
153
|
-
|
|
154
|
-
## Key Directories
|
|
155
|
-
|
|
156
|
-
```
|
|
157
|
-
platform/
|
|
158
|
-
├── automations/ # Automation scripts (Python)
|
|
159
|
-
├── collections/ # Collection schemas (JSON)
|
|
160
|
-
└── hooks/ # Server-side JavaScript hooks
|
|
161
|
-
|
|
162
|
-
scripts/ # Local scripts (seed, migrate, etc.)
|
|
163
|
-
|
|
164
|
-
src/ # React frontend
|
|
165
|
-
├── routes/ # TanStack Router pages
|
|
166
|
-
├── lib/ # queries.ts, api helpers
|
|
167
|
-
└── components/ # React components
|
|
168
|
-
|
|
169
|
-
.venv/ # Python venv with lumera SDK (for IDE autocomplete)
|
|
170
|
-
```
|
|
171
|
-
|
|
172
|
-
### Python Environment
|
|
173
|
-
|
|
174
|
-
A Python virtual environment is created at `.venv/` with the `lumera` SDK pre-installed. This provides IDE autocomplete when writing automations and scripts.
|
|
175
|
-
|
|
176
|
-
```bash
|
|
177
|
-
# Activate venv (optional - for IDE integration)
|
|
178
|
-
source .venv/bin/activate
|
|
179
|
-
|
|
180
|
-
# SDK is available for import
|
|
181
|
-
python -c "from lumera import pb; print(pb)"
|
|
182
|
-
```
|
|
183
|
-
|
|
184
|
-
> **Note:** You don't need to activate the venv to run scripts - `lumera run` handles this automatically.
|
|
185
|
-
|
|
186
|
-
---
|
|
187
|
-
|
|
188
|
-
## Frontend Patterns
|
|
189
|
-
|
|
190
|
-
### Fetching Data
|
|
191
|
-
|
|
192
|
-
```typescript
|
|
193
|
-
import { pbSql, pbList } from '@lumerahq/ui/lib';
|
|
194
|
-
|
|
195
|
-
const result = await pbSql<{ id: string; name: string }>({
|
|
196
|
-
sql: 'SELECT id, name FROM users WHERE active = true'
|
|
197
|
-
});
|
|
198
|
-
|
|
199
|
-
const items = await pbList<User>('users', {
|
|
200
|
-
filter: JSON.stringify({ status: "active" }),
|
|
201
|
-
sort: '-created',
|
|
202
|
-
perPage: 50,
|
|
203
|
-
});
|
|
204
|
-
```
|
|
205
|
-
|
|
206
|
-
### Running Automations from Frontend
|
|
207
|
-
|
|
208
|
-
```typescript
|
|
209
|
-
import { createRun, pollRun } from '@lumerahq/ui/lib';
|
|
210
|
-
|
|
211
|
-
const run = await createRun({
|
|
212
|
-
automationId: '{{projectName}}:process_data',
|
|
213
|
-
inputs: { file_id: 'abc123' },
|
|
214
|
-
});
|
|
215
|
-
|
|
216
|
-
const result = await pollRun(run.id);
|
|
217
|
-
```
|
|
218
|
-
|
|
219
|
-
---
|
|
220
|
-
|
|
221
|
-
## Debugging with Lumera SDK
|
|
222
|
-
|
|
223
|
-
```python
|
|
224
|
-
uv run python << 'EOF'
|
|
225
|
-
from lumera import pb
|
|
226
|
-
|
|
227
|
-
# List collections
|
|
228
|
-
print(pb.list_collections())
|
|
229
|
-
|
|
230
|
-
# Search records
|
|
231
|
-
result = pb.search("my_collection", per_page=10)
|
|
232
|
-
print(result)
|
|
233
|
-
|
|
234
|
-
# Get single record
|
|
235
|
-
record = pb.get("my_collection", "record_id")
|
|
236
|
-
print(record)
|
|
237
|
-
EOF
|
|
238
|
-
```
|
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
# {{projectTitle}}
|
|
2
|
-
|
|
3
|
-
Lumera custom embedded app.
|
|
4
|
-
|
|
5
|
-
## Getting Started
|
|
6
|
-
|
|
7
|
-
1. Install dependencies:
|
|
8
|
-
```bash
|
|
9
|
-
pnpm install
|
|
10
|
-
```
|
|
11
|
-
|
|
12
|
-
2. Login to Lumera:
|
|
13
|
-
```bash
|
|
14
|
-
lumera login
|
|
15
|
-
```
|
|
16
|
-
|
|
17
|
-
3. Start development server:
|
|
18
|
-
```bash
|
|
19
|
-
pnpm dev
|
|
20
|
-
```
|
|
21
|
-
|
|
22
|
-
## CLI Commands
|
|
23
|
-
|
|
24
|
-
```bash
|
|
25
|
-
# Development
|
|
26
|
-
lumera dev # Start dev server
|
|
27
|
-
lumera dev --port 3000 # Custom port
|
|
28
|
-
|
|
29
|
-
# Deployment
|
|
30
|
-
lumera apply app # Build and deploy frontend
|
|
31
|
-
lumera apply # Apply all resources
|
|
32
|
-
lumera plan # Preview changes
|
|
33
|
-
|
|
34
|
-
# Scripts
|
|
35
|
-
lumera run scripts/seed-demo.py # Run seed script
|
|
36
|
-
|
|
37
|
-
# Project info
|
|
38
|
-
lumera status # Show project info
|
|
39
|
-
```
|
|
40
|
-
|
|
41
|
-
## Project Structure
|
|
42
|
-
|
|
43
|
-
```
|
|
44
|
-
├── src/ # Frontend (React + TanStack)
|
|
45
|
-
│ ├── routes/ # TanStack Router pages
|
|
46
|
-
│ ├── components/ # React components
|
|
47
|
-
│ └── lib/ # API utilities
|
|
48
|
-
├── platform/ # Backend resources
|
|
49
|
-
│ ├── collections/ # Collection schemas (JSON)
|
|
50
|
-
│ ├── automations/ # Automation scripts (Python)
|
|
51
|
-
│ └── hooks/ # Server-side hooks (JS)
|
|
52
|
-
├── scripts/ # Local scripts
|
|
53
|
-
└── CLAUDE.md # AI assistant instructions
|
|
54
|
-
```
|
|
55
|
-
|
|
56
|
-
## Documentation
|
|
57
|
-
|
|
58
|
-
- `CLAUDE.md` - AI coding assistant instructions
|
|
59
|
-
- `ARCHITECTURE.md` - System architecture (customize for your app)
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"$schema": "https://biomejs.dev/schemas/2.0.0/schema.json",
|
|
3
|
-
"vcs": {
|
|
4
|
-
"enabled": true,
|
|
5
|
-
"clientKind": "git",
|
|
6
|
-
"useIgnoreFile": true
|
|
7
|
-
},
|
|
8
|
-
"files": {
|
|
9
|
-
"ignoreUnknown": false,
|
|
10
|
-
"includes": ["**", "!!**/node_modules", "!!**/dist", "!!**/*routeTree.gen.ts"]
|
|
11
|
-
},
|
|
12
|
-
"formatter": {
|
|
13
|
-
"enabled": true,
|
|
14
|
-
"indentStyle": "space",
|
|
15
|
-
"indentWidth": 2,
|
|
16
|
-
"lineWidth": 100
|
|
17
|
-
},
|
|
18
|
-
"linter": {
|
|
19
|
-
"enabled": true,
|
|
20
|
-
"rules": {
|
|
21
|
-
"recommended": true,
|
|
22
|
-
"suspicious": { "noExplicitAny": "off" },
|
|
23
|
-
"style": { "noNonNullAssertion": "off" }
|
|
24
|
-
}
|
|
25
|
-
},
|
|
26
|
-
"javascript": {
|
|
27
|
-
"formatter": {
|
|
28
|
-
"quoteStyle": "single",
|
|
29
|
-
"semicolons": "always",
|
|
30
|
-
"trailingCommas": "es5"
|
|
31
|
-
}
|
|
32
|
-
},
|
|
33
|
-
"css": {
|
|
34
|
-
"parser": {
|
|
35
|
-
"tailwindDirectives": true
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
}
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
<!doctype html>
|
|
2
|
-
<html lang="en">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="UTF-8" />
|
|
5
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
-
<meta name="theme-color" content="#000000" />
|
|
7
|
-
<title>{{projectTitle}}</title>
|
|
8
|
-
</head>
|
|
9
|
-
<body>
|
|
10
|
-
<div id="app"></div>
|
|
11
|
-
<script type="module" src="/src/main.tsx"></script>
|
|
12
|
-
</body>
|
|
13
|
-
</html>
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "{{projectName}}",
|
|
3
|
-
"private": true,
|
|
4
|
-
"type": "module",
|
|
5
|
-
"pnpm": {
|
|
6
|
-
"overrides": {
|
|
7
|
-
"@tanstack/router-core": "1.155.0"
|
|
8
|
-
}
|
|
9
|
-
},
|
|
10
|
-
"lumera": {
|
|
11
|
-
"version": 1,
|
|
12
|
-
"name": "{{projectTitle}}"
|
|
13
|
-
},
|
|
14
|
-
"scripts": {
|
|
15
|
-
"dev": "pnpm dlx @lumerahq/cli dev",
|
|
16
|
-
"deploy": "pnpm dlx @lumerahq/cli apply app",
|
|
17
|
-
"dev:vite": "vite",
|
|
18
|
-
"build": "vite build && tsc",
|
|
19
|
-
"preview": "vite preview",
|
|
20
|
-
"typecheck": "tsr generate && tsc --noEmit",
|
|
21
|
-
"lint": "biome lint --write .",
|
|
22
|
-
"format": "biome format --write .",
|
|
23
|
-
"check": "biome check --write .",
|
|
24
|
-
"check:ci": "biome check . && tsr generate && tsc --noEmit"
|
|
25
|
-
},
|
|
26
|
-
"dependencies": {
|
|
27
|
-
"@lumerahq/ui": "^0.5.0",
|
|
28
|
-
"@tanstack/react-query": "^5.90.11",
|
|
29
|
-
"@tanstack/react-router": "1.155.0",
|
|
30
|
-
"@tanstack/router-plugin": "1.155.0",
|
|
31
|
-
"lucide-react": "^0.469.0",
|
|
32
|
-
"react": "^19.2.0",
|
|
33
|
-
"react-dom": "^19.2.0",
|
|
34
|
-
"sonner": "^2.0.7",
|
|
35
|
-
"tailwindcss": "^4.0.6"
|
|
36
|
-
},
|
|
37
|
-
"devDependencies": {
|
|
38
|
-
"@biomejs/biome": "^2.0.0",
|
|
39
|
-
"@tailwindcss/vite": "^4.0.6",
|
|
40
|
-
"@tanstack/router-cli": "1.155.0",
|
|
41
|
-
"@types/react": "^19.2.0",
|
|
42
|
-
"@types/react-dom": "^19.2.0",
|
|
43
|
-
"@vitejs/plugin-react": "^5.0.4",
|
|
44
|
-
"typescript": "^5.7.2",
|
|
45
|
-
"vite": "^7.1.7"
|
|
46
|
-
}
|
|
47
|
-
}
|
|
File without changes
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"id": "example_items",
|
|
3
|
-
"name": "example_items",
|
|
4
|
-
"fields": [
|
|
5
|
-
{
|
|
6
|
-
"name": "name",
|
|
7
|
-
"type": "text",
|
|
8
|
-
"required": true
|
|
9
|
-
},
|
|
10
|
-
{
|
|
11
|
-
"name": "status",
|
|
12
|
-
"type": "select",
|
|
13
|
-
"values": ["pending", "processing", "completed"],
|
|
14
|
-
"default": "pending"
|
|
15
|
-
},
|
|
16
|
-
{
|
|
17
|
-
"name": "description",
|
|
18
|
-
"type": "text"
|
|
19
|
-
},
|
|
20
|
-
{
|
|
21
|
-
"name": "source_id",
|
|
22
|
-
"type": "text"
|
|
23
|
-
}
|
|
24
|
-
],
|
|
25
|
-
"indexes": [{ "fields": ["source_id"], "unique": true }]
|
|
26
|
-
}
|
|
File without changes
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
# /// script
|
|
2
|
-
# dependencies = ["lumera"]
|
|
3
|
-
# ///
|
|
4
|
-
"""
|
|
5
|
-
Seed demo data into Lumera (idempotent - safe to run multiple times).
|
|
6
|
-
|
|
7
|
-
Usage:
|
|
8
|
-
lumera run scripts/seed-demo.py
|
|
9
|
-
"""
|
|
10
|
-
|
|
11
|
-
from lumera import pb
|
|
12
|
-
|
|
13
|
-
# Seed demo items
|
|
14
|
-
demo_items = [
|
|
15
|
-
{
|
|
16
|
-
"external_id": "demo:item_1",
|
|
17
|
-
"name": "Example Item 1",
|
|
18
|
-
"status": "pending",
|
|
19
|
-
"description": "This is an example item",
|
|
20
|
-
},
|
|
21
|
-
{
|
|
22
|
-
"external_id": "demo:item_2",
|
|
23
|
-
"name": "Example Item 2",
|
|
24
|
-
"status": "pending",
|
|
25
|
-
"description": "Another example item",
|
|
26
|
-
},
|
|
27
|
-
]
|
|
28
|
-
|
|
29
|
-
print("Seeding demo data...")
|
|
30
|
-
|
|
31
|
-
for item in demo_items:
|
|
32
|
-
pb.upsert("example_items", item)
|
|
33
|
-
print(f" ✓ {item['name']}")
|
|
34
|
-
|
|
35
|
-
print(f"\nSeeded {len(demo_items)} items")
|
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
import { cn } from '@lumerahq/ui/lib';
|
|
2
|
-
import { Link, useRouterState } from '@tanstack/react-router';
|
|
3
|
-
import { ChevronLeft, ChevronRight, LayoutDashboard, Settings } from 'lucide-react';
|
|
4
|
-
import { useState } from 'react';
|
|
5
|
-
|
|
6
|
-
type NavItem = {
|
|
7
|
-
to: string;
|
|
8
|
-
label: string;
|
|
9
|
-
icon: React.ElementType;
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
const navItems: NavItem[] = [
|
|
13
|
-
{ to: '/', label: 'Dashboard', icon: LayoutDashboard },
|
|
14
|
-
{ to: '/settings', label: 'Settings', icon: Settings },
|
|
15
|
-
];
|
|
16
|
-
|
|
17
|
-
export function Sidebar() {
|
|
18
|
-
const [collapsed, setCollapsed] = useState(false);
|
|
19
|
-
const routerState = useRouterState();
|
|
20
|
-
const currentPath = routerState.location.pathname;
|
|
21
|
-
|
|
22
|
-
const isActive = (path: string) => {
|
|
23
|
-
if (path === '/') return currentPath === '/';
|
|
24
|
-
return currentPath.startsWith(path);
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
return (
|
|
28
|
-
<aside
|
|
29
|
-
className={cn(
|
|
30
|
-
'flex flex-col bg-slate-900 text-white border-r border-slate-800 transition-all duration-200',
|
|
31
|
-
collapsed ? 'w-16' : 'w-56'
|
|
32
|
-
)}
|
|
33
|
-
>
|
|
34
|
-
{/* Logo */}
|
|
35
|
-
<div className="flex items-center h-14 px-4 border-b border-slate-800">
|
|
36
|
-
<Link to="/" className="flex items-center gap-2">
|
|
37
|
-
<div className="size-8 rounded-lg bg-primary flex items-center justify-center text-primary-foreground font-bold text-sm">
|
|
38
|
-
{{projectInitial}}
|
|
39
|
-
</div>
|
|
40
|
-
{!collapsed && <span className="font-semibold text-sm">{{projectTitle}}</span>}
|
|
41
|
-
</Link>
|
|
42
|
-
</div>
|
|
43
|
-
|
|
44
|
-
{/* Navigation */}
|
|
45
|
-
<nav className="flex-1 p-2 space-y-1">
|
|
46
|
-
{navItems.map((item) => {
|
|
47
|
-
const active = isActive(item.to);
|
|
48
|
-
return (
|
|
49
|
-
<Link
|
|
50
|
-
key={item.to}
|
|
51
|
-
to={item.to}
|
|
52
|
-
className={cn(
|
|
53
|
-
'flex items-center gap-3 px-3 py-2 rounded-md text-sm font-medium transition-colors',
|
|
54
|
-
active
|
|
55
|
-
? 'bg-slate-800 text-white'
|
|
56
|
-
: 'text-slate-400 hover:bg-slate-800/50 hover:text-white'
|
|
57
|
-
)}
|
|
58
|
-
>
|
|
59
|
-
<item.icon className="size-4 shrink-0" />
|
|
60
|
-
{!collapsed && <span>{item.label}</span>}
|
|
61
|
-
</Link>
|
|
62
|
-
);
|
|
63
|
-
})}
|
|
64
|
-
</nav>
|
|
65
|
-
|
|
66
|
-
{/* Collapse toggle */}
|
|
67
|
-
<div className="p-2 border-t border-slate-800">
|
|
68
|
-
<button
|
|
69
|
-
type="button"
|
|
70
|
-
onClick={() => setCollapsed(!collapsed)}
|
|
71
|
-
className={cn(
|
|
72
|
-
'flex items-center gap-2 w-full px-3 py-2 rounded-md text-sm text-slate-400 hover:text-white hover:bg-slate-800/50 transition-colors',
|
|
73
|
-
collapsed && 'justify-center'
|
|
74
|
-
)}
|
|
75
|
-
>
|
|
76
|
-
{collapsed ? <ChevronRight className="size-4" /> : <ChevronLeft className="size-4" />}
|
|
77
|
-
{!collapsed && <span>Collapse</span>}
|
|
78
|
-
</button>
|
|
79
|
-
</div>
|
|
80
|
-
</aside>
|
|
81
|
-
);
|
|
82
|
-
}
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import { cn } from '@lumerahq/ui/lib';
|
|
2
|
-
import type { ReactNode } from 'react';
|
|
3
|
-
|
|
4
|
-
interface StatCardProps {
|
|
5
|
-
title: string;
|
|
6
|
-
value: string | number;
|
|
7
|
-
subtitle?: string;
|
|
8
|
-
icon?: ReactNode;
|
|
9
|
-
className?: string;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export function StatCard({ title, value, subtitle, icon, className }: StatCardProps) {
|
|
13
|
-
return (
|
|
14
|
-
<div className={cn('rounded-xl border bg-card p-5', className)}>
|
|
15
|
-
<div className="flex items-start justify-between">
|
|
16
|
-
<div className="space-y-1">
|
|
17
|
-
<p className="text-sm font-medium text-muted-foreground">{title}</p>
|
|
18
|
-
<p className="text-2xl font-semibold tracking-tight">{value}</p>
|
|
19
|
-
{subtitle && <p className="text-sm text-muted-foreground">{subtitle}</p>}
|
|
20
|
-
</div>
|
|
21
|
-
{icon && <div className="rounded-lg bg-muted p-2.5 text-muted-foreground">{icon}</div>}
|
|
22
|
-
</div>
|
|
23
|
-
</div>
|
|
24
|
-
);
|
|
25
|
-
}
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import type { ReactNode } from 'react';
|
|
2
|
-
import { Sidebar } from './Sidebar';
|
|
3
|
-
|
|
4
|
-
export function AppLayout({ children }: { children: ReactNode }) {
|
|
5
|
-
return (
|
|
6
|
-
<div className="flex h-screen bg-background">
|
|
7
|
-
<Sidebar />
|
|
8
|
-
<main className="flex-1 overflow-auto">
|
|
9
|
-
<div className="p-6">{children}</div>
|
|
10
|
-
</main>
|
|
11
|
-
</div>
|
|
12
|
-
);
|
|
13
|
-
}
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import { type PbRecord, pbList } from '@lumerahq/ui/lib';
|
|
2
|
-
|
|
3
|
-
export type ExampleRecord = PbRecord & {
|
|
4
|
-
name: string;
|
|
5
|
-
status: string;
|
|
6
|
-
};
|
|
7
|
-
|
|
8
|
-
export async function getExampleData() {
|
|
9
|
-
// Replace with your actual data fetching logic
|
|
10
|
-
return {
|
|
11
|
-
message: 'Replace this with your data fetching logic',
|
|
12
|
-
timestamp: new Date().toISOString(),
|
|
13
|
-
instructions: [
|
|
14
|
-
'1. Edit src/lib/queries.ts',
|
|
15
|
-
'2. Use pbSql or pbList from @lumerahq/ui/lib',
|
|
16
|
-
'3. Create query functions for your data',
|
|
17
|
-
],
|
|
18
|
-
};
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export async function listExampleRecords(page = 1) {
|
|
22
|
-
return pbList<ExampleRecord>('your_collection', {
|
|
23
|
-
page,
|
|
24
|
-
perPage: 20,
|
|
25
|
-
sort: '-created',
|
|
26
|
-
});
|
|
27
|
-
}
|