@squadbase/vantage-template 0.1.1 → 0.2.0-dev1.0c136ab

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. package/README.md +19 -9
  2. package/base-template/.squadbase/skills/vantage-add-feature/SKILL.md +14 -7
  3. package/base-template/.squadbase/skills/vantage-app/SKILL.md +4 -0
  4. package/base-template/.squadbase/skills/vantage-design/SKILL.md +62 -52
  5. package/base-template/.squadbase/skills/vantage-design/references/extract.md +26 -27
  6. package/base-template/.squadbase/skills/vantage-design/references/format.md +12 -172
  7. package/base-template/.squadbase/skills/vantage-design/references/presets.md +50 -110
  8. package/base-template/.squadbase/skills/vantage-pitfalls/SKILL.md +50 -1
  9. package/base-template/.squadbase/skills/vantage-slide/SKILL.md +299 -0
  10. package/base-template/.squadbase/skills/vantage-slide/references/morph.md +102 -0
  11. package/base-template/.squadbase/skills/vantage-slide/references/steps.md +57 -0
  12. package/base-template/.squadbase/skills/vantage-slide/references/tokens.md +87 -0
  13. package/base-template/.squadbase/skills/vantage-slide/references/transitions.md +196 -0
  14. package/base-template/AGENTS.md +225 -18
  15. package/base-template/agents/api.md +36 -0
  16. package/base-template/agents/csv.md +2 -1
  17. package/base-template/agents/doc.md +52 -3
  18. package/base-template/agents/page.md +92 -10
  19. package/base-template/agents/slide.md +90 -4
  20. package/base-template/agents/xlsx.md +2 -1
  21. package/base-template/bun.lock +917 -0
  22. package/base-template/bunfig.toml +5 -0
  23. package/base-template/package.json +6 -6
  24. package/base-template/squadbase.yml +1 -0
  25. package/base-template/src/_layout.tsx +16 -7
  26. package/base-template/src/components/build-recovery.tsx +37 -1
  27. package/base-template/src/components/user-card.tsx +11 -11
  28. package/base-template/src/index.tsx +8 -3
  29. package/base-template/src/styles.css +108 -12
  30. package/dist/index.js +971 -6
  31. package/package.json +20 -17
  32. package/ui-templates/CLAUDE.md +14 -11
  33. package/ui-templates/chart-grid/lib/mock-data.ts +0 -3
  34. package/ui-templates/chart-grid/lib/types.ts +6 -6
  35. package/ui-templates/chart-grid/pages/index.tsx +11 -161
  36. package/ui-templates/chart-grid-ja/lib/mock-data.ts +0 -3
  37. package/ui-templates/chart-grid-ja/lib/types.ts +6 -6
  38. package/ui-templates/chart-grid-ja/pages/index.tsx +11 -161
  39. package/ui-templates/funnel/lib/types.ts +2 -2
  40. package/ui-templates/funnel/pages/index.tsx +10 -88
  41. package/ui-templates/funnel-ja/lib/types.ts +2 -2
  42. package/ui-templates/funnel-ja/pages/index.tsx +10 -87
  43. package/ui-templates/kpi-chart-advanced/lib/mock-data.ts +6 -6
  44. package/ui-templates/kpi-chart-advanced/lib/types.ts +6 -7
  45. package/ui-templates/kpi-chart-advanced/pages/index.tsx +6 -117
  46. package/ui-templates/kpi-chart-advanced-ja/lib/mock-data.ts +6 -6
  47. package/ui-templates/kpi-chart-advanced-ja/lib/types.ts +6 -7
  48. package/ui-templates/kpi-chart-advanced-ja/pages/index.tsx +6 -117
  49. package/ui-templates/kpi-chart-simple/lib/types.ts +4 -4
  50. package/ui-templates/kpi-chart-simple/pages/index.tsx +51 -89
  51. package/ui-templates/kpi-chart-simple-ja/lib/types.ts +4 -4
  52. package/ui-templates/kpi-chart-simple-ja/pages/index.tsx +51 -87
  53. package/ui-templates/table-focus/lib/types.ts +3 -3
  54. package/ui-templates/table-focus/pages/index.tsx +3 -45
  55. package/ui-templates/table-focus-ja/lib/types.ts +3 -3
  56. package/ui-templates/table-focus-ja/pages/index.tsx +3 -45
  57. package/base-template/DESIGN.md +0 -108
  58. package/base-template/package-lock.json +0 -5841
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@squadbase/vantage-template",
3
- "version": "0.1.1",
3
+ "version": "0.2.0-dev1.0c136ab",
4
4
  "description": "CLI tool for initializing and customizing Squadbase Vantage projects.",
