@squadbase/vantage-template 0.2.0-dev1.af2f601 → 0.2.0-dev1.c68cce4
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/base-template/AGENTS.md +3 -3
- package/base-template/agents/page.md +5 -4
- package/base-template/bun.lock +7 -7
- package/base-template/package.json +2 -2
- package/package.json +2 -2
- package/ui-templates/CLAUDE.md +13 -12
- 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 +4 -42
- package/ui-templates/kpi-chart-simple-ja/lib/types.ts +4 -4
- package/ui-templates/kpi-chart-simple-ja/pages/index.tsx +4 -40
- 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
|
@@ -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>
|
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
DashboardCardContent,
|
|
11
11
|
DashboardCardPreset,
|
|
12
12
|
DateRangePicker,
|
|
13
|
-
|
|
13
|
+
FunnelSteps,
|
|
14
14
|
PageShell,
|
|
15
15
|
PageShellHeader,
|
|
16
16
|
PageShellHeading,
|
|
@@ -20,12 +20,10 @@ import {
|
|
|
20
20
|
PageShellContent,
|
|
21
21
|
Placeholder,
|
|
22
22
|
} from "@squadbase/vantage/components"
|
|
23
|
-
import type { DateRange
|
|
24
|
-
import { ToggleGroup, ToggleGroupItem } from "@squadbase/vantage/ui"
|
|
23
|
+
import type { DateRange } from "@squadbase/vantage/components"
|
|
25
24
|
|
|
26
25
|
import { StageTable } from "./components/funnel/stage-table"
|
|
27
26
|
import { funnelStages, stageRows } from "./components/funnel/mock-data"
|
|
28
|
-
import type { FunnelStage } from "./components/funnel/types"
|
|
29
27
|
|
|
30
28
|
export const page = definePage({
|
|
31
29
|
title: "Funnel",
|
|
@@ -36,65 +34,7 @@ export const page = definePage({
|
|
|
36
34
|
const today = new Date()
|
|
37
35
|
|
|
38
36
|
const STAGE_NAMES = ["Stage 1", "Stage 2", "Stage 3", "Stage 4", "Stage 5"]
|
|
39
|
-
|
|
40
|
-
type DisplayMode = "count" | "percent"
|
|
41
|
-
|
|
42
|
-
function funnelOption(
|
|
43
|
-
data: FunnelStage[],
|
|
44
|
-
labels: string[],
|
|
45
|
-
display: DisplayMode,
|
|
46
|
-
): EChartsOption {
|
|
47
|
-
const max = data[0]?.value ?? 0
|
|
48
|
-
|
|
49
|
-
return {
|
|
50
|
-
tooltip: { trigger: "item" },
|
|
51
|
-
series: [
|
|
52
|
-
{
|
|
53
|
-
type: "funnel",
|
|
54
|
-
orient: "horizontal",
|
|
55
|
-
funnelAlign: "center",
|
|
56
|
-
left: "2%",
|
|
57
|
-
right: "2%",
|
|
58
|
-
top: 16,
|
|
59
|
-
bottom: 64,
|
|
60
|
-
min: 0,
|
|
61
|
-
max,
|
|
62
|
-
sort: "descending",
|
|
63
|
-
gap: 4,
|
|
64
|
-
label: {
|
|
65
|
-
show: true,
|
|
66
|
-
position: "bottom",
|
|
67
|
-
formatter: (p: unknown) => {
|
|
68
|
-
const params = p as {
|
|
69
|
-
name: string
|
|
70
|
-
value: number
|
|
71
|
-
data: { conversionRate: number; share: number }
|
|
72
|
-
}
|
|
73
|
-
const main =
|
|
74
|
-
display === "percent"
|
|
75
|
-
? `${params.data.share.toFixed(1)}%`
|
|
76
|
-
: params.value.toLocaleString("en-US")
|
|
77
|
-
return `{name|${params.name}}\n{value|${main}}`
|
|
78
|
-
},
|
|
79
|
-
rich: {
|
|
80
|
-
name: { fontSize: 12, fontWeight: 600, lineHeight: 16, align: "center" },
|
|
81
|
-
value: { fontSize: 11, opacity: 0.6, lineHeight: 14, align: "center" },
|
|
82
|
-
},
|
|
83
|
-
},
|
|
84
|
-
labelLine: { show: false },
|
|
85
|
-
// No `itemStyle.color` and no `option.color`: the stages take
|
|
86
|
-
// `--chart-1..5`, and the labels the foreground token, so the chart
|
|
87
|
-
// rebrands with the theme (v0.2.1+).
|
|
88
|
-
data: data.map((d, i) => ({
|
|
89
|
-
name: labels[i] ?? "",
|
|
90
|
-
value: d.value,
|
|
91
|
-
conversionRate: d.conversionRate,
|
|
92
|
-
share: (d.value / Math.max(1, max)) * 100,
|
|
93
|
-
})),
|
|
94
|
-
},
|
|
95
|
-
] as EChartsOption["series"],
|
|
96
|
-
}
|
|
97
|
-
}
|
|
37
|
+
const funnelRows = funnelStages.map((d, i) => ({ stage: STAGE_NAMES[i], value: d.value }))
|
|
98
38
|
|
|
99
39
|
const summaryCards = [
|
|
100
40
|
{
|
|
@@ -128,7 +68,6 @@ export default function HomePage() {
|
|
|
128
68
|
from: subDays(today, 29),
|
|
129
69
|
to: today,
|
|
130
70
|
})
|
|
131
|
-
const [display, setDisplay] = useState<DisplayMode>("count")
|
|
132
71
|
|
|
133
72
|
return (
|
|
134
73
|
<PageShell>
|
|
@@ -140,19 +79,6 @@ export default function HomePage() {
|
|
|
140
79
|
</PageShellDescription>
|
|
141
80
|
</PageShellHeading>
|
|
142
81
|
<PageShellHeaderEnd className="flex-row items-center gap-2">
|
|
143
|
-
{/* Base UI toggle groups are always multi-value — keep the array at length 1. */}
|
|
144
|
-
<ToggleGroup
|
|
145
|
-
value={[display]}
|
|
146
|
-
onValueChange={(value) => {
|
|
147
|
-
const next = value[0]
|
|
148
|
-
if (next) setDisplay(next as DisplayMode)
|
|
149
|
-
}}
|
|
150
|
-
variant="outline"
|
|
151
|
-
size="sm"
|
|
152
|
-
>
|
|
153
|
-
<ToggleGroupItem value="count">Option A</ToggleGroupItem>
|
|
154
|
-
<ToggleGroupItem value="percent">Option B</ToggleGroupItem>
|
|
155
|
-
</ToggleGroup>
|
|
156
82
|
<DateRangePicker
|
|
157
83
|
value={dateRange}
|
|
158
84
|
onChange={setDateRange}
|
|
@@ -180,17 +106,13 @@ export default function HomePage() {
|
|
|
180
106
|
</div>
|
|
181
107
|
|
|
182
108
|
<div className="grid gap-4 lg:grid-cols-3">
|
|
183
|
-
<
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
height="440px"
|
|
191
|
-
/>
|
|
192
|
-
</DashboardCardPreset>
|
|
193
|
-
</div>
|
|
109
|
+
<DashboardCardPreset
|
|
110
|
+
className="lg:col-span-2"
|
|
111
|
+
title="Conversion funnel"
|
|
112
|
+
description="Stage-by-stage conversion"
|
|
113
|
+
>
|
|
114
|
+
<FunnelSteps data={funnelRows} index="stage" categories={["value"]} />
|
|
115
|
+
</DashboardCardPreset>
|
|
194
116
|
<DashboardCardPreset title="Stages" description="Conversion by stage">
|
|
195
117
|
<StagesSidebar />
|
|
196
118
|
</DashboardCardPreset>
|
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
DashboardCardContent,
|
|
11
11
|
DashboardCardPreset,
|
|
12
12
|
DateRangePicker,
|
|
13
|
-
|
|
13
|
+
FunnelSteps,
|
|
14
14
|
PageShell,
|
|
15
15
|
PageShellHeader,
|
|
16
16
|
PageShellHeading,
|
|
@@ -20,12 +20,10 @@ import {
|
|
|
20
20
|
PageShellContent,
|
|
21
21
|
Placeholder,
|
|
22
22
|
} from "@squadbase/vantage/components"
|
|
23
|
-
import type { DateRange
|
|
24
|
-
import { ToggleGroup, ToggleGroupItem } from "@squadbase/vantage/ui"
|
|
23
|
+
import type { DateRange } from "@squadbase/vantage/components"
|
|
25
24
|
|
|
26
25
|
import { StageTable } from "./components/funnel/stage-table"
|
|
27
26
|
import { funnelStages, stageRows } from "./components/funnel/mock-data"
|
|
28
|
-
import type { FunnelStage } from "./components/funnel/types"
|
|
29
27
|
|
|
30
28
|
export const page = definePage({
|
|
31
29
|
title: "ファネル",
|
|
@@ -36,64 +34,7 @@ export const page = definePage({
|
|
|
36
34
|
const today = new Date()
|
|
37
35
|
|
|
38
36
|
const STAGE_NAMES = ["ステージ1", "ステージ2", "ステージ3", "ステージ4", "ステージ5"]
|
|
39
|
-
|
|
40
|
-
type DisplayMode = "count" | "percent"
|
|
41
|
-
|
|
42
|
-
function funnelOption(
|
|
43
|
-
data: FunnelStage[],
|
|
44
|
-
labels: string[],
|
|
45
|
-
display: DisplayMode,
|
|
46
|
-
): EChartsOption {
|
|
47
|
-
const max = data[0]?.value ?? 0
|
|
48
|
-
|
|
49
|
-
return {
|
|
50
|
-
tooltip: { trigger: "item" },
|
|
51
|
-
series: [
|
|
52
|
-
{
|
|
53
|
-
type: "funnel",
|
|
54
|
-
orient: "horizontal",
|
|
55
|
-
funnelAlign: "center",
|
|
56
|
-
left: "2%",
|
|
57
|
-
right: "2%",
|
|
58
|
-
top: 16,
|
|
59
|
-
bottom: 64,
|
|
60
|
-
min: 0,
|
|
61
|
-
max,
|
|
62
|
-
sort: "descending",
|
|
63
|
-
gap: 4,
|
|
64
|
-
label: {
|
|
65
|
-
show: true,
|
|
66
|
-
position: "bottom",
|
|
67
|
-
formatter: (p: unknown) => {
|
|
68
|
-
const params = p as {
|
|
69
|
-
name: string
|
|
70
|
-
value: number
|
|
71
|
-
data: { conversionRate: number; share: number }
|
|
72
|
-
}
|
|
73
|
-
const main =
|
|
74
|
-
display === "percent"
|
|
75
|
-
? `${params.data.share.toFixed(1)}%`
|
|
76
|
-
: params.value.toLocaleString("ja-JP")
|
|
77
|
-
return `{name|${params.name}}\n{value|${main}}`
|
|
78
|
-
},
|
|
79
|
-
rich: {
|
|
80
|
-
name: { fontSize: 12, fontWeight: 600, lineHeight: 16, align: "center" },
|
|
81
|
-
value: { fontSize: 11, opacity: 0.6, lineHeight: 14, align: "center" },
|
|
82
|
-
},
|
|
83
|
-
},
|
|
84
|
-
labelLine: { show: false },
|
|
85
|
-
// itemStyle.color も option.color も持たせない: ステージは --chart-1..5、
|
|
86
|
-
// ラベルは文字色トークンで描かれ、テーマ差し替えに追従する (v0.2.1〜)。
|
|
87
|
-
data: data.map((d, i) => ({
|
|
88
|
-
name: labels[i] ?? "",
|
|
89
|
-
value: d.value,
|
|
90
|
-
conversionRate: d.conversionRate,
|
|
91
|
-
share: (d.value / Math.max(1, max)) * 100,
|
|
92
|
-
})),
|
|
93
|
-
},
|
|
94
|
-
] as EChartsOption["series"],
|
|
95
|
-
}
|
|
96
|
-
}
|
|
37
|
+
const funnelRows = funnelStages.map((d, i) => ({ stage: STAGE_NAMES[i], value: d.value }))
|
|
97
38
|
|
|
98
39
|
const summaryCards = [
|
|
99
40
|
{
|
|
@@ -127,7 +68,6 @@ export default function HomePage() {
|
|
|
127
68
|
from: subDays(today, 29),
|
|
128
69
|
to: today,
|
|
129
70
|
})
|
|
130
|
-
const [display, setDisplay] = useState<DisplayMode>("count")
|
|
131
71
|
|
|
132
72
|
return (
|
|
133
73
|
<PageShell>
|
|
@@ -139,19 +79,6 @@ export default function HomePage() {
|
|
|
139
79
|
</PageShellDescription>
|
|
140
80
|
</PageShellHeading>
|
|
141
81
|
<PageShellHeaderEnd className="flex-row items-center gap-2">
|
|
142
|
-
{/* Base UI の ToggleGroup は常に配列。単一選択は長さ 1 に保つ。 */}
|
|
143
|
-
<ToggleGroup
|
|
144
|
-
value={[display]}
|
|
145
|
-
onValueChange={(value) => {
|
|
146
|
-
const next = value[0]
|
|
147
|
-
if (next) setDisplay(next as DisplayMode)
|
|
148
|
-
}}
|
|
149
|
-
variant="outline"
|
|
150
|
-
size="sm"
|
|
151
|
-
>
|
|
152
|
-
<ToggleGroupItem value="count">選択肢A</ToggleGroupItem>
|
|
153
|
-
<ToggleGroupItem value="percent">選択肢B</ToggleGroupItem>
|
|
154
|
-
</ToggleGroup>
|
|
155
82
|
<DateRangePicker
|
|
156
83
|
value={dateRange}
|
|
157
84
|
onChange={setDateRange}
|
|
@@ -179,17 +106,13 @@ export default function HomePage() {
|
|
|
179
106
|
</div>
|
|
180
107
|
|
|
181
108
|
<div className="grid gap-4 lg:grid-cols-3">
|
|
182
|
-
<
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
height="440px"
|
|
190
|
-
/>
|
|
191
|
-
</DashboardCardPreset>
|
|
192
|
-
</div>
|
|
109
|
+
<DashboardCardPreset
|
|
110
|
+
className="lg:col-span-2"
|
|
111
|
+
title="コンバージョンファネル"
|
|
112
|
+
description="ステージ別コンバージョン"
|
|
113
|
+
>
|
|
114
|
+
<FunnelSteps data={funnelRows} index="stage" categories={["value"]} />
|
|
115
|
+
</DashboardCardPreset>
|
|
193
116
|
<DashboardCardPreset title="ステージ" description="ステージ別コンバージョン">
|
|
194
117
|
<StagesSidebar />
|
|
195
118
|
</DashboardCardPreset>
|
|
@@ -40,12 +40,12 @@ export const trendSeries: TrendPoint[] = [
|
|
|
40
40
|
]
|
|
41
41
|
|
|
42
42
|
export const comparisonSeries: ComparisonPoint[] = [
|
|
43
|
-
{ period: "2023-10", current: 308820, previous: 285196
|
|
44
|
-
{ period: "2023-11", current: 360871, previous: 300245
|
|
45
|
-
{ period: "2023-12", current: 369038, previous: 344274
|
|
46
|
-
{ period: "2024-01", current: 402455, previous: 338646
|
|
47
|
-
{ period: "2024-02", current: 443741, previous: 396096
|
|
48
|
-
{ period: "2024-03", current: 445883, previous: 387812
|
|
43
|
+
{ period: "2023-10", current: 308820, previous: 285196 },
|
|
44
|
+
{ period: "2023-11", current: 360871, previous: 300245 },
|
|
45
|
+
{ period: "2023-12", current: 369038, previous: 344274 },
|
|
46
|
+
{ period: "2024-01", current: 402455, previous: 338646 },
|
|
47
|
+
{ period: "2024-02", current: 443741, previous: 396096 },
|
|
48
|
+
{ period: "2024-03", current: 445883, previous: 387812 },
|
|
49
49
|
]
|
|
50
50
|
|
|
51
51
|
export const breakdownSlices: BreakdownSlice[] = [
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export
|
|
1
|
+
export type KpiItem = {
|
|
2
2
|
id: "revenue" | "users" | "conversion" | "aov" | "sessions"
|
|
3
3
|
value: string
|
|
4
4
|
change: number
|
|
@@ -7,24 +7,23 @@ export interface KpiItem {
|
|
|
7
7
|
sparklineData: number[]
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
-
export
|
|
10
|
+
export type ComparisonPoint = {
|
|
11
11
|
period: string
|
|
12
12
|
current: number
|
|
13
13
|
previous: number
|
|
14
|
-
growth: number
|
|
15
14
|
}
|
|
16
15
|
|
|
17
|
-
export
|
|
16
|
+
export type BreakdownSlice = {
|
|
18
17
|
segment: string
|
|
19
18
|
value: number
|
|
20
19
|
}
|
|
21
20
|
|
|
22
|
-
export
|
|
21
|
+
export type TrendPoint = {
|
|
23
22
|
date: string
|
|
24
23
|
value: number
|
|
25
24
|
}
|
|
26
25
|
|
|
27
|
-
export
|
|
26
|
+
export type CampaignRow = {
|
|
28
27
|
id: string
|
|
29
28
|
name: string
|
|
30
29
|
channel: string
|
|
@@ -33,6 +32,6 @@ export interface CampaignRow {
|
|
|
33
32
|
roi: number
|
|
34
33
|
}
|
|
35
34
|
|
|
36
|
-
export
|
|
35
|
+
export type DashboardFilters = {
|
|
37
36
|
dateRange: { from: Date | undefined; to: Date | undefined }
|
|
38
37
|
}
|