Skip to content

Commit 2b12202

Browse files
committed
add state
1 parent 95367ec commit 2b12202

File tree

4 files changed

+83
-36
lines changed

4 files changed

+83
-36
lines changed

app/lib/actions.ts

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
"use server";
2+
3+
import { Resend } from "resend";
4+
import { VercelInviteUserEmail } from "../../emails/vercel-invite-user";
5+
6+
const resend = new Resend(process.env.RESEND_API_KEY);
7+
8+
export async function send(prevState: string | undefined, formData: FormData) {
9+
const email = formData.get("email") as string;
10+
11+
const { data, error } = await resend.emails.send({
12+
from: "Vercel <[email protected]>",
13+
to: [email],
14+
subject: "Join team on Vercel",
15+
react: VercelInviteUserEmail({}),
16+
});
17+
18+
if (error) {
19+
return error.message;
20+
}
21+
22+
console.log(data);
23+
24+
return "Email sent!";
25+
}

app/page.tsx

+48-36
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,33 @@
1-
import { VercelInviteUserEmail } from '../emails/vercel-invite-user';
2-
import { Resend } from 'resend';
1+
"use client";
32

4-
export default function Page() {
5-
async function send(formData: FormData) {
6-
'use server';
7-
8-
const resend = new Resend(process.env.RESEND_API_KEY);
9-
const email = formData.get('email') as string;
10-
11-
const { data, error } = await resend.emails.send({
12-
from: 'Vercel <[email protected]>',
13-
to: [email],
14-
subject: 'Join team on Vercel',
15-
react: VercelInviteUserEmail({}),
16-
});
3+
import clsx from "clsx";
4+
import { useFormState, useFormStatus } from "react-dom";
5+
import { send } from "./lib/actions";
6+
import React from "react";
177

18-
if (error) {
19-
console.log(error);
20-
}
21-
22-
console.log(data);
23-
}
8+
export default function Page() {
9+
const [, dispatch] = useFormState(send, undefined);
2410

2511
return (
26-
<div className="bg-zinc-950 py-16 sm:py-24 h-[100vh]">
27-
<div className="mx-auto max-w-7xl sm:px-6 lg:px-8">
28-
<div className="relative isolate overflow-hidden bg-gray-900 px-6 py-24 shadow-2xl sm:rounded-3xl sm:px-24 xl:py-32">
29-
<h2 className="mx-auto max-w-2xl text-center text-3xl font-bold tracking-tight text-white sm:text-4xl">
12+
<div className="bg-zinc-950 p-8 min-h-screen flex justify-center items-center sm:items-start sm:p-24">
13+
<div className="mx-auto w-full max-w-5xl sm:px-6 lg:px-8">
14+
<div className="relative isolate overflow-hidden bg-gray-900 px-6 py-24 shadow-2xl rounded-lg sm:rounded-3xl sm:px-24 xl:py-32 flex items-center flex-col">
15+
<h2 className="max-w-2xl text-center text-3xl font-bold tracking-tight text-white sm:text-4xl">
3016
Get invited to a team
3117
</h2>
32-
<p className="mx-auto mt-2 max-w-xl text-center text-lg leading-8 text-gray-300">
18+
19+
<p className="mt-2 max-w-xl text-center text-lg leading-8 text-gray-300">
3320
Type your email address to get invited to a team.
3421
</p>
35-
<form className="mx-auto mt-10 flex max-w-md gap-x-4" action={send}>
22+
23+
<form
24+
className="mt-10 flex max-w-md gap-4 items-start w-full"
25+
action={dispatch}
26+
>
3627
<label htmlFor="email" className="sr-only">
3728
Email address
3829
</label>
30+
3931
<input
4032
id="email"
4133
name="email"
@@ -44,21 +36,25 @@ export default function Page() {
4436
defaultValue="[email protected]"
4537
placeholder="[email protected]"
4638
autoComplete="email"
47-
className="min-w-0 flex-auto rounded-md border-0 bg-white/5 px-3.5 py-2 text-white shadow-sm ring-1 ring-inset ring-white/10 focus:ring-2 focus:ring-inset focus:ring-white sm:text-sm sm:leading-6"
39+
className="w-full rounded-md border-0 bg-white/5 px-3.5 py-2 text-white shadow-sm ring-1 ring-inset ring-white/10 focus:ring-2 focus:ring-inset focus:ring-white sm:text-sm sm:leading-6"
4840
/>
49-
<button
50-
type="submit"
51-
className="flex-none rounded-md bg-white px-3.5 py-2.5 text-sm font-semibold text-gray-900 shadow-sm hover:bg-gray-100 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-white"
52-
>
53-
Invite
54-
</button>
41+
42+
<SubmitButton />
5543
</form>
44+
5645
<svg
5746
viewBox="0 0 1024 1024"
5847
aria-hidden="true"
5948
className="absolute left-1/2 top-1/2 -z-10 h-[64rem] w-[64rem] -translate-x-1/2"
6049
>
61-
<circle r={512} cx={512} cy={512} fill="url(#759c1415-0410-454c-8f7c-9a820de03641)" fillOpacity="0.7" />
50+
<circle
51+
r={512}
52+
cx={512}
53+
cy={512}
54+
fill="url(#759c1415-0410-454c-8f7c-9a820de03641)"
55+
fillOpacity="0.7"
56+
/>
57+
6258
<defs>
6359
<radialGradient
6460
r={1}
@@ -77,4 +73,20 @@ export default function Page() {
7773
</div>
7874
</div>
7975
);
80-
}
76+
}
77+
78+
function SubmitButton() {
79+
const { pending } = useFormStatus();
80+
81+
return (
82+
<button
83+
type="submit"
84+
className={clsx(
85+
"flex-none rounded-md bg-white px-3.5 py-2.5 text-sm font-semibold text-gray-900 shadow-sm hover:bg-gray-100 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-white",
86+
pending && "opacity-50 cursor-not-allowed"
87+
)}
88+
>
89+
Invite
90+
</button>
91+
);
92+
}

package.json

+1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
},
88
"dependencies": {
99
"@react-email/components": "0.0.22",
10+
"clsx": "^2.1.1",
1011
"next": "14.2.5",
1112
"react": "18.2.0",
1213
"react-dom": "18.2.0",

pnpm-lock.yaml

+9
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)