@tanstack/cta-framework-react-cra 0.37.2 → 0.38.0

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.
@@ -4,4 +4,7 @@ Sentry.init({
4
4
  // Adds request headers and IP for users, for more info visit:
5
5
  // https://docs.sentry.io/platforms/javascript/guides/tanstackstart-react/configuration/options/#sendDefaultPii
6
6
  sendDefaultPii: true,
7
+ tracesSampleRate: 1.0,
8
+ replaysSessionSampleRate: 1.0,
9
+ replaysOnErrorSampleRate: 1.0,
7
10
  })
@@ -10,7 +10,7 @@ import * as fs from 'node:fs/promises'
10
10
  import { createFileRoute } from '@tanstack/react-router'
11
11
  import { createServerFn } from '@tanstack/react-start'
12
12
  import * as Sentry from '@sentry/tanstackstart-react'
13
- import { useState, useEffect, useRef } from 'react'
13
+ import { useState, useEffect } from 'react'
14
14
 
15
15
  export const Route = createFileRoute('/demo/sentry/testing')({
16
16
  component: RouteComponent,
@@ -18,10 +18,38 @@ export const Route = createFileRoute('/demo/sentry/testing')({
18
18
  useEffect(() => {
19
19
  Sentry.captureException(error)
20
20
  }, [error])
21
- return <div>Error: {error.message}</div>
21
+ return (
22
+ <div className="min-h-screen flex items-center justify-center bg-[#181423]">
23
+ <div className="text-center p-8">
24
+ <SentryLogo />
25
+ <h1 className="text-2xl font-bold text-white mt-4 mb-2">
26
+ Something went wrong
27
+ </h1>
28
+ <p className="text-[#A49FB5]">{error.message}</p>
29
+ </div>
30
+ </div>
31
+ )
22
32
  },
23
33
  })
24
34
 
35
+ // Sentry Logo Component
36
+ function SentryLogo({ size = 48 }: { size?: number }) {
37
+ return (
38
+ <svg
39
+ height={size}
40
+ width={size}
41
+ fill="none"
42
+ xmlns="http://www.w3.org/2000/svg"
43
+ viewBox="0 0 40 40"
44
+ >
45
+ <path
46
+ d="M21.85 2.995a3.698 3.698 0 0 1 1.353 1.354l16.303 28.278a3.703 3.703 0 0 1-1.354 5.053 3.694 3.694 0 0 1-1.848.496h-3.828a31.149 31.149 0 0 0 0-3.09h3.815a.61.61 0 0 0 .537-.917L20.523 5.893a.61.61 0 0 0-1.057 0l-3.739 6.494a28.948 28.948 0 0 1 9.63 10.453 28.988 28.988 0 0 1 3.499 13.78v1.542h-9.852v-1.544a19.106 19.106 0 0 0-2.182-8.85 19.08 19.08 0 0 0-6.032-6.829l-1.85 3.208a15.377 15.377 0 0 1 6.382 12.484v1.542H3.696A3.694 3.694 0 0 1 0 34.473c0-.648.17-1.286.494-1.849l2.33-4.074a8.562 8.562 0 0 1 2.689 1.536L3.158 34.17a.611.611 0 0 0 .538.917h8.448a12.481 12.481 0 0 0-6.037-9.09l-1.344-.772 4.908-8.545 1.344.77a22.16 22.16 0 0 1 7.705 7.444 22.193 22.193 0 0 1 3.316 10.193h3.699a25.892 25.892 0 0 0-3.811-12.033 25.856 25.856 0 0 0-9.046-8.796l-1.344-.772 5.269-9.136a3.698 3.698 0 0 1 3.2-1.849c.648 0 1.285.17 1.847.495Z"
47
+ fill="currentColor"
48
+ />
49
+ </svg>
50
+ )
51
+ }
52
+
25
53
  // Server function that will error
26
54
  const badServerFunc = createServerFn({
27
55
  method: 'GET',
@@ -59,65 +87,224 @@ const goodServerFunc = createServerFn({
59
87
  )
60
88
  })
61
89
 
62
- function RouteComponent() {
63
- const [isLoading, setIsLoading] = useState<Record<string, boolean>>({})
64
- const [hasError, setHasError] = useState<Record<string, boolean>>({})
65
- const [showTrace, setShowTrace] = useState<Record<string, boolean>>({})
66
- const [spanOps, setSpanOps] = useState<Record<string, string>>({})
67
- const [demoStep, setDemoStep] = useState(0)
68
- const [replayEvents, setReplayEvents] = useState<string[]>([])
69
- const [copiedSpan, setCopiedSpan] = useState<string | null>(null)
70
- const startTimeRef = useRef<string>('')
90
+ // 3D Button Component inspired by Sentry wizard
91
+ function SentryButton({
92
+ children,
93
+ onClick,
94
+ variant = 'primary',
95
+ disabled = false,
96
+ loading = false,
97
+ }: {
98
+ children: React.ReactNode
99
+ onClick: () => void
100
+ variant?: 'primary' | 'error'
101
+ disabled?: boolean
102
+ loading?: boolean
103
+ }) {
104
+ const baseColor = variant === 'error' ? '#E50045' : '#553DB8'
105
+ const topColor = variant === 'error' ? '#FF1A5C' : '#7553FF'
71
106
 
72
- useEffect(() => {
73
- // Set initial timestamp only once on client
74
- if (!startTimeRef.current) {
75
- startTimeRef.current = new Date().toISOString()
76
- }
107
+ return (
108
+ <button
109
+ type="button"
110
+ onClick={onClick}
111
+ disabled={disabled || loading}
112
+ className="group w-full rounded-lg text-white cursor-pointer border-none p-0 transition-all disabled:cursor-not-allowed disabled:opacity-60"
113
+ style={{ backgroundColor: baseColor }}
114
+ >
115
+ <span
116
+ className="flex items-center justify-center gap-3 px-6 py-4 rounded-lg text-lg font-semibold transition-transform group-hover:-translate-y-1 group-active:translate-y-0 group-disabled:translate-y-0"
117
+ style={{
118
+ backgroundColor: topColor,
119
+ border: `1px solid ${baseColor}`,
120
+ }}
121
+ >
122
+ {loading && (
123
+ <svg
124
+ className="animate-spin h-5 w-5"
125
+ xmlns="http://www.w3.org/2000/svg"
126
+ fill="none"
127
+ viewBox="0 0 24 24"
128
+ >
129
+ <circle
130
+ className="opacity-25"
131
+ cx="12"
132
+ cy="12"
133
+ r="10"
134
+ stroke="currentColor"
135
+ strokeWidth="4"
136
+ />
137
+ <path
138
+ className="opacity-75"
139
+ fill="currentColor"
140
+ d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
141
+ />
142
+ </svg>
143
+ )}
144
+ {children}
145
+ </span>
146
+ </button>
147
+ )
148
+ }
77
149
 
78
- if (demoStep > 0) {
79
- const secondsElapsed = (
80
- (new Date().getTime() - new Date(startTimeRef.current).getTime()) /
81
- 1000
82
- ).toFixed(1)
83
- setReplayEvents((prev) => [
84
- ...prev,
85
- `Step ${demoStep}: +${secondsElapsed}s`,
86
- ])
87
- }
88
- }, [demoStep])
150
+ // Feature Card Component
151
+ function FeatureCard({
152
+ icon,
153
+ title,
154
+ description,
155
+ }: {
156
+ icon: React.ReactNode
157
+ title: string
158
+ description: string
159
+ }) {
160
+ return (
161
+ <div className="bg-[#1C1825] rounded-xl p-4 border border-[#2D2640] hover:border-[#7553FF]/50 transition-all group">
162
+ <div className="flex items-center gap-3 mb-2">
163
+ <div className="text-[#7553FF] group-hover:scale-110 transition-transform">
164
+ {icon}
165
+ </div>
166
+ <h3 className="font-semibold text-white">{title}</h3>
167
+ </div>
168
+ <p className="text-sm text-[#A49FB5] pl-9">{description}</p>
169
+ </div>
170
+ )
171
+ }
172
+
173
+ // Result Badge Component
174
+ function ResultBadge({
175
+ type,
176
+ spanOp,
177
+ onCopy,
178
+ }: {
179
+ type: 'success' | 'error'
180
+ spanOp: string
181
+ onCopy: () => void
182
+ }) {
183
+ const [copied, setCopied] = useState(false)
89
184
 
90
- const handleCopy = (operation: string) => {
91
- navigator.clipboard.writeText(operation)
92
- setCopiedSpan(operation)
93
- setTimeout(() => setCopiedSpan(null), 2000)
185
+ const handleCopy = () => {
186
+ navigator.clipboard.writeText(spanOp)
187
+ setCopied(true)
188
+ onCopy()
189
+ setTimeout(() => setCopied(false), 2000)
94
190
  }
95
191
 
192
+ return (
193
+ <div className="mt-4 space-y-3">
194
+ {type === 'error' && (
195
+ <div className="flex items-center gap-2 bg-[#E50045]/10 border border-[#E50045]/30 rounded-lg px-4 py-3">
196
+ <svg
197
+ className="w-5 h-5 text-[#FF1A5C]"
198
+ fill="none"
199
+ strokeLinecap="round"
200
+ strokeLinejoin="round"
201
+ strokeWidth="2"
202
+ viewBox="0 0 24 24"
203
+ stroke="currentColor"
204
+ >
205
+ <title>Error captured</title>
206
+ <path d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" />
207
+ </svg>
208
+ <span className="text-[#FF1A5C] text-sm font-medium">
209
+ Error captured and sent to Sentry
210
+ </span>
211
+ </div>
212
+ )}
213
+
214
+ {type === 'success' && (
215
+ <div className="flex items-center gap-2 bg-[#00F261]/10 border border-[#00BF4D]/30 rounded-lg px-4 py-3">
216
+ <svg
217
+ className="w-5 h-5 text-[#00F261]"
218
+ fill="none"
219
+ strokeLinecap="round"
220
+ strokeLinejoin="round"
221
+ strokeWidth="2"
222
+ viewBox="0 0 24 24"
223
+ stroke="currentColor"
224
+ >
225
+ <title>Trace complete</title>
226
+ <path d="M5 13l4 4L19 7" />
227
+ </svg>
228
+ <span className="text-[#00F261] text-sm font-medium">
229
+ Trace completed successfully
230
+ </span>
231
+ </div>
232
+ )}
233
+
234
+ <button
235
+ type="button"
236
+ onClick={handleCopy}
237
+ className="relative flex items-center gap-2 bg-[#7553FF]/10 hover:bg-[#7553FF]/20 border border-[#7553FF]/30 rounded-lg px-4 py-2 transition-all cursor-pointer w-full"
238
+ >
239
+ <span className="text-[#B3A1FF] text-sm">span.op:</span>
240
+ <code className="text-[#7553FF] font-mono text-sm">{spanOp}</code>
241
+ <svg
242
+ className="w-4 h-4 text-[#B3A1FF] ml-auto"
243
+ fill="none"
244
+ strokeLinecap="round"
245
+ strokeLinejoin="round"
246
+ strokeWidth="2"
247
+ viewBox="0 0 24 24"
248
+ stroke="currentColor"
249
+ >
250
+ <title>Copy to clipboard</title>
251
+ <path d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z" />
252
+ </svg>
253
+ {copied && (
254
+ <span className="absolute -top-8 left-1/2 -translate-x-1/2 bg-[#00F261] text-[#181423] text-xs font-medium px-2 py-1 rounded animate-pulse">
255
+ Copied!
256
+ </span>
257
+ )}
258
+ </button>
259
+ </div>
260
+ )
261
+ }
262
+
263
+ // Progress Bar Component
264
+ function ProgressBar({ loading }: { loading: boolean }) {
265
+ return (
266
+ <div className="mt-4 flex items-center gap-3">
267
+ <div
268
+ className={`w-3 h-3 rounded-full transition-all ${loading ? 'bg-[#7553FF] animate-pulse' : 'bg-[#00F261]'}`}
269
+ />
270
+ <div className="flex-1 h-2 bg-[#2D2640] rounded-full overflow-hidden">
271
+ <div
272
+ className="h-full bg-gradient-to-r from-[#7553FF] to-[#B3A1FF] rounded-full transition-all duration-500"
273
+ style={{ width: loading ? '60%' : '100%' }}
274
+ />
275
+ </div>
276
+ <span className="text-xs text-[#A49FB5] w-16 text-right">
277
+ {loading ? 'Running...' : 'Complete'}
278
+ </span>
279
+ </div>
280
+ )
281
+ }
282
+
283
+ function RouteComponent() {
284
+ const [isLoading, setIsLoading] = useState<Record<string, boolean>>({})
285
+ const [results, setResults] = useState<
286
+ Record<string, { type: 'success' | 'error'; spanOp: string }>
287
+ >({})
288
+
96
289
  const handleClientError = async () => {
97
290
  setIsLoading((prev) => ({ ...prev, clientError: true }))
98
- setHasError((prev) => ({ ...prev, clientError: false }))
99
- setShowTrace((prev) => ({ ...prev, clientError: true }))
100
-
101
291
  try {
102
292
  await Sentry.startSpan(
103
- {
104
- name: 'Client Error Flow Demo',
105
- op: 'demo.client-error-flow',
106
- },
293
+ { name: 'Client Error Flow Demo', op: 'demo.client-error' },
107
294
  async () => {
108
295
  Sentry.setContext('demo', {
109
296
  feature: 'client-error-demo',
110
297
  triggered_at: new Date().toISOString(),
111
298
  })
112
-
113
- // Simulate a client-side error
114
299
  throw new Error('Client-side error demonstration')
115
300
  },
116
301
  )
117
302
  } catch (error) {
118
- setHasError((prev) => ({ ...prev, clientError: true }))
119
- setSpanOps((prev) => ({ ...prev, clientError: 'demo.client-error-flow' }))
120
303
  Sentry.captureException(error)
304
+ setResults((prev) => ({
305
+ ...prev,
306
+ clientError: { type: 'error', spanOp: 'demo.client-error' },
307
+ }))
121
308
  } finally {
122
309
  setIsLoading((prev) => ({ ...prev, clientError: false }))
123
310
  }
@@ -125,447 +312,265 @@ function RouteComponent() {
125
312
 
126
313
  const handleServerError = async () => {
127
314
  setIsLoading((prev) => ({ ...prev, serverError: true }))
128
- setHasError((prev) => ({ ...prev, serverError: false }))
129
- setShowTrace((prev) => ({ ...prev, serverError: true }))
130
-
131
315
  try {
132
316
  await Sentry.startSpan(
133
- {
134
- name: 'Server Error Flow Demo',
135
- op: 'demo.server-error-flow',
136
- },
317
+ { name: 'Server Error Flow Demo', op: 'demo.server-error' },
137
318
  async () => {
138
319
  Sentry.setContext('demo', {
139
320
  feature: 'server-error-demo',
140
321
  triggered_at: new Date().toISOString(),
141
322
  })
142
-
143
323
  await badServerFunc()
144
324
  },
145
325
  )
146
326
  } catch (error) {
147
- setHasError((prev) => ({ ...prev, serverError: true }))
148
- setSpanOps((prev) => ({ ...prev, serverError: 'demo.server-error-flow' }))
149
327
  Sentry.captureException(error)
328
+ setResults((prev) => ({
329
+ ...prev,
330
+ serverError: { type: 'error', spanOp: 'demo.server-error' },
331
+ }))
150
332
  } finally {
151
333
  setIsLoading((prev) => ({ ...prev, serverError: false }))
152
334
  }
153
335
  }
154
336
 
155
337
  const handleClientTrace = async () => {
156
- setIsLoading((prev) => ({ ...prev, client: true }))
157
- setShowTrace((prev) => ({ ...prev, client: true }))
158
-
338
+ setIsLoading((prev) => ({ ...prev, clientTrace: true }))
159
339
  await Sentry.startSpan(
160
- {
161
- name: 'Client Operation',
162
- op: 'demo.client',
163
- },
340
+ { name: 'Client Operation', op: 'demo.client-trace' },
164
341
  async () => {
165
- // Simulate some client-side work
166
342
  await new Promise((resolve) => setTimeout(resolve, 1000))
167
343
  },
168
344
  )
169
-
170
- setSpanOps((prev) => ({ ...prev, client: 'demo.client' }))
171
- setIsLoading((prev) => ({ ...prev, client: false }))
345
+ setResults((prev) => ({
346
+ ...prev,
347
+ clientTrace: { type: 'success', spanOp: 'demo.client-trace' },
348
+ }))
349
+ setIsLoading((prev) => ({ ...prev, clientTrace: false }))
172
350
  }
173
351
 
174
352
  const handleServerTrace = async () => {
175
- setIsLoading((prev) => ({ ...prev, server: true }))
176
- setShowTrace((prev) => ({ ...prev, server: true }))
177
-
353
+ setIsLoading((prev) => ({ ...prev, serverTrace: true }))
178
354
  try {
179
355
  await Sentry.startSpan(
180
- {
181
- name: 'Server Operation',
182
- op: 'demo.server',
183
- },
356
+ { name: 'Server Operation', op: 'demo.server-trace' },
184
357
  async () => {
185
358
  await goodServerFunc()
186
359
  },
187
360
  )
188
- setSpanOps((prev) => ({ ...prev, server: 'demo.server' }))
361
+ setResults((prev) => ({
362
+ ...prev,
363
+ serverTrace: { type: 'success', spanOp: 'demo.server-trace' },
364
+ }))
189
365
  } finally {
190
- setIsLoading((prev) => ({ ...prev, server: false }))
366
+ setIsLoading((prev) => ({ ...prev, serverTrace: false }))
191
367
  }
192
368
  }
193
369
 
194
370
  return (
195
- <>
196
- <style
197
- dangerouslySetInnerHTML={{
198
- __html: `
199
- @keyframes fadeOut {
200
- from { opacity: 1; transform: translateY(0); }
201
- to { opacity: 0; transform: translateY(-10px); }
202
- }
203
- .animate-fade-out {
204
- animation: fadeOut 2s ease-out forwards;
205
- }
206
- `,
207
- }}
208
- />
209
- <div
210
- className="min-h-[calc(100vh-32px)] text-white p-8"
211
- style={{
212
- backgroundImage:
213
- 'radial-gradient(41.11% 49.93% at 50% 49.93%, #8d5494 0%, #563275 52.26%, #1f1633 100%)',
214
- }}
215
- >
216
- <div className="max-w-7xl mx-auto">
217
- {/* Header */}
218
- <div className="text-center mb-12">
219
- <h1 className="text-8xl font-bold mb-4 text-white">Sentry</h1>
220
- <p className="text-4xl font-semibold text-white">
221
- Code <span className="inline-block -rotate-9">breaks</span>, fix
222
- it faster
223
- </p>
371
+ <div
372
+ className="min-h-screen text-white"
373
+ style={{
374
+ fontFamily:
375
+ 'system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", sans-serif',
376
+ background:
377
+ 'linear-gradient(180deg, #181423 0%, #1C1825 50%, #181423 100%)',
378
+ }}
379
+ >
380
+ <div className="max-w-5xl mx-auto px-6 py-16">
381
+ {/* Header */}
382
+ <div className="text-center mb-16">
383
+ <div className="inline-flex items-center gap-4 mb-8">
384
+ <div className="text-[#7553FF]">
385
+ <SentryLogo size={56} />
386
+ </div>
387
+ <div className="text-left">
388
+ <h1 className="text-3xl font-bold text-white tracking-tight">
389
+ Sentry Demo
390
+ </h1>
391
+ <p className="text-[#A49FB5] text-sm">
392
+ Error monitoring & performance tracing
393
+ </p>
394
+ </div>
224
395
  </div>
396
+ <p className="text-lg text-[#A49FB5] max-w-xl mx-auto leading-relaxed">
397
+ Click the buttons below to trigger errors and traces, then view
398
+ them in your{' '}
399
+ <a
400
+ href="https://sentry.io"
401
+ target="_blank"
402
+ rel="noopener noreferrer"
403
+ className="text-[#7553FF] hover:text-[#B3A1FF] underline transition-colors"
404
+ >
405
+ Sentry dashboard
406
+ </a>
407
+ .
408
+ </p>
409
+ </div>
225
410
 
226
- {/* Content Grid */}
227
- <div className="space-y-8">
228
- {/* Information Section - Top */}
229
- <div className="bg-[#1C2333] rounded-lg border border-gray-800 p-6">
230
- <div className="space-y-4 text-gray-300">
231
- <p>
232
- The Sentry integration monitors this application across all
233
- routes; not just this one (we care about all tabs) using our{' '}
234
- <code>@sentry/react</code> and <code>@sentry/node</code>{' '}
235
- packages.
236
- </p>
237
- <div className="grid grid-cols-4 gap-4">
238
- <div className="bg-[#2D3555] rounded-lg p-4 border border-gray-700 hover:border-purple-500/50 transition-colors">
239
- <div className="font-bold mb-1">Error Monitoring</div>
240
- <div className="text-sm text-gray-400">
241
- across client side and server functions
242
- </div>
243
- </div>
244
- <div className="bg-[#2D3555] rounded-lg p-4 border border-gray-700 hover:border-purple-500/50 transition-colors">
245
- <div className="font-bold mb-1">Tracing and Spans</div>
246
- <div className="text-sm text-gray-400">
247
- for client and server side performance
248
- </div>
249
- </div>
250
- <div className="bg-[#2D3555] rounded-lg p-4 border border-gray-700 hover:border-purple-500/50 transition-colors">
251
- <div className="font-bold mb-1">Session replay</div>
252
- <div className="text-sm text-gray-400">
253
- real user session playback
254
- </div>
255
- </div>
256
- <div className="bg-[#2D3555] rounded-lg p-4 border border-gray-700 hover:border-purple-500/50 transition-colors">
257
- <div className="font-bold mb-1">Real-time alerts</div>
258
- <div className="text-sm text-gray-400">
259
- because sleep is overrated anyway
260
- </div>
261
- </div>
262
- </div>
263
- </div>
411
+ {/* Features Grid */}
412
+ <div className="grid grid-cols-1 md:grid-cols-4 gap-4 mb-12">
413
+ <FeatureCard
414
+ icon={
415
+ <svg className="w-6 h-6" fill="currentColor" viewBox="0 0 24 24">
416
+ <path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z" />
417
+ </svg>
418
+ }
419
+ title="Error Monitoring"
420
+ description="Client & server error tracking"
421
+ />
422
+ <FeatureCard
423
+ icon={
424
+ <svg className="w-6 h-6" fill="currentColor" viewBox="0 0 24 24">
425
+ <path d="M13 3v18h-2V3h2zm6 6v12h-2V9h2zM7 14v7H5v-7h2z" />
426
+ </svg>
427
+ }
428
+ title="Performance"
429
+ description="Tracing and spans visualization"
430
+ />
431
+ <FeatureCard
432
+ icon={
433
+ <svg className="w-6 h-6" fill="currentColor" viewBox="0 0 24 24">
434
+ <path d="M17 10.5V7c0-.55-.45-1-1-1H4c-.55 0-1 .45-1 1v10c0 .55.45 1 1 1h12c.55 0 1-.45 1-1v-3.5l4 4v-11l-4 4z" />
435
+ </svg>
436
+ }
437
+ title="Session Replay"
438
+ description="Real user session playback"
439
+ />
440
+ <FeatureCard
441
+ icon={
442
+ <svg className="w-6 h-6" fill="currentColor" viewBox="0 0 24 24">
443
+ <path d="M12 22c1.1 0 2-.9 2-2h-4c0 1.1.89 2 2 2zm6-6v-5c0-3.07-1.64-5.64-4.5-6.32V4c0-.83-.67-1.5-1.5-1.5s-1.5.67-1.5 1.5v.68C7.63 5.36 6 7.92 6 11v5l-2 2v1h16v-1l-2-2z" />
444
+ </svg>
445
+ }
446
+ title="Real-time Alerts"
447
+ description="Instant issue notifications"
448
+ />
449
+ </div>
450
+
451
+ {/* Testing Panels */}
452
+ <div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
453
+ {/* Client-Side Panel */}
454
+ <div className="bg-[#1C1825] rounded-2xl p-8 border border-[#2D2640]">
455
+ <div className="flex items-center gap-3 mb-6">
456
+ <div className="w-3 h-3 rounded-full bg-[#00F261]" />
457
+ <h2 className="text-xl font-semibold">Client-Side Testing</h2>
264
458
  </div>
265
459
 
266
- {/* Testing Sections - Bottom Grid */}
267
- <div className="grid grid-cols-2 gap-8">
268
- {/* Client Side Testing */}
269
- <div className="bg-[#1C2333] rounded-lg p-6 border border-gray-800">
270
- <h2 className="text-xl font-semibold text-white mb-6">
271
- Client-Side Testing
272
- </h2>
273
- <div className="space-y-6">
274
- <div>
275
- <button
276
- type="button"
277
- onClick={() => {
278
- setDemoStep((prev) => prev + 1)
279
- handleClientError()
280
- }}
281
- className="w-full text-white rounded-md p-4 relative overflow-hidden group"
282
- style={{
283
- background:
284
- 'linear-gradient(120deg, #c83852, #b44092 25%, #6a5fc1 50%, #452650 55%, #452650)',
285
- backgroundPosition: '2% 0',
286
- backgroundSize: '250% 100%',
287
- }}
288
- >
289
- <div className="absolute inset-0 bg-gradient-to-r from-red-500/10 to-orange-500/10 opacity-0 group-hover:opacity-100 transition-opacity" />
290
- <div className="relative">
291
- <div className="flex items-center mb-2">
292
- <span className="font-medium">
293
- Trigger Client-Side Error
294
- </span>
295
- </div>
296
- </div>
297
- </button>
298
- {hasError.clientError && (
299
- <div className="mt-4 space-y-2">
300
- <div className="bg-red-900/20 border border-red-500/50 rounded-lg p-2">
301
- <div className="flex items-center text-red-400 text-sm">
302
- <svg
303
- className="w-4 h-4 mr-2"
304
- fill="none"
305
- strokeLinecap="round"
306
- strokeLinejoin="round"
307
- strokeWidth="2"
308
- viewBox="0 0 24 24"
309
- stroke="currentColor"
310
- >
311
- <title>Red Warning Sign</title>
312
- <path d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" />
313
- </svg>
314
- Client-side error captured and traced
315
- </div>
316
- </div>
317
- <div className="bg-purple-900/20 border border-purple-500/50 rounded-lg p-3">
318
- <div className="flex items-center justify-between">
319
- <div className="relative">
320
- <button
321
- type="button"
322
- className={`inline-flex items-center bg-purple-900/40 px-3 py-1.5 rounded-lg border border-purple-500/50 cursor-pointer hover:bg-purple-900/60 transition-all ${copiedSpan === spanOps.clientError ? 'scale-95' : ''}`}
323
- onClick={() => handleCopy(spanOps.clientError)}
324
- title="Click to copy operation name"
325
- >
326
- <span className="text-purple-300 text-sm font-medium mr-2">
327
- span.op
328
- </span>
329
- <code className="text-purple-400 text-sm font-mono">
330
- {spanOps.clientError}
331
- </code>
332
- </button>
333
- {copiedSpan === spanOps.clientError && (
334
- <div className="absolute -top-8 left-1/2 -translate-x-1/2 bg-green-500/90 text-white text-xs px-2 py-1 rounded animate-fade-out">
335
- Copied!
336
- </div>
337
- )}
338
- </div>
339
- </div>
340
- </div>
341
- </div>
342
- )}
343
- </div>
460
+ <div className="space-y-4">
461
+ <div>
462
+ <SentryButton
463
+ variant="error"
464
+ onClick={handleClientError}
465
+ loading={isLoading.clientError}
466
+ >
467
+ Trigger Client Error
468
+ </SentryButton>
469
+ {isLoading.clientError && (
470
+ <ProgressBar loading={isLoading.clientError} />
471
+ )}
472
+ {results.clientError && !isLoading.clientError && (
473
+ <ResultBadge
474
+ type={results.clientError.type}
475
+ spanOp={results.clientError.spanOp}
476
+ onCopy={() => {}}
477
+ />
478
+ )}
479
+ </div>
344
480
 
345
- <div>
346
- <button
347
- type="button"
348
- onClick={() => {
349
- setDemoStep((prev) => prev + 1)
350
- handleClientTrace()
351
- }}
352
- className="w-full text-white rounded-md p-4 relative overflow-hidden group"
353
- style={{
354
- background:
355
- 'linear-gradient(120deg, #c83852, #b44092 25%, #6a5fc1 50%, #452650 55%, #452650)',
356
- backgroundPosition: '2% 0',
357
- backgroundSize: '250% 100%',
358
- }}
359
- >
360
- <div className="absolute inset-0 bg-gradient-to-r from-blue-500/10 to-purple-500/10 opacity-0 group-hover:opacity-100 transition-opacity" />
361
- <div className="relative">
362
- <div className="flex items-center mb-2">
363
- <span className="font-medium">
364
- Test Client-Side Span
365
- </span>
366
- </div>
367
- </div>
368
- </button>
369
- {showTrace.client && (
370
- <div className="mt-4 space-y-2">
371
- <div className="flex items-center">
372
- <div
373
- className={`w-3 h-3 rounded-full ${isLoading.client ? 'bg-blue-500 animate-pulse' : 'bg-green-500'}`}
374
- />
375
- <div className="ml-2 flex-1">
376
- <div className="h-1.5 bg-[#2D3555] rounded">
377
- <div
378
- className="h-full bg-blue-500 rounded transition-all duration-500"
379
- style={{
380
- width: isLoading.client ? '60%' : '100%',
381
- }}
382
- />
383
- </div>
384
- </div>
385
- </div>
386
- {!isLoading.client && spanOps.client && (
387
- <div className="bg-purple-900/20 border border-purple-500/50 rounded-lg p-3">
388
- <div className="flex items-center justify-between">
389
- <div className="relative">
390
- <button
391
- type="button"
392
- className={`inline-flex items-center bg-purple-900/40 px-3 py-1.5 rounded-lg border border-purple-500/50 cursor-pointer hover:bg-purple-900/60 transition-all ${copiedSpan === spanOps.client ? 'scale-95' : ''}`}
393
- onClick={() => handleCopy(spanOps.client)}
394
- title="Click to copy operation name"
395
- >
396
- <span className="text-purple-300 text-sm font-medium mr-2">
397
- span.op
398
- </span>
399
- <code className="text-purple-400 text-sm font-mono">
400
- {spanOps.client}
401
- </code>
402
- </button>
403
- {copiedSpan === spanOps.client && (
404
- <div className="absolute -top-8 left-1/2 -translate-x-1/2 bg-green-500/90 text-white text-xs px-2 py-1 rounded animate-fade-out">
405
- Copied!
406
- </div>
407
- )}
408
- </div>
409
- </div>
410
- </div>
411
- )}
412
- </div>
413
- )}
414
- </div>
415
- </div>
481
+ <div>
482
+ <SentryButton
483
+ variant="primary"
484
+ onClick={handleClientTrace}
485
+ loading={isLoading.clientTrace}
486
+ >
487
+ Test Client Trace
488
+ </SentryButton>
489
+ {isLoading.clientTrace && (
490
+ <ProgressBar loading={isLoading.clientTrace} />
491
+ )}
492
+ {results.clientTrace && !isLoading.clientTrace && (
493
+ <ResultBadge
494
+ type={results.clientTrace.type}
495
+ spanOp={results.clientTrace.spanOp}
496
+ onCopy={() => {}}
497
+ />
498
+ )}
416
499
  </div>
500
+ </div>
501
+ </div>
417
502
 
418
- {/* Server Side Testing */}
419
- <div className="bg-[#1C2333] rounded-lg p-6 border border-gray-800">
420
- <h2 className="text-xl font-semibold text-white mb-6">
421
- Server-Side Testing
422
- </h2>
423
- <div className="space-y-6">
424
- <div>
425
- <button
426
- type="button"
427
- onClick={() => {
428
- setDemoStep((prev) => prev + 1)
429
- handleServerError()
430
- }}
431
- className="w-full text-white rounded-md p-4 relative overflow-hidden group"
432
- style={{
433
- background:
434
- 'linear-gradient(120deg, #c83852, #b44092 25%, #6a5fc1 50%, #452650 55%, #452650)',
435
- backgroundPosition: '2% 0',
436
- backgroundSize: '250% 100%',
437
- }}
438
- >
439
- <div className="absolute inset-0 bg-gradient-to-r from-red-500/10 to-orange-500/10 opacity-0 group-hover:opacity-100 transition-opacity" />
440
- <div className="relative">
441
- <div className="flex items-center mb-2">
442
- <span className="font-medium">
443
- Trigger Server Error
444
- </span>
445
- </div>
446
- </div>
447
- </button>
448
- {hasError.serverError && (
449
- <div className="mt-4 space-y-2">
450
- <div className="bg-red-900/20 border border-red-500/50 rounded-lg p-3">
451
- <div className="flex items-center text-red-400 text-sm">
452
- <svg
453
- className="w-4 h-4 mr-2"
454
- fill="none"
455
- strokeLinecap="round"
456
- strokeLinejoin="round"
457
- strokeWidth="2"
458
- viewBox="0 0 24 24"
459
- stroke="currentColor"
460
- >
461
- <title>Red Warning Sign</title>
462
- <path d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" />
463
- </svg>
464
- Server-side error captured and traced
465
- </div>
466
- </div>
467
- <div className="bg-purple-900/20 border border-purple-500/50 rounded-lg p-3">
468
- <div className="flex items-center justify-between">
469
- <div className="relative">
470
- <button
471
- type="button"
472
- className={`inline-flex items-center bg-purple-900/40 px-3 py-1.5 rounded-lg border border-purple-500/50 cursor-pointer hover:bg-purple-900/60 transition-all ${copiedSpan === spanOps.serverError ? 'scale-95' : ''}`}
473
- onClick={() => handleCopy(spanOps.serverError)}
474
- title="Click to copy operation name"
475
- >
476
- <span className="text-purple-300 text-sm font-medium mr-2">
477
- span.op
478
- </span>
479
- <code className="text-purple-400 text-sm font-mono">
480
- {spanOps.serverError}
481
- </code>
482
- </button>
483
- {copiedSpan === spanOps.serverError && (
484
- <div className="absolute -top-8 left-1/2 -translate-x-1/2 bg-green-500/90 text-white text-xs px-2 py-1 rounded animate-fade-out">
485
- Copied!
486
- </div>
487
- )}
488
- </div>
489
- </div>
490
- </div>
491
- </div>
492
- )}
493
- </div>
503
+ {/* Server-Side Panel */}
504
+ <div className="bg-[#1C1825] rounded-2xl p-8 border border-[#2D2640]">
505
+ <div className="flex items-center gap-3 mb-6">
506
+ <div className="w-3 h-3 rounded-full bg-[#7553FF]" />
507
+ <h2 className="text-xl font-semibold">Server-Side Testing</h2>
508
+ </div>
494
509
 
495
- <div>
496
- <button
497
- type="button"
498
- onClick={() => {
499
- setDemoStep((prev) => prev + 1)
500
- handleServerTrace()
501
- }}
502
- className="w-full text-white rounded-md p-4 relative overflow-hidden group"
503
- style={{
504
- background:
505
- 'linear-gradient(120deg, #c83852, #b44092 25%, #6a5fc1 50%, #452650 55%, #452650)',
506
- backgroundPosition: '2% 0',
507
- backgroundSize: '250% 100%',
508
- }}
509
- >
510
- <div className="absolute inset-0 bg-gradient-to-r from-blue-500/10 to-purple-500/10 opacity-0 group-hover:opacity-100 transition-opacity" />
511
- <div className="relative">
512
- <div className="flex items-center mb-2">
513
- <span className="font-medium">Test server Trace</span>
514
- </div>
515
- </div>
516
- </button>
517
- {showTrace.server && (
518
- <div className="mt-4 space-y-2">
519
- <div className="flex items-center">
520
- <div
521
- className={`w-3 h-3 rounded-full ${isLoading.server ? 'bg-purple-500 animate-pulse' : 'bg-green-500'}`}
522
- />
523
- <div className="ml-2 flex-1">
524
- <div className="h-1.5 bg-[#2D3555] rounded">
525
- <div
526
- className="h-full bg-purple-500 rounded transition-all duration-500"
527
- style={{
528
- width: isLoading.server ? '60%' : '100%',
529
- }}
530
- />
531
- </div>
532
- </div>
533
- </div>
534
- {!isLoading.server && spanOps.server && (
535
- <div className="bg-purple-900/20 border border-purple-500/50 rounded-lg p-3">
536
- <div className="flex items-center justify-between">
537
- <div className="relative">
538
- <button
539
- type="button"
540
- className={`inline-flex items-center bg-purple-900/40 px-3 py-1.5 rounded-lg border border-purple-500/50 cursor-pointer hover:bg-purple-900/60 transition-all ${copiedSpan === spanOps.server ? 'scale-95' : ''}`}
541
- onClick={() => handleCopy(spanOps.server)}
542
- title="Click to copy operation name"
543
- >
544
- <span className="text-purple-300 text-sm font-medium mr-2">
545
- span.op
546
- </span>
547
- <code className="text-purple-400 text-sm font-mono">
548
- {spanOps.server}
549
- </code>
550
- </button>
551
- {copiedSpan === spanOps.server && (
552
- <div className="absolute -top-8 left-1/2 -translate-x-1/2 bg-green-500/90 text-white text-xs px-2 py-1 rounded animate-fade-out">
553
- Copied!
554
- </div>
555
- )}
556
- </div>
557
- </div>
558
- </div>
559
- )}
560
- </div>
561
- )}
562
- </div>
563
- </div>
510
+ <div className="space-y-4">
511
+ <div>
512
+ <SentryButton
513
+ variant="error"
514
+ onClick={handleServerError}
515
+ loading={isLoading.serverError}
516
+ >
517
+ Trigger Server Error
518
+ </SentryButton>
519
+ {isLoading.serverError && (
520
+ <ProgressBar loading={isLoading.serverError} />
521
+ )}
522
+ {results.serverError && !isLoading.serverError && (
523
+ <ResultBadge
524
+ type={results.serverError.type}
525
+ spanOp={results.serverError.spanOp}
526
+ onCopy={() => {}}
527
+ />
528
+ )}
529
+ </div>
530
+
531
+ <div>
532
+ <SentryButton
533
+ variant="primary"
534
+ onClick={handleServerTrace}
535
+ loading={isLoading.serverTrace}
536
+ >
537
+ Test Server Trace
538
+ </SentryButton>
539
+ {isLoading.serverTrace && (
540
+ <ProgressBar loading={isLoading.serverTrace} />
541
+ )}
542
+ {results.serverTrace && !isLoading.serverTrace && (
543
+ <ResultBadge
544
+ type={results.serverTrace.type}
545
+ spanOp={results.serverTrace.spanOp}
546
+ onCopy={() => {}}
547
+ />
548
+ )}
564
549
  </div>
565
550
  </div>
566
551
  </div>
567
552
  </div>
553
+
554
+ {/* Footer Note */}
555
+ <div className="mt-12 text-center">
556
+ <p className="text-sm text-[#6E6C75]">
557
+ This page uses{' '}
558
+ <code className="bg-[#1C1825] px-2 py-1 rounded text-[#B3A1FF]">
559
+ @sentry/tanstackstart-react
560
+ </code>{' '}
561
+ for full-stack error monitoring.
562
+ <br />
563
+ <a
564
+ href="https://docs.sentry.io/platforms/javascript/guides/tanstackstart-react/"
565
+ target="_blank"
566
+ rel="noopener noreferrer"
567
+ className="text-[#7553FF] hover:text-[#B3A1FF] underline transition-colors"
568
+ >
569
+ Read the documentation →
570
+ </a>
571
+ </p>
572
+ </div>
568
573
  </div>
569
- </>
574
+ </div>
570
575
  )
571
576
  }
@@ -41,6 +41,8 @@ export const getRouter = () => {
41
41
  Sentry.init({
42
42
  dsn: import.meta.env.VITE_SENTRY_DSN,
43
43
  integrations: [],
44
+ tracesSampleRate: 1.0,
45
+ sendDefaultPii: true,
44
46
  });
45
47
  }
46
48
  <% } %>
package/dist/checksum.js CHANGED
@@ -1,3 +1,3 @@
1
1
  // This file is auto-generated. Do not edit manually.
2
2
  // Generated from add-ons, examples, hosts, project, and toolchains directories
3
- export const contentChecksum = 'a42905c8800e56f1a97cdfc380ba4543e2d296e1fde95246036a4eacdaa694d7';
3
+ export const contentChecksum = 'a2932d45e0b8b23cc8ea876ec00379239a4dbacc8f01dbe99a4f6bf68bffe6b4';
@@ -1 +1 @@
1
- export declare const contentChecksum = "a42905c8800e56f1a97cdfc380ba4543e2d296e1fde95246036a4eacdaa694d7";
1
+ export declare const contentChecksum = "a2932d45e0b8b23cc8ea876ec00379239a4dbacc8f01dbe99a4f6bf68bffe6b4";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanstack/cta-framework-react-cra",
3
- "version": "0.37.2",
3
+ "version": "0.38.0",
4
4
  "description": "CTA Framework for React (Create React App)",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
package/src/checksum.ts CHANGED
@@ -1,3 +1,3 @@
1
1
  // This file is auto-generated. Do not edit manually.
2
2
  // Generated from add-ons, examples, hosts, project, and toolchains directories
3
- export const contentChecksum = 'a42905c8800e56f1a97cdfc380ba4543e2d296e1fde95246036a4eacdaa694d7'
3
+ export const contentChecksum = 'a2932d45e0b8b23cc8ea876ec00379239a4dbacc8f01dbe99a4f6bf68bffe6b4'