Skip to content

Commit

Permalink
add lead capture landing page
Browse files Browse the repository at this point in the history
  • Loading branch information
xvvvyz committed Sep 8, 2024
1 parent a57ad56 commit 29206f1
Show file tree
Hide file tree
Showing 9 changed files with 167 additions and 11 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ Vercel environment secrets:
- LEMON_SQUEEZY_API_KEY
- LEMON_SQUEEZY_VARIANT_ID
- LEMON_SQUEEZY_WEBHOOK_SECRET
- RESEND_API_KEY
- SUPABASE_SERVICE_KEY

GitHub repo secrets:
Expand Down
26 changes: 26 additions & 0 deletions app/(pages)/(marketing)/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import Button from '@/_components/button';
import ContactForm from '@/_components/contact-form';

const Page = () => (
<div className="mx-auto flex min-h-full max-w-2xl select-text flex-col items-center justify-center px-6 py-16 text-center">
<h1 className="max-w-sm text-2xl font-bold md:max-w-xl md:text-4xl">
<span className="text-fg-1">llog</span>&mdash;achieve lasting behavior
changes with your&nbsp;clients.
</h1>
<p className="mx-auto mt-6 max-w-sm">
Streamline <b>data collection</b>, create{' '}
<b>data-driven training plans</b> and easily <b>monitor&nbsp;progress</b>.
</p>
<div className="mt-9 w-full max-w-lg rounded border border-alpha-1 bg-bg-2 p-8 text-left sm:rounded-[4rem] sm:p-16">
<ContactForm />
</div>
<p className="mt-9 flex justify-center gap-4">
<span className="text-fg-4">Have an account?</span>
<Button href="/sign-in" variant="link">
Sign in
</Button>
</p>
</div>
);

export default Page;
4 changes: 2 additions & 2 deletions app/(pages)/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ const mono = Inconsolata({ subsets: ['latin'], variable: '--font-mono' });

export const metadata = {
description:
'Delight your clients with the ultimate collaborative behavior tracking platform.',
'Achieve lasting behavior changes with your clients. Streamline data collection, create data-driven training plans and easily monitor progress.',
title: {
default: 'llog behavior tracking platform',
default: 'llog - collaborative behavior modification',
template: '%s - llog',
},
};
Expand Down
9 changes: 5 additions & 4 deletions app/(pages)/share/[subjectId]/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,12 +50,13 @@ const Layout = async ({ children, params: { subjectId } }: LayoutProps) => {
) : (
<>
<span className="text-sm leading-tight text-fg-4">
Delight your clients with the ultimate
<span className="text-fg-2">llog</span>&mdash;achieve lasting
behavior
<br />
collaborative behavior tracking platform.
changes with your clients.
</span>
<Button href="/sign-up" size="sm">
Sign up
<Button href="/" size="sm">
Request a demo
</Button>
</>
)}
Expand Down
107 changes: 107 additions & 0 deletions app/_components/contact-form.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
'use client';

import Button from '@/_components/button';
import Input from '@/_components/input';
import Select, { IOption } from '@/_components/select';
import newLead from '@/_mutations/new-lead';
import { Controller, useForm } from 'react-hook-form';

export interface ContactFormValues {
email: string;
name: string;
phone?: string;
profession?: IOption;
types: IOption[];
website?: string;
}

const ContactForm = () => {
const form = useForm<ContactFormValues>({
defaultValues: {
email: '',
name: '',
phone: '',
types: [],
website: '',
},
});

return (
<form
className="flex flex-col gap-8"
onSubmit={form.handleSubmit((values) => newLead(values))}
>
<Input label="Name" required {...form.register('name')} />
<Input
label="Email address"
required
type="email"
{...form.register('email')}
/>
<Input label="Phone number" type="tel" {...form.register('phone')} />
<Input label="Company website" type="url" {...form.register('website')} />
<Controller
control={form.control}
name="profession"
render={({ field }) => (
<Select
isClearable={false}
label="Profession"
name={field.name}
onBlur={field.onBlur}
onChange={(value) => field.onChange(value)}
options={[
{ id: 'trainer', label: 'Trainer' },
{ id: 'veterinarian', label: 'Veterinarian' },
{ id: 'other', label: 'Other' },
]}
value={field.value}
/>
)}
/>
<Controller
control={form.control}
name="types"
render={({ field }) => (
<Select
isClearable={false}
isMulti
label="Case types"
name={field.name}
onBlur={field.onBlur}
onChange={(value) => field.onChange(value)}
options={[
{ id: 'aggression', label: 'Aggression' },
{ id: 'anxiety', label: 'Anxiety' },
{ id: 'fear', label: 'Fear' },
{ id: 'reactivity', label: 'Reactivity' },
{ id: 'other', label: 'Other' },
]}
value={field.value}
/>
)}
/>
{form.formState.errors.root && (
<div className="text-center">{form.formState.errors.root.message}</div>
)}
{form.formState.isSubmitSuccessful ? (
<p className="mt-8 text-center">
Thank you for your interest in llog!
<br />
We will be in touch soon.
</p>
) : (
<Button
className="mt-8"
loading={form.formState.isSubmitting}
loadingText="Requesting…"
type="submit"
>
Request a demo
</Button>
)}
</form>
);
};

export default ContactForm;
25 changes: 25 additions & 0 deletions app/_mutations/new-lead.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
'use server';

import { ContactFormValues } from '@/_components/contact-form';
import { Resend } from 'resend';

const newLead = async (data: ContactFormValues) => {
const resend = new Resend(process.env.RESEND_API_KEY);

await resend.emails.send({
from: '[email protected]',
html: `<pre>${JSON.stringify(
{
...data,
profession: data.profession?.label,
types: data.types.map((type) => type.label),
},
null,
2,
)}</pre>`,
subject: 'New llog demo request',
to: ['[email protected]'],
});
};

export default newLead;
Binary file modified bun.lockb
Binary file not shown.
5 changes: 0 additions & 5 deletions middleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,6 @@ export const middleware = async (req: NextRequest) => {
return NextResponse.redirect(new URL('/subjects', req.url));
}
} else {
if (req.nextUrl.pathname === '/') {
// temporary landing page redirect
return NextResponse.redirect(new URL('/sign-in', req.url));
}

const forcePrivateStartsWith = [
'/account',
'/inputs',
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@
"react-dropzone": "^14.2.3",
"react-hook-form": "^7.53.0",
"react-select": "^5.8.0",
"resend": "^4.0.0",
"supabase": "^1.191.3",
"tailwind-merge": "^2.5.2",
"tailwindcss": "^3.4.10",
Expand Down

0 comments on commit 29206f1

Please sign in to comment.