1+ import { motion } from 'framer-motion' ;
12import { useCallback , useEffect , useMemo , useState } from 'react' ;
23import { useOnboarding } from '@/hooks/use-onboarding' ;
34import { useFireworks } from '@/hooks/use-fireworks' ;
@@ -13,14 +14,14 @@ import {
1314import { GetFederatedGraphByNameResponse } from '@wundergraph/cosmo-connect/dist/platform/v1/platform_pb' ;
1415import { useCurrentOrganization } from '@/hooks/use-current-organization' ;
1516import { useToast } from '../ui/use-toast' ;
16- import { useRouter } from 'next/router' ;
1717import { EnumStatusCode } from '@wundergraph/cosmo-connect/dist/common/common_pb' ;
1818import { Tabs , TabsList , TabsTrigger , TabsContent } from '../ui/tabs' ;
1919import { CLI } from '../ui/cli' ;
2020import { Kbd } from '../ui/kbd' ;
2121import { CheckCircledIcon } from '@radix-ui/react-icons' ;
2222import { Button } from '../ui/button' ;
2323import { MetricsMonitor } from './metrics-monitor' ;
24+ import { StepFinished } from './step-finished' ;
2425
2526const DEFAULT_ROUTING_URL = 'http://localhost:3002' ;
2627
@@ -95,7 +96,7 @@ const MetricsStatusText = ({ status, onRetry }: { status: OnboardingStatus; onRe
9596} ;
9697
9798export const Step3 = ( ) => {
98- const router = useRouter ( ) ;
99+ const [ isFinished , setIsFinished ] = useState ( false ) ;
99100 const { toast } = useToast ( ) ;
100101 const { setStep, setSkipped, setOnboarding } = useOnboarding ( ) ;
101102 const currentOrg = useCurrentOrganization ( ) ;
@@ -185,8 +186,7 @@ export const Step3 = () => {
185186 email : Boolean ( prev ?. email ) ,
186187 } ) ) ;
187188
188- setStep ( undefined ) ;
189- router . push ( '/' ) ;
189+ setIsFinished ( true ) ;
190190 } ,
191191 onError : ( error ) => {
192192 toast ( {
@@ -197,133 +197,164 @@ export const Step3 = () => {
197197 } ) ;
198198
199199 return (
200- < OnboardingContainer >
201- < div className = "mt-4 flex w-full flex-col gap-4 text-left" >
202- < div >
203- < h2 className = "text-2xl font-semibold tracking-tight" > Run your services</ h2 >
204- < p className = "mt-1 text-sm text-muted-foreground" >
205- Start the router and send your first query to see live traffic in action.
206- </ p >
207- </ div >
200+ < div className = "relative w-full" style = { { perspective : '1600px' } } >
201+ < motion . div
202+ className = "grid w-full [&>*]:col-start-1 [&>*]:row-start-1"
203+ animate = { { rotateY : isFinished ? 180 : 0 } }
204+ transition = { { duration : 0.7 , ease : 'easeInOut' } }
205+ style = { { transformStyle : 'preserve-3d' , minHeight : 788 } }
206+ >
207+ < div
208+ className = "flex min-h-[788px] flex-col rounded-lg border bg-card p-6 text-card-foreground shadow-sm"
209+ style = { {
210+ backfaceVisibility : 'hidden' ,
211+ WebkitBackfaceVisibility : 'hidden' ,
212+ transform : 'translateZ(1px)' ,
213+ } }
214+ aria-hidden = { isFinished }
215+ >
216+ < OnboardingContainer >
217+ < div className = "mt-4 flex w-full flex-col gap-4 text-left" >
218+ < div >
219+ < h2 className = "text-2xl font-semibold tracking-tight" > Run your services</ h2 >
220+ < p className = "mt-1 text-sm text-muted-foreground" >
221+ Start the router and send your first query to see live traffic in action.
222+ </ p >
223+ </ div >
208224
209- < div className = "flex gap-3" >
210- < span className = "-mt-0.5 flex size-6 shrink-0 items-center justify-center rounded-full bg-muted text-xs font-medium" >
211- 1
212- </ span >
213- < div className = "flex min-w-0 flex-1 flex-col gap-2" >
214- < div className = "flex items-center justify-between" >
215- < p className = "text-sm font-semibold" > Start the router</ p >
216- { hasActiveRouter ? (
217- < span className = "flex items-center gap-1.5 text-xs text-green-600 dark:text-green-400" >
218- < CheckCircledIcon className = "size-3.5" />
219- Connected
220- </ span >
221- ) : isPolling ? (
222- < span className = "flex items-center gap-1.5 text-xs text-muted-foreground" >
223- < span className = "relative flex size-3.5 items-center justify-center" >
224- < span className = "absolute inline-flex size-2 animate-ping rounded-full bg-green-400 opacity-75" />
225- < span className = "relative inline-flex size-2 rounded-full bg-green-500" />
226- </ span >
227- Waiting…
228- </ span >
229- ) : (
230- < span className = "flex items-center gap-1.5 text-xs text-destructive" >
231- < span className = "inline-flex size-2 rounded-full bg-destructive" />
232- Not detected
225+ < div className = "flex gap-3" >
226+ < span className = "-mt-0.5 flex size-6 shrink-0 items-center justify-center rounded-full bg-muted text-xs font-medium" >
227+ 1
233228 </ span >
234- ) }
235- </ div >
236- < Tabs defaultValue = "demo" >
237- < TabsList >
238- < TabsTrigger value = "demo" > CLI</ TabsTrigger >
239- < TabsTrigger value = "manual" > Manual</ TabsTrigger >
240- </ TabsList >
241- < TabsContent value = "demo" className = "min-h-28" >
242- < div className = "flex flex-col gap-2" >
243- < p className = "text-sm text-muted-foreground" >
244- If you ran < code className = "rounded bg-muted px-1 py-0.5 font-mono text-xs" > npx wgc demo</ code > in
245- the previous step, the router is already running. Otherwise, re-run the command:
246- </ p >
247- < CLI command = "npx wgc demo" />
229+ < div className = "flex min-w-0 flex-1 flex-col gap-2" >
230+ < div className = "flex items-center justify-between" >
231+ < p className = "text-sm font-semibold" > Start the router</ p >
232+ { hasActiveRouter ? (
233+ < span className = "flex items-center gap-1.5 text-xs text-green-600 dark:text-green-400" >
234+ < CheckCircledIcon className = "size-3.5" />
235+ Connected
236+ </ span >
237+ ) : isPolling ? (
238+ < span className = "flex items-center gap-1.5 text-xs text-muted-foreground" >
239+ < span className = "relative flex size-3.5 items-center justify-center" >
240+ < span className = "absolute inline-flex size-2 animate-ping rounded-full bg-green-400 opacity-75" />
241+ < span className = "relative inline-flex size-2 rounded-full bg-green-500" />
242+ </ span >
243+ Waiting…
244+ </ span >
245+ ) : (
246+ < span className = "flex items-center gap-1.5 text-xs text-destructive" >
247+ < span className = "inline-flex size-2 rounded-full bg-destructive" />
248+ Not detected
249+ </ span >
250+ ) }
251+ </ div >
252+ < Tabs defaultValue = "demo" >
253+ < TabsList >
254+ < TabsTrigger value = "demo" > CLI</ TabsTrigger >
255+ < TabsTrigger value = "manual" > Manual</ TabsTrigger >
256+ </ TabsList >
257+ < TabsContent value = "demo" className = "min-h-28" >
258+ < div className = "flex flex-col gap-2" >
259+ < p className = "text-sm text-muted-foreground" >
260+ If you ran{ ' ' }
261+ < code className = "rounded bg-muted px-1 py-0.5 font-mono text-xs" > npx wgc demo</ code > in the
262+ previous step, the router is already running. Otherwise, re-run the command:
263+ </ p >
264+ < CLI command = "npx wgc demo" />
265+ </ div >
266+ </ TabsContent >
267+ < TabsContent value = "manual" className = "min-h-28" >
268+ < div className = "flex flex-col gap-2" >
269+ < p className = "text-sm text-muted-foreground" >
270+ Generate a router token and start the router with Docker.
271+ </ p >
272+ < CLI command = "export GRAPH_API_TOKEN=$(npx wgc router token create demo-token --graph-name demo --namespace default --raw)" />
273+ < CLI
274+ command = { `docker run --rm -p ${ port } :${ port } --add-host=host.docker.internal:host-gateway --pull always -e GRAPH_API_TOKEN=$GRAPH_API_TOKEN -e DEV_MODE=true -e PLUGINS_ENABLED=true -e LISTEN_ADDR=0.0.0.0:${ port } ghcr.io/wundergraph/cosmo/router:latest` }
275+ />
276+ </ div >
277+ </ TabsContent >
278+ </ Tabs >
248279 </ div >
249- </ TabsContent >
250- < TabsContent value = "manual" className = "min-h-28" >
251- < div className = "flex flex-col gap-2" >
252- < p className = "text-sm text-muted-foreground" >
253- Generate a router token and start the router with Docker.
254- </ p >
255- < CLI command = "export GRAPH_API_TOKEN=$(npx wgc router token create demo-token --graph-name demo --namespace default --raw)" />
256- < CLI
257- command = { `docker run --rm -p ${ port } :${ port } --add-host=host.docker.internal:host-gateway --pull always -e GRAPH_API_TOKEN=$GRAPH_API_TOKEN -e DEV_MODE=true -e PLUGINS_ENABLED=true -e LISTEN_ADDR=0.0.0.0:${ port } ghcr.io/wundergraph/cosmo/router:latest` }
258- />
280+ </ div >
281+
282+ < div className = "flex gap-3" >
283+ < span className = "-mt-0.5 flex size-6 shrink-0 items-center justify-center rounded-full bg-muted text-xs font-medium" >
284+ 2
285+ </ span >
286+ < div className = "flex min-w-0 flex-1 flex-col gap-2" >
287+ < p className = "text-sm font-semibold" > Send a test query</ p >
288+ < Tabs defaultValue = "demo-query" >
289+ < TabsList >
290+ < TabsTrigger value = "demo-query" > CLI</ TabsTrigger >
291+ < TabsTrigger value = "curl" > cURL</ TabsTrigger >
292+ < TabsTrigger value = "playground" > Playground</ TabsTrigger >
293+ </ TabsList >
294+ < TabsContent value = "demo-query" className = "min-h-24" >
295+ < p className = "text-sm text-muted-foreground" >
296+ While < code className = "rounded bg-muted px-1 py-0.5 font-mono text-xs" > npx wgc demo</ code > is
297+ running, press < Kbd > r</ Kbd > in the terminal to send a test query.
298+ </ p >
299+ </ TabsContent >
300+ < TabsContent value = "curl" className = "min-h-24" >
301+ < div className = "flex flex-col gap-2" >
302+ < CLI command = { curlCommand } />
303+ </ div >
304+ </ TabsContent >
305+ < TabsContent value = "playground" className = "min-h-24" >
306+ < p className = "text-sm text-muted-foreground" >
307+ Open the{ ' ' }
308+ < a
309+ href = { `/${ currentOrg ?. slug } /default/graph/demo/playground?operation=${ encodeURIComponent ( DEMO_QUERY ) } &variables=${ encodeURIComponent ( DEMO_VARIABLES ) } ` }
310+ className = "text-primary"
311+ >
312+ Playground
313+ </ a > { ' ' }
314+ to explore the schema and run queries interactively.
315+ </ p >
316+ </ TabsContent >
317+ </ Tabs >
259318 </ div >
260- </ TabsContent >
261- </ Tabs >
262- </ div >
263- </ div >
319+ </ div >
264320
265- < div className = "flex gap-3" >
266- < span className = "-mt-0.5 flex size-6 shrink-0 items-center justify-center rounded-full bg-muted text-xs font-medium" >
267- 2
268- </ span >
269- < div className = "flex min-w-0 flex-1 flex-col gap-2" >
270- < p className = "text-sm font-semibold" > Send a test query</ p >
271- < Tabs defaultValue = "demo-query" >
272- < TabsList >
273- < TabsTrigger value = "demo-query" > CLI</ TabsTrigger >
274- < TabsTrigger value = "curl" > cURL</ TabsTrigger >
275- < TabsTrigger value = "playground" > Playground</ TabsTrigger >
276- </ TabsList >
277- < TabsContent value = "demo-query" className = "min-h-24" >
278- < p className = "text-sm text-muted-foreground" >
279- While < code className = "rounded bg-muted px-1 py-0.5 font-mono text-xs" > npx wgc demo</ code > is running,
280- press < Kbd > r</ Kbd > in the terminal to send a test query.
281- </ p >
282- </ TabsContent >
283- < TabsContent value = "curl" className = "min-h-24" >
284- < div className = "flex flex-col gap-2" >
285- < CLI command = { curlCommand } />
321+ < div className = "-mt-2 flex flex-col gap-3" >
322+ < div className = "flex gap-3" >
323+ < StatusIcon status = { metricsStatus } />
324+ < div className = "flex min-w-0 flex-1 flex-col gap-2" >
325+ < MetricsStatusText status = { metricsStatus } onRetry = { restartMetricsPolling } />
326+ </ div >
286327 </ div >
287- </ TabsContent >
288- < TabsContent value = "playground" className = "min-h-24" >
289- < p className = "text-sm text-muted-foreground" >
290- Open the{ ' ' }
291- < a
292- href = { `/${ currentOrg ?. slug } /default/graph/demo/playground?operation=${ encodeURIComponent ( DEMO_QUERY ) } &variables=${ encodeURIComponent ( DEMO_VARIABLES ) } ` }
293- className = "text-primary"
294- >
295- Playground
296- </ a > { ' ' }
297- to explore the schema and run queries interactively.
298- </ p >
299- </ TabsContent >
300- </ Tabs >
301- </ div >
302- </ div >
303328
304- < div className = "-mt-2 flex flex-col gap-3" >
305- < div className = "flex gap-3" >
306- < StatusIcon status = { metricsStatus } />
307- < div className = "flex min-w-0 flex-1 flex-col gap-2" >
308- < MetricsStatusText status = { metricsStatus } onRetry = { restartMetricsPolling } />
329+ < MetricsMonitor status = { metricsStatus } />
330+ </ div >
309331 </ div >
310- </ div >
311332
312- < MetricsMonitor status = { metricsStatus } />
333+ < OnboardingNavigation
334+ className = "pt-2"
335+ onSkip = { setSkipped }
336+ backHref = "/onboarding/2"
337+ forward = { {
338+ onClick : ( ) => mutate ( { } ) ,
339+ isLoading : isPending ,
340+ disabled : metricsStatus !== 'ok' ,
341+ } }
342+ forwardLabel = "Finish"
343+ />
344+ </ OnboardingContainer >
313345 </ div >
314- </ div >
315-
316- < OnboardingNavigation
317- className = "pt-2"
318- onSkip = { setSkipped }
319- backHref = "/onboarding/2"
320- forward = { {
321- onClick : ( ) => mutate ( { } ) ,
322- isLoading : isPending ,
323- disabled : metricsStatus !== 'ok' ,
324- } }
325- forwardLabel = "Finish"
326- />
327- </ OnboardingContainer >
346+ < div
347+ className = "flex min-h-[788px] flex-col rounded-lg border bg-card p-6 text-card-foreground shadow-sm"
348+ style = { {
349+ backfaceVisibility : 'hidden' ,
350+ WebkitBackfaceVisibility : 'hidden' ,
351+ transform : 'rotateY(180deg) translateZ(1px)' ,
352+ } }
353+ aria-hidden = { ! isFinished }
354+ >
355+ < StepFinished />
356+ </ div >
357+ </ motion . div >
358+ </ div >
328359 ) ;
329360} ;
0 commit comments