@squadbase/vantage-template 0.0.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/README.md +112 -0
- package/base-template/.squadbase/skills/vantage-add-feature/SKILL.md +162 -0
- package/base-template/.squadbase/skills/vantage-app/SKILL.md +120 -0
- package/base-template/.squadbase/skills/vantage-pitfalls/SKILL.md +137 -0
- package/base-template/AGENTS.md +339 -0
- package/base-template/_404.tsx +18 -0
- package/base-template/_error.tsx +14 -0
- package/base-template/_gitignore +5 -0
- package/base-template/_layout.tsx +60 -0
- package/base-template/components/user-card.tsx +132 -0
- package/base-template/index.tsx +59 -0
- package/base-template/package.json +27 -0
- package/base-template/server/api/health.ts +12 -0
- package/base-template/squadbase.yml +5 -0
- package/base-template/styles.css +18 -0
- package/base-template/tsconfig.json +28 -0
- package/chart-presets/berry.css +16 -0
- package/chart-presets/blue.css +16 -0
- package/chart-presets/forest.css +16 -0
- package/chart-presets/mono.css +16 -0
- package/chart-presets/ocean.css +16 -0
- package/chart-presets/sunset.css +16 -0
- package/dist/index.js +1157 -0
- package/package.json +55 -0
- package/ui-templates/CLAUDE.md +200 -0
- package/ui-templates/blank/manifest.json +8 -0
- package/ui-templates/blank/pages/index.tsx +33 -0
- package/ui-templates/blank-ja/manifest.json +8 -0
- package/ui-templates/blank-ja/pages/index.tsx +33 -0
- package/ui-templates/chart-grid/lib/mock-data.ts +139 -0
- package/ui-templates/chart-grid/lib/types.ts +31 -0
- package/ui-templates/chart-grid/manifest.json +10 -0
- package/ui-templates/chart-grid/pages/index.tsx +331 -0
- package/ui-templates/chart-grid/preview-square.png +0 -0
- package/ui-templates/chart-grid/preview-wide.png +0 -0
- package/ui-templates/chart-grid-ja/lib/mock-data.ts +139 -0
- package/ui-templates/chart-grid-ja/lib/types.ts +31 -0
- package/ui-templates/chart-grid-ja/manifest.json +10 -0
- package/ui-templates/chart-grid-ja/pages/index.tsx +331 -0
- package/ui-templates/chart-grid-ja/preview-square.png +0 -0
- package/ui-templates/chart-grid-ja/preview-wide.png +0 -0
- package/ui-templates/funnel/components/chart-helpers.ts +11 -0
- package/ui-templates/funnel/components/stage-table.tsx +63 -0
- package/ui-templates/funnel/lib/mock-data.ts +22 -0
- package/ui-templates/funnel/lib/types.ts +11 -0
- package/ui-templates/funnel/manifest.json +12 -0
- package/ui-templates/funnel/pages/index.tsx +252 -0
- package/ui-templates/funnel/preview-square.png +0 -0
- package/ui-templates/funnel/preview-wide.png +0 -0
- package/ui-templates/funnel-ja/components/chart-helpers.ts +11 -0
- package/ui-templates/funnel-ja/components/stage-table.tsx +63 -0
- package/ui-templates/funnel-ja/lib/mock-data.ts +22 -0
- package/ui-templates/funnel-ja/lib/types.ts +11 -0
- package/ui-templates/funnel-ja/manifest.json +12 -0
- package/ui-templates/funnel-ja/pages/index.tsx +251 -0
- package/ui-templates/funnel-ja/preview-square.png +0 -0
- package/ui-templates/funnel-ja/preview-wide.png +0 -0
- package/ui-templates/kpi-chart-advanced/components/campaign-table.tsx +69 -0
- package/ui-templates/kpi-chart-advanced/lib/mock-data.ts +109 -0
- package/ui-templates/kpi-chart-advanced/lib/types.ts +38 -0
- package/ui-templates/kpi-chart-advanced/manifest.json +11 -0
- package/ui-templates/kpi-chart-advanced/pages/index.tsx +339 -0
- package/ui-templates/kpi-chart-advanced/preview-square.png +0 -0
- package/ui-templates/kpi-chart-advanced/preview-wide.png +0 -0
- package/ui-templates/kpi-chart-advanced-ja/components/campaign-table.tsx +69 -0
- package/ui-templates/kpi-chart-advanced-ja/lib/mock-data.ts +109 -0
- package/ui-templates/kpi-chart-advanced-ja/lib/types.ts +38 -0
- package/ui-templates/kpi-chart-advanced-ja/manifest.json +11 -0
- package/ui-templates/kpi-chart-advanced-ja/pages/index.tsx +333 -0
- package/ui-templates/kpi-chart-advanced-ja/preview-square.png +0 -0
- package/ui-templates/kpi-chart-advanced-ja/preview-wide.png +0 -0
- package/ui-templates/kpi-chart-simple/components/top-items-table.tsx +73 -0
- package/ui-templates/kpi-chart-simple/lib/types.ts +44 -0
- package/ui-templates/kpi-chart-simple/manifest.json +11 -0
- package/ui-templates/kpi-chart-simple/pages/index.tsx +200 -0
- package/ui-templates/kpi-chart-simple/preview-square.png +0 -0
- package/ui-templates/kpi-chart-simple/preview-wide.png +0 -0
- package/ui-templates/kpi-chart-simple/server/kpi-summary.ts +141 -0
- package/ui-templates/kpi-chart-simple-ja/components/top-items-table.tsx +73 -0
- package/ui-templates/kpi-chart-simple-ja/lib/types.ts +44 -0
- package/ui-templates/kpi-chart-simple-ja/manifest.json +11 -0
- package/ui-templates/kpi-chart-simple-ja/pages/index.tsx +200 -0
- package/ui-templates/kpi-chart-simple-ja/preview-square.png +0 -0
- package/ui-templates/kpi-chart-simple-ja/preview-wide.png +0 -0
- package/ui-templates/kpi-chart-simple-ja/server/kpi-summary.ts +141 -0
- package/ui-templates/table-focus/components/detail-table.tsx +88 -0
- package/ui-templates/table-focus/lib/mock-data.ts +69 -0
- package/ui-templates/table-focus/lib/types.ts +24 -0
- package/ui-templates/table-focus/manifest.json +11 -0
- package/ui-templates/table-focus/pages/index.tsx +180 -0
- package/ui-templates/table-focus/preview-square.png +0 -0
- package/ui-templates/table-focus/preview-wide.png +0 -0
- package/ui-templates/table-focus-ja/components/detail-table.tsx +95 -0
- package/ui-templates/table-focus-ja/lib/mock-data.ts +69 -0
- package/ui-templates/table-focus-ja/lib/types.ts +24 -0
- package/ui-templates/table-focus-ja/manifest.json +11 -0
- package/ui-templates/table-focus-ja/pages/index.tsx +180 -0
- package/ui-templates/table-focus-ja/preview-square.png +0 -0
- package/ui-templates/table-focus-ja/preview-wide.png +0 -0
package/README.md
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
# @squadbase/vantage-template
|
|
2
|
+
|
|
3
|
+
CLI tool for initializing and customizing Squadbase Vantage projects.
|
|
4
|
+
|
|
5
|
+
It scaffolds a dashboard app built on [`@squadbase/vantage`](https://vantage-framework-vantage.vercel.app/) — a config-free React framework with file-based routing — and applies ready-made UI-pattern templates on top of it.
|
|
6
|
+
|
|
7
|
+
## Usage
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npx @squadbase/vantage-template <command> [options]
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
### Commands
|
|
14
|
+
|
|
15
|
+
#### `init`
|
|
16
|
+
|
|
17
|
+
Initialize a new Vantage project in the current directory. Copies the base template — `index.tsx`, `_layout.tsx`, `_404.tsx`, `_error.tsx`, `server/api/`, `styles.css`, `tsconfig.json`, `squadbase.yml`, plus the agent guidance in `AGENTS.md` and `.squadbase/skills/` — and installs dependencies.
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npx @squadbase/vantage-template init
|
|
21
|
+
npx @squadbase/vantage-template init --force # Overwrite existing files
|
|
22
|
+
npx @squadbase/vantage-template init --skip-install # Skip npm install
|
|
23
|
+
npx @squadbase/vantage-template init --chart sunset # Apply a chart preset during init
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
After initialization:
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
npm run dev # vantage dev — dev server + API on :5173
|
|
30
|
+
npm run build # vantage build → dist/
|
|
31
|
+
npm run check # vantage check — static diagnostics
|
|
32
|
+
npm run routes # vantage routes — page/API route map
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
There is no `vite.config.ts`, no `main.tsx`, and no route table: adding a file adds a route (`index.tsx` → `/`, `sales/[id].tsx` → `/sales/:id`), and `_layout.tsx` builds the nav from `useRoutes()`.
|
|
36
|
+
|
|
37
|
+
#### `add <template-name>`
|
|
38
|
+
|
|
39
|
+
Apply a UI-pattern template to an existing Vantage project. The entry page lands on `index.tsx`, and template-owned files go under `components/<slug>/` (plus `server/api/` for API-backed templates).
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
npx @squadbase/vantage-template add kpi-chart-simple
|
|
43
|
+
npx @squadbase/vantage-template add funnel --dry-run # Preview without writing
|
|
44
|
+
npx @squadbase/vantage-template add funnel --force # Overwrite existing files
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
`add` replaces `index.tsx`, so it warns before overwriting it, and it lists leftover files from a previously applied template (it never deletes them — you may have edited them).
|
|
48
|
+
|
|
49
|
+
#### `chart <preset-name>`
|
|
50
|
+
|
|
51
|
+
Switch the chart color preset. Rewrites a marked block of `--chart-1`–`--chart-5` inside the project's `styles.css`, leaving any other overrides in that file intact. Re-running replaces the block rather than stacking.
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
npx @squadbase/vantage-template chart ocean
|
|
55
|
+
npx @squadbase/vantage-template chart forest --dry-run
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Requires `@squadbase/vantage` v0.2.1+ (v0.2.2+ recommended), where `EChart` resolves its colors from the theme tokens and follows stylesheet changes.
|
|
59
|
+
|
|
60
|
+
#### `list`
|
|
61
|
+
|
|
62
|
+
List available UI templates and chart presets.
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
npx @squadbase/vantage-template list
|
|
66
|
+
npx @squadbase/vantage-template list --lang ja # Japanese variants (names ending in -ja)
|
|
67
|
+
npx @squadbase/vantage-template list --json # Machine-readable, includes preview image URLs
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## UI templates
|
|
71
|
+
|
|
72
|
+
| Template | Description |
|
|
73
|
+
|---|---|
|
|
74
|
+
| `blank` | Empty `PageShell` with a title and description — the minimal scaffold |
|
|
75
|
+
| `kpi-chart-simple` | 4 KPI cards, one trend line, and a ranked-items table — served by `server/api` and filtered through the URL |
|
|
76
|
+
| `kpi-chart-advanced` | Hero KPI (1 large + 4 small), bar+line comparison, donut breakdown, daily trend, detail table |
|
|
77
|
+
| `chart-grid` | Six-chart grid — area, line, bar, scatter, radar, heatmap — with category filter chips |
|
|
78
|
+
| `funnel` | Funnel chart with per-stage drop-off sidebar, pipeline KPIs, and a stage-performance ranking |
|
|
79
|
+
| `table-focus` | Workbench layout — search, status chips, sortable detail table, trend + segment sidebar |
|
|
80
|
+
|
|
81
|
+
Every template has a Japanese variant under `<name>-ja`. The EN and JA variants write to the same destinations, so apply only one of the pair.
|
|
82
|
+
|
|
83
|
+
## AI customization (`add --prompt`)
|
|
84
|
+
|
|
85
|
+
Passing `--prompt` relabels the applied template's display copy toward your domain, right after the files are copied. Structure, data, and logic are left alone — only string literals in pages and components are rewritten, and the data layer (mock data, types, API handlers) is excluded from the pass by the manifest.
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
npx @squadbase/vantage-template add kpi-chart-simple \
|
|
89
|
+
--prompt "SaaS MRR / ARR / churn dashboard" \
|
|
90
|
+
--provider openai --apiKey $OPENAI_API_KEY
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
| Flag | Description |
|
|
94
|
+
|---|---|
|
|
95
|
+
| `--prompt <text>` | Customization intent. Without it, no AI call is made. |
|
|
96
|
+
| `--provider <name>` | `openai`, `anthropic`, `google`, `mistral`, `xai`, `groq`, … |
|
|
97
|
+
| `--model <id>` | Model id. Each provider has a default. |
|
|
98
|
+
| `--apiKey <key>` | Falls back to the provider's env var (`OPENAI_API_KEY`, `ANTHROPIC_API_KEY`, …). |
|
|
99
|
+
| `--env-file <path>` | Load a `.env` before the call; existing `process.env` wins. Needs Node.js 20.12+. |
|
|
100
|
+
| `--base-url <url>` | OpenAI-compatible endpoint. |
|
|
101
|
+
| `--dry-run` | Print the edits as a unified diff without writing. |
|
|
102
|
+
| `--json` | Emit the result (`edits`, `unchanged`, `skipped`, `failedVerification`, `aiError`, `notes`) as JSON. |
|
|
103
|
+
|
|
104
|
+
Each edit is verified against the current file content before it is applied; a file whose edits don't match cleanly is left untouched and reported. `ai` and `@ai-sdk/*` are optional dependencies, imported only when `--prompt` is used — if they are missing, the CLI tells you what to install and exits.
|
|
105
|
+
|
|
106
|
+
## Requirements
|
|
107
|
+
|
|
108
|
+
Node.js 18 or later. `--env-file` additionally requires 20.12+.
|
|
109
|
+
|
|
110
|
+
## License
|
|
111
|
+
|
|
112
|
+
See the [squadbase-templates](https://github.com/squadbase/squadbase-templates) repository.
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: vantage-add-feature
|
|
3
|
+
description: 既存の Vantage(@squadbase/vantage)アプリにページ・API エンドポイント・UI コンポーネント・ブロックを追加する定型ワークフロー。vantage add コマンドで雛形を出し、規約(動的パラメータの綴り・server 境界・大文字メソッド)を守り、vantage check / routes で確認するまでの一連。Vantage アプリに機能を1つ足すときに使う。
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Vantage への機能追加(page / api / ui / block)
|
|
7
|
+
|
|
8
|
+
既存の Vantage アプリに要素を1つ追加するときの定型手順。アプリの新規作成や fullstack 化の
|
|
9
|
+
全体像は `vantage-app` スキル、実装で踏みやすい罠は `vantage-pitfalls` スキルを参照。
|
|
10
|
+
|
|
11
|
+
CLI は雛形を出すだけで、規約の遵守はこちらの責任。**追加したら必ず `vantage check` と
|
|
12
|
+
`vantage routes` で確認する**。
|
|
13
|
+
|
|
14
|
+
## `vantage add` の4種別
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
vantage add page <name> # ルートページ(.tsx)
|
|
18
|
+
vantage add api <name> # server/api/**.ts の API モジュール
|
|
19
|
+
vantage add ui <name> # registry から UI ソースを components/ui/ にコピー
|
|
20
|
+
vantage add block <name> # registry からブロックを components/blocks/ にコピー
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
`--force` で既存ファイルを上書き。`add` の雛形は import に `@squadbase/vantage` を焼き込む。
|
|
24
|
+
|
|
25
|
+
## ページを追加する
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
vantage add page monthly-analysis # → ./monthly-analysis.tsx → ルート /monthly-analysis
|
|
29
|
+
vantage add page sales/index # → ./sales/index.tsx → ルート /sales
|
|
30
|
+
vantage add page "sales/[customerId]" # → 動的ルート /sales/:customerId
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
出力される雛形は `definePage` + デフォルトエクスポートのコンポーネント。ファイル名の規約:
|
|
34
|
+
|
|
35
|
+
| ファイル | ルート | 用途 |
|
|
36
|
+
| --- | --- | --- |
|
|
37
|
+
| `foo.tsx` | `/foo` | 通常ページ |
|
|
38
|
+
| `foo/index.tsx` | `/foo` | ディレクトリのインデックス |
|
|
39
|
+
| `foo/[id].tsx` | `/foo/:id` | 動的パラメータ |
|
|
40
|
+
| `_layout.tsx` | — | 配下を包むネストレイアウト(`Outlet` を描く) |
|
|
41
|
+
| `_404.tsx` / `_error.tsx` | — | Not Found / エラーページ |
|
|
42
|
+
|
|
43
|
+
チェックリスト:
|
|
44
|
+
|
|
45
|
+
1. **デフォルトエクスポートは必須**(無いと `MISSING_DEFAULT_EXPORT` エラー)。
|
|
46
|
+
2. `definePage({ title })` の値は**リテラル**で書く(ビルド時に静的抽出されるため)。ナビに出す
|
|
47
|
+
短い名前が要るなら `navLabel` も足す(`useRoutes()` が読む。既定は `title`)。
|
|
48
|
+
`_layout.tsx` のナビを `useRoutes()` で組んでいれば、ページを足すだけでリンクも増える。
|
|
49
|
+
3. 動的ページなら **3つの綴りを同期**:
|
|
50
|
+
- ファイル `foo/[id].tsx`
|
|
51
|
+
- 表示ルート `/foo/:id`
|
|
52
|
+
- リンク `to="/foo/$id"` + `params={{ id }}`、取り出しは `const { id } = useParams()`
|
|
53
|
+
4. `components/`・`hooks/`・`lib/`・`server/`・`public/` はページにならない(ルート走査対象外)。
|
|
54
|
+
|
|
55
|
+
## API を追加する(fullstack)
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
vantage add api monthly-analysis # → server/api/monthly-analysis.ts → GET /api/monthly-analysis
|
|
59
|
+
vantage add api "customers/[id]" # → server/api/customers/[id].ts → GET /api/customers/:id
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
**`server/` を作った時点でアプリは fullstack モードに切り替わる**(SPA → fullstack)。
|
|
63
|
+
雛形は `GET` ハンドラ:
|
|
64
|
+
|
|
65
|
+
```ts
|
|
66
|
+
import type { ApiContext } from "@squadbase/vantage/server"
|
|
67
|
+
|
|
68
|
+
export async function GET({ request, params }: ApiContext) {
|
|
69
|
+
return Response.json({ ok: true, params })
|
|
70
|
+
}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
チェックリスト:
|
|
74
|
+
|
|
75
|
+
1. **エクスポート名は大文字の HTTP メソッド**のみ(`GET`/`POST`/`PUT`/`PATCH`/`DELETE`/`OPTIONS`)。
|
|
76
|
+
他の名前は `INVALID_API_EXPORT` エラー。
|
|
77
|
+
2. `ApiContext` は Web 標準ベース: `request`(`Request`)・`params`・`env`・`waitUntil`。
|
|
78
|
+
3. 動的パラメータは `params.id`。`noUncheckedIndexedAccess` が効くので `params.id!` などで narrowing。
|
|
79
|
+
4. **クライアントに見せるエラーは `HttpError(status, msg)` を throw**。それ以外の throw は
|
|
80
|
+
500 に丸められログに出る。
|
|
81
|
+
```ts
|
|
82
|
+
import { HttpError, type ApiContext } from "@squadbase/vantage/server"
|
|
83
|
+
export async function GET({ params }: ApiContext) {
|
|
84
|
+
const row = findCustomer(params.id!)
|
|
85
|
+
if (!row) throw new HttpError(404, `Customer ${params.id} not found`)
|
|
86
|
+
return Response.json(row)
|
|
87
|
+
}
|
|
88
|
+
```
|
|
89
|
+
5. **シークレットは `ctx.env` からのみ**読む。クライアントには決して渡さない。
|
|
90
|
+
6. サーバー専用の共有コードは `server/utils.ts` 等に置く。**クライアントから `server/` を
|
|
91
|
+
import しない**(`CLIENT_IMPORTS_SERVER` エラー。共有は `lib/` に置く)。
|
|
92
|
+
|
|
93
|
+
クライアント側から叩くときは `useApiQuery`(ベース URL 解決・JSON パース・非 2xx の `ApiError`
|
|
94
|
+
化・クエリキー `["api", url]` が入っている):
|
|
95
|
+
|
|
96
|
+
```tsx
|
|
97
|
+
import { useApiQuery } from "@squadbase/vantage/query"
|
|
98
|
+
|
|
99
|
+
const q = useApiQuery<Customer>(`/api/customers/${id}`)
|
|
100
|
+
if (q.isError) return <ErrorState message={q.error.message} /> // HttpError のメッセージ
|
|
101
|
+
|
|
102
|
+
// クエリ文字列は search で(undefined の項目は落ちる)
|
|
103
|
+
const rows = useApiQuery<Row[]>("/api/customers", { search: { segment } })
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
書き込みは `useApiMutation(path, { method: "POST" })` — 変数がそのまま JSON ボディになる。
|
|
107
|
+
|
|
108
|
+
## UI コンポーネント / ブロックを追加する
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
vantage add ui data-table # → components/ui/data-table.tsx(編集可能なソースをコピー)
|
|
112
|
+
vantage add block sales-overview # → components/blocks/sales-overview.tsx
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
- **多くの UI コンポーネントは「import 専用」で、`add ui` でコピーできる名前は限られる。**
|
|
116
|
+
現在 registry からコピー可能なのは `data-table`(ui)と `sales-overview`(block)のみ。
|
|
117
|
+
未知の名前を渡すと利用可能な名前一覧を出して失敗する。
|
|
118
|
+
- コピーが要らない大半のプリミティブは `@squadbase/vantage/ui` から、複合パーツは
|
|
119
|
+
`@squadbase/vantage/components` から**そのまま import**するのが基本(コピー不要)。
|
|
120
|
+
```tsx
|
|
121
|
+
import { Button, Loading, ErrorState } from "@squadbase/vantage/ui"
|
|
122
|
+
import { DashboardCardPreset, DataTablePreset, EChart } from "@squadbase/vantage/components"
|
|
123
|
+
```
|
|
124
|
+
- **props は `vantage docs <name>` で確認してから書く**(`vantage docs button`・
|
|
125
|
+
`vantage docs parts/data-table`。名前が分からなければ `vantage search <やりたいこと>` で
|
|
126
|
+
探してから `vantage docs` に渡す)。
|
|
127
|
+
|
|
128
|
+
## 追加後に必ず確認する
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
vantage routes # 追加したページ/API が意図した URL に出ているか
|
|
132
|
+
vantage check # 規約違反が無いか(exit 1 ならエラーあり)
|
|
133
|
+
vantage dev # 実際に動くか(console は開発ターミナルに [browser:…] で転送)
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
`vantage routes` は `--pages` / `--apis` で片側だけに絞れる。`--detail` を足すと各ルートの静的な
|
|
137
|
+
仕様(ページ: パスパラメータ + `definePage` のメタ / API: メソッド・`searchParams` のキー・
|
|
138
|
+
リクエストボディ・レスポンスの status と形・`HttpError`)まで出るので、**既存 API の呼び方を
|
|
139
|
+
確かめる**ときはこれが速い(ソースを読むだけの推定で、実行はしない)。
|
|
140
|
+
|
|
141
|
+
```bash
|
|
142
|
+
vantage routes --apis --detail # 既存 API のメソッド/入出力を確認
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
エージェントで自動処理するなら機械可読出力を使う:
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
vantage routes --json # { pages, layouts, notFound, error, apis, hasServer }
|
|
149
|
+
vantage routes --apis --detail --json # { apis: [{ route, file, params, spec }], hasServer }
|
|
150
|
+
vantage check --json # { ok, errorCount, warningCount, diagnostics[] }
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
`vantage check` が出しうる診断コード(参考):
|
|
154
|
+
|
|
155
|
+
| code | 意味 |
|
|
156
|
+
| --- | --- |
|
|
157
|
+
| `FORBIDDEN_FILE` | `vite.config.*` などの禁止設定ファイルがある |
|
|
158
|
+
| `ROUTE_CONFLICT` | 同じルートを指すファイルが複数ある |
|
|
159
|
+
| `MISSING_DEFAULT_EXPORT` | ページ/レイアウトにデフォルトエクスポートが無い |
|
|
160
|
+
| `INVALID_API_EXPORT` | API モジュールが大文字メソッド以外をエクスポート |
|
|
161
|
+
| `CLIENT_IMPORTS_SERVER` | クライアントが `server/` を import している |
|
|
162
|
+
| `PUBLIC_ENV_MISUSE`(warn) | `PUBLIC_*` 以外の env をクライアントで読んでいる |
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: vantage-app
|
|
3
|
+
description: Vantage(@squadbase/vantage)ダッシュボードアプリを一から作る / 既存アプリの構成を広げるときの進め方。骨組みの用意、ルートの決め方、データ取得の選択(外部 API か自前の server/api か)、どこで手を止めて検証するかの順序を扱う。規約そのものはアプリルートの AGENTS.md が正本。
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Vantage アプリの作り方 — 進め方と検証の順序
|
|
7
|
+
|
|
8
|
+
規約・不変条件・import サブパスの一覧・各 API の書式は、アプリルートの **`AGENTS.md`** が正本。
|
|
9
|
+
**先にそれを読む**(無ければ `vantage add agents` で置ける)。このスキルは「どの順で作り、どこで
|
|
10
|
+
手を止めて検証するか」だけを扱い、AGENTS.md の内容は繰り返さない。
|
|
11
|
+
|
|
12
|
+
- ページ / API / UI を 1 つ足すだけなら → `vantage-add-feature`
|
|
13
|
+
- 実装したのに静かに壊れたら → `vantage-pitfalls`
|
|
14
|
+
|
|
15
|
+
## 全体の流れ
|
|
16
|
+
|
|
17
|
+
1. **骨組み** — `pnpm dev` が上がるところまで
|
|
18
|
+
2. **ルートを決める** — ページファイルを置き、`vantage routes` で URL を確認
|
|
19
|
+
3. **データを繋ぐ** — 外部 API か、自前の `server/api` か
|
|
20
|
+
4. **仕上げ** — `check` → `build` → `preview`
|
|
21
|
+
|
|
22
|
+
**各段階の終わりに `vantage check` を通す。** 静的診断(禁止ファイル・ルート衝突・境界違反・
|
|
23
|
+
API export・env 誤用)はユーザーコードを実行しないので速く、エラーがあれば exit 1 になる。
|
|
24
|
+
まとめて最後に回すと、原因の切り分けが難しくなる。
|
|
25
|
+
|
|
26
|
+
## Step 1 — 骨組み
|
|
27
|
+
|
|
28
|
+
新規なら `package.json` と `index.tsx` の 2 ファイルだけ。設定ファイルは**作らない**
|
|
29
|
+
(`vite.config.*` 等は `vantage check` がエラーにする)。
|
|
30
|
+
|
|
31
|
+
```json
|
|
32
|
+
{
|
|
33
|
+
"name": "my-dashboard",
|
|
34
|
+
"private": true,
|
|
35
|
+
"type": "module",
|
|
36
|
+
"scripts": {
|
|
37
|
+
"dev": "vantage dev",
|
|
38
|
+
"build": "vantage build",
|
|
39
|
+
"preview": "vantage preview",
|
|
40
|
+
"check": "vantage check",
|
|
41
|
+
"routes": "vantage routes"
|
|
42
|
+
},
|
|
43
|
+
"dependencies": {
|
|
44
|
+
"@squadbase/vantage": "^0.2.0",
|
|
45
|
+
"react": "^19.2.7",
|
|
46
|
+
"react-dom": "^19.2.7"
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
`index.tsx` はデフォルトエクスポートの React コンポーネント 1 つ(これが `/` になる)。あとは
|
|
52
|
+
`pnpm install && pnpm dev` で http://localhost:5173 が上がる。
|
|
53
|
+
|
|
54
|
+
**既存アプリに合流したときは、作る前に現状を読む:**
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
vantage routes # 既にあるページと API の URL マップ
|
|
58
|
+
vantage check # いま壊れていないか(これから出すエラーと切り分ける)
|
|
59
|
+
ls server/ # あれば fullstack モード。無ければ SPA
|
|
60
|
+
vantage add skill # 配置済みの skill(このファイルの仲間)と、その場所
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Step 2 — ルートを決める
|
|
64
|
+
|
|
65
|
+
ファイル名がそのままルートになる(対応表は AGENTS.md「ルーティング規約」)。**ルーターを設定
|
|
66
|
+
する場所は無い**ので、決めるのは「どんなファイル名で置くか」だけ。
|
|
67
|
+
|
|
68
|
+
- ページを置いたら `vantage routes` で URL を確かめる。意図と違うなら**ファイル名が違う**。
|
|
69
|
+
- ナビゲーションはリンク配列を手で持たず、`useRoutes()` から組む。この形にしておくと以後は
|
|
70
|
+
ページファイルを足すだけでナビが増える(AGENTS.md「ナビはルート一覧から組む」)。
|
|
71
|
+
- 共通の枠(ヘッダ・サイドバー)は `_layout.tsx`、Not Found と例外は `_404.tsx` / `_error.tsx`。
|
|
72
|
+
- 一覧 → 詳細を作るなら、**動的パラメータの「3 つの綴り」を先に決めてから**両方のファイルを
|
|
73
|
+
書く。後から変えると静かにマッチしなくなる。
|
|
74
|
+
|
|
75
|
+
## Step 3 — データを繋ぐ
|
|
76
|
+
|
|
77
|
+
**どちらの経路かを先に決める。** ここを間違えると後で全部書き直しになる:
|
|
78
|
+
|
|
79
|
+
| データ元 | 使うもの | `server/` |
|
|
80
|
+
| --- | --- | --- |
|
|
81
|
+
| ブラウザから直接叩ける公開 API | `useQuery` + `fetch` | 不要 |
|
|
82
|
+
| DB / シークレットが要る / CORS で叩けない | `server/api/*.ts` + `useApiQuery` | 必要 |
|
|
83
|
+
|
|
84
|
+
**API キーが要る時点で後者しかない** — クライアントに届く env は `PUBLIC_*` だけで、
|
|
85
|
+
シークレットは `ApiContext.env`(= `server/` の中)にしか届かない。
|
|
86
|
+
|
|
87
|
+
後者の手順:
|
|
88
|
+
|
|
89
|
+
1. `server/api/<name>.ts` に `GET` を書く。**このディレクトリを作った時点で fullstack モード**
|
|
90
|
+
になる(設定変更は不要)。
|
|
91
|
+
2. `vantage routes` に `/api/<name>` が出ることを確認する。
|
|
92
|
+
3. dev サーバーか `curl` で叩き、**返す JSON の形を確定させてから** UI を書く。エラー応答は
|
|
93
|
+
`HttpError(status, msg)` を throw して作る。
|
|
94
|
+
4. クライアントから `useApiQuery<T>("/api/<name>")` で受ける。`isPending` / `isError` の分岐を
|
|
95
|
+
最初から書く(`Loading` / `ErrorState` が `ui/` にある)。
|
|
96
|
+
|
|
97
|
+
ダッシュボードの**絞り込みは `useState` ではなく `useSearchParam` / `useSearchState`** に置く。
|
|
98
|
+
リロードで消えず、URL をそのまま共有できる。これも後から差し替えると全ページに波及するので、
|
|
99
|
+
最初のフィルタを作る時点で決める。
|
|
100
|
+
|
|
101
|
+
## Step 4 — 仕上げ
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
pnpm check # エラーが残っていないか(exit 1 なら残っている)
|
|
105
|
+
pnpm routes # 公開される URL の最終確認
|
|
106
|
+
pnpm build # dist/ に client(+ server)+ vantage-manifest.json
|
|
107
|
+
pnpm preview # 本番ビルドをローカルで動かす
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
`vantage-manifest.json` の `mode` は `server/` の有無から自動で決まる(手で書かない)。
|
|
111
|
+
`preview` まで通れば、そのまま同じ成果物がデプロイされる。
|
|
112
|
+
|
|
113
|
+
## 詰まったら
|
|
114
|
+
|
|
115
|
+
- ブラウザの `console.*` とランタイムエラーは、**開発ターミナルに `[browser:…]` として転送
|
|
116
|
+
される**。ブラウザの devtools を開かなくても読める。
|
|
117
|
+
- `vantage check --json` / `vantage routes --json` は機械可読出力。
|
|
118
|
+
- props や規約を確かめたいときは `vantage docs <name>`、名前が思い出せないときは
|
|
119
|
+
`vantage search <やりたいこと>`。**推測で props を書かない。**
|
|
120
|
+
- Base UI(≠ Radix)の癖など、静かに壊れる系は `vantage-pitfalls` にまとまっている。
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: vantage-pitfalls
|
|
3
|
+
description: Vantage(@squadbase/vantage)アプリを書くときに静かに壊れる落とし穴のリファレンス。Base UI は Radix ではない(asChild 無し・render プロップ)、Select の value/label、クライアントから server/ を import しない、EChart の配色と高さ、動的パラメータの3綴り同期、PUBLIC_ env、HttpError など。Vantage の UI コンポーネントやルーティングが思った通りに動かないとき、ビルドやスタイルが静かに欠落するときに参照する。
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Vantage アプリの落とし穴リファレンス
|
|
7
|
+
|
|
8
|
+
Vantage アプリで「型は通るのに実行時/ビルド時に静かに壊れる」パターン集。症状が出たとき、
|
|
9
|
+
または該当機能を書く前に参照する。アプリ作成の流れは `vantage-app`、機能追加は
|
|
10
|
+
`vantage-add-feature` スキル。個々のコンポーネントの props と使用例は
|
|
11
|
+
`vantage docs <name>`(例: `vantage docs ui/select`)で引ける。
|
|
12
|
+
|
|
13
|
+
## UI:Base UI は Radix ではない
|
|
14
|
+
|
|
15
|
+
Vantage の UI キットは shadcn/ui の **Base UI バリアント**。Radix の癖で書くと動かない。
|
|
16
|
+
|
|
17
|
+
- **`asChild` は無い。** 代わりに `render` プロップを使う。
|
|
18
|
+
- **`Select` の `onValueChange` は `string | null`** を渡す(null が来うる)。
|
|
19
|
+
- **`Checkbox` の `checked` は `boolean` のみ。** 中間状態は `indeterminate` プロップ。
|
|
20
|
+
- **`ToggleGroup` の `value` は配列。**
|
|
21
|
+
|
|
22
|
+
### `SelectValue` の value/label は Vantage 側が吸収済み(残る 1 ケースだけ注意)
|
|
23
|
+
|
|
24
|
+
Base UI の `SelectValue` は選択中の **value をそのまま描き**、`SelectItem` の children を見ない
|
|
25
|
+
部品。Vantage の `Select` は children から `value` → ラベルを集めて渡すので、普通に書けば
|
|
26
|
+
トリガーに「関東」と出る。**手当てが要るのは 1 ケースだけ** — `SelectItem` を別のコンポーネント
|
|
27
|
+
が返している場合は集められないので、`items` を明示する:
|
|
28
|
+
|
|
29
|
+
```tsx
|
|
30
|
+
// SelectItem がこの場に無い(<RegionItems /> の中で作られる)ときだけ必要
|
|
31
|
+
<Select items={{ kanto: "関東", kansai: "関西" }} …>
|
|
32
|
+
<SelectContent><RegionItems /></SelectContent>
|
|
33
|
+
</Select>
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
トリガーに `kanto` や `/sales` と生の値が出たら、まずこれを疑う。
|
|
37
|
+
|
|
38
|
+
## チャート:`EChart` の配色はトークン追従。上書きは `option.color`
|
|
39
|
+
|
|
40
|
+
系列色は `--chart-1..5`、軸・凡例・ツールチップは文字色/境界色のトークンから組まれる
|
|
41
|
+
(init 時にトークンの実値を解決している)。ライト / ダークの切り替えにも、`styles.css` の
|
|
42
|
+
書き換えやテーマプリセットの適用にも追従する。
|
|
43
|
+
|
|
44
|
+
- **`theme` プロップを渡すと追従は完全に止まる。** 系列の色だけ変えたいなら `option.color` を
|
|
45
|
+
使う ― option はテーマより優先されるので、軸まわりの追従は残る。
|
|
46
|
+
- キャンバス系なので、**コンテナに高さを与えないと何も見えない**(既定は `h-[400px]`。
|
|
47
|
+
`h-full` を使うなら親に確定した高さが要る)。
|
|
48
|
+
- `option` は `EChartsOption` を annotate するか `satisfies` を付ける。付けないと
|
|
49
|
+
`type: "category"` が `string` に広がってビルドが落ちる。
|
|
50
|
+
|
|
51
|
+
## 境界:クライアントから `server/` を import しない
|
|
52
|
+
|
|
53
|
+
- クライアント側のどのファイルも `server/` 配下を import してはいけない(`CLIENT_IMPORTS_SERVER`
|
|
54
|
+
エラー。静的にもビルド時にも弾かれる)。**共有したいコードは `lib/` に置く。**
|
|
55
|
+
- シークレット等の env は `ApiContext.env`(server/ 内)にだけ届く。クライアントには渡らない。
|
|
56
|
+
|
|
57
|
+
## env:クライアントに届くのは `PUBLIC_` 接頭辞だけ
|
|
58
|
+
|
|
59
|
+
- クライアントで読めるのは `import.meta.env.PUBLIC_*` と `MODE`/`DEV`/`PROD`/`SSR`/`BASE_URL` のみ。
|
|
60
|
+
それ以外を `import.meta.env` で読むと `PUBLIC_ENV_MISUSE` 警告。
|
|
61
|
+
- `VITE_` 系の公開 API は無い。サーバーのシークレットは `ctx.env` で読む。
|
|
62
|
+
|
|
63
|
+
## ルーティング:動的パラメータは「3つの綴り」を同期させる
|
|
64
|
+
|
|
65
|
+
ズレると静かにマッチしなくなる:
|
|
66
|
+
|
|
67
|
+
- ファイル `sales/[customerId].tsx`
|
|
68
|
+
- 表示ルート `/sales/:customerId`
|
|
69
|
+
- リンク `to="/sales/$customerId"` + `params={{ customerId }}` / 取り出し `useParams().customerId`
|
|
70
|
+
|
|
71
|
+
## ページ:`definePage` の値はリテラルで書く
|
|
72
|
+
|
|
73
|
+
title/description/navLabel は**ビルド時に静的抽出**される。変数・関数呼び出し・テンプレート補間を
|
|
74
|
+
使うと抽出できない。`export const page = definePage({ title: "…" })` の右辺はリテラルにする。
|
|
75
|
+
なお `page` エクスポートはランタイムでは読まれない(抽出専用)。
|
|
76
|
+
|
|
77
|
+
## ナビ:`useRoutes()` は動的ルートも返す
|
|
78
|
+
|
|
79
|
+
`/sales/:customerId` のような動的ルートは URL が 1 つに定まらない。ナビに出すなら
|
|
80
|
+
`useRoutes().filter((r) => !r.dynamic)` で外す。リンク先は `r.path`(`:id` 形式)ではなく
|
|
81
|
+
**`r.to`**(`$id` 形式)を `Link` に渡す。表示名は `r.label`(`navLabel` → `title` → `path` の順)。
|
|
82
|
+
|
|
83
|
+
`useCurrentRoute()` は 404 のとき `undefined` を返す。動的ルートを開いているときに親を
|
|
84
|
+
アクティブにしたいなら `current?.path.startsWith("/sales")` のように前方一致で判定する。
|
|
85
|
+
|
|
86
|
+
## URL 状態:`useSearchParam` の値は常に string、既定値はURLから消える
|
|
87
|
+
|
|
88
|
+
- TanStack は `?year=2024` を数値としてパースするが、`useSearchParam` は必ず `string` に戻す
|
|
89
|
+
(数値が欲しければ自分で `Number(...)`)。配列やオブジェクトは `useSearchState` を使う。
|
|
90
|
+
- **デフォルト値または `null` を書き込むとキーが URL から消える**。「明示的に既定値を選んだ」
|
|
91
|
+
状態を URL に残すことはできない。
|
|
92
|
+
- 履歴は既定で `replace`。戻るボタンで1手ずつ戻したいときだけ `{ replace: false }`。
|
|
93
|
+
|
|
94
|
+
## API 呼び出し:`useApiQuery` のエラーは `ApiError`
|
|
95
|
+
|
|
96
|
+
`(q.error as Error)` のキャストは要らない。`q.error` は `ApiError | null` で、`message` には
|
|
97
|
+
サーバーが `HttpError` で明示したメッセージが入る(それ以外は汎用のステータス文言)。
|
|
98
|
+
`status`・`body`・`requestId` も持つ。クエリキーは `["api", url]` なので、
|
|
99
|
+
`invalidateQueries({ queryKey: ["api"] })` で API 由来のキャッシュを一括で捨てられる。
|
|
100
|
+
|
|
101
|
+
## API:見せたいエラーは `HttpError` を throw する
|
|
102
|
+
|
|
103
|
+
```ts
|
|
104
|
+
import { HttpError } from "@squadbase/vantage/server"
|
|
105
|
+
throw new HttpError(404, "Not found") // → クライアントに 404 + message
|
|
106
|
+
throw new Error("boom") // → ログに出て汎用 500 に丸められる
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
`instanceof HttpError` は dev の module realm 跨ぎで壊れることがある。ライブラリ側の判定は
|
|
110
|
+
`isHttpError` を使う設計。アプリ側は素直に `throw new HttpError(...)` すればよい。
|
|
111
|
+
|
|
112
|
+
## 設定ファイル:作った時点で `vantage check` がエラーにする
|
|
113
|
+
|
|
114
|
+
以下はすべて禁止(`FORBIDDEN_FILE`)。Vantage が所有しているので置かない:
|
|
115
|
+
|
|
116
|
+
- `vite.config.*` — Vite 設定は Vantage が所有
|
|
117
|
+
- `tailwind.config.*` — テーマは `styles.css` のトークンで調整
|
|
118
|
+
- `postcss.config.*` — Vantage が管理
|
|
119
|
+
- `components.json` — shadcn CLI は使わない。UI は `@squadbase/vantage/ui` から import する
|
|
120
|
+
- `vantage.config.*` — v1 に設定ファイルは無い(規約で表現)
|
|
121
|
+
|
|
122
|
+
## Markdown:`MarkdownRenderer` は `@squadbase/vantage/markdown` から
|
|
123
|
+
|
|
124
|
+
Markdown 描画は専用サブパスに隔離されている(Shiki の全言語文法を引き込むため)。
|
|
125
|
+
`components/` から import しない。使うアプリだけが `@squadbase/vantage/markdown` から取り込む。
|
|
126
|
+
|
|
127
|
+
## テーブル:`ColumnDef<Row>[]` を明示的に注釈する
|
|
128
|
+
|
|
129
|
+
```tsx
|
|
130
|
+
import { type ColumnDef } from "@squadbase/vantage/components"
|
|
131
|
+
const columns: ColumnDef<Row>[] = [ … ] // 注釈しないと accessorKey が string に広がり弾かれる
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
## 生成物:`.vantage/` と `dist/` は触らない
|
|
135
|
+
|
|
136
|
+
どちらも gitignore 済みの生成物。編集しても次のコマンドで上書きされる。`.vantage/` は任意の CLI
|
|
137
|
+
コマンド、または `vantage upgrade` で再生成される。
|