5
5
  "keywords": [
6
6
  "squadbase",
@@ -36,29 +36,32 @@
36
36
  "access": "public",
37
37
  "registry": "https://registry.npmjs.org"
38
38
  },
39
- "scripts": {
40
- "sync-base": "rsync -a --delete --exclude='node_modules' --exclude='dist' --exclude='.vantage' --exclude='*.tsbuildinfo' --exclude='*.log' template/ base-template/ && mv base-template/.gitignore base-template/_gitignore",
41
- "build": "npm run sync-base && tsup",
42
- "type-check": "tsc",
43
- "agents": "node scripts/build-agents.cjs",
44
- "agents:check": "node scripts/build-agents.cjs --check",
45
- "template:install": "npm --prefix template ci",
46
- "template:build": "npm --prefix template run build",
47
- "prepublishOnly": "npm run agents:check && npm run build",
48
- "release": "npm publish --@squadbase:registry=https://registry.npmjs.org",
49
- "dev": "node scripts/dev.mjs",
50
- "screenshot": "node scripts/screenshot.mjs"
51
- },
52
39
  "devDependencies": {
53
40
  "@types/node": "^22.15.0",
54
41
  "playwright": "^1.49.0",
55
42
  "tsup": "^8.4.0",
56
- "typescript": "~5.7.0"
43
+ "typescript": "~5.7.0",
44
+ "vitest": "^5.0.5",
45
+ "@squadbase/squadkit": "0.0.0"
57
46
  },
58
47
  "optionalDependencies": {
59
48
  "@ai-sdk/anthropic": "^2.0.0",
60
49
  "@ai-sdk/google": "^2.0.0",
61
- "@ai-sdk/openai": "^2.0.0",
50
+ "@ai-sdk/openai": "2.0.122",
62
51
  "ai": "^5.0.52"
52
+ },
53
+ "gitHead": "0c136abfd64c5a27978b5bdb29c586394c852b5f",
54
+ "scripts": {
55
+ "sync-base": "rsync -a --delete --exclude='node_modules' --exclude='dist' --exclude='.vantage' --exclude='*.tsbuildinfo' --exclude='*.log' template/ base-template/ && mv base-template/.gitignore base-template/_gitignore",
56
+ "build": "pnpm run sync-base && tsup",
57
+ "type-check": "tsc",
58
+ "test": "vitest run",
59
+ "agents": "node scripts/build-agents.cjs",
60
+ "agents:check": "node scripts/build-agents.cjs --check",
61
+ "template:install": "bun install --cwd template --frozen-lockfile",
62
+ "template:build": "bun run --cwd template build",
63
+ "release": "pnpm publish --@squadbase:registry=https://registry.npmjs.org",
64
+ "dev": "node scripts/dev.mjs",
65
+ "screenshot": "node scripts/screenshot.mjs"
63
66
  }
64
- }
67
+ }
@@ -40,22 +40,23 @@ src/components/<slug>/
40
40
 
41
41
  **`components/` を使う理由(重要)**: Vantage は**ファイルベースルーティング**で、`.tsx` ファイルは原則すべてページ扱いになる。`components/` / `hooks/` / `lib/` / `server/` / `public/` だけがルート走査から除外される(`NON_ROUTE_DIRS`)ため、`src/templates/<slug>/` のようなディレクトリに `.tsx` を置くと **`/templates/<slug>/...` という意図しないルートが生え、`vantage check` が `MISSING_DEFAULT_EXPORT` で落ちる**(`SUSPICIOUS_ROUTE_DIR` 警告も出る)。データファイル(`.ts`)も同じディレクトリに置いてコロケーションを保つ。
42
42
 
43
- ### 2. インライン優先 — 薄いラッパ component を作らない
43
+ ### 2. チャートは型付き部品で書く — EChart + option 関数は作らない
44
44
 
45
- **`data → EChartsOption → <EChart>` だけの薄いチャート component は作らず、`index.tsx` 内のオプションビルダ関数にする。**
45
+ **折れ線 / 面 / 棒 / 散布図 / ヒートマップ / ドーナツ / ファネルは、カタログ部品(`LineChart` / `AreaChart` / `BarChart` / `ScatterChart` / `Heatmap` / `DonutChart` / `FunnelSteps`)に行配列とキー名を渡して書く。** 行配列は識別子(`mock-data.ts` の配列、または `index.tsx` 内で一度だけ作る `const xxxRows`)で渡す。JSX の prop に `.map(...)` や option 関数の呼び出しを書かない(Bluetree がチャートを部品として読めなくなる)。
46
46
 
