@frank-auth/react
Version:
Flexible and customizable React UI components for Frank Authentication
483 lines (482 loc) • 14.3 kB
JavaScript
'use client';
import { jsx as e, jsxs as l } from "react/jsx-runtime";
import "@emotion/styled";
import { Button as w } from "../../ui/button/button.js";
import { Card as J, CardBody as K } from "../../ui/card/card.js";
import { motion as O } from "framer-motion";
import m from "react";
import { useConfig as $ } from "../../../hooks/use-config.js";
import { useMagicLink as D } from "../../../hooks/use-magic-link.js";
import { EmailField as q } from "../../forms/email-field.js";
import { FormWrapper as Q } from "../../forms/form-wrapper.js";
import { LoadingSpinner as U } from "./loading-spinner.js";
function F({
type: i = "sign-in",
email: d = "",
redirectUrl: x,
organizationId: f,
onSuccess: t,
onError: u,
onEmailSent: c,
title: N,
subtitle: C,
buttonText: k,
showValidation: o = !0,
showResend: v = !0,
className: y = "",
disabled: h = !1,
size: a = "md",
radius: r = "md"
}) {
const {
sendMagicLink: b,
isLoading: n,
error: L,
lastSentEmail: j,
canResend: M,
resendMagicLink: S,
isValidEmail: g,
clearState: T
} = D(), { organizationSettings: z } = $(), [s, P] = m.useState(d), [A, R] = m.useState(!1), B = (() => {
switch (i) {
case "sign-up":
return {
title: "Create your account",
subtitle: "Enter your email to get started with a magic link",
buttonText: "Send magic link"
};
case "verify-email":
return {
title: "Verify your email",
subtitle: "Click the link in your email to verify your account",
buttonText: "Send verification link"
};
case "password-reset":
return {
title: "Reset your password",
subtitle: "Enter your email to receive a password reset link",
buttonText: "Send reset link"
};
case "sign-in":
default:
return {
title: "Sign in with magic link",
subtitle: "Enter your email to receive a secure sign-in link",
buttonText: "Send magic link"
};
}
})(), V = N || B.title, W = C || B.subtitle, Y = k || B.buttonText, _ = m.useCallback(
async (E) => {
if (E.preventDefault(), !(!s || !g(s)))
try {
const p = await b(s, {
redirectUrl: x,
organizationId: f,
template: i
});
p.success && (R(!0), c?.(s), t?.(p));
} catch (p) {
const I = p instanceof Error ? p : new Error("Failed to send magic link");
u?.(I);
}
},
[
s,
g,
b,
x,
f,
i,
c,
t,
u
]
), G = m.useCallback(async () => {
try {
(await S()).success && c?.(s);
} catch (E) {
const p = E instanceof Error ? E : new Error("Failed to resend magic link");
u?.(p);
}
}, [S, s, c, u]), H = m.useCallback(() => {
R(!1), T();
}, [T]);
return A ? /* @__PURE__ */ l(
O.div,
{
initial: { opacity: 0, scale: 0.95 },
animate: { opacity: 1, scale: 1 },
className: `text-center space-y-4 ${y}`,
children: [
/* @__PURE__ */ e("div", { className: "mx-auto w-16 h-16 bg-success-100 dark:bg-success-900/30 rounded-full flex items-center justify-center", children: /* @__PURE__ */ e(
"svg",
{
className: "w-8 h-8 text-success-600 dark:text-success-400",
fill: "none",
stroke: "currentColor",
viewBox: "0 0 24 24",
children: /* @__PURE__ */ e(
"path",
{
strokeLinecap: "round",
strokeLinejoin: "round",
strokeWidth: 2,
d: "M12 19l9 2-9-18-9 18 9-2zm0 0v-8"
}
)
}
) }),
/* @__PURE__ */ l("div", { children: [
/* @__PURE__ */ e("h3", { className: "text-xl font-semibold text-foreground mb-2", children: "Check your email" }),
/* @__PURE__ */ l("p", { className: "text-default-500 text-sm", children: [
"We sent a magic link to ",
/* @__PURE__ */ e("strong", { children: j || s })
] })
] }),
/* @__PURE__ */ e("div", { className: "bg-default-100 dark:bg-default-800 rounded-lg p-4", children: /* @__PURE__ */ l("p", { className: "text-sm text-default-600 dark:text-default-400", children: [
"Click the link in your email to",
" ",
i === "sign-up" ? "create your account" : "sign in",
". The link will expire in 15 minutes."
] }) }),
/* @__PURE__ */ l("div", { className: "space-y-3", children: [
v && /* @__PURE__ */ e("div", { children: M ? /* @__PURE__ */ e(
w,
{
variant: "light",
color: "primary",
size: a,
radius: r,
onPress: G,
isLoading: n,
isDisabled: h,
disabled: h,
children: "Resend magic link"
}
) : /* @__PURE__ */ e("p", { className: "text-sm text-default-500", children: "You can resend the link in a few seconds" }) }),
/* @__PURE__ */ e(
w,
{
variant: "light",
size: "sm",
radius: r,
onPress: H,
isDisabled: n,
disabled: n,
children: "Use a different email"
}
)
] }),
L && /* @__PURE__ */ e("div", { className: "text-danger-600 text-sm bg-danger-50 dark:bg-danger-900/20 rounded-lg p-3", children: L.message })
]
}
) : /* @__PURE__ */ l("form", { onSubmit: _, className: `space-y-4 ${y}`, children: [
/* @__PURE__ */ l("div", { className: "text-center space-y-2", children: [
V && /* @__PURE__ */ e("h3", { className: "text-xl font-semibold text-foreground", children: V }),
W && /* @__PURE__ */ e("p", { className: "text-default-500 text-sm", children: W })
] }),
/* @__PURE__ */ e(
q,
{
name: "email",
label: "Email address",
placeholder: "Enter your email",
value: s,
onChange: P,
required: !0,
disabled: h || n,
validateFormat: o,
size: a,
radius: r,
autoFocus: !0
}
),
/* @__PURE__ */ e(
w,
{
type: "submit",
color: "primary",
size: a,
radius: r,
className: "w-full",
isLoading: n,
isDisabled: h || !s || o && !g(s),
disabled: h || !s || o && !g(s),
children: n ? "Sending..." : Y
}
),
L && /* @__PURE__ */ e("div", { className: "text-danger-600 text-sm bg-danger-50 dark:bg-danger-900/20 rounded-lg p-3", children: L.message })
] });
}
function X({
type: i = "sign-in",
email: d = "",
redirectUrl: x,
organizationId: f,
onSuccess: t,
onError: u,
onEmailSent: c,
buttonText: N,
className: C = "",
disabled: k = !1,
size: o = "md",
radius: v = "md",
buttonProps: y = {}
}) {
const { sendMagicLink: h, isLoading: a } = D(), [r, b] = m.useState(!d), [n, L] = m.useState(d), M = N || (() => {
switch (i) {
case "sign-up":
return "Sign up with magic link";
case "verify-email":
return "Send verification link";
case "password-reset":
return "Send reset link";
default:
return "Sign in with magic link";
}
})(), S = m.useCallback(async () => {
if (!n) {
b(!0);
return;
}
try {
const g = await h(n, {
redirectUrl: x,
organizationId: f,
template: i
});
g.success && (c?.(n), t?.(g));
} catch (g) {
const T = g instanceof Error ? g : new Error("Failed to send magic link");
u?.(T);
}
}, [
n,
h,
x,
f,
i,
c,
t,
u
]);
return r && !d ? /* @__PURE__ */ l("div", { className: `space-y-3 ${C}`, children: [
/* @__PURE__ */ e(
q,
{
name: "email",
label: "Email address",
placeholder: "Enter your email",
value: n,
onChange: L,
required: !0,
disabled: k || a,
size: o,
radius: v,
autoFocus: !0
}
),
/* @__PURE__ */ l("div", { className: "flex gap-2", children: [
/* @__PURE__ */ e(
w,
{
color: "primary",
size: o,
radius: v,
onPress: S,
isLoading: a,
isDisabled: k || !n,
disabled: k || !n,
className: "flex-1",
...y,
children: a ? "Sending..." : M
}
),
/* @__PURE__ */ e(
w,
{
variant: "light",
size: o,
radius: v,
onPress: () => b(!1),
isDisabled: a,
children: "Cancel"
}
)
] })
] }) : /* @__PURE__ */ e(
w,
{
color: "primary",
size: o,
radius: v,
onPress: S,
isLoading: a,
isDisabled: k,
className: C,
startContent: /* @__PURE__ */ e(
"svg",
{
className: "w-4 h-4",
fill: "none",
stroke: "currentColor",
viewBox: "0 0 24 24",
children: /* @__PURE__ */ e(
"path",
{
strokeLinecap: "round",
strokeLinejoin: "round",
strokeWidth: 2,
d: "M12 19l9 2-9-18-9 18 9-2zm0 0v-8"
}
)
}
),
...y,
children: M
}
);
}
function Z({
variant: i = "form",
cardProps: d = {},
size: x = "md",
radius: f = "md",
...t
}) {
const { components: u } = $(), c = u.MagicLink;
if (c)
return /* @__PURE__ */ e(c, { variant: i, cardProps: d, ...t });
switch (i) {
case "button":
return /* @__PURE__ */ e(X, { ...t });
case "card":
return /* @__PURE__ */ e(J, { ...d, children: /* @__PURE__ */ e(K, { children: /* @__PURE__ */ e(F, { ...t }) }) });
case "inline":
return /* @__PURE__ */ e(F, { ...t });
case "form":
default:
return /* @__PURE__ */ e(
Q,
{
title: t.title,
subtitle: t.subtitle,
className: t.className,
showCard: !1,
size: x,
radius: f,
children: /* @__PURE__ */ e(F, { ...t })
}
);
}
}
function me({
onSuccess: i,
onError: d,
loadingComponent: x,
errorComponent: f,
className: t = "",
size: u = "md",
radius: c = "md"
}) {
const { verifyFromUrl: N, isLoading: C, error: k } = D(), [o, v] = m.useState("verifying"), [y, h] = m.useState(null);
if (m.useEffect(() => {
(async () => {
try {
const r = await N();
if (r.success)
v("success"), h(r), i?.(r);
else
throw new Error(r.error || "Verification failed");
} catch (r) {
const b = r instanceof Error ? r : new Error("Verification failed");
v("error"), d?.(b);
}
})();
}, [N, i, d]), o === "verifying" || C)
return /* @__PURE__ */ e("div", { className: `flex items-center justify-center min-h-64 ${t}`, children: x || /* @__PURE__ */ e(
U,
{
variant: "pulse",
size: "lg",
showText: !0,
text: "Verifying magic link...",
centered: !0
}
) });
if (o === "error" || k) {
const a = k || new Error("Verification failed");
return f ? /* @__PURE__ */ e(
f,
{
error: a,
retry: () => window.location.reload()
}
) : /* @__PURE__ */ l("div", { className: `text-center space-y-4 ${t}`, children: [
/* @__PURE__ */ e("div", { className: "mx-auto w-16 h-16 bg-danger-100 dark:bg-danger-900/30 rounded-full flex items-center justify-center", children: /* @__PURE__ */ e(
"svg",
{
className: "w-8 h-8 text-danger-600 dark:text-danger-400",
fill: "none",
stroke: "currentColor",
viewBox: "0 0 24 24",
children: /* @__PURE__ */ e(
"path",
{
strokeLinecap: "round",
strokeLinejoin: "round",
strokeWidth: 2,
d: "M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.732-.833-2.5 0L4.732 16.5c-.77.833.192 2.5 1.732 2.5z"
}
)
}
) }),
/* @__PURE__ */ l("div", { children: [
/* @__PURE__ */ e("h3", { className: "text-xl font-semibold text-foreground mb-2", children: "Verification Failed" }),
/* @__PURE__ */ e("p", { className: "text-default-500 text-sm", children: a.message })
] }),
/* @__PURE__ */ e(
w,
{
color: "primary",
size: u,
radius: c,
onPress: () => window.location.reload(),
children: "Try Again"
}
)
] });
}
return o === "success" ? /* @__PURE__ */ l("div", { className: `text-center space-y-4 ${t}`, children: [
/* @__PURE__ */ e("div", { className: "mx-auto w-16 h-16 bg-success-100 dark:bg-success-900/30 rounded-full flex items-center justify-center", children: /* @__PURE__ */ e(
"svg",
{
className: "w-8 h-8 text-success-600 dark:text-success-400",
fill: "none",
stroke: "currentColor",
viewBox: "0 0 24 24",
children: /* @__PURE__ */ e(
"path",
{
strokeLinecap: "round",
strokeLinejoin: "round",
strokeWidth: 2,
d: "M5 13l4 4L19 7"
}
)
}
) }),
/* @__PURE__ */ l("div", { children: [
/* @__PURE__ */ e("h3", { className: "text-xl font-semibold text-foreground mb-2", children: "Success!" }),
/* @__PURE__ */ e("p", { className: "text-default-500 text-sm", children: "You have been successfully authenticated." })
] }),
y?.requiresAdditionalVerification && /* @__PURE__ */ e("div", { className: "text-sm text-warning-600", children: "Additional verification required. Redirecting..." })
] }) : null;
}
var fe = Z;
export {
Z as MagicLink,
me as MagicLinkVerification,
fe as default
};
//# sourceMappingURL=magic-link.js.map