UNPKG

@frank-auth/react

Version:

Flexible and customizable React UI components for Frank Authentication

483 lines (482 loc) 14.3 kB
'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