@helloleo/plugins 0.2.0-beta.3 → 0.2.0-beta.5
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/dist/index.js +568 -14
- package/dist/templates/tanstack-start-2026-06-18/package.json +63 -63
- package/dist/templates/tanstack-start-2026-06-18/src/components/ui/calendar.tsx +1 -1
- package/dist/templates/tanstack-start-2026-06-18/src/components/ui/chart.tsx +253 -213
- package/dist/templates/tanstack-start-2026-06-18/src/routeTree.gen.ts +30 -3
- package/dist/templates/tanstack-start-2026-06-18/src/routes/README.md +15 -0
- package/dist/templates/tanstack-start-2026-06-18/src/routes/__root.tsx +3 -2
- package/package.json +1 -1
- package/src/index.ts +1 -0
- package/src/templates/tanstack-start-2026-06-18/package.json +63 -63
- package/src/templates/tanstack-start-2026-06-18/src/components/ui/calendar.tsx +1 -1
- package/src/templates/tanstack-start-2026-06-18/src/components/ui/chart.tsx +253 -213
- package/src/templates/tanstack-start-2026-06-18/src/routeTree.gen.ts +30 -3
- package/src/templates/tanstack-start-2026-06-18/src/routes/README.md +15 -0
- package/src/templates/tanstack-start-2026-06-18/src/routes/__root.tsx +3 -2
- package/src/tools/inspect-http.ts +47 -12
- package/src/tools/scaffold.ts +1 -1
- package/src/tools/setup-password-auth.ts +599 -0
|
@@ -1,71 +1,91 @@
|
|
|
1
|
-
import * as React from "react"
|
|
2
|
-
import * as RechartsPrimitive from "recharts"
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
import * as RechartsPrimitive from "recharts"
|
|
3
|
+
import type { TooltipValueType } from "recharts"
|
|
3
4
|
|
|
4
|
-
import { cn } from "@/lib/utils"
|
|
5
|
+
import { cn } from "@/lib/utils"
|
|
5
6
|
|
|
6
7
|
// Format: { THEME_NAME: CSS_SELECTOR }
|
|
7
|
-
const THEMES = { light: "", dark: ".dark" } as const
|
|
8
|
+
const THEMES = { light: "", dark: ".dark" } as const
|
|
8
9
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
const INITIAL_DIMENSION = { width: 320, height: 200 } as const
|
|
11
|
+
type TooltipNameType = number | string
|
|
12
|
+
|
|
13
|
+
export type ChartConfig = Record<
|
|
14
|
+
string,
|
|
15
|
+
{
|
|
16
|
+
label?: React.ReactNode
|
|
17
|
+
icon?: React.ComponentType
|
|
13
18
|
} & (
|
|
14
19
|
| { color?: string; theme?: never }
|
|
15
20
|
| { color?: never; theme: Record<keyof typeof THEMES, string> }
|
|
16
|
-
)
|
|
17
|
-
|
|
21
|
+
)
|
|
22
|
+
>
|
|
18
23
|
|
|
19
24
|
type ChartContextProps = {
|
|
20
|
-
config: ChartConfig
|
|
21
|
-
}
|
|
25
|
+
config: ChartConfig
|
|
26
|
+
}
|
|
22
27
|
|
|
23
|
-
const ChartContext = React.createContext<ChartContextProps | null>(null)
|
|
28
|
+
const ChartContext = React.createContext<ChartContextProps | null>(null)
|
|
24
29
|
|
|
25
30
|
function useChart() {
|
|
26
|
-
const context = React.useContext(ChartContext)
|
|
31
|
+
const context = React.useContext(ChartContext)
|
|
27
32
|
|
|
28
33
|
if (!context) {
|
|
29
|
-
throw new Error("useChart must be used within a <ChartContainer />")
|
|
34
|
+
throw new Error("useChart must be used within a <ChartContainer />")
|
|
30
35
|
}
|
|
31
36
|
|
|
32
|
-
return context
|
|
37
|
+
return context
|
|
33
38
|
}
|
|
34
39
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
+
function ChartContainer({
|
|
41
|
+
id,
|
|
42
|
+
className,
|
|
43
|
+
children,
|
|
44
|
+
config,
|
|
45
|
+
initialDimension = INITIAL_DIMENSION,
|
|
46
|
+
...props
|
|
47
|
+
}: React.ComponentProps<"div"> & {
|
|
48
|
+
config: ChartConfig
|
|
49
|
+
children: React.ComponentProps<
|
|
50
|
+
typeof RechartsPrimitive.ResponsiveContainer
|
|
51
|
+
>["children"]
|
|
52
|
+
initialDimension?: {
|
|
53
|
+
width: number
|
|
54
|
+
height: number
|
|
40
55
|
}
|
|
41
|
-
|
|
42
|
-
const uniqueId = React.useId()
|
|
43
|
-
const chartId = `chart-${id
|
|
56
|
+
}) {
|
|
57
|
+
const uniqueId = React.useId()
|
|
58
|
+
const chartId = `chart-${id ?? uniqueId.replace(/:/g, "")}`
|
|
44
59
|
|
|
45
60
|
return (
|
|
46
61
|
<ChartContext.Provider value={{ config }}>
|
|
47
62
|
<div
|
|
63
|
+
data-slot="chart"
|
|
48
64
|
data-chart={chartId}
|
|
49
|
-
ref={ref}
|
|
50
65
|
className={cn(
|
|
51
|
-
"flex aspect-video justify-center text-xs [&_.recharts-cartesian-axis-tick_text]:fill-muted-foreground [&_.recharts-cartesian-grid_line[stroke='#ccc']]:stroke-border/50 [&_.recharts-curve.recharts-tooltip-cursor]:stroke-border [&_.recharts-dot[stroke='#fff']]:stroke-transparent [&_.recharts-layer]:outline-
|
|
52
|
-
className
|
|
66
|
+
"flex aspect-video justify-center text-xs [&_.recharts-cartesian-axis-tick_text]:fill-muted-foreground [&_.recharts-cartesian-grid_line[stroke='#ccc']]:stroke-border/50 [&_.recharts-curve.recharts-tooltip-cursor]:stroke-border [&_.recharts-dot[stroke='#fff']]:stroke-transparent [&_.recharts-layer]:outline-hidden [&_.recharts-polar-grid_[stroke='#ccc']]:stroke-border [&_.recharts-radial-bar-background-sector]:fill-muted [&_.recharts-rectangle.recharts-tooltip-cursor]:fill-muted [&_.recharts-reference-line_[stroke='#ccc']]:stroke-border [&_.recharts-sector]:outline-hidden [&_.recharts-sector[stroke='#fff']]:stroke-transparent [&_.recharts-surface]:outline-hidden",
|
|
67
|
+
className
|
|
53
68
|
)}
|
|
54
69
|
{...props}
|
|
55
70
|
>
|
|
56
71
|
<ChartStyle id={chartId} config={config} />
|
|
57
|
-
<RechartsPrimitive.ResponsiveContainer
|
|
72
|
+
<RechartsPrimitive.ResponsiveContainer
|
|
73
|
+
initialDimension={initialDimension}
|
|
74
|
+
>
|
|
75
|
+
{children}
|
|
76
|
+
</RechartsPrimitive.ResponsiveContainer>
|
|
58
77
|
</div>
|
|
59
78
|
</ChartContext.Provider>
|
|
60
|
-
)
|
|
61
|
-
}
|
|
62
|
-
ChartContainer.displayName = "Chart";
|
|
79
|
+
)
|
|
80
|
+
}
|
|
63
81
|
|
|
64
82
|
const ChartStyle = ({ id, config }: { id: string; config: ChartConfig }) => {
|
|
65
|
-
const colorConfig = Object.entries(config).filter(
|
|
83
|
+
const colorConfig = Object.entries(config).filter(
|
|
84
|
+
([, config]) => config.theme ?? config.color
|
|
85
|
+
)
|
|
66
86
|
|
|
67
87
|
if (!colorConfig.length) {
|
|
68
|
-
return null
|
|
88
|
+
return null
|
|
69
89
|
}
|
|
70
90
|
|
|
71
91
|
return (
|
|
@@ -77,203 +97,214 @@ const ChartStyle = ({ id, config }: { id: string; config: ChartConfig }) => {
|
|
|
77
97
|
${prefix} [data-chart=${id}] {
|
|
78
98
|
${colorConfig
|
|
79
99
|
.map(([key, itemConfig]) => {
|
|
80
|
-
const color =
|
|
81
|
-
|
|
100
|
+
const color =
|
|
101
|
+
itemConfig.theme?.[theme as keyof typeof itemConfig.theme] ??
|
|
102
|
+
itemConfig.color
|
|
103
|
+
return color ? ` --color-${key}: ${color};` : null
|
|
82
104
|
})
|
|
83
105
|
.join("\n")}
|
|
84
106
|
}
|
|
85
|
-
|
|
107
|
+
`
|
|
86
108
|
)
|
|
87
109
|
.join("\n"),
|
|
88
110
|
}}
|
|
89
111
|
/>
|
|
90
|
-
)
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
const ChartTooltip = RechartsPrimitive.Tooltip
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
112
|
+
)
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const ChartTooltip = RechartsPrimitive.Tooltip
|
|
116
|
+
|
|
117
|
+
function ChartTooltipContent({
|
|
118
|
+
active,
|
|
119
|
+
payload,
|
|
120
|
+
className,
|
|
121
|
+
indicator = "dot",
|
|
122
|
+
hideLabel = false,
|
|
123
|
+
hideIndicator = false,
|
|
124
|
+
label,
|
|
125
|
+
labelFormatter,
|
|
126
|
+
labelClassName,
|
|
127
|
+
formatter,
|
|
128
|
+
color,
|
|
129
|
+
nameKey,
|
|
130
|
+
labelKey,
|
|
131
|
+
}: React.ComponentProps<typeof RechartsPrimitive.Tooltip> &
|
|
132
|
+
React.ComponentProps<"div"> & {
|
|
133
|
+
hideLabel?: boolean
|
|
134
|
+
hideIndicator?: boolean
|
|
135
|
+
indicator?: "line" | "dot" | "dashed"
|
|
136
|
+
nameKey?: string
|
|
137
|
+
labelKey?: string
|
|
138
|
+
} & Omit<
|
|
139
|
+
RechartsPrimitive.DefaultTooltipContentProps<
|
|
140
|
+
TooltipValueType,
|
|
141
|
+
TooltipNameType
|
|
142
|
+
>,
|
|
143
|
+
"accessibilityLayer"
|
|
144
|
+
>) {
|
|
145
|
+
const { config } = useChart()
|
|
146
|
+
|
|
147
|
+
const tooltipLabel = React.useMemo(() => {
|
|
148
|
+
if (hideLabel || !payload?.length) {
|
|
149
|
+
return null
|
|
104
150
|
}
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
{
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
labelKey,
|
|
121
|
-
},
|
|
122
|
-
ref,
|
|
123
|
-
) => {
|
|
124
|
-
const { config } = useChart();
|
|
125
|
-
|
|
126
|
-
const tooltipLabel = React.useMemo(() => {
|
|
127
|
-
if (hideLabel || !payload?.length) {
|
|
128
|
-
return null;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
const [item] = payload;
|
|
132
|
-
const key = `${labelKey || item?.dataKey || item?.name || "value"}`;
|
|
133
|
-
const itemConfig = getPayloadConfigFromPayload(config, item, key);
|
|
134
|
-
const value =
|
|
135
|
-
!labelKey && typeof label === "string"
|
|
136
|
-
? config[label as keyof typeof config]?.label || label
|
|
137
|
-
: itemConfig?.label;
|
|
138
|
-
|
|
139
|
-
if (labelFormatter) {
|
|
140
|
-
return (
|
|
141
|
-
<div className={cn("font-medium", labelClassName)}>{labelFormatter(value, payload)}</div>
|
|
142
|
-
);
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
if (!value) {
|
|
146
|
-
return null;
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
return <div className={cn("font-medium", labelClassName)}>{value}</div>;
|
|
150
|
-
}, [label, labelFormatter, payload, hideLabel, labelClassName, config, labelKey]);
|
|
151
|
-
|
|
152
|
-
if (!active || !payload?.length) {
|
|
153
|
-
return null;
|
|
151
|
+
|
|
152
|
+
const [item] = payload
|
|
153
|
+
const key = `${labelKey ?? item?.dataKey ?? item?.name ?? "value"}`
|
|
154
|
+
const itemConfig = getPayloadConfigFromPayload(config, item, key)
|
|
155
|
+
const value =
|
|
156
|
+
!labelKey && typeof label === "string"
|
|
157
|
+
? (config[label]?.label ?? label)
|
|
158
|
+
: itemConfig?.label
|
|
159
|
+
|
|
160
|
+
if (labelFormatter) {
|
|
161
|
+
return (
|
|
162
|
+
<div className={cn("font-medium", labelClassName)}>
|
|
163
|
+
{labelFormatter(value, payload)}
|
|
164
|
+
</div>
|
|
165
|
+
)
|
|
154
166
|
}
|
|
155
167
|
|
|
156
|
-
|
|
168
|
+
if (!value) {
|
|
169
|
+
return null
|
|
170
|
+
}
|
|
157
171
|
|
|
158
|
-
return (
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
172
|
+
return <div className={cn("font-medium", labelClassName)}>{value}</div>
|
|
173
|
+
}, [
|
|
174
|
+
label,
|
|
175
|
+
labelFormatter,
|
|
176
|
+
payload,
|
|
177
|
+
hideLabel,
|
|
178
|
+
labelClassName,
|
|
179
|
+
config,
|
|
180
|
+
labelKey,
|
|
181
|
+
])
|
|
182
|
+
|
|
183
|
+
if (!active || !payload?.length) {
|
|
184
|
+
return null
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
const nestLabel = payload.length === 1 && indicator !== "dot"
|
|
188
|
+
|
|
189
|
+
return (
|
|
190
|
+
<div
|
|
191
|
+
className={cn(
|
|
192
|
+
"grid min-w-32 items-start gap-1.5 rounded-lg border border-border/50 bg-background px-2.5 py-1.5 text-xs shadow-xl",
|
|
193
|
+
className
|
|
194
|
+
)}
|
|
195
|
+
>
|
|
196
|
+
{!nestLabel ? tooltipLabel : null}
|
|
197
|
+
<div className="grid gap-1.5">
|
|
198
|
+
{payload
|
|
199
|
+
.filter((item) => item.type !== "none")
|
|
200
|
+
.map((item, index) => {
|
|
201
|
+
const key = `${nameKey ?? item.name ?? item.dataKey ?? "value"}`
|
|
202
|
+
const itemConfig = getPayloadConfigFromPayload(config, item, key)
|
|
203
|
+
const indicatorColor = color ?? item.payload?.fill ?? item.color
|
|
204
|
+
|
|
205
|
+
return (
|
|
206
|
+
<div
|
|
207
|
+
key={index}
|
|
208
|
+
className={cn(
|
|
209
|
+
"flex w-full flex-wrap items-stretch gap-2 [&>svg]:h-2.5 [&>svg]:w-2.5 [&>svg]:text-muted-foreground",
|
|
210
|
+
indicator === "dot" && "items-center"
|
|
211
|
+
)}
|
|
212
|
+
>
|
|
213
|
+
{formatter && item?.value !== undefined && item.name ? (
|
|
214
|
+
formatter(item.value, item.name, item, index, item.payload)
|
|
215
|
+
) : (
|
|
216
|
+
<>
|
|
217
|
+
{itemConfig?.icon ? (
|
|
218
|
+
<itemConfig.icon />
|
|
219
|
+
) : (
|
|
220
|
+
!hideIndicator && (
|
|
221
|
+
<div
|
|
222
|
+
className={cn(
|
|
223
|
+
"shrink-0 rounded-[2px] border-(--color-border) bg-(--color-bg)",
|
|
224
|
+
{
|
|
225
|
+
"h-2.5 w-2.5": indicator === "dot",
|
|
226
|
+
"w-1": indicator === "line",
|
|
227
|
+
"w-0 border-[1.5px] border-dashed bg-transparent":
|
|
228
|
+
indicator === "dashed",
|
|
229
|
+
"my-0.5": nestLabel && indicator === "dashed",
|
|
207
230
|
}
|
|
208
|
-
|
|
209
|
-
|
|
231
|
+
)}
|
|
232
|
+
style={
|
|
233
|
+
{
|
|
234
|
+
"--color-bg": indicatorColor,
|
|
235
|
+
"--color-border": indicatorColor,
|
|
236
|
+
} as React.CSSProperties
|
|
237
|
+
}
|
|
238
|
+
/>
|
|
239
|
+
)
|
|
240
|
+
)}
|
|
241
|
+
<div
|
|
242
|
+
className={cn(
|
|
243
|
+
"flex flex-1 justify-between leading-none",
|
|
244
|
+
nestLabel ? "items-end" : "items-center"
|
|
210
245
|
)}
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
<div className="grid gap-1.5">
|
|
218
|
-
{nestLabel ? tooltipLabel : null}
|
|
219
|
-
<span className="text-muted-foreground">
|
|
220
|
-
{itemConfig?.label || item.name}
|
|
221
|
-
</span>
|
|
222
|
-
</div>
|
|
223
|
-
{item.value && (
|
|
224
|
-
<span className="font-mono font-medium tabular-nums text-foreground">
|
|
225
|
-
{item.value.toLocaleString()}
|
|
226
|
-
</span>
|
|
227
|
-
)}
|
|
246
|
+
>
|
|
247
|
+
<div className="grid gap-1.5">
|
|
248
|
+
{nestLabel ? tooltipLabel : null}
|
|
249
|
+
<span className="text-muted-foreground">
|
|
250
|
+
{itemConfig?.label ?? item.name}
|
|
251
|
+
</span>
|
|
228
252
|
</div>
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
253
|
+
{item.value != null && (
|
|
254
|
+
<span className="font-mono font-medium text-foreground tabular-nums">
|
|
255
|
+
{typeof item.value === "number"
|
|
256
|
+
? item.value.toLocaleString()
|
|
257
|
+
: String(item.value)}
|
|
258
|
+
</span>
|
|
259
|
+
)}
|
|
260
|
+
</div>
|
|
261
|
+
</>
|
|
262
|
+
)}
|
|
263
|
+
</div>
|
|
264
|
+
)
|
|
265
|
+
})}
|
|
235
266
|
</div>
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
267
|
+
</div>
|
|
268
|
+
)
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
const ChartLegend = RechartsPrimitive.Legend
|
|
272
|
+
|
|
273
|
+
function ChartLegendContent({
|
|
274
|
+
className,
|
|
275
|
+
hideIcon = false,
|
|
276
|
+
payload,
|
|
277
|
+
verticalAlign = "bottom",
|
|
278
|
+
nameKey,
|
|
279
|
+
}: React.ComponentProps<"div"> & {
|
|
280
|
+
hideIcon?: boolean
|
|
281
|
+
nameKey?: string
|
|
282
|
+
} & RechartsPrimitive.DefaultLegendContentProps) {
|
|
283
|
+
const { config } = useChart()
|
|
252
284
|
|
|
253
285
|
if (!payload?.length) {
|
|
254
|
-
return null
|
|
286
|
+
return null
|
|
255
287
|
}
|
|
256
288
|
|
|
257
289
|
return (
|
|
258
290
|
<div
|
|
259
|
-
ref={ref}
|
|
260
291
|
className={cn(
|
|
261
292
|
"flex items-center justify-center gap-4",
|
|
262
293
|
verticalAlign === "top" ? "pb-3" : "pt-3",
|
|
263
|
-
className
|
|
294
|
+
className
|
|
264
295
|
)}
|
|
265
296
|
>
|
|
266
297
|
{payload
|
|
267
298
|
.filter((item) => item.type !== "none")
|
|
268
|
-
.map((item) => {
|
|
269
|
-
const key = `${nameKey
|
|
270
|
-
const itemConfig = getPayloadConfigFromPayload(config, item, key)
|
|
299
|
+
.map((item, index) => {
|
|
300
|
+
const key = `${nameKey ?? item.dataKey ?? "value"}`
|
|
301
|
+
const itemConfig = getPayloadConfigFromPayload(config, item, key)
|
|
271
302
|
|
|
272
303
|
return (
|
|
273
304
|
<div
|
|
274
|
-
key={
|
|
305
|
+
key={index}
|
|
275
306
|
className={cn(
|
|
276
|
-
"flex items-center gap-1.5 [&>svg]:h-3 [&>svg]:w-3 [&>svg]:text-muted-foreground"
|
|
307
|
+
"flex items-center gap-1.5 [&>svg]:h-3 [&>svg]:w-3 [&>svg]:text-muted-foreground"
|
|
277
308
|
)}
|
|
278
309
|
>
|
|
279
310
|
{itemConfig?.icon && !hideIcon ? (
|
|
@@ -288,37 +319,46 @@ const ChartLegendContent = React.forwardRef<
|
|
|
288
319
|
)}
|
|
289
320
|
{itemConfig?.label}
|
|
290
321
|
</div>
|
|
291
|
-
)
|
|
322
|
+
)
|
|
292
323
|
})}
|
|
293
324
|
</div>
|
|
294
|
-
)
|
|
295
|
-
}
|
|
296
|
-
ChartLegendContent.displayName = "ChartLegend";
|
|
325
|
+
)
|
|
326
|
+
}
|
|
297
327
|
|
|
298
|
-
|
|
299
|
-
|
|
328
|
+
function getPayloadConfigFromPayload(
|
|
329
|
+
config: ChartConfig,
|
|
330
|
+
payload: unknown,
|
|
331
|
+
key: string
|
|
332
|
+
) {
|
|
300
333
|
if (typeof payload !== "object" || payload === null) {
|
|
301
|
-
return undefined
|
|
334
|
+
return undefined
|
|
302
335
|
}
|
|
303
336
|
|
|
304
337
|
const payloadPayload =
|
|
305
|
-
"payload" in payload &&
|
|
338
|
+
"payload" in payload &&
|
|
339
|
+
typeof payload.payload === "object" &&
|
|
340
|
+
payload.payload !== null
|
|
306
341
|
? payload.payload
|
|
307
|
-
: undefined
|
|
342
|
+
: undefined
|
|
308
343
|
|
|
309
|
-
let configLabelKey: string = key
|
|
344
|
+
let configLabelKey: string = key
|
|
310
345
|
|
|
311
|
-
if (
|
|
312
|
-
|
|
346
|
+
if (
|
|
347
|
+
key in payload &&
|
|
348
|
+
typeof payload[key as keyof typeof payload] === "string"
|
|
349
|
+
) {
|
|
350
|
+
configLabelKey = payload[key as keyof typeof payload] as string
|
|
313
351
|
} else if (
|
|
314
352
|
payloadPayload &&
|
|
315
353
|
key in payloadPayload &&
|
|
316
354
|
typeof payloadPayload[key as keyof typeof payloadPayload] === "string"
|
|
317
355
|
) {
|
|
318
|
-
configLabelKey = payloadPayload[
|
|
356
|
+
configLabelKey = payloadPayload[
|
|
357
|
+
key as keyof typeof payloadPayload
|
|
358
|
+
] as string
|
|
319
359
|
}
|
|
320
360
|
|
|
321
|
-
return configLabelKey in config ? config[configLabelKey] : config[key
|
|
361
|
+
return configLabelKey in config ? config[configLabelKey] : config[key]
|
|
322
362
|
}
|
|
323
363
|
|
|
324
364
|
export {
|
|
@@ -328,4 +368,4 @@ export {
|
|
|
328
368
|
ChartLegend,
|
|
329
369
|
ChartLegendContent,
|
|
330
370
|
ChartStyle,
|
|
331
|
-
}
|
|
371
|
+
}
|
|
@@ -10,33 +10,43 @@
|
|
|
10
10
|
|
|
11
11
|
import { Route as rootRouteImport } from './routes/__root'
|
|
12
12
|
import { Route as IndexRouteImport } from './routes/index'
|
|
13
|
+
import { Route as ApiHealthRouteImport } from './routes/api/health'
|
|
13
14
|
|
|
14
15
|
const IndexRoute = IndexRouteImport.update({
|
|
15
16
|
id: '/',
|
|
16
17
|
path: '/',
|
|
17
18
|
getParentRoute: () => rootRouteImport,
|
|
18
19
|
} as any)
|
|
20
|
+
const ApiHealthRoute = ApiHealthRouteImport.update({
|
|
21
|
+
id: '/api/health',
|
|
22
|
+
path: '/api/health',
|
|
23
|
+
getParentRoute: () => rootRouteImport,
|
|
24
|
+
} as any)
|
|
19
25
|
|
|
20
26
|
export interface FileRoutesByFullPath {
|
|
21
27
|
'/': typeof IndexRoute
|
|
28
|
+
'/api/health': typeof ApiHealthRoute
|
|
22
29
|
}
|
|
23
30
|
export interface FileRoutesByTo {
|
|
24
31
|
'/': typeof IndexRoute
|
|
32
|
+
'/api/health': typeof ApiHealthRoute
|
|
25
33
|
}
|
|
26
34
|
export interface FileRoutesById {
|
|
27
35
|
__root__: typeof rootRouteImport
|
|
28
36
|
'/': typeof IndexRoute
|
|
37
|
+
'/api/health': typeof ApiHealthRoute
|
|
29
38
|
}
|
|
30
39
|
export interface FileRouteTypes {
|
|
31
40
|
fileRoutesByFullPath: FileRoutesByFullPath
|
|
32
|
-
fullPaths: '/'
|
|
41
|
+
fullPaths: '/' | '/api/health'
|
|
33
42
|
fileRoutesByTo: FileRoutesByTo
|
|
34
|
-
to: '/'
|
|
35
|
-
id: '__root__' | '/'
|
|
43
|
+
to: '/' | '/api/health'
|
|
44
|
+
id: '__root__' | '/' | '/api/health'
|
|
36
45
|
fileRoutesById: FileRoutesById
|
|
37
46
|
}
|
|
38
47
|
export interface RootRouteChildren {
|
|
39
48
|
IndexRoute: typeof IndexRoute
|
|
49
|
+
ApiHealthRoute: typeof ApiHealthRoute
|
|
40
50
|
}
|
|
41
51
|
|
|
42
52
|
declare module '@tanstack/react-router' {
|
|
@@ -48,12 +58,29 @@ declare module '@tanstack/react-router' {
|
|
|
48
58
|
preLoaderRoute: typeof IndexRouteImport
|
|
49
59
|
parentRoute: typeof rootRouteImport
|
|
50
60
|
}
|
|
61
|
+
'/api/health': {
|
|
62
|
+
id: '/api/health'
|
|
63
|
+
path: '/api/health'
|
|
64
|
+
fullPath: '/api/health'
|
|
65
|
+
preLoaderRoute: typeof ApiHealthRouteImport
|
|
66
|
+
parentRoute: typeof rootRouteImport
|
|
67
|
+
}
|
|
51
68
|
}
|
|
52
69
|
}
|
|
53
70
|
|
|
54
71
|
const rootRouteChildren: RootRouteChildren = {
|
|
55
72
|
IndexRoute: IndexRoute,
|
|
73
|
+
ApiHealthRoute: ApiHealthRoute,
|
|
56
74
|
}
|
|
57
75
|
export const routeTree = rootRouteImport
|
|
58
76
|
._addFileChildren(rootRouteChildren)
|
|
59
77
|
._addFileTypes<FileRouteTypes>()
|
|
78
|
+
|
|
79
|
+
import type { getRouter } from './router.tsx'
|
|
80
|
+
import type { createStart } from '@tanstack/react-start'
|
|
81
|
+
declare module '@tanstack/react-start' {
|
|
82
|
+
interface Register {
|
|
83
|
+
ssr: true
|
|
84
|
+
router: Awaited<ReturnType<typeof getRouter>>
|
|
85
|
+
}
|
|
86
|
+
}
|
|
@@ -23,6 +23,21 @@ TanStack Start uses file-based routing. Every `.ts` and `.tsx` file in this dire
|
|
|
23
23
|
- Do not `export default` route components, it breaks code splitting. Use a named function passed as `component:`.
|
|
24
24
|
- `routeTree.gen.ts` is auto-generated. Don't edit it by hand.
|
|
25
25
|
|
|
26
|
+
## API routes vs server functions
|
|
27
|
+
|
|
28
|
+
TanStack Start offers two ways to run server-side code:
|
|
29
|
+
|
|
30
|
+
- [Server routes](https://tanstack.com/start/latest/docs/framework/react/guide/server-routes) — files under `src/routes/api/` that export HTTP handlers.
|
|
31
|
+
- [Server functions](https://tanstack.com/start/latest/docs/framework/react/guide/server-functions) — `createServerFn()` calls colocated with components.
|
|
32
|
+
|
|
33
|
+
Prefer API routes. Create them under `src/routes/api/` following the same
|
|
34
|
+
convention as `src/routes/api/health.ts` (one handler per HTTP method under
|
|
35
|
+
`server.handlers`, returning `Response.json()`). This is more scalable and
|
|
36
|
+
gives the user visually readable endpoints (`/api/todos`, `/api/todos/:id`)
|
|
37
|
+
they can recognize in the network monitor. Server functions are served from
|
|
38
|
+
generated URLs that read as random gibberish, which makes requests hard to
|
|
39
|
+
identify and debug.
|
|
40
|
+
|
|
26
41
|
## Authenticated routes
|
|
27
42
|
|
|
28
43
|
Put protected pages inside a pathless `_authed` layout and keep login/signup
|