diff --git a/components/poll/notifications-toggle.tsx b/components/poll/notifications-toggle.tsx index 08ca05de4..59a9d104c 100644 --- a/components/poll/notifications-toggle.tsx +++ b/components/poll/notifications-toggle.tsx @@ -37,7 +37,9 @@ const NotificationsToggle: React.VoidFunctionComponent email: poll.user.email, }} components={{ - b: , + b: ( + + ), }} /> diff --git a/components/poll/poll-subheader.tsx b/components/poll/poll-subheader.tsx index 05a798d79..fdd3f6b44 100644 --- a/components/poll/poll-subheader.tsx +++ b/components/poll/poll-subheader.tsx @@ -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 = () => { 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 (
@@ -37,22 +48,40 @@ const PollSubheader: React.VoidFunctionComponent = () => { Verified ) : ( - + + Unverified + + } + > +
+
, + b: ( + + ), }} />
- } - > - Unverified - + {didSendVerificationEmail ? ( +
Verification email sent.
+ ) : ( + + )} +
+
) ) : null}
diff --git a/components/popover.tsx b/components/popover.tsx index f7bcc741f..0c76178d8 100644 --- a/components/popover.tsx +++ b/components/popover.tsx @@ -34,7 +34,7 @@ const Popover: React.VoidFunctionComponent = ({ const portal = document.getElementById("portal"); return ( - + { 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" }); }); diff --git a/style.css b/style.css index 1510f0bfc..15d935653 100644 --- a/style.css +++ b/style.css @@ -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 {