@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.
Files changed (99) hide show
  1. package/README.md +112 -0
  2. package/base-template/.squadbase/skills/vantage-add-feature/SKILL.md +162 -0
  3. package/base-template/.squadbase/skills/vantage-app/SKILL.md +120 -0
  4. package/base-template/.squadbase/skills/vantage-pitfalls/SKILL.md +137 -0
  5. package/base-template/AGENTS.md +339 -0
  6. package/base-template/_404.tsx +18 -0
  7. package/base-template/_error.tsx +14 -0
  8. package/base-template/_gitignore +5 -0
  9. package/base-template/_layout.tsx +60 -0
  10. package/base-template/components/user-card.tsx +132 -0
  11. package/base-template/index.tsx +59 -0
  12. package/base-template/package.json +27 -0
  13. package/base-template/server/api/health.ts +12 -0
  14. package/base-template/squadbase.yml +5 -0
  15. package/base-template/styles.css +18 -0
  16. package/base-template/tsconfig.json +28 -0
  17. package/chart-presets/berry.css +16 -0
  18. package/chart-presets/blue.css +16 -0
  19. package/chart-presets/forest.css +16 -0
  20. package/chart-presets/mono.css +16 -0
  21. package/chart-presets/ocean.css +16 -0
  22. package/chart-presets/sunset.css +16 -0
  23. package/dist/index.js +1157 -0
  24. package/package.json +55 -0
  25. package/ui-templates/CLAUDE.md +200 -0
  26. package/ui-templates/blank/manifest.json +8 -0
  27. package/ui-templates/blank/pages/index.tsx +33 -0
  28. package/ui-templates/blank-ja/manifest.json +8 -0
  29. package/ui-templates/blank-ja/pages/index.tsx +33 -0
  30. package/ui-templates/chart-grid/lib/mock-data.ts +139 -0
  31. package/ui-templates/chart-grid/lib/types.ts +31 -0
  32. package/ui-templates/chart-grid/manifest.json +10 -0
  33. package/ui-templates/chart-grid/pages/index.tsx +331 -0
  34. package/ui-templates/chart-grid/preview-square.png +0 -0
  35. package/ui-templates/chart-grid/preview-wide.png +0 -0
  36. package/ui-templates/chart-grid-ja/lib/mock-data.ts +139 -0
  37. package/ui-templates/chart-grid-ja/lib/types.ts +31 -0
  38. package/ui-templates/chart-grid-ja/manifest.json +10 -0
  39. package/ui-templates/chart-grid-ja/pages/index.tsx +331 -0
  40. package/ui-templates/chart-grid-ja/preview-square.png +0 -0
  41. package/ui-templates/chart-grid-ja/preview-wide.png +0 -0
  42. package/ui-templates/funnel/components/chart-helpers.ts +11 -0
  43. package/ui-templates/funnel/components/stage-table.tsx +63 -0
  44. package/ui-templates/funnel/lib/mock-data.ts +22 -0
  45. package/ui-templates/funnel/lib/types.ts +11 -0
  46. package/ui-templates/funnel/manifest.json +12 -0
  47. package/ui-templates/funnel/pages/index.tsx +252 -0
  48. package/ui-templates/funnel/preview-square.png +0 -0
  49. package/ui-templates/funnel/preview-wide.png +0 -0
  50. package/ui-templates/funnel-ja/components/chart-helpers.ts +11 -0
  51. package/ui-templates/funnel-ja/components/stage-table.tsx +63 -0
  52. package/ui-templates/funnel-ja/lib/mock-data.ts +22 -0
  53. package/ui-templates/funnel-ja/lib/types.ts +11 -0
  54. package/ui-templates/funnel-ja/manifest.json +12 -0
  55. package/ui-templates/funnel-ja/pages/index.tsx +251 -0
  56. package/ui-templates/funnel-ja/preview-square.png +0 -0
  57. package/ui-templates/funnel-ja/preview-wide.png +0 -0
  58. package/ui-templates/kpi-chart-advanced/components/campaign-table.tsx +69 -0
  59. package/ui-templates/kpi-chart-advanced/lib/mock-data.ts +109 -0
  60. package/ui-templates/kpi-chart-advanced/lib/types.ts +38 -0
  61. package/ui-templates/kpi-chart-advanced/manifest.json +11 -0
  62. package/ui-templates/kpi-chart-advanced/pages/index.tsx +339 -0
  63. package/ui-templates/kpi-chart-advanced/preview-square.png +0 -0
  64. package/ui-templates/kpi-chart-advanced/preview-wide.png +0 -0
  65. package/ui-templates/kpi-chart-advanced-ja/components/campaign-table.tsx +69 -0
  66. package/ui-templates/kpi-chart-advanced-ja/lib/mock-data.ts +109 -0
  67. package/ui-templates/kpi-chart-advanced-ja/lib/types.ts +38 -0
  68. package/ui-templates/kpi-chart-advanced-ja/manifest.json +11 -0
  69. package/ui-templates/kpi-chart-advanced-ja/pages/index.tsx +333 -0
  70. package/ui-templates/kpi-chart-advanced-ja/preview-square.png +0 -0
  71. package/ui-templates/kpi-chart-advanced-ja/preview-wide.png +0 -0
  72. package/ui-templates/kpi-chart-simple/components/top-items-table.tsx +73 -0
  73. package/ui-templates/kpi-chart-simple/lib/types.ts +44 -0
  74. package/ui-templates/kpi-chart-simple/manifest.json +11 -0
  75. package/ui-templates/kpi-chart-simple/pages/index.tsx +200 -0
  76. package/ui-templates/kpi-chart-simple/preview-square.png +0 -0
  77. package/ui-templates/kpi-chart-simple/preview-wide.png +0 -0
  78. package/ui-templates/kpi-chart-simple/server/kpi-summary.ts +141 -0
  79. package/ui-templates/kpi-chart-simple-ja/components/top-items-table.tsx +73 -0
  80. package/ui-templates/kpi-chart-simple-ja/lib/types.ts +44 -0
  81. package/ui-templates/kpi-chart-simple-ja/manifest.json +11 -0
  82. package/ui-templates/kpi-chart-simple-ja/pages/index.tsx +200 -0
  83. package/ui-templates/kpi-chart-simple-ja/preview-square.png +0 -0
  84. package/ui-templates/kpi-chart-simple-ja/preview-wide.png +0 -0
  85. package/ui-templates/kpi-chart-simple-ja/server/kpi-summary.ts +141 -0
  86. package/ui-templates/table-focus/components/detail-table.tsx +88 -0
  87. package/ui-templates/table-focus/lib/mock-data.ts +69 -0
  88. package/ui-templates/table-focus/lib/types.ts +24 -0
  89. package/ui-templates/table-focus/manifest.json +11 -0
  90. package/ui-templates/table-focus/pages/index.tsx +180 -0
  91. package/ui-templates/table-focus/preview-square.png +0 -0
  92. package/ui-templates/table-focus/preview-wide.png +0 -0
  93. package/ui-templates/table-focus-ja/components/detail-table.tsx +95 -0
  94. package/ui-templates/table-focus-ja/lib/mock-data.ts +69 -0
  95. package/ui-templates/table-focus-ja/lib/types.ts +24 -0
  96. package/ui-templates/table-focus-ja/manifest.json +11 -0
  97. package/ui-templates/table-focus-ja/pages/index.tsx +180 -0
  98. package/ui-templates/table-focus-ja/preview-square.png +0 -0
  99. package/ui-templates/table-focus-ja/preview-wide.png +0 -0
