@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.
- package/README.md +19 -9
- package/base-template/.squadbase/skills/vantage-add-feature/SKILL.md +14 -7
- package/base-template/.squadbase/skills/vantage-app/SKILL.md +4 -0
- package/base-template/.squadbase/skills/vantage-design/SKILL.md +62 -52
- package/base-template/.squadbase/skills/vantage-design/references/extract.md +26 -27
- package/base-template/.squadbase/skills/vantage-design/references/format.md +12 -172
- package/base-template/.squadbase/skills/vantage-design/references/presets.md +50 -110
- package/base-template/.squadbase/skills/vantage-pitfalls/SKILL.md +50 -1
- package/base-template/.squadbase/skills/vantage-slide/SKILL.md +299 -0
- package/base-template/.squadbase/skills/vantage-slide/references/morph.md +102 -0
- package/base-template/.squadbase/skills/vantage-slide/references/steps.md +57 -0
- package/base-template/.squadbase/skills/vantage-slide/references/tokens.md +87 -0
- package/base-template/.squadbase/skills/vantage-slide/references/transitions.md +196 -0
- package/base-template/AGENTS.md +225 -18
- package/base-template/agents/api.md +36 -0
- package/base-template/agents/csv.md +2 -1
- package/base-template/agents/doc.md +52 -3
- package/base-template/agents/page.md +92 -10
- package/base-template/agents/slide.md +90 -4
- package/base-template/agents/xlsx.md +2 -1
- package/base-template/bun.lock +917 -0
- package/base-template/bunfig.toml +5 -0
- package/base-template/package.json +6 -6
- package/base-template/squadbase.yml +1 -0
- package/base-template/src/_layout.tsx +16 -7
- package/base-template/src/components/build-recovery.tsx +37 -1
- package/base-template/src/components/user-card.tsx +11 -11
- package/base-template/src/index.tsx +8 -3
- package/base-template/src/styles.css +108 -12
- package/dist/index.js +971 -6
- package/package.json +20 -17
- package/ui-templates/CLAUDE.md +14 -11
- package/ui-templates/chart-grid/lib/mock-data.ts +0 -3
- package/ui-templates/chart-grid/lib/types.ts +6 -6
- package/ui-templates/chart-grid/pages/index.tsx +11 -161
- package/ui-templates/chart-grid-ja/lib/mock-data.ts +0 -3
- package/ui-templates/chart-grid-ja/lib/types.ts +6 -6
- package/ui-templates/chart-grid-ja/pages/index.tsx +11 -161
- package/ui-templates/funnel/lib/types.ts +2 -2
- package/ui-templates/funnel/pages/index.tsx +10 -88
- package/ui-templates/funnel-ja/lib/types.ts +2 -2
- package/ui-templates/funnel-ja/pages/index.tsx +10 -87
- package/ui-templates/kpi-chart-advanced/lib/mock-data.ts +6 -6
- package/ui-templates/kpi-chart-advanced/lib/types.ts +6 -7
- package/ui-templates/kpi-chart-advanced/pages/index.tsx +6 -117
- package/ui-templates/kpi-chart-advanced-ja/lib/mock-data.ts +6 -6
- package/ui-templates/kpi-chart-advanced-ja/lib/types.ts +6 -7
- package/ui-templates/kpi-chart-advanced-ja/pages/index.tsx +6 -117
- package/ui-templates/kpi-chart-simple/lib/types.ts +4 -4
- package/ui-templates/kpi-chart-simple/pages/index.tsx +51 -89
- package/ui-templates/kpi-chart-simple-ja/lib/types.ts +4 -4
- package/ui-templates/kpi-chart-simple-ja/pages/index.tsx +51 -87
- package/ui-templates/table-focus/lib/types.ts +3 -3
- package/ui-templates/table-focus/pages/index.tsx +3 -45
- package/ui-templates/table-focus-ja/lib/types.ts +3 -3
- package/ui-templates/table-focus-ja/pages/index.tsx +3 -45
- package/base-template/DESIGN.md +0 -108
- 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.
|
|
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": "
|
|
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
|
+
}
|
package/ui-templates/CLAUDE.md
CHANGED
|
@@ -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.
|
|
43
|
+
### 2. チャートは型付き部品で書く — EChart + option 関数は作らない
|
|
44
44
|
|
|
45
|
-
|
|
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
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
|
|
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
|
-
|
|
57
|
-
|
|
58
|
-
|
|
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
|
|
1
|
+
export type TimePoint = {
|
|
2
2
|
date: string
|
|
3
3
|
series1: number
|
|
4
4
|
series2: number
|
|
5
5
|
}
|
|
6
6
|
|
|
7
|
-
export
|
|
7
|
+
export type BarPoint = {
|
|
8
8
|
value: number
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
-
export
|
|
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
|
|
18
|
+
export type RadarPoint = {
|
|
19
19
|
current: number
|
|
20
20
|
benchmark: number
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
export
|
|
23
|
+
export type HeatmapCell = {
|
|
24
24
|
x: string
|
|
25
25
|
y: string
|
|
26
26
|
value: number
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
export
|
|
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
|
-
|
|
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
|
-
<
|
|
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
|
-
<
|
|
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
|
-
<
|
|
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
|
-
<
|
|
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
|
-
<
|
|
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
|
|
1
|
+
export type TimePoint = {
|
|
2
2
|
date: string
|
|
3
3
|
series1: number
|
|
4
4
|
series2: number
|
|
5
5
|
}
|
|
6
6
|
|
|
7
|
-
export
|
|
7
|
+
export type BarPoint = {
|
|
8
8
|
value: number
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
-
export
|
|
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
|
|
18
|
+
export type RadarPoint = {
|
|
19
19
|
current: number
|
|
20
20
|
benchmark: number
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
export
|
|
23
|
+
export type HeatmapCell = {
|
|
24
24
|
x: string
|
|
25
25
|
y: string
|
|
26
26
|
value: number
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
export
|
|
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
|
-
|
|
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
|
-
<
|
|
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
|
-
<
|
|
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
|
-
<
|
|
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
|
-
<
|
|
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
|
-
<
|
|
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>
|