Skip to content

Commit 351feff

Browse files
committed
feat: finish UI + invitation flow
1 parent f3a45a2 commit 351feff

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 { useCallback, useEffect, useMemo, 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

@@ -95,7 +96,7 @@ const MetricsStatusText = ({ status, onRetry }: { status: OnboardingStatus; onRe
9596
};
9697

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

Comments
 (0)