@@ -0,0 +1,331 @@
1
+ import { useState } from "react"
2
+ import { subDays } from "date-fns"
3
+ import { definePage } from "@squadbase/vantage"
4
+ import {
5
+ DashboardCardPreset,
6
+ DateRangePicker,
7
+ EChart,
8
+ PageShell,
9
+ PageShellContent,
10
+ PageShellDescription,
11
+ PageShellHeader,
12
+ PageShellHeaderEnd,
13
+ PageShellHeading,
14
+ PageShellTitle,
15
+ } from "@squadbase/vantage/components"
16
+ import type { EChartsOption } from "@squadbase/vantage/components"
17
+ import { ToggleGroup, ToggleGroupItem } from "@squadbase/vantage/ui"
18
+
19
+ import {
20
+ timeSeries,
21
+ barSeries,
22
+ scatterPoints,
23
+ radarAxes,
24
+ heatmapCells,
25
+ HEATMAP_X_AXIS,
26
+ HEATMAP_Y_AXIS,
27
+ } from "./components/chart-grid/mock-data"
28
+ import type {
29
+ TimePoint,
30
+ BarPoint,
31
+ ScatterPoint,
32
+ RadarPoint,
33
+ HeatmapCell,
34
+ DashboardFilters,
35
+ } from "./components/chart-grid/types"
36
+
37
+ export const page = definePage({
38
+ title: "チャート概要",
39
+ description:
40
+ "エリア/折れ線/棒/散布/レーダー/ヒートマップの6チャート格子。ヘッダーにカテゴリフィルタチップを配置。",
41
+ })
42
+
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
+ const BAR_CATEGORIES = [
124
+ "項目1",
125
+ "項目2",
126
+ "項目3",
127
+ "項目4",
128
+ "項目5",
129
+ "項目6",
130
+ "項目7",
131
+ "項目8",
132
+ ]
133
+
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
+ const RADAR_AXES = ["軸1", "軸2", "軸3", "軸4", "軸5", "軸6"]
174
+
175
+ function radarOption(data: RadarPoint[]): EChartsOption {
176
+ return {
177
+ tooltip: { trigger: "item" },
178
+ legend: { bottom: 0 },
179
+ radar: {
180
+ indicator: data.map((_, i) => ({ name: RADAR_AXES[i], max: 100 })),
181
+ radius: "65%",
182
+ },
183
+ series: [
184
+ {
185
+ type: "radar",
186
+ data: [
187
+ {
188
+ value: data.map((r) => r.current),
189
+ name: "系列A",
190
+ areaStyle: { opacity: 0.28 },
191
+ },
192
+ {
193
+ value: data.map((r) => r.benchmark),
194
+ name: "系列B",
195
+ areaStyle: { opacity: 0.18 },
196
+ },
197
+ ],
198
+ },
199
+ ],
200
+ }
201
+ }
202
+
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, shadowColor: "rgba(0,0,0,0.4)" } },
227
+ },
228
+ ],
229
+ }
230
+ }
231
+
232
+ // ── page ─────────────────────────────────────────────────────────────────────
233
+
234
+ const today = new Date()
235
+ const initialFilters: DashboardFilters = {
236
+ dateRange: {
237
+ from: subDays(today, 29),
238
+ to: today,
239
+ },
240
+ }
241
+
242
+ type Category = "all" | "trend" | "ranking" | "distribution"
243
+
244
+ export default function HomePage() {
245
+ const [filters, setFilters] = useState<DashboardFilters>(initialFilters)
246
+ const [category, setCategory] = useState<Category>("all")
247
+
248
+ const show = (target: Exclude<Category, "all">) =>
249
+ category === "all" || category === target
250
+
251
+ return (
252
+ <PageShell>
253
+ <PageShellHeader>
254
+ <PageShellHeading>
255
+ <PageShellTitle>[テンプレート] チャート概要</PageShellTitle>
256
+ <PageShellDescription>
257
+ 選択期間のチャートをグリッド表示。
258
+ </PageShellDescription>
259
+ </PageShellHeading>
260
+ <PageShellHeaderEnd className="flex-row items-center gap-3">
261
+ {/* Base UI の ToggleGroup は単一選択でも value が配列 */}
262
+ <ToggleGroup
263
+ value={[category]}
264
+ onValueChange={(values) => {
265
+ const next = values[0]
266
+ if (next) setCategory(next as Category)
267
+ }}
268
+ variant="outline"
269
+ size="sm"
270
+ >
271
+ <ToggleGroupItem value="all">すべて</ToggleGroupItem>
272
+ <ToggleGroupItem value="trend">グループA</ToggleGroupItem>
273
+ <ToggleGroupItem value="ranking">グループB</ToggleGroupItem>
274
+ <ToggleGroupItem value="distribution">グループC</ToggleGroupItem>
275
+ </ToggleGroup>
276
+ <DateRangePicker
277
+ value={filters.dateRange}
278
+ onChange={(range) =>
279
+ setFilters((prev) => ({ ...prev, dateRange: range }))
280
+ }
281
+ maxDate={today}
282
+ />
283
+ </PageShellHeaderEnd>
284
+ </PageShellHeader>
285
+
286
+ <PageShellContent>
287
+ <div className="grid gap-4 lg:grid-cols-2">
288
+ {show("trend") && (
289
+ <DashboardCardPreset
290
+ title="トレンド (エリア)"
291
+ description="選択期間の2系列"
292
+ >
293
+ <EChart option={areaOption(timeSeries)} height="280px" />
294
+ </DashboardCardPreset>
295
+ )}
296
+ {show("trend") && (
297
+ <DashboardCardPreset
298
+ title="トレンド (折れ線)"
299
+ description="選択期間の2系列"
300
+ >
301
+ <EChart option={lineOption(timeSeries)} height="280px" />
302
+ </DashboardCardPreset>
303
+ )}
304
+ {show("ranking") && (
305
+ <DashboardCardPreset title="上位カテゴリ" description="値で並べ替え">
306
+ <EChart option={barOption(barSeries)} height="280px" />
307
+ </DashboardCardPreset>
308
+ )}
309
+ {show("distribution") && (
310
+ <DashboardCardPreset title="相関" description="各点が1項目">
311
+ <EChart option={scatterOption(scatterPoints)} height="280px" />
312
+ </DashboardCardPreset>
313
+ )}
314
+ {show("distribution") && (
315
+ <DashboardCardPreset title="プロファイル" description="現在 vs ベンチマーク">
316
+ <EChart option={radarOption(radarAxes)} height="280px" />
317
+ </DashboardCardPreset>
318
+ )}
319
+ {show("distribution") && (
320
+ <DashboardCardPreset title="アクティビティ" description="セルごとの強度">
321
+ <EChart
322
+ option={heatmapOption(heatmapCells, HEATMAP_X_AXIS, HEATMAP_Y_AXIS)}
323
+ height="280px"
324
+ />
325
+ </DashboardCardPreset>
326
+ )}
327
+ </div>
328
+ </PageShellContent>
329
+ </PageShell>
330
+ )
331
+ }
@@ -0,0 +1,11 @@
1
+ export function formatNumber(n: number): string {
2
+ const sign = n < 0 ? "-" : ""
3
+ const abs = Math.abs(n)
4
+ if (abs >= 1_000_000) return `${sign}${(abs / 1_000_000).toFixed(1)}M`
5
+ if (abs >= 1_000) return `${sign}${(abs / 1_000).toFixed(1)}K`
6
+ return n.toLocaleString("en-US")
7
+ }
8
+
9
+ export function formatPercent(value: number, digits = 1): string {
10
+ return `${value.toFixed(digits)}%`
11
+ }
@@ -0,0 +1,63 @@
1
+ import { useMemo } from "react"
2
+ import { DataTablePreset, Placeholder } from "@squadbase/vantage/components"
3
+ import type { ColumnDef } from "@squadbase/vantage/components"
4
+
5
+ import { formatNumber, formatPercent } from "./chart-helpers"
6
+ import type { StageRow } from "./types"
7
+
8
+ const STEP_NAMES = ["Stage 1 → 2", "Stage 2 → 3", "Stage 3 → 4", "Stage 4 → 5"]
9
+
10
+ interface StageTableProps {
11
+ data: StageRow[]
12
+ }
13
+
14
+ export function StageTable({ data }: StageTableProps) {
15
+ const columns = useMemo<ColumnDef<StageRow>[]>(
16
+ () => [
17
+ {
18
+ id: "step",
19
+ header: "Column 1",
20
+ cell: ({ row }) => STEP_NAMES[row.index],
21
+ },
22
+ {
23
+ accessorKey: "count",
24
+ header: "Column 2",
25
+ cell: ({ row }) => (
26
+ <div className="flex justify-end">
27
+ <Placeholder>{formatNumber(row.original.count)}</Placeholder>
28
+ </div>
29
+ ),
30
+ },
31
+ {
32
+ accessorKey: "conversionRate",
33
+ header: "Column 3",
34
+ cell: ({ row }) => (
35
+ <div className="flex justify-end">
36
+ <Placeholder>{formatPercent(row.original.conversionRate)}</Placeholder>
37
+ </div>
38
+ ),
39
+ },
40
+ {
41
+ accessorKey: "avgDays",
42
+ header: "Column 4",
43
+ cell: ({ row }) => (
44
+ <div className="flex justify-end">
45
+ <Placeholder>{`${row.original.avgDays.toFixed(1)}d`}</Placeholder>
46
+ </div>
47
+ ),
48
+ },
49
+ {
50
+ accessorKey: "dropoff",
51
+ header: "Column 5",
52
+ cell: ({ row }) => (
53
+ <div className="flex justify-end">
54
+ <Placeholder>{formatNumber(row.original.dropoff)}</Placeholder>
55
+ </div>
56
+ ),
57
+ },
58
+ ],
59
+ [],
60
+ )
61
+
62
+ return <DataTablePreset columns={columns} data={data} enableSorting />
63
+ }
@@ -0,0 +1,22 @@
1
+ import type { FunnelStage, StageRow } from "./types"
2
+
3
+ const VISITORS = 124_500
4
+ const LEADS = 38_240
5
+ const QUALIFIED = 12_180
6
+ const OPPORTUNITIES = 4_520
7
+ const CONVERSIONS = 1_186
8
+
9
+ export const funnelStages: FunnelStage[] = [
10
+ { value: VISITORS, conversionRate: 100 },
11
+ { value: LEADS, conversionRate: (LEADS / VISITORS) * 100 },
12
+ { value: QUALIFIED, conversionRate: (QUALIFIED / LEADS) * 100 },
13
+ { value: OPPORTUNITIES, conversionRate: (OPPORTUNITIES / QUALIFIED) * 100 },
14
+ { value: CONVERSIONS, conversionRate: (CONVERSIONS / OPPORTUNITIES) * 100 },
15
+ ]
16
+
17
+ export const stageRows: StageRow[] = [
18
+ { count: LEADS, conversionRate: (LEADS / VISITORS) * 100, avgDays: 0.6, dropoff: VISITORS - LEADS },
19
+ { count: QUALIFIED, conversionRate: (QUALIFIED / LEADS) * 100, avgDays: 3.2, dropoff: LEADS - QUALIFIED },
20
+ { count: OPPORTUNITIES, conversionRate: (OPPORTUNITIES / QUALIFIED) * 100, avgDays: 7.4, dropoff: QUALIFIED - OPPORTUNITIES },
21
+ { count: CONVERSIONS, conversionRate: (CONVERSIONS / OPPORTUNITIES) * 100, avgDays: 12.8, dropoff: OPPORTUNITIES - CONVERSIONS },
22
+ ]
@@ -0,0 +1,11 @@
1
+ export interface FunnelStage {
2
+ value: number
3
+ conversionRate: number
4
+ }
5
+
6
+ export interface StageRow {
7
+ count: number
8
+ conversionRate: number
9
+ avgDays: number
10
+ dropoff: number
11
+ }
@@ -0,0 +1,12 @@
1
+ {
2
+ "name": "funnel",
3
+ "description": "Funnel — Pipeline-centric layout: 4 summary KPIs (Top of funnel / End-to-end conversion / Best stage / Bottleneck) on top, then a left-aligned funnel chart coloured from the theme tokens with a per-stage drop-off + pass-through sidebar, and a stage-performance ranking table below. For conversion / pipeline analysis dashboards.",
4
+ "version": "0.1.0",
5
+ "files": [
6
+ { "src": "pages/index.tsx", "dest": "index.tsx", "action": "replace" },
7
+ { "src": "components/stage-table.tsx", "dest": "components/funnel/stage-table.tsx", "action": "add" },
8
+ { "src": "components/chart-helpers.ts", "dest": "components/funnel/chart-helpers.ts", "action": "add" },
9
+ { "src": "lib/mock-data.ts", "dest": "components/funnel/mock-data.ts", "action": "add", "relabel": false },
10
+ { "src": "lib/types.ts", "dest": "components/funnel/types.ts", "action": "add", "relabel": false }
11
+ ]
12
+ }