1+ import { motion } from 'framer-motion' ;
12import { useEffect , useMemo , useReducer , 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
@@ -119,7 +120,7 @@ const MetricsStatusText = ({ status, onRetry }: { status: OnboardingStatus; onRe
119120} ;
120121
121122export const Step3 = ( ) => {
122- const router = useRouter ( ) ;
123+ const [ isFinished , setIsFinished ] = useState ( false ) ;
123124 const { toast } = useToast ( ) ;
124125 const { setStep, setSkipped, setOnboarding } = useOnboarding ( ) ;
125126 const currentOrg = useCurrentOrganization ( ) ;
@@ -211,8 +212,7 @@ export const Step3 = () => {
211212 email : Boolean ( prev ?. email ) ,
212213 } ) ) ;
213214
214- setStep ( undefined ) ;
215- router . push ( '/' ) ;
215+ setIsFinished ( true ) ;
216216 } ,
217217 onError : ( error ) => {
218218 toast ( {
@@ -223,133 +223,164 @@ export const Step3 = () => {
223223 } ) ;
224224
225225 return (
226- < OnboardingContainer >
227- < div className = "mt-4 flex w-full flex-col gap-4 text-left" >
228- < div >
229- < h2 className = "text-2xl font-semibold tracking-tight" > Run your services</ h2 >
230- < p className = "mt-1 text-sm text-muted-foreground" >
231- Start the router and send your first query to see live traffic in action.
232- </ p >
233- </ div >
226+ < div className = "relative w-full" style = { { perspective : '1600px' } } >
227+ < motion . div
228+ className = "grid w-full [&>*]:col-start-1 [&>*]:row-start-1"
229+ animate = { { rotateY : isFinished ? 180 : 0 } }
230+ transition = { { duration : 0.7 , ease : 'easeInOut' } }
231+ style = { { transformStyle : 'preserve-3d' , minHeight : 788 } }
232+ >
233+ < div
234+ className = "flex min-h-[788px] flex-col rounded-lg border bg-card p-6 text-card-foreground shadow-sm"
235+ style = { {
236+ backfaceVisibility : 'hidden' ,
237+ WebkitBackfaceVisibility : 'hidden' ,
238+ transform : 'translateZ(1px)' ,
239+ } }
240+ aria-hidden = { isFinished }
241+ >
242+ < OnboardingContainer >
243+ < div className = "mt-4 flex w-full flex-col gap-4 text-left" >
244+ < div >
245+ < h2 className = "text-2xl font-semibold tracking-tight" > Run your services</ h2 >
246+ < p className = "mt-1 text-sm text-muted-foreground" >
247+ Start the router and send your first query to see live traffic in action.
248+ </ p >
249+ </ div >
234250
235- < div className = "flex gap-3" >
236- < span className = "-mt-0.5 flex size-6 shrink-0 items-center justify-center rounded-full bg-muted text-xs font-medium" >
237- 1
238- </ span >
239- < div className = "flex min-w-0 flex-1 flex-col gap-2" >
240- < div className = "flex items-center justify-between" >
241- < p className = "text-sm font-semibold" > Start the router</ p >
242- { hasActiveRouter ? (
243- < span className = "flex items-center gap-1.5 text-xs text-green-600 dark:text-green-400" >
244- < CheckCircledIcon className = "size-3.5" />
245- Connected
246- </ span >
247- ) : routerPolling ? (
248- < span className = "flex items-center gap-1.5 text-xs text-muted-foreground" >
249- < span className = "relative flex size-3.5 items-center justify-center" >
250- < span className = "absolute inline-flex size-2 animate-ping rounded-full bg-green-400 opacity-75" />
251- < span className = "relative inline-flex size-2 rounded-full bg-green-500" />
252- </ span >
253- Waiting…
254- </ span >
255- ) : (
256- < span className = "flex items-center gap-1.5 text-xs text-destructive" >
257- < span className = "inline-flex size-2 rounded-full bg-destructive" />
258- Not detected
251+ < div className = "flex gap-3" >
252+ < span className = "-mt-0.5 flex size-6 shrink-0 items-center justify-center rounded-full bg-muted text-xs font-medium" >
253+ 1
259254 </ span >
260- ) }
261- </ div >
262- < Tabs defaultValue = "demo" >
263- < TabsList >
264- < TabsTrigger value = "demo" > CLI</ TabsTrigger >
265- < TabsTrigger value = "manual" > Manual</ TabsTrigger >
266- </ TabsList >
267- < TabsContent value = "demo" className = "min-h-28" >
268- < div className = "flex flex-col gap-2" >
269- < p className = "text-sm text-muted-foreground" >
270- If you ran < code className = "rounded bg-muted px-1 py-0.5 font-mono text-xs" > npx wgc demo</ code > in
271- the previous step, the router is already running. Otherwise, re-run the command:
272- </ p >
273- < CLI command = "npx wgc demo" />
255+ < div className = "flex min-w-0 flex-1 flex-col gap-2" >
256+ < div className = "flex items-center justify-between" >
257+ < p className = "text-sm font-semibold" > Start the router</ p >
258+ { hasActiveRouter ? (
259+ < span className = "flex items-center gap-1.5 text-xs text-green-600 dark:text-green-400" >
260+ < CheckCircledIcon className = "size-3.5" />
261+ Connected
262+ </ span >
263+ ) : routerPolling ? (
264+ < span className = "flex items-center gap-1.5 text-xs text-muted-foreground" >
265+ < span className = "relative flex size-3.5 items-center justify-center" >
266+ < span className = "absolute inline-flex size-2 animate-ping rounded-full bg-green-400 opacity-75" />
267+ < span className = "relative inline-flex size-2 rounded-full bg-green-500" />
268+ </ span >
269+ Waiting…
270+ </ span >
271+ ) : (
272+ < span className = "flex items-center gap-1.5 text-xs text-destructive" >
273+ < span className = "inline-flex size-2 rounded-full bg-destructive" />
274+ Not detected
275+ </ span >
276+ ) }
277+ </ div >
278+ < Tabs defaultValue = "demo" >
279+ < TabsList >
280+ < TabsTrigger value = "demo" > CLI</ TabsTrigger >
281+ < TabsTrigger value = "manual" > Manual</ TabsTrigger >
282+ </ TabsList >
283+ < TabsContent value = "demo" className = "min-h-28" >
284+ < div className = "flex flex-col gap-2" >
285+ < p className = "text-sm text-muted-foreground" >
286+ If you ran{ ' ' }
287+ < code className = "rounded bg-muted px-1 py-0.5 font-mono text-xs" > npx wgc demo</ code > in the
288+ previous step, the router is already running. Otherwise, re-run the command:
289+ </ p >
290+ < CLI command = "npx wgc demo" />
291+ </ div >
292+ </ TabsContent >
293+ < TabsContent value = "manual" className = "min-h-28" >
294+ < div className = "flex flex-col gap-2" >
295+ < p className = "text-sm text-muted-foreground" >
296+ Generate a router token and start the router with Docker.
297+ </ p >
298+ < CLI command = "export GRAPH_API_TOKEN=$(npx wgc router token create demo-token --graph-name demo --namespace default --raw)" />
299+ < CLI
300+ 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` }
301+ />
302+ </ div >
303+ </ TabsContent >
304+ </ Tabs >
274305 </ div >
275- </ TabsContent >
276- < TabsContent value = "manual" className = "min-h-28" >
277- < div className = "flex flex-col gap-2" >
278- < p className = "text-sm text-muted-foreground" >
279- Generate a router token and start the router with Docker.
280- </ p >
281- < CLI command = "export GRAPH_API_TOKEN=$(npx wgc router token create demo-token --graph-name demo --namespace default --raw)" />
282- < CLI
283- 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` }
284- />
306+ </ div >
307+
308+ < div className = "flex gap-3" >
309+ < span className = "-mt-0.5 flex size-6 shrink-0 items-center justify-center rounded-full bg-muted text-xs font-medium" >
310+ 2
311+ </ span >
312+ < div className = "flex min-w-0 flex-1 flex-col gap-2" >
313+ < p className = "text-sm font-semibold" > Send a test query</ p >
314+ < Tabs defaultValue = "demo-query" >
315+ < TabsList >
316+ < TabsTrigger value = "demo-query" > CLI</ TabsTrigger >
317+ < TabsTrigger value = "curl" > cURL</ TabsTrigger >
318+ < TabsTrigger value = "playground" > Playground</ TabsTrigger >
319+ </ TabsList >
320+ < TabsContent value = "demo-query" className = "min-h-24" >
321+ < p className = "text-sm text-muted-foreground" >
322+ While < code className = "rounded bg-muted px-1 py-0.5 font-mono text-xs" > npx wgc demo</ code > is
323+ running, press < Kbd > r</ Kbd > in the terminal to send a test query.
324+ </ p >
325+ </ TabsContent >
326+ < TabsContent value = "curl" className = "min-h-24" >
327+ < div className = "flex flex-col gap-2" >
328+ < CLI command = { curlCommand } />
329+ </ div >
330+ </ TabsContent >
331+ < TabsContent value = "playground" className = "min-h-24" >
332+ < p className = "text-sm text-muted-foreground" >
333+ Open the{ ' ' }
334+ < a
335+ href = { `/${ currentOrg ?. slug } /default/graph/demo/playground?operation=${ encodeURIComponent ( DEMO_QUERY ) } &variables=${ encodeURIComponent ( DEMO_VARIABLES ) } ` }
336+ className = "text-primary"
337+ >
338+ Playground
339+ </ a > { ' ' }
340+ to explore the schema and run queries interactively.
341+ </ p >
342+ </ TabsContent >
343+ </ Tabs >
285344 </ div >
286- </ TabsContent >
287- </ Tabs >
288- </ div >
289- </ div >
345+ </ div >
290346
291- < div className = "flex gap-3" >
292- < span className = "-mt-0.5 flex size-6 shrink-0 items-center justify-center rounded-full bg-muted text-xs font-medium" >
293- 2
294- </ span >
295- < div className = "flex min-w-0 flex-1 flex-col gap-2" >
296- < p className = "text-sm font-semibold" > Send a test query</ p >
297- < Tabs defaultValue = "demo-query" >
298- < TabsList >
299- < TabsTrigger value = "demo-query" > CLI</ TabsTrigger >
300- < TabsTrigger value = "curl" > cURL</ TabsTrigger >
301- < TabsTrigger value = "playground" > Playground</ TabsTrigger >
302- </ TabsList >
303- < TabsContent value = "demo-query" className = "min-h-24" >
304- < p className = "text-sm text-muted-foreground" >
305- While < code className = "rounded bg-muted px-1 py-0.5 font-mono text-xs" > npx wgc demo</ code > is running,
306- press < Kbd > r</ Kbd > in the terminal to send a test query.
307- </ p >
308- </ TabsContent >
309- < TabsContent value = "curl" className = "min-h-24" >
310- < div className = "flex flex-col gap-2" >
311- < CLI command = { curlCommand } />
347+ < div className = "-mt-2 flex flex-col gap-3" >
348+ < div className = "flex gap-3" >
349+ < StatusIcon status = { metricsStatus } />
350+ < div className = "flex min-w-0 flex-1 flex-col gap-2" >
351+ < MetricsStatusText status = { metricsStatus } onRetry = { restartMetricsPolling } />
352+ </ div >
312353 </ div >
313- </ TabsContent >
314- < TabsContent value = "playground" className = "min-h-24" >
315- < p className = "text-sm text-muted-foreground" >
316- Open the{ ' ' }
317- < a
318- href = { `/${ currentOrg ?. slug } /default/graph/demo/playground?operation=${ encodeURIComponent ( DEMO_QUERY ) } &variables=${ encodeURIComponent ( DEMO_VARIABLES ) } ` }
319- className = "text-primary"
320- >
321- Playground
322- </ a > { ' ' }
323- to explore the schema and run queries interactively.
324- </ p >
325- </ TabsContent >
326- </ Tabs >
327- </ div >
328- </ div >
329354
330- < div className = "-mt-2 flex flex-col gap-3" >
331- < div className = "flex gap-3" >
332- < StatusIcon status = { metricsStatus } />
333- < div className = "flex min-w-0 flex-1 flex-col gap-2" >
334- < MetricsStatusText status = { metricsStatus } onRetry = { restartMetricsPolling } />
355+ < MetricsMonitor status = { metricsStatus } />
356+ </ div >
335357 </ div >
336- </ div >
337358
338- < MetricsMonitor status = { metricsStatus } />
359+ < OnboardingNavigation
360+ className = "pt-2"
361+ onSkip = { setSkipped }
362+ backHref = "/onboarding/2"
363+ forward = { {
364+ onClick : ( ) => mutate ( { } ) ,
365+ isLoading : isPending ,
366+ disabled : metricsStatus !== 'ok' ,
367+ } }
368+ forwardLabel = "Finish"
369+ />
370+ </ OnboardingContainer >
339371 </ div >
340- </ div >
341-
342- < OnboardingNavigation
343- className = "pt-2"
344- onSkip = { setSkipped }
345- backHref = "/onboarding/2"
346- forward = { {
347- onClick : ( ) => mutate ( { } ) ,
348- isLoading : isPending ,
349- disabled : metricsStatus !== 'ok' ,
350- } }
351- forwardLabel = "Finish"
352- />
353- </ OnboardingContainer >
372+ < div
373+ className = "flex min-h-[788px] flex-col rounded-lg border bg-card p-6 text-card-foreground shadow-sm"
374+ style = { {
375+ backfaceVisibility : 'hidden' ,
376+ WebkitBackfaceVisibility : 'hidden' ,
377+ transform : 'rotateY(180deg) translateZ(1px)' ,
378+ } }
379+ aria-hidden = { ! isFinished }
380+ >
381+ < StepFinished />
382+ </ div >
383+ </ motion . div >
384+ </ div >
354385 ) ;
355386} ;
0 commit comments