Skip to content

Commit b7a1299

Browse files
committed
feat: finish UI + invitation flow
1 parent 01721f1 commit b7a1299

File tree

4 files changed

+441
-127
lines changed

4 files changed

+441
-127
lines changed

studio/src/components/layout/onboarding-layout.tsx

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,15 @@ import { Stepper } from '../onboarding/stepper';
44
import { ONBOARDING_STEPS } from '../onboarding/onboarding-steps';
55
import { useOnboarding } from '@/hooks/use-onboarding';
66

7-
export const OnboardingLayout = ({ children, title }: { children?: React.ReactNode; title?: string }) => {
7+
export const OnboardingLayout = ({
8+
children,
9+
title,
10+
bare = false,
11+
}: {
12+
children?: React.ReactNode;
13+
title?: string;
14+
bare?: boolean;
15+
}) => {
816
const { currentStep } = useOnboarding();
917

1018
return (
@@ -15,9 +23,13 @@ export const OnboardingLayout = ({ children, title }: { children?: React.ReactNo
1523
<Stepper steps={ONBOARDING_STEPS} currentStep={(currentStep ?? 1) - 1} className="ml-auto" />
1624
</header>
1725
<main className="w-full flex-1 px-6 pb-4 pt-12">
18-
<Card className="mx-auto w-full max-w-2xl">
19-
<CardContent className="flex min-h-[788px] flex-col p-6">{children}</CardContent>
20-
</Card>
26+
{bare ? (
27+
<div className="mx-auto w-full max-w-2xl">{children}</div>
28+
) : (
29+
<Card className="mx-auto w-full max-w-2xl">
30+
<CardContent className="flex min-h-[788px] flex-col p-6">{children}</CardContent>
31+
</Card>
32+
)}
2133
</main>
2234
</div>
2335
);

studio/src/components/onboarding/step-3.tsx

Lines changed: 153 additions & 122 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { motion } from 'framer-motion';
12
import { useEffect, useMemo, useReducer, useState } from 'react';
23
import { useOnboarding } from '@/hooks/use-onboarding';
34
import { useFireworks } from '@/hooks/use-fireworks';
@@ -13,14 +14,14 @@ import {
1314
import { GetFederatedGraphByNameResponse } from '@wundergraph/cosmo-connect/dist/platform/v1/platform_pb';
1415
import { useCurrentOrganization } from '@/hooks/use-current-organization';
1516
import { useToast } from '../ui/use-toast';
16-
import { useRouter } from 'next/router';
1717
import { EnumStatusCode } from '@wundergraph/cosmo-connect/dist/common/common_pb';
1818
import { Tabs, TabsList, TabsTrigger, TabsContent } from '../ui/tabs';
1919
import { CLI } from '../ui/cli';
2020
import { Kbd } from '../ui/kbd';
2121
import { CheckCircledIcon } from '@radix-ui/react-icons';
2222
import { Button } from '../ui/button';
2323
import { MetricsMonitor } from './metrics-monitor';
24+
import { StepFinished } from './step-finished';
2425

2526
const DEFAULT_ROUTING_URL = 'http://localhost:3002';
2627

@@ -119,7 +120,7 @@ const MetricsStatusText = ({ status, onRetry }: { status: OnboardingStatus; onRe
119120
};
120121

121122
export 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

Comments
 (0)