Enable resending verification emails

This commit is contained in:
Luke Vella 2022-04-12 11:04:55 +01:00
parent e9ac2a8e71
commit a6e39cc2c3
5 changed files with 87 additions and 21 deletions

View file

@ -37,7 +37,9 @@ const NotificationsToggle: React.VoidFunctionComponent<NotificationsToggleProps>
email: poll.user.email,
}}
components={{
b: <span className="email" />,
b: (
<span className="text-indigo-300 whitespace-nowrap font-medium font-mono " />
),
}}
/>
</div>

View file

@ -1,14 +1,25 @@
import { formatRelative } from "date-fns";
import * as React from "react";
import { Trans, useTranslation } from "next-i18next";
import Tooltip from "../tooltip";
import Button from "../button";
import { usePoll } from "../use-poll";
import Popover from "../popover";
import { useMutation } from "react-query";
import axios from "axios";
export interface PollSubheaderProps {}
const PollSubheader: React.VoidFunctionComponent<PollSubheaderProps> = () => {
const poll = usePoll();
const { t } = useTranslation("app");
const {
mutate: sendVerificationEmail,
isLoading: isSendingVerificationEmail,
isSuccess: didSendVerificationEmail,
} = useMutation(async () => {
await axios.post(`/api/poll/${poll.urlId}/verify`);
});
return (
<div className="text-slate-500">
<div className="md:inline">
@ -37,22 +48,40 @@ const PollSubheader: React.VoidFunctionComponent<PollSubheaderProps> = () => {
Verified
</span>
) : (
<Tooltip
content={
<div className="max-w-sm">
<Popover
trigger={
<button className="badge transition-colors hover:text-indigo-500 hover:border-slate-300 hover:bg-slate-100 cursor-pointer text-slate-400">
Unverified
</button>
}
>
<div className="max-w-sm">
<div className="mb-4">
<Trans
t={t}
i18nKey="unverifiedMessage"
values={{ email: poll.user.email }}
components={{
b: <span className="email" />,
b: (
<span className="text-indigo-500 font-medium font-mono whitespace-nowrap" />
),
}}
/>
</div>
}
>
<span className="badge text-slate-400">Unverified</span>
</Tooltip>
{didSendVerificationEmail ? (
<div className="text-green-500">Verification email sent.</div>
) : (
<Button
onClick={() => {
sendVerificationEmail();
}}
loading={isSendingVerificationEmail}
>
Resend verification email
</Button>
)}
</div>
</Popover>
)
) : null}
</div>

View file

@ -34,7 +34,7 @@ const Popover: React.VoidFunctionComponent<PopoverProps> = ({
const portal = document.getElementById("portal");
return (
<HeadlessPopover>
<HeadlessPopover as={React.Fragment}>
<HeadlessPopover.Button
ref={setReferenceElement}
as="div"

View file

@ -1,12 +1,49 @@
import absoluteUrl from "utils/absolute-url";
import { prisma } from "../../../../db";
import { withLink } from "../../../../utils/api-utils";
import { sendEmailTemplate, withLink } from "../../../../utils/api-utils";
export default withLink(async (req, res, link) => {
if (req.method === "POST") {
if (link.role !== "admin") {
return res.status(401).end();
return res
.status(401)
.json({ status: 401, message: "Only admins can verify polls" });
}
const verificationCode = req.body ? req.body.verificationCode : undefined;
if (!verificationCode) {
const poll = await prisma.poll.findUnique({
where: {
urlId: link.pollId,
},
include: {
user: true,
},
});
if (!poll) {
return res.status(404).json({ status: 404, message: "Poll not found" });
}
const homePageUrl = absoluteUrl(req).origin;
const pollUrl = `${homePageUrl}/admin/${link.urlId}`;
const verifyEmailUrl = `${pollUrl}?code=${poll.verificationCode}`;
await sendEmailTemplate({
templateName: "new-poll",
to: poll.user.email,
subject: `Rallly: ${poll.title} - Verify your email address`,
templateVars: {
title: poll.title,
name: poll.user.name,
pollUrl,
verifyEmailUrl,
homePageUrl,
supportEmail: process.env.NEXT_PUBLIC_SUPPORT_EMAIL,
},
});
return res.send("ok");
}
const { verificationCode } = req.body;
try {
await prisma.poll.update({
where: {
@ -19,14 +56,16 @@ export default withLink(async (req, res, link) => {
verified: true,
},
});
return res.end();
return res.send("ok");
} catch {
console.error(
`Failed to verify poll "${link.pollId}" with code ${verificationCode}`,
);
return res.status(500).end();
return res
.status(500)
.json({ status: 500, message: "Could not verify poll" });
}
}
return res.status(405).end();
return res.status(405).json({ status: 405, message: "Invalid http method" });
});

View file

@ -134,10 +134,6 @@
[data-popper-placement="bottom"] .tooltip-arrow {
@apply bottom-full border-b-slate-700;
}
.email {
@apply font-medium font-mono whitespace-nowrap text-indigo-300;
}
}
@layer components {