Skip to content

Commit

Permalink
feat: add waitlist (#35)
Browse files Browse the repository at this point in the history
  • Loading branch information
secondl1ght authored Jul 25, 2024
1 parent 685cb62 commit 4cca9b7
Show file tree
Hide file tree
Showing 7 changed files with 254 additions and 2 deletions.
20 changes: 20 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
"next-themes": "^0.3.0",
"nostr-tools": "^2.7.0",
"react": "^18",
"react-confetti": "^6.1.0",
"react-dom": "^18",
"react-hook-form": "^7.52.1",
"sharp": "^0.33.4",
Expand Down
64 changes: 64 additions & 0 deletions src/app/success/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
'use client';

import { ChevronLeft } from 'lucide-react';
import Link from 'next/link';
import { ReactNode, useMemo } from 'react';
import Confetti from 'react-confetti';
import { useIsClient } from 'usehooks-ts';

import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { ROUTES } from '@/utils/routes';

export default function Page({
searchParams,
}: {
searchParams: { variant: string };
}) {
const isClient = useIsClient();

const content = useMemo((): {
title: string;
description?: string;
button?: ReactNode;
} => {
switch (searchParams.variant) {
case 'waitlist':
return {
title: "You're On the List!",
description:
"You're one step closer to experiencing MiBanco. We'll notify you as soon as we're ready for you.",
button: (
<Link
href={ROUTES.home}
className="mb-2 flex text-muted-foreground hover:text-foreground"
>
<ChevronLeft className="size-4" />
<p className="text-xs">Home</p>
</Link>
),
};

default:
return { title: 'Success!' };
}
}, [searchParams]);

return (
<main className="flex h-screen w-full flex-col items-center justify-center">
{isClient ? <Confetti /> : null}
<div className="z-50 mx-4 md:mx-0">
{content.button || null}
<Card className="max-w-96 text-center">
<CardHeader>
<CardTitle className="text-xl">{content.title}</CardTitle>
</CardHeader>
{content.description ? (
<CardContent>
<p className="text-muted-foreground">{content.description}</p>
</CardContent>
) : null}
</Card>
</div>
</main>
);
}
8 changes: 7 additions & 1 deletion src/components/form/SignUpForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ import {
import { Checkbox } from '../ui/checkbox';
import { Progress } from '../ui/progress';
import { useToast } from '../ui/use-toast';
import { WaitlistForm } from './WaitlistForm';

const FormSchema = z
.object({
Expand Down Expand Up @@ -87,6 +88,9 @@ export function SignUpForm() {

const workerRef = useRef<Worker>();

const [view, setView] = useState<'waitlist' | 'sign-up'>(
referralParam ? 'sign-up' : 'waitlist'
);
const [loading, setLoading] = useState(true);
const [clickedGenerate, setClickedGenerate] = useState(false);
const [showPassword, setShowPassword] = useState(false);
Expand Down Expand Up @@ -207,7 +211,9 @@ export function SignUpForm() {
setClickedGenerate(true);
};

return (
return view === 'waitlist' ? (
<WaitlistForm setView={setView} />
) : (
<Card>
<CardHeader>
<CardTitle>Sign up</CardTitle>
Expand Down
158 changes: 158 additions & 0 deletions src/components/form/WaitlistForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
'use client';

import { zodResolver } from '@hookform/resolvers/zod';
import { Loader2 } from 'lucide-react';
import { useRouter, useSearchParams } from 'next/navigation';
import { Dispatch, FC, SetStateAction, useState } from 'react';
import { useForm } from 'react-hook-form';
import { z } from 'zod';

import { ROUTES } from '@/utils/routes';

import { Button } from '../ui/button';
import {
Card,
CardContent,
CardDescription,
CardFooter,
CardHeader,
CardTitle,
} from '../ui/card';
import {
Form,
FormControl,
FormDescription,
FormField,
FormItem,
FormLabel,
FormMessage,
} from '../ui/form';
import { Input } from '../ui/input';
import { useToast } from '../ui/use-toast';

const FormSchema = z.object({
email: z.string().email().min(5, {
message: 'Invalid email.',
}),
});

export const WaitlistForm: FC<{
setView: Dispatch<SetStateAction<'waitlist' | 'sign-up'>>;
}> = ({ setView }) => {
const searchParams = useSearchParams();
const emailParam = searchParams.get('email');

const { push } = useRouter();
const { toast } = useToast();

const [loading, setLoading] = useState(false);

const form = useForm<z.infer<typeof FormSchema>>({
reValidateMode: 'onChange',
resolver: zodResolver(FormSchema),
defaultValues: {
email: emailParam || '',
},
});

const onSubmit = async (values: z.infer<typeof FormSchema>) => {
setLoading(true);

try {
const result = await fetch('https://reflex.amboss.space/graphql', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
variables: { input: { email: values.email, interest: 'BANCO' } },
query: `mutation Add_interest($input: WaitlistInput!) {
public {
waitlist {
add_interest(input: $input) {
email
}
}
}
}`,
}),
});

const response = await result.json();

if (response.data) {
push(ROUTES.success.waitlist);
} else {
toast({
variant: 'destructive',
title: 'Error joining waitlist.',
description: response.errors
.map((e: { message: string }) => e.message)
.join(', '),
});
}
} catch (error) {
console.log(error);

toast({
variant: 'destructive',
title: 'Error joining waitlist.',
description: 'Please try again.',
});
} finally {
setLoading(false);
}
};

return (
<Card>
<CardHeader>
<CardTitle>The MiBanco Waitlist</CardTitle>
<CardDescription>Join us . No bank required.</CardDescription>
</CardHeader>
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)}>
<CardContent className="flex flex-col gap-4">
<FormField
control={form.control}
name="email"
render={({ field }) => (
<FormItem>
<FormLabel>Email</FormLabel>
<FormControl>
<Input placeholder="[email protected]" {...field} />
</FormControl>
<FormMessage />
<FormDescription>
{"We'll notify you as soon as we're ready for you."}
</FormDescription>
</FormItem>
)}
/>
</CardContent>

<CardFooter>
<div className="w-full">
<Button type="submit" disabled={loading} className="w-full">
{loading ? (
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
) : null}
Join Waitlist
</Button>

<Button
type="button"
onClick={() => setView('sign-up')}
disabled={loading}
variant={'ghost'}
className="mt-4 w-full"
>
I have a Referral Code
</Button>
</div>
</CardFooter>
</form>
</Form>
</Card>
);
};
3 changes: 3 additions & 0 deletions src/utils/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ export const ROUTES = {
restore: '/setup/wallet/restore',
},
},
success: {
waitlist: '/success?variant=waitlist',
},
docs: {
privacyPolicy: '/docs/privacy-policy',
termsOfService: '/docs/terms-of-service',
Expand Down
2 changes: 1 addition & 1 deletion src/views/wallet/Settings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -255,7 +255,7 @@ export const WalletSettings: FC<{ walletId: string }> = ({ walletId }) => {
}

return (
<div className="flex max-w-screen-lg flex-col gap-10 pt-4 md:gap-16">
<div className="flex max-w-screen-lg flex-col gap-10 py-4 md:gap-16">
<WalletName walletId={walletId} />
<Section
title="MIBAN Code"
Expand Down

0 comments on commit 4cca9b7

Please sign in to comment.