Show invited people the Rule, let them copy its cards for their community, and replace an expired invitation link.
This commit is contained in:
1 parent
727a92a9ac
commit
3c984e00c5
37 files changed
+1962
-19
No files matched your search
@@ -0,0 +1,149 @@
|
||||
"use client";
|
||||
|
||||
import { useId, useState } from "react";
|
||||
import Link from "next/link";
|
||||
import { useRouter, useSearchParams } from "next/navigation";
|
||||
import { useTranslation } from "../../../contexts/MessagesContext";
|
||||
import Login from "../../../components/modals/Login";
|
||||
import Button from "../../../components/buttons/Button";
|
||||
import TextInput from "../../../components/controls/TextInput";
|
||||
import ContentLockup from "../../../components/type/ContentLockup";
|
||||
import { EMAIL_MAX_LEN } from "../../../../lib/create/isValidCreateFlowSaveEmail";
|
||||
import { INVITE_EMAIL_PATTERN } from "../../../../lib/create/parseInviteEmails";
|
||||
import { resendExpiredStakeholderInvite } from "../../../../lib/create/api";
|
||||
|
||||
const expiredPageBgClass =
|
||||
"min-h-[100dvh] bg-[var(--color-surface-invert-brand-primary)]";
|
||||
|
||||
export function ExpiredInviteScreen() {
|
||||
const t = useTranslation("pages.invite");
|
||||
const router = useRouter();
|
||||
const searchParams = useSearchParams();
|
||||
const token = searchParams.get("token") ?? "";
|
||||
const titleId = useId();
|
||||
const [email, setEmail] = useState("");
|
||||
const [emailError, setEmailError] = useState("");
|
||||
const [formError, setFormError] = useState("");
|
||||
const [submitting, setSubmitting] = useState(false);
|
||||
const [sent, setSent] = useState(false);
|
||||
|
||||
const heading = sent
|
||||
? { title: t("sentTitle"), description: t("sentBody") }
|
||||
: { title: t("expiredTitle"), description: t("expiredDescription") };
|
||||
|
||||
async function sendLink() {
|
||||
const trimmed = email.trim().toLowerCase();
|
||||
if (!INVITE_EMAIL_PATTERN.test(trimmed)) {
|
||||
setEmailError(t("emailInvalid"));
|
||||
return;
|
||||
}
|
||||
if (token.length < 10) {
|
||||
setFormError(t("missingToken"));
|
||||
return;
|
||||
}
|
||||
setEmailError("");
|
||||
setFormError("");
|
||||
setSubmitting(true);
|
||||
const result = await resendExpiredStakeholderInvite(token, trimmed);
|
||||
setSubmitting(false);
|
||||
if (result.ok === false) {
|
||||
if (result.retryAfterMs) {
|
||||
const seconds = Math.max(1, Math.ceil(result.retryAfterMs / 1000));
|
||||
setFormError(t("rateLimited").replace("{seconds}", String(seconds)));
|
||||
return;
|
||||
}
|
||||
setFormError(result.error || t("genericError"));
|
||||
return;
|
||||
}
|
||||
setSent(true);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={expiredPageBgClass}>
|
||||
<Login
|
||||
isOpen
|
||||
usePortal={false}
|
||||
backdropVariant="solid"
|
||||
onClose={() => {
|
||||
router.push("/");
|
||||
}}
|
||||
ariaLabelledBy={titleId}
|
||||
belowCard={
|
||||
<Link
|
||||
href="/"
|
||||
className="text-small-paragraph text-[var(--color-content-invert-tertiary,#2d2d2d)] text-center hover:opacity-90"
|
||||
>
|
||||
{t("backToHome")}
|
||||
</Link>
|
||||
}
|
||||
>
|
||||
<div className="flex flex-col gap-6">
|
||||
<ContentLockup
|
||||
titleId={titleId}
|
||||
title={heading.title}
|
||||
description={heading.description}
|
||||
variant="login"
|
||||
alignment="left"
|
||||
/>
|
||||
{formError ? (
|
||||
<p
|
||||
role="alert"
|
||||
className="text-[14px] text-[var(--color-border-default-utility-negative)]"
|
||||
>
|
||||
{formError}
|
||||
</p>
|
||||
) : null}
|
||||
{!sent ? (
|
||||
<form
|
||||
className="flex flex-col gap-4"
|
||||
onSubmit={(event) => {
|
||||
event.preventDefault();
|
||||
void sendLink();
|
||||
}}
|
||||
noValidate
|
||||
>
|
||||
<TextInput
|
||||
label={t("emailLabel")}
|
||||
placeholder={t("emailPlaceholder")}
|
||||
type="email"
|
||||
name="email"
|
||||
autoComplete="email"
|
||||
inputMode="email"
|
||||
value={email}
|
||||
onChange={(event) => {
|
||||
setEmail(event.target.value);
|
||||
setEmailError("");
|
||||
}}
|
||||
disabled={submitting}
|
||||
error={Boolean(emailError)}
|
||||
showHelpIcon
|
||||
maxLength={EMAIL_MAX_LEN}
|
||||
/>
|
||||
{emailError ? (
|
||||
<p
|
||||
role="alert"
|
||||
className="text-[14px] text-[var(--color-border-default-utility-negative)]"
|
||||
>
|
||||
{emailError}
|
||||
</p>
|
||||
) : null}
|
||||
<p className="text-small-paragraph text-[var(--color-content-default-tertiary)]">
|
||||
{t("notInvitedAddress")}
|
||||
</p>
|
||||
<Button
|
||||
type="submit"
|
||||
size="large"
|
||||
buttonType="filled"
|
||||
palette="default"
|
||||
disabled={submitting}
|
||||
className="w-full !justify-center text-center px-[var(--space-400)] py-[var(--space-300)]"
|
||||
>
|
||||
{t("sendNewLink")}
|
||||
</Button>
|
||||
</form>
|
||||
) : null}
|
||||
</div>
|
||||
</Login>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user