Let guests keep a published rule via Save & Exit and a post-finalize sign-in prompt.

Email is optional so they can continue without saving; skip from Save & Exit leaves the flow instead of returning to completed.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
adilallo
2026-09-02 16:18:41 -06:00
co-authored by Cursor
parent 84ef943df4
commit db34a1f0df
17 changed files with 521 additions and 60 deletions
+4 -1
View File
@@ -31,7 +31,10 @@ export function LoginView({
<div
ref={backdropRef}
className={`fixed inset-0 z-[9998] flex flex-col items-center justify-center gap-6 overflow-y-auto px-4 py-8 ${backdropClasses[backdropVariant]}`}
onClick={onClose}
onPointerDown={(event) => {
if (event.target !== event.currentTarget) return;
onClose();
}}
role="presentation"
>
<div
+54 -20
View File
@@ -44,17 +44,51 @@ function MailIconInline() {
const EMAIL_PATTERN = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
export type LoginFormVariant = "default" | "saveProgress";
export type LoginFormVariant = "default" | "saveProgress" | "keepRule";
function loginFormHeading(
variant: LoginFormVariant,
sent: boolean,
t: (_key: string) => string,
): { title: string; description: string } {
if (sent) {
return { title: t("successTitle"), description: t("successBody") };
}
if (variant === "saveProgress") {
return {
title: t("saveProgressTitle"),
description: t("saveProgressSubtitle"),
};
}
if (variant === "keepRule") {
return {
title: t("keepRuleTitle"),
description: t("keepRuleSubtitle"),
};
}
return { title: t("title"), description: t("subtitle") };
}
function shouldAttachCreateFlowDraft(
variant: LoginFormVariant,
nextPath: string,
): boolean {
if (variant === "keepRule") return false;
return variant === "saveProgress" || nextPath.includes("syncDraft=1");
}
export type LoginFormProps = {
variant?: LoginFormVariant;
/** Overrides URL `next` for `requestMagicLink` (e.g. create-flow exit modal). */
magicLinkNextPath?: string;
/** `keepRule`: Continue without saving. */
onDismiss?: () => void;
};
export default function LoginForm({
variant = "default",
magicLinkNextPath,
onDismiss,
}: LoginFormProps) {
const t = useTranslation("pages.login");
const tFooter = useTranslation("footer");
@@ -72,7 +106,7 @@ export default function LoginForm({
const nextParam = searchParams.get("next");
const errorParam = searchParams.get("error");
const isSaveProgress = variant === "saveProgress";
const heading = loginFormHeading(variant, sent, t);
/** Drop `error` from the URL so URL-driven messages dont linger after a new attempt. */
const stripErrorQuery = useCallback(() => {
@@ -96,8 +130,7 @@ export default function LoginForm({
try {
const rawNext = magicLinkNextPath ?? nextParam;
const nextPath = safeInternalPath(rawNext);
const shouldAttachDraft =
isSaveProgress || nextPath.includes("syncDraft=1");
const shouldAttachDraft = shouldAttachCreateFlowDraft(variant, nextPath);
const localDraft = readAnonymousCreateFlowState();
const draft =
shouldAttachDraft && Object.keys(localDraft).length > 0
@@ -115,7 +148,7 @@ export default function LoginForm({
}
return;
}
if (isSaveProgress || nextPath.includes("syncDraft=1")) {
if (shouldAttachDraft) {
setTransferPendingFlag();
}
setEmail(trimmed);
@@ -127,11 +160,11 @@ export default function LoginForm({
}
}, [
email,
isSaveProgress,
magicLinkNextPath,
nextParam,
stripErrorQuery,
t,
variant,
]);
const urlErrorMessage =
@@ -155,20 +188,8 @@ export default function LoginForm({
</div>
<ContentLockup
titleId={titleId}
title={
sent
? t("successTitle")
: isSaveProgress
? t("saveProgressTitle")
: t("title")
}
description={
sent
? t("successBody")
: isSaveProgress
? t("saveProgressSubtitle")
: t("subtitle")
}
title={heading.title}
description={heading.description}
variant="login"
alignment="left"
/>
@@ -255,6 +276,19 @@ export default function LoginForm({
>
{t("sendMagicLink")}
</Button>
{onDismiss ? (
<Button
type="button"
size="large"
buttonType="ghost"
palette="default"
disabled={submitting}
onClick={onDismiss}
className="w-full !justify-center text-center px-[var(--spacing-scale-016)] py-[var(--spacing-scale-012)]"
>
{t("continueWithoutSaving")}
</Button>
) : null}
<p className="text-center text-small-paragraph text-[var(--color-content-default-tertiary)]">
{t("legalPrefix")}
<Link