47
47
  ```tsx
48
- // ✅ 推奨: index.tsx 内のオプションビルダ関数 + EChart 直レンダ
49
- function areaOption(data: TimePoint[]): EChartsOption {
50
- return { /* ... */ }
51
- }
52
- // JSX 内:
53
- <EChart option={areaOption(timeSeries)} height="280px" />
48
+ <AreaChart
49
+ data={timeSeries}
50
+ index="date"
51
+ categories={["series1", "series2"]}
52
+ labels={{ series1: "Series A", series2: "Series B" }}
53
+ height="280px"
54
+ />
54
55
  ```
55
56
 
56
- チャート整形ヘルパ(`getBaseGrid` / `formatNumber` 等)も同様に `index.tsx` 内へ inline する。
57
-
58
- **チャートに色をハードコードしない。** `@squadbase/vantage` v0.2.1 以降、`EChart` は init 時に `--chart-1..5`(系列色)と文字色/境界色トークン(軸・凡例・ツールチップ)を読み、light / dark の切り替えにも追従する。v0.2.2 からはスタイルシートの差し替え(`chart <preset>` の適用や `styles.css` の HMR)も拾い、トークンの実値が変わった時だけ再描画する。`option.color` / `itemStyle.color` / ラベルの `color` を書くと、その分だけテンプレがテーマ差し替え(`vantage-template chart <preset>` や `styles.css` の override)から外れる。**ui-templates では書かない**のが既定 — 意味色(成功=緑・失敗=赤など)を出したいときだけ、その series に限って指定する。`theme` プロップは使わない(渡した時点でトークン追従が完全に止まる)。
57
+ - データ型は `interface` ではなく `type` で宣言する(部品の `data` は `Record<string, unknown>[]` を要求し、`interface` は代入できない)。
58
+ - 部品が表せない表現(第2軸の折れ線の重ね描き、`visualMap` など)のために `EChart` + option 関数へ戻さない。**カタログに部品が無いチャート(現状はレーダーだけ)に限り**、`index.tsx` 内のオプションビルダ関数 + `<EChart option={...} />` で書く。
59
+ - 色は書かない。部品も `EChart` も `--chart-1..5` に追従するので、`option.color` / `itemStyle.color` / ラベルの `color` / `theme` プロップは使わない。意味色(成功=緑・失敗=赤)が要る series だけ例外。
59
60
 
60
61
  ### 3. データ層(mock-data / types)は分離する
61
62
 
@@ -96,6 +97,8 @@ server/api/<endpoint>.ts # ★ サンプルデータ + GET ハ
96
97
  | --- | --- |
97
98
  | `PageShell*` / `DashboardCard*` / `EChart` / `EChartsOption` / `DataTable*` / `ColumnDef` / `DateRangePicker` / `FilterBar*` / `FunnelSteps` / `MetricValue` / `TrendIndicator` / `StatusBadge` / `SegmentedControl` / `MultiSelect` / `SearchableSelect` / `SectionHeader` / `AppShell` / `Placeholder` / `Sparkline` | `@squadbase/vantage/components` |
98
99
  | `Button` / `Input` / `Badge` / `Tabs*` / `ToggleGroup*` / `Table*` / `Select*` / `Dialog*` / `cn` ほか shadcn プリミティブ | `@squadbase/vantage/ui` |
100
+
101
+ 型付きチャート(`LineChart` / `AreaChart` / `BarChart` / `ScatterChart` / `Heatmap` / `DonutChart` / `FunnelSteps` / `BarList` / `CategoryBar` / `ProgressCircle` / `Tracker`、`@squadbase/vantage/components`)は 0.6.3 以降の部品。雛形の依存は `^0.7.0` なので使ってよい。`Avatar` / `Pagination` / `RadioGroup` / `AlertDialog` / `Drawer` / `Toaster` / `Field` / `Combobox` / `HoverCard` / `Kbd`(`@squadbase/vantage/ui`)も同じ。
99
102
  | `definePage` | `@squadbase/vantage` |
100
103
  | `Link` / `Outlet` / `useParams` / `useSearch` / `useNavigate` / `useRoutes` / `useCurrentRoute` / `useSearchParam` / `useSearchState` | `@squadbase/vantage/router` |
101
104
  | `useApiQuery` / `useApiMutation` / `useQuery` / `useMutation` / `apiFetch` | `@squadbase/vantage/query` |
