Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add login and pro platform to reactflow.com #699

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,4 @@ yarn-error.log*
tmp
vite.config.ts.*
.idea/
tsconfig.tsbuildinfo
4 changes: 2 additions & 2 deletions packages/xy-ui/components/ui/select.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ const SelectContent = React.forwardRef<
<SelectPrimitive.Content
ref={ref}
className={cn(
'relative z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
'relative z-50 min-w-[8rem] overflow-hidden rounded-md border border-gray-200 bg-popover text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
position === 'popper' &&
'data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1',
className,
Expand All @@ -52,7 +52,7 @@ const SelectContent = React.forwardRef<
className={cn(
'p-1',
position === 'popper' &&
'h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]',
'h-[--radix-select-trigger-height] w-full min-w-[--radix-select-trigger-width]',
)}
>
{children}
Expand Down
923 changes: 870 additions & 53 deletions pnpm-lock.yaml

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions sites/reactflow.dev/.env.development
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,5 @@ NEXT_PUBLIC_PRO_EXAMPLES_URL=https://pro-examples-staging.reactflow.dev
NEXT_PUBLIC_PRO_PLATFORM_URL=http://localhost:3000
NEXT_PUBLIC_NHOST_API_URL=https://local.functions.nhost.run/v1
NEXT_PUBLIC_UI_COMPONENTS_URL=http://localhost:3004
NEXT_PUBLIC_NHOST_SUBDOMAIN=
NEXT_PUBLIC_NHOST_REGION=
2 changes: 2 additions & 0 deletions sites/reactflow.dev/.env.production
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
NEXT_PUBLIC_EXAMPLES_URL=https://example-apps.xyflow.com
NEXT_PUBLIC_PRO_PLATFORM_URL=https://pro.reactflow.dev
NEXT_PUBLIC_NHOST_API_URL=https://fdsuchqhfchojqpemwyn.functions.eu-central-1.nhost.run/v1
NEXT_PUBLIC_NHOST_SUBDOMAIN=
NEXT_PUBLIC_NHOST_REGION=
5 changes: 4 additions & 1 deletion sites/reactflow.dev/next.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@ import reactFlowPackageJson from '@xyflow/react/package.json' with { type: 'json
// This is used for finding out the real deploy slug for a preview deployment
// afaik this is the only way because Vercel doesn't expose this information
const slugRegex = /-git-(.*?)\.vercel\.app/;
export function parsePreviewDeploySlug(branchUrl: string) {
export function parsePreviewDeploySlug(branchUrl?: string) {
if (!branchUrl) {
throw new Error('Missing branchUrl');
}
return branchUrl.match(slugRegex)?.[1];
}

Expand Down
15 changes: 14 additions & 1 deletion sites/reactflow.dev/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"version": "1.0.0",
"private": true,
"scripts": {
"types:check": "tsc --noEmit",
"dev": "next --port 3002",
"build": "next build",
"postbuild": "next-sitemap",
Expand All @@ -19,14 +20,26 @@
"@xyflow/react": "^12.4.4",
"@xyflow/xy-ui": "workspace:*",
"d3": "^7.9.0",
"next": "^15.1.7",
"next": "^15.2.1",
"next-sitemap": "^4.2.3",
"nextra": "^4.2.13",
"nextra-theme-docs": "^4.2.13",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-icons": "^4.10.1",
"timeago-react": "^3.0.6",
"@nhost/nhost-js": "^3.2.5",
"@nhost/react": "^3.10.1",
"@nhost/react-apollo": "^17.0.1",
"remark-gfm": "4.0.0",
"react-syntax-highlighter": "15.5.0",
"react-markdown": "9.0.0",
"@codesandbox/sandpack-react": "2.6.7",
"@codesandbox/sandpack-themes": "2.0.21",
"file-saver": "2.0.5",
"jszip": "3.10.1",
"@apollo/client": "3.7.15",
"fathom-client": "3.5.0",
"ui-components": "workspace:*",
"xy-shared": "workspace:*"
},
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
'use client';

import React from 'react';
import Link from 'next/link';
import { Heading, Text } from '@xyflow/xy-ui';
import { useSearchParams } from 'next/navigation';
import { EnvelopeIcon } from '@heroicons/react/24/outline';

function VerifyEmailPage() {
const defaultEmail = useSearchParams()?.get('email');
const linkQueryParams = defaultEmail ? `?email=${defaultEmail}` : '';

return (
<div className="mx-auto mt-16 mb-8 max-w-2xl text-center">
<h3 className="text-sm font-bold mb-6 flex items-center uppercase tracking-wider text-primary justify-center">
<EnvelopeIcon className="inline-block w-6 h-6 mr-1" />
Verification
</h3>
<Heading className="mb-4 font-black">We just sent you an email</Heading>

<Text className="text-xl mt-6">
If you didn{"'"}t receive an email, you can request a new one{' '}
<Link
className="text-primary hover:underline"
href={`/email-verification/resend-link${linkQueryParams}`}
>
here
</Link>
.
</Text>
</div>
);
}

export default VerifyEmailPage;
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import {
ResendVerificationLink,
AuthFormWrapper,
} from '@/components/pro/AuthForms';

const ResendVerificationLinkPage = () => {
return (
<AuthFormWrapper
title="Resend Verification Link"
description="Enter your email to send a new verification link."
showOAuth={false}
showHero={false}
>
<ResendVerificationLink />
</AuthFormWrapper>
);
};

export default ResendVerificationLinkPage;
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { redirect } from 'next/navigation';
import { getNhost } from '@/utils/nhost';
import { FC } from 'react';
import { SearchParams } from '@/types';

type PageProps = {
searchParams: SearchParams;
};

const VerifyEmailPage: FC<PageProps> = async (props) => {
const searchParams = await props.searchParams;
const nhost = await getNhost();
const { ticket, redirectTo, type } = searchParams;

if (ticket && redirectTo && type) {
redirect(
`${nhost.auth.url}/verify?ticket=${ticket}&type=${type}&redirectTo=${redirectTo}`,
);
}

redirect('/?error=invalid-ticket');
};

export default VerifyEmailPage;
30 changes: 30 additions & 0 deletions sites/reactflow.dev/src/app/(content-pages)/(auth)/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { FC, ReactNode, Suspense } from 'react';
import { PageLoader } from '@/components/pro/Loader';
import { getNhost } from '@/utils/nhost';
import { redirect } from 'next/navigation';

const Layout: FC<{ children: ReactNode }> = async ({ children }) => {
const nhost = await getNhost();
const isAuthenticated = nhost.auth.isAuthenticated();

if (isAuthenticated) {
redirect('/');
}

return (
<Suspense fallback={<PageLoader />}>
<div className="relative h-full">
<div
className="absolute opacity-10 w-full h-[70vh] left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 pointer-events-none z-10"
style={{
background:
'radial-gradient(rgba(68,91,222,1) 0%, rgba(215,78,243,1) 25%, rgba(255,255,255,1) 50%)',
}}
/>
<div className="max-w-6xl mx-auto pt-10 mb-20">{children}</div>
</div>
</Suspense>
);
};

export default Layout;
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { ResetPassword, AuthFormWrapper } from '@/components/pro/AuthForms';

const authFormLinks = [{ href: '/signin', label: 'Back to login' }];

const ResetPasswordPage = () => {
return (
<AuthFormWrapper
title="Reset Password"
description="Enter your email to reset your password."
links={authFormLinks}
showOAuth={false}
showHero={false}
>
<ResetPassword />
</AuthFormWrapper>
);
};

export default ResetPasswordPage;
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { SignInMagicLink, AuthFormWrapper } from '@/components/pro/AuthForms';

const authFormLinks = [
{ href: '/signin', label: 'Sign in using Email and Password' },
];

const SignInEmailPasswordPage = () => {
return (
<AuthFormWrapper
links={authFormLinks}
title="Get a Magic Link"
description="We'll send you a link to sign in or create a new account."
>
<SignInMagicLink />
</AuthFormWrapper>
);
};

export default SignInEmailPasswordPage;
18 changes: 18 additions & 0 deletions sites/reactflow.dev/src/app/(content-pages)/(auth)/signin/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import {
SignInEmailPassword,
AuthFormWrapper,
} from '@/components/pro/AuthForms';

const authFormLinks = [
{ href: '/signup', label: "Don't have an account? Sign Up" },
];

const SignInEmailPasswordPage = () => {
return (
<AuthFormWrapper links={authFormLinks} title="Sign In">
<SignInEmailPassword />
</AuthFormWrapper>
);
};

export default SignInEmailPasswordPage;
22 changes: 22 additions & 0 deletions sites/reactflow.dev/src/app/(content-pages)/(auth)/signup/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import {
SignUpEmailPassword,
AuthFormWrapper,
} from '@/components/pro/AuthForms';

const authFormLinks = [
{ href: '/signin', label: 'Already have an account? Sign In' },
];

const SignUpPage = () => {
return (
<AuthFormWrapper
links={authFormLinks}
title="Sign Up"
description="Enter your email and password to create an account."
>
<SignUpEmailPassword />
</AuthFormWrapper>
);
};

export default SignUpPage;
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import {
Card,
CardHeader,
CardDescription,
CardTitle,
CardFooter,
} from '@xyflow/xy-ui';
import CustomerPortalButton from '@/components/pro/CustomerPortalButton';

function BillingCard() {
return (
<Card>
<CardHeader>
<CardTitle>Billing & Subscription</CardTitle>
<CardDescription>
Open the Stripe customer portal to download your invoices, change your
billing details and change or cancel your subscription plan.
</CardDescription>
</CardHeader>
<CardFooter className="bg-muted">
<CustomerPortalButton variant="react">
Open Customer Portal
</CustomerPortalButton>
</CardFooter>
</Card>
);
}

export default BillingCard;
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
'use client';

import { useState } from 'react';
import { useUserEmail, useChangeEmail } from '@nhost/react';
import {
Card,
CardHeader,
CardTitle,
CardDescription,
CardFooter,
Button,
Input,
InputLabel,
} from '@xyflow/xy-ui';

function ChangeEmailCard() {
const email = useUserEmail();
const { changeEmail, isLoading, needsEmailVerification, isError, error } =
useChangeEmail();
const [newEmail, setNewEmail] = useState<string>('');

const handleSubmit = async (evt: React.SyntheticEvent) => {
evt.preventDefault();
await changeEmail(newEmail);
};

return (
<Card>
<CardHeader>
<CardTitle>Change Email</CardTitle>
{needsEmailVerification && (
<CardDescription>
Please confirm your new email by clicking the link we sent to{' '}
<span className="font-bold">{newEmail}</span>.
</CardDescription>
)}
{isError && (
<CardDescription className="text-red-500">
{error ? error.message : 'Something went wrong.'}
</CardDescription>
)}
</CardHeader>
<CardFooter className="bg-muted space-x-10">
<form onSubmit={handleSubmit} className="flex justify-between w-full">
<div className="flex-1">
<InputLabel htmlFor="email">New Email</InputLabel>
<Input
variant="square"
className="max-w-xs"
type="email"
value={newEmail}
onChange={(evt) => setNewEmail(evt.target.value)}
required
id="email"
placeholder={email}
disabled={isLoading || needsEmailVerification}
/>
</div>
<Button
disabled={isLoading || needsEmailVerification}
className="shrink-0 ml-auto mt-auto"
variant="react"
type="submit"
>
Update Email
</Button>
</form>
</CardFooter>
</Card>
);
}

export default ChangeEmailCard;
Loading