@@ -90,9 +90,6 @@ export const radarAxes: RadarPoint[] = [
90
90
  { current: 79, benchmark: 70 },
91
91
  ]
92
92
 
93
- export const HEATMAP_X_AXIS = ["0", "1", "2", "3", "4", "5"]
94
- export const HEATMAP_Y_AXIS = ["Row 1", "Row 2", "Row 3", "Row 4", "Row 5", "Row 6", "Row 7"]
95
-
96
93
  export const heatmapCells: HeatmapCell[] = [
97
94
  { x: "0", y: "Row 1", value: 57 },
98
95
  { x: "1", y: "Row 1", value: 25 },
@@ -1,31 +1,31 @@
1
- export interface TimePoint {
1
+ export type TimePoint = {
2
2
  date: string
3
3
  series1: number
4
4
  series2: number
5
5
  }
6
6
 
7
- export interface BarPoint {
7
+ export type BarPoint = {
8
8
  value: number
9
9
  }
10
10
 
11
- export interface ScatterPoint {
11
+ export type ScatterPoint = {
12
12
  x: number
13
13
  y: number
14
14
  size: number
15
15
  label: string
16
16
  }
17
17
 
18
- export interface RadarPoint {
18
+ export type RadarPoint = {
19
19
  current: number
20
20
  benchmark: number
21
21
  }
22
22
 
23
- export interface HeatmapCell {
23
+ export type HeatmapCell = {
24
24
  x: string
25
25
  y: string
26
26
  value: number
27
27
  }
28
28
 
29
- export interface DashboardFilters {
29
+ export type DashboardFilters = {
30
30
  dateRange: { from: Date | undefined; to: Date | undefined }
31
31
  }
@@ -3,6 +3,11 @@ import { subDays } from "date-fns"
3
3
  import { definePage } from "@squadbase/vantage"
4
4
  import {
5
5
  DashboardCardPreset,
6
+ AreaChart,
7
+ BarChart,
8
+ Heatmap,
9
+ LineChart,
10
+ ScatterChart,
6
11
  DateRangePicker,
7
12
  EChart,
8
13
  PageShell,
@@ -22,15 +27,9 @@ import {
22
27
  scatterPoints,
23
28
  radarAxes,
24
29
  heatmapCells,
25
- HEATMAP_X_AXIS,
26
- HEATMAP_Y_AXIS,
27
30
  } from "./components/chart-grid/mock-data"
28
31
  import type {
29
- TimePoint,
30
- BarPoint,
31
- ScatterPoint,
32
32
  RadarPoint,
33
- HeatmapCell,
34
33
  DashboardFilters,
35
34
  } from "./components/chart-grid/types"
36
35
 
@@ -40,86 +39,6 @@ export const page = definePage({
40
39
  "Six-chart grid (area, line, bar, scatter, radar, heatmap) with category filter chips in the header.",
41
40
  })
42
41
 
43
- // ── chart helpers ───────────────────────────────────────────────────────────
44
-
45
- function getBaseGrid() {
46
- return { left: "3%", right: "4%", bottom: "10%", containLabel: true }
47
- }
48
-
49
- function formatNumber(n: number): string {
50
- const sign = n < 0 ? "-" : ""
51
- const abs = Math.abs(n)
52
- if (abs >= 1_000_000) return `${sign}${(abs / 1_000_000).toFixed(1)}M`
53
- if (abs >= 1_000) return `${sign}${(abs / 1_000).toFixed(1)}K`
54
- return n.toLocaleString("en-US")
55
- }
56
-
57
- // ── chart options ────────────────────────────────────────────────────────────
58
-
59
- function areaOption(data: TimePoint[]): EChartsOption {
60
- return {
61
- tooltip: { trigger: "axis" },
62
- legend: { bottom: 0 },
63
- grid: getBaseGrid(),
64
- xAxis: {
65
- type: "category",
66
- data: data.map((d) => d.date),
67
- boundaryGap: false,
68
- axisLabel: { formatter: (v: string) => v.slice(5) },
69
- },
70
- yAxis: { type: "value", axisLabel: { formatter: (v: number) => formatNumber(v) } },
71
- series: [
72
- {
73
- name: "Series A",
74
- type: "line",
75
- smooth: true,
76
- showSymbol: false,
77
- data: data.map((d) => d.series1),
78
- areaStyle: { opacity: 0.22 },
79
- },
80
- {
81
- name: "Series B",
82
- type: "line",
83
- smooth: true,
84
- showSymbol: false,
85
- data: data.map((d) => d.series2),
86
- areaStyle: { opacity: 0.14 },
87
- },
88
- ],
89
- }
90
- }
91
-
92
- function lineOption(data: TimePoint[]): EChartsOption {
93
- return {
94
- tooltip: { trigger: "axis" },
95
- legend: { bottom: 0 },
96
- grid: getBaseGrid(),
97
- xAxis: {
98
- type: "category",
99
- data: data.map((d) => d.date),
100
- boundaryGap: false,
101
- axisLabel: { formatter: (v: string) => v.slice(5) },
102
- },
103
- yAxis: { type: "value", axisLabel: { formatter: (v: number) => formatNumber(v) } },
104
- series: [
105
- {
106
- name: "Series A",
107
- type: "line",
108
- smooth: true,
109
- showSymbol: false,
110
- data: data.map((d) => d.series1),
111
- },
112
- {
113
- name: "Series B",
114
- type: "line",
115
- smooth: true,
116
- showSymbol: false,
117
- data: data.map((d) => d.series2),
118
- },
119
- ],
120
- }
121
- }
122
-
123
42
  const BAR_CATEGORIES = [
124
43
  "Item 1",
125
44
  "Item 2",
@@ -131,45 +50,6 @@ const BAR_CATEGORIES = [
131
50
  "Item 8",
132
51
  ]
133
52
 
134
- function barOption(data: BarPoint[]): EChartsOption {
135
- return {
136
- tooltip: { trigger: "axis", axisPointer: { type: "shadow" } },
137
- grid: getBaseGrid(),
138
- xAxis: { type: "category", data: BAR_CATEGORIES },
139
- yAxis: { type: "value", axisLabel: { formatter: (v: number) => formatNumber(v) } },
140
- series: [
141
- {
142
- name: "Series A",
143
- type: "bar",
144
- data: data.map((d) => d.value),
145
- barMaxWidth: 28,
146
- },
147
- ],
148
- }
149
- }
150
-
151
- function scatterOption(data: ScatterPoint[]): EChartsOption {
152
- return {
153
- tooltip: { trigger: "item" },
154
- grid: getBaseGrid(),
155
- xAxis: { type: "value" },
156
- yAxis: { type: "value" },
157
- series: [
158
- {
159
- type: "scatter",
160
- data: data.map((p) => ({
161
- value: [p.x, p.y, p.size] as [number, number, number],
162
- label: p.label,
163
- })),
164
- symbolSize: (val: unknown) => {
165
- const arr = val as [number, number, number]
166
- return arr[2]
167
- },
168
- },
169
- ] as EChartsOption["series"],
170
- }
171
- }
172
-
173
53
  const RADAR_AXES = ["Axis 1", "Axis 2", "Axis 3", "Axis 4", "Axis 5", "Axis 6"]
174
54
 
175
55
  function radarOption(data: RadarPoint[]): EChartsOption {
@@ -200,34 +80,7 @@ function radarOption(data: RadarPoint[]): EChartsOption {
200
80
  }
201
81
  }
202
82
 
203
- function heatmapOption(
204
- data: HeatmapCell[],
205
- xAxis: string[],
206
- yAxis: string[],
207
- ): EChartsOption {
208
- const max = Math.max(...data.map((c) => c.value))
209
- return {
210
- tooltip: { position: "top" },
211
- grid: { height: "70%", top: "8%", left: "10%", right: "8%" },
212
- xAxis: { type: "category", data: xAxis, splitArea: { show: true } },
213
- yAxis: { type: "category", data: yAxis, splitArea: { show: true } },
214
- visualMap: {
215
- min: 0,
216
- max,
217
- calculable: true,
218
- orient: "horizontal",
219
- bottom: 0,
220
- },
221
- series: [
222
- {
223
- type: "heatmap",
224
- data: data.map((c) => [xAxis.indexOf(c.x), yAxis.indexOf(c.y), c.value]),
225
- label: { show: false },
226
- emphasis: { itemStyle: { shadowBlur: 8 } },
227
- },
228
- ],
229
- }
230
- }
83
+ const barRows = barSeries.map((d, i) => ({ item: BAR_CATEGORIES[i], value: d.value }))
231
84
 
232
85
  // ── page ─────────────────────────────────────────────────────────────────────
233
86
 
@@ -290,7 +143,7 @@ export default function HomePage() {
290
143
  title="Trend (area)"
291
144
  description="Two series over the selected period"
292
145
  >
293
- <EChart option={areaOption(timeSeries)} height="280px" />
146
+ <AreaChart data={timeSeries} index="date" categories={["series1", "series2"]} labels={{ series1: "Series A", series2: "Series B" }} height="280px" />
294
147
  </DashboardCardPreset>
295
148
  )}
296
149
  {show("trend") && (
@@ -298,17 +151,17 @@ export default function HomePage() {
298
151
  title="Trend (line)"
299
152
  description="Two series over the selected period"
300
153
  >
301
- <EChart option={lineOption(timeSeries)} height="280px" />
154
+ <LineChart data={timeSeries} index="date" categories={["series1", "series2"]} labels={{ series1: "Series A", series2: "Series B" }} height="280px" />
302
155
  </DashboardCardPreset>
303
156
  )}
304
157
  {show("ranking") && (
305
158
  <DashboardCardPreset title="Top categories" description="Ranked by value">
306
- <EChart option={barOption(barSeries)} height="280px" />
159
+ <BarChart data={barRows} index="item" categories={["value"]} labels={{ value: "Series A" }} showLegend={false} height="280px" />
307
160
  </DashboardCardPreset>
308
161
  )}
309
162
  {show("distribution") && (
310
163
  <DashboardCardPreset title="Correlation" description="Each point is an item">
311
- <EChart option={scatterOption(scatterPoints)} height="280px" />
164
+ <ScatterChart data={scatterPoints} x="x" y="y" size="size" index="label" height="280px" />
312
165
  </DashboardCardPreset>
313
166
  )}
314
167
  {show("distribution") && (
@@ -318,10 +171,7 @@ export default function HomePage() {
318
171
  )}
319
172
  {show("distribution") && (
320
173
  <DashboardCardPreset title="Activity" description="Intensity by cell">
321
- <EChart
322
- option={heatmapOption(heatmapCells, HEATMAP_X_AXIS, HEATMAP_Y_AXIS)}
323
- height="280px"
324
- />
174
+ <Heatmap data={heatmapCells} x="x" y="y" value="value" xLabels={["0", "1", "2", "3", "4", "5"]} yLabels={["Row 7", "Row 6", "Row 5", "Row 4", "Row 3", "Row 2", "Row 1"]} height={280} />
325
175
  </DashboardCardPreset>
326
176
  )}
327
177
  </div>
@@ -90,9 +90,6 @@ export const radarAxes: RadarPoint[] = [
90
90
  { current: 79, benchmark: 70 },
91
91
  ]
92
92
 
93
- export const HEATMAP_X_AXIS = ["0", "1", "2", "3", "4", "5"]
94
- export const HEATMAP_Y_AXIS = ["行1", "行2", "行3", "行4", "行5", "行6", "行7"]
95
-
96
93
  export const heatmapCells: HeatmapCell[] = [
97
94
  { x: "0", y: "行1", value: 57 },
98
95
  { x: "1", y: "行1", value: 25 },
@@ -1,31 +1,31 @@
1
- export interface TimePoint {
1
+ export type TimePoint = {
2
2
  date: string
3
3
  series1: number
4
4
  series2: number
5
5
  }
6
6
 
7
- export interface BarPoint {
7
+ export type BarPoint = {
8
8
  value: number
9
9
  }
10
10
 
11
- export interface ScatterPoint {
11
+ export type ScatterPoint = {
12
12
  x: number
13
13
  y: number
14
14
  size: number
15
15
  label: string
16
16
  }
17
17
 
18
- export interface RadarPoint {
18
+ export type RadarPoint = {
19
19
  current: number
20
20
  benchmark: number
21
21
  }
22
22
 
23
- export interface HeatmapCell {
23
+ export type HeatmapCell = {
24
24
  x: string
25
25
  y: string
26
26
  value: number
27
27
  }
28
28
 
29
- export interface DashboardFilters {
29
+ export type DashboardFilters = {
30
30
  dateRange: { from: Date | undefined; to: Date | undefined }
31
31
  }
@@ -3,6 +3,11 @@ import { subDays } from "date-fns"
3
3
  import { definePage } from "@squadbase/vantage"
4
4
  import {
5
5
  DashboardCardPreset,
6
+ AreaChart,
7
+ BarChart,
8
+ Heatmap,
9
+ LineChart,
10
+ ScatterChart,
6
11
  DateRangePicker,
7
12
  EChart,
8
13
  PageShell,
@@ -22,15 +27,9 @@ import {
22
27
  scatterPoints,
23
28
  radarAxes,
24
29
  heatmapCells,
25
- HEATMAP_X_AXIS,
26
- HEATMAP_Y_AXIS,
27
30
  } from "./components/chart-grid/mock-data"
28
31
  import type {
29
- TimePoint,
30
- BarPoint,
31
- ScatterPoint,
32
32
  RadarPoint,
33
- HeatmapCell,
34
33
  DashboardFilters,
35
34
  } from "./components/chart-grid/types"
36
35
 
@@ -40,86 +39,6 @@ export const page = definePage({
40
39
  "エリア/折れ線/棒/散布/レーダー/ヒートマップの6チャート格子。ヘッダーにカテゴリフィルタチップを配置。",
41
40
  })
42
41
 
43
- // ── chart helpers ───────────────────────────────────────────────────────────
44
-
45
- function getBaseGrid() {
46
- return { left: "3%", right: "4%", bottom: "10%", containLabel: true }
47
- }
48
-
49
- function formatNumber(n: number): string {
50
- const sign = n < 0 ? "-" : ""
51
- const abs = Math.abs(n)
52
- if (abs >= 100_000_000) return `${sign}${(abs / 100_000_000).toFixed(1)}億`
53
- if (abs >= 10_000) return `${sign}${(abs / 10_000).toFixed(0)}万`
54
- return n.toLocaleString("ja-JP")
55
- }
56
-
57
- // ── chart options ────────────────────────────────────────────────────────────
58
-
59
- function areaOption(data: TimePoint[]): EChartsOption {
60
- return {
61
- tooltip: { trigger: "axis" },
62
- legend: { bottom: 0 },
63
- grid: getBaseGrid(),
64
- xAxis: {
65
- type: "category",
66
- data: data.map((d) => d.date),
67
- boundaryGap: false,
68
- axisLabel: { formatter: (v: string) => v.slice(5) },
69
- },
70
- yAxis: { type: "value", axisLabel: { formatter: (v: number) => formatNumber(v) } },
71
- series: [
72
- {
73
- name: "系列A",
74
- type: "line",
75
- smooth: true,
76
- showSymbol: false,
77
- data: data.map((d) => d.series1),
78
- areaStyle: { opacity: 0.22 },
79
- },
80
- {
81
- name: "系列B",
82
- type: "line",
83
- smooth: true,
84
- showSymbol: false,
85
- data: data.map((d) => d.series2),
86
- areaStyle: { opacity: 0.14 },
87
- },
88
- ],
89
- }
90
- }
91
-
92
- function lineOption(data: TimePoint[]): EChartsOption {
93
- return {
94
- tooltip: { trigger: "axis" },
95
- legend: { bottom: 0 },
96
- grid: getBaseGrid(),
97
- xAxis: {
98
- type: "category",
99
- data: data.map((d) => d.date),
100
- boundaryGap: false,
101
- axisLabel: { formatter: (v: string) => v.slice(5) },
102
- },
103
- yAxis: { type: "value", axisLabel: { formatter: (v: number) => formatNumber(v) } },
104
- series: [
105
- {
106
- name: "系列A",
107
- type: "line",
108
- smooth: true,
109
- showSymbol: false,
110
- data: data.map((d) => d.series1),
111
- },
112
- {
113
- name: "系列B",
114
- type: "line",
115
- smooth: true,
116
- showSymbol: false,
117
- data: data.map((d) => d.series2),
118
- },
119
- ],
120
- }
121
- }
122
-
123
42
  const BAR_CATEGORIES = [
124
43
  "項目1",
125
44
  "項目2",
@@ -131,45 +50,6 @@ const BAR_CATEGORIES = [
131
50
  "項目8",
132
51
  ]
133
52
 
134
- function barOption(data: BarPoint[]): EChartsOption {
135
- return {
136
- tooltip: { trigger: "axis", axisPointer: { type: "shadow" } },
137
- grid: getBaseGrid(),
138
- xAxis: { type: "category", data: BAR_CATEGORIES },
139
- yAxis: { type: "value", axisLabel: { formatter: (v: number) => formatNumber(v) } },
140
- series: [
141
- {
142
- name: "系列A",
143
- type: "bar",
144
- data: data.map((d) => d.value),
145
- barMaxWidth: 28,
146
- },
147
- ],
148
- }
149
- }
150
-
151
- function scatterOption(data: ScatterPoint[]): EChartsOption {
152
- return {
153
- tooltip: { trigger: "item" },
154
- grid: getBaseGrid(),
155
- xAxis: { type: "value" },
156
- yAxis: { type: "value" },
157
- series: [
158
- {
159
- type: "scatter",
160
- data: data.map((p) => ({
161
- value: [p.x, p.y, p.size] as [number, number, number],
162
- label: p.label,
163
- })),
164
- symbolSize: (val: unknown) => {
165
- const arr = val as [number, number, number]
166
- return arr[2]
167
- },
168
- },
169
- ] as EChartsOption["series"],
170
- }
171
- }
172
-
173
53
  const RADAR_AXES = ["軸1", "軸2", "軸3", "軸4", "軸5", "軸6"]
174
54
 
175
55
  function radarOption(data: RadarPoint[]): EChartsOption {
@@ -200,34 +80,7 @@ function radarOption(data: RadarPoint[]): EChartsOption {
200
80
  }
201
81
  }
202
82
 
203
- function heatmapOption(
204
- data: HeatmapCell[],
205
- xAxis: string[],
206
- yAxis: string[],
207
- ): EChartsOption {
208
- const max = Math.max(...data.map((c) => c.value))
209
- return {
210
- tooltip: { position: "top" },
211
- grid: { height: "70%", top: "8%", left: "10%", right: "8%" },
212
- xAxis: { type: "category", data: xAxis, splitArea: { show: true } },
213
- yAxis: { type: "category", data: yAxis, splitArea: { show: true } },
214
- visualMap: {
215
- min: 0,
216
- max,
217
- calculable: true,
218
- orient: "horizontal",
219
- bottom: 0,
220
- },
221
- series: [
222
- {
223
- type: "heatmap",
224
- data: data.map((c) => [xAxis.indexOf(c.x), yAxis.indexOf(c.y), c.value]),
225
- label: { show: false },
226
- emphasis: { itemStyle: { shadowBlur: 8 } },
227
- },
228
- ],
229
- }
230
- }
83
+ const barRows = barSeries.map((d, i) => ({ item: BAR_CATEGORIES[i], value: d.value }))
231
84
 
232
85
  // ── page ─────────────────────────────────────────────────────────────────────
233
86
 
@@ -290,7 +143,7 @@ export default function HomePage() {
290
143
  title="トレンド (エリア)"
291
144
  description="選択期間の2系列"
292
145
  >
293
- <EChart option={areaOption(timeSeries)} height="280px" />
146
+ <AreaChart data={timeSeries} index="date" categories={["series1", "series2"]} labels={{ series1: "系列A", series2: "系列B" }} height="280px" />
294
147
  </DashboardCardPreset>
295
148
  )}
296
149
  {show("trend") && (
@@ -298,17 +151,17 @@ export default function HomePage() {
298
151
  title="トレンド (折れ線)"
299
152
  description="選択期間の2系列"
300
153
  >
301
- <EChart option={lineOption(timeSeries)} height="280px" />
154
+ <LineChart data={timeSeries} index="date" categories={["series1", "series2"]} labels={{ series1: "系列A", series2: "系列B" }} height="280px" />
302
155
  </DashboardCardPreset>
303
156
  )}
304
157
  {show("ranking") && (
305
158
  <DashboardCardPreset title="上位カテゴリ" description="値で並べ替え">
306
- <EChart option={barOption(barSeries)} height="280px" />
159
+ <BarChart data={barRows} index="item" categories={["value"]} labels={{ value: "系列A" }} showLegend={false} height="280px" />
307
160
  </DashboardCardPreset>
308
161
  )}
309
162
  {show("distribution") && (
310
163
  <DashboardCardPreset title="相関" description="各点が1項目">
311
- <EChart option={scatterOption(scatterPoints)} height="280px" />
164
+ <ScatterChart data={scatterPoints} x="x" y="y" size="size" index="label" height="280px" />
312
165
  </DashboardCardPreset>
313
166
  )}
314
167
  {show("distribution") && (
@@ -318,10 +171,7 @@ export default function HomePage() {
318
171
  )}
319
172
  {show("distribution") && (
320
173
  <DashboardCardPreset title="アクティビティ" description="セルごとの強度">
321
- <EChart
322
- option={heatmapOption(heatmapCells, HEATMAP_X_AXIS, HEATMAP_Y_AXIS)}
323
- height="280px"
324
- />
174
+ <Heatmap data={heatmapCells} x="x" y="y" value="value" xLabels={["0", "1", "2", "3", "4", "5"]} yLabels={["行7", "行6", "行5", "行4", "行3", "行2", "行1"]} height={280} />
325
175
  </DashboardCardPreset>
326
176
  )}
327
177
  </div>