UNPKG

@frank-auth/react

Version:

Flexible and customizable React UI components for Frank Authentication

413 lines (412 loc) 16 kB
'use client'; import { jsx as e, jsxs as r, Fragment as U } from "react/jsx-runtime"; import { useDisclosure as H, Card as C, CardHeader as b, Divider as L, CardBody as M, Button as N, Chip as q, Switch as A, Modal as I, ModalContent as W, ModalHeader as D, ModalBody as V } from "@heroui/react"; import v from "react"; import { useAuth as $ } from "../../../hooks/use-auth.js"; import { useConfig as R } from "../../../hooks/use-config.js"; import { useSession as T } from "../../../hooks/use-session.js"; import { useUser as G } from "../../../hooks/use-user.js"; import { PasswordField as z } from "../../forms/password-field.js"; function J(n) { const l = n?.device?.toLowerCase() || "", a = n?.os?.toLowerCase() || ""; return l.includes("mobile") || a.includes("ios") || a.includes("android") ? /* @__PURE__ */ e( "svg", { className: "w-5 h-5 text-default-400", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ e( "path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M12 18h.01M8 21h8a1 1 0 001-1V4a1 1 0 00-1-1H8a1 1 0 00-1 1v16a1 1 0 001 1z" } ) } ) : /* @__PURE__ */ e( "svg", { className: "w-5 h-5 text-default-400", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ e( "path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M9.75 17L9 20l-1 1h8l-1-1-.75-3M3 13h18M5 17h14a2 2 0 002-2V5a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z" } ) } ); } function K(n) { if (!n) return "Never"; const a = (/* @__PURE__ */ new Date()).getTime() - n.getTime(), d = Math.floor(a / (1e3 * 60)), h = Math.floor(a / (1e3 * 60 * 60)), c = Math.floor(a / (1e3 * 60 * 60 * 24)); return d < 1 ? "Just now" : d < 60 ? `${d}m ago` : h < 24 ? `${h}h ago` : `${c}d ago`; } function Q({ onSuccess: n, onError: l, onClose: a, isOpen: d }) { const { changePassword: h } = G(), [c, g] = v.useState(""), [t, u] = v.useState(""), [w, x] = v.useState(""), [m, f] = v.useState(!1), [y, S] = v.useState({}); v.useEffect(() => { d || (g(""), u(""), x(""), S({})); }, [d]); const j = () => { const i = {}; return c || (i.currentPassword = "Current password is required"), t ? t.length < 8 && (i.newPassword = "Password must be at least 8 characters") : i.newPassword = "New password is required", w ? t !== w && (i.confirmPassword = "Passwords do not match") : i.confirmPassword = "Please confirm your new password", S(i), Object.keys(i).length === 0; }; return /* @__PURE__ */ r("form", { onSubmit: async (i) => { if (i.preventDefault(), !!j()) try { f(!0), await h({ currentPassword: c, newPassword: t }), n("Password changed successfully"), a(); } catch (P) { const p = P instanceof Error ? P.message : "Failed to change password"; l(p); } finally { f(!1); } }, className: "space-y-4", children: [ /* @__PURE__ */ e( z, { label: "Current Password", placeholder: "Enter your current password", value: c, onChange: g, error: y.currentPassword, required: !0 } ), /* @__PURE__ */ e( z, { label: "New Password", placeholder: "Enter your new password", value: t, onChange: u, error: y.newPassword, showStrength: !0, required: !0 } ), /* @__PURE__ */ e( z, { label: "Confirm New Password", placeholder: "Confirm your new password", value: w, onChange: x, error: y.confirmPassword, required: !0 } ), /* @__PURE__ */ r("div", { className: "flex justify-end gap-2 pt-4", children: [ /* @__PURE__ */ e(N, { variant: "light", onPress: a, isDisabled: m, children: "Cancel" }), /* @__PURE__ */ e( N, { type: "submit", color: "primary", isLoading: m, isDisabled: !c || !t || !w, children: "Change Password" } ) ] }) ] }); } function ne({ onSuccess: n, onError: l, showPasswordChange: a = !0, showSessionManagement: d = !0, showSecurityPreferences: h = !1, showActivityLog: c = !1, className: g = "", isDisabled: t = !1, variant: u = "bordered", size: w = "md", customSections: x = [], hideSections: m = [] }) { const { user: f } = $(), { sessions: y, isCurrentDevice: S, hasMultipleSessions: j, revokeSession: B, revokeAllSessions: i } = T(), { components: P } = R(), p = H(), O = P.SecurityPanel; if (O) return /* @__PURE__ */ e( O, { onSuccess: n, onError: l, showPasswordChange: a, showSessionManagement: d, showSecurityPreferences: h, showActivityLog: c, className: g, isDisabled: t, variant: u, size: w, customSections: x, hideSections: m } ); const E = async (s) => { try { await B(s), n?.("Session revoked successfully"); } catch (o) { const k = o instanceof Error ? o.message : "Failed to revoke session"; l?.(k); } }, F = async () => { try { await i(!0), n?.("All other sessions revoked successfully"); } catch (s) { const o = s instanceof Error ? s.message : "Failed to revoke sessions"; l?.(o); } }; return f ? /* @__PURE__ */ r("div", { className: `space-y-6 ${g}`, children: [ a && !m.includes("password") && /* @__PURE__ */ r(C, { variant: u, children: [ /* @__PURE__ */ e(b, { children: /* @__PURE__ */ r("div", { className: "flex items-center gap-3", children: [ /* @__PURE__ */ e("div", { className: "flex items-center justify-center w-10 h-10 bg-primary/10 rounded-lg", children: /* @__PURE__ */ e( "svg", { className: "w-5 h-5 text-primary", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ e( "path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z" } ) } ) }), /* @__PURE__ */ r("div", { children: [ /* @__PURE__ */ e("h4", { className: "text-md font-semibold", children: "Password" }), /* @__PURE__ */ e("p", { className: "text-sm text-default-500", children: "Manage your password and account security" }) ] }) ] }) }), /* @__PURE__ */ e(L, {}), /* @__PURE__ */ e(M, { children: /* @__PURE__ */ r("div", { className: "flex items-center justify-between", children: [ /* @__PURE__ */ r("div", { children: [ /* @__PURE__ */ e("p", { className: "text-sm font-medium", children: "Password" }), /* @__PURE__ */ r("p", { className: "text-xs text-default-500", children: [ "Last changed:", " ", f.passwordUpdatedAt ? new Date(f.passwordUpdatedAt).toLocaleDateString() : "Unknown" ] }) ] }), /* @__PURE__ */ e( N, { variant: "bordered", size: "sm", onPress: p.onOpen, isDisabled: t, children: "Change Password" } ) ] }) }) ] }), d && !m.includes("sessions") && /* @__PURE__ */ r(C, { variant: u, children: [ /* @__PURE__ */ e(b, { children: /* @__PURE__ */ r("div", { className: "flex items-center justify-between w-full", children: [ /* @__PURE__ */ r("div", { className: "flex items-center gap-3", children: [ /* @__PURE__ */ e("div", { className: "flex items-center justify-center w-10 h-10 bg-warning/10 rounded-lg", children: /* @__PURE__ */ e( "svg", { className: "w-5 h-5 text-warning", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ e( "path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M9.75 17L9 20l-1 1h8l-1-1-.75-3M3 13h18M5 17h14a2 2 0 002-2V5a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z" } ) } ) }), /* @__PURE__ */ r("div", { children: [ /* @__PURE__ */ e("h4", { className: "text-md font-semibold", children: "Active Sessions" }), /* @__PURE__ */ e("p", { className: "text-sm text-default-500", children: "Manage devices that are signed into your account" }) ] }) ] }), j && /* @__PURE__ */ e( N, { variant: "light", color: "danger", size: "sm", onPress: F, isDisabled: t, children: "Sign out all others" } ) ] }) }), /* @__PURE__ */ e(L, {}), /* @__PURE__ */ e(M, { children: /* @__PURE__ */ e("div", { className: "space-y-3", children: y.map((s, o) => { const k = o === 0; return /* @__PURE__ */ r( "div", { className: "flex items-center justify-between p-3 border border-default-200 rounded-lg", children: [ /* @__PURE__ */ r("div", { className: "flex items-center gap-3", children: [ J(s.deviceInfo), /* @__PURE__ */ r("div", { className: "flex flex-col", children: [ /* @__PURE__ */ r("div", { className: "flex items-center gap-2", children: [ /* @__PURE__ */ e("span", { className: "text-sm font-medium", children: s.deviceInfo?.browser || "Unknown Browser" }), k && /* @__PURE__ */ e(q, { size: "sm", color: "success", variant: "flat", children: "Current" }) ] }), /* @__PURE__ */ r("div", { className: "flex items-center gap-2 text-xs text-default-500", children: [ /* @__PURE__ */ e("span", { children: s.deviceInfo?.os || "Unknown OS" }), /* @__PURE__ */ e("span", { children: "•" }), /* @__PURE__ */ e("span", { children: s.deviceInfo?.location || "Unknown Location" }), /* @__PURE__ */ e("span", { children: "•" }), /* @__PURE__ */ r("span", { children: [ "Active ", K(s.lastActiveAt) ] }) ] }) ] }) ] }), !k && /* @__PURE__ */ e( N, { variant: "light", color: "danger", size: "sm", onPress: () => E(s.id), isDisabled: t, children: "Sign out" } ) ] }, s.id ); }) }) }) ] }), h && !m.includes("preferences") && /* @__PURE__ */ r(C, { variant: u, children: [ /* @__PURE__ */ e(b, { children: /* @__PURE__ */ r("div", { className: "flex items-center gap-3", children: [ /* @__PURE__ */ e("div", { className: "flex items-center justify-center w-10 h-10 bg-secondary/10 rounded-lg", children: /* @__PURE__ */ e( "svg", { className: "w-5 h-5 text-secondary", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ e( "path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z" } ) } ) }), /* @__PURE__ */ r("div", { children: [ /* @__PURE__ */ e("h4", { className: "text-md font-semibold", children: "Security Preferences" }), /* @__PURE__ */ e("p", { className: "text-sm text-default-500", children: "Configure your security and privacy settings" }) ] }) ] }) }), /* @__PURE__ */ e(L, {}), /* @__PURE__ */ e(M, { children: /* @__PURE__ */ r("div", { className: "space-y-4", children: [ /* @__PURE__ */ r("div", { className: "flex items-center justify-between", children: [ /* @__PURE__ */ r("div", { children: [ /* @__PURE__ */ e("p", { className: "text-sm font-medium", children: "Email notifications for sign-ins" }), /* @__PURE__ */ e("p", { className: "text-xs text-default-500", children: "Get notified when someone signs into your account" }) ] }), /* @__PURE__ */ e(A, { defaultSelected: !0, size: "sm", isDisabled: t }) ] }), /* @__PURE__ */ r("div", { className: "flex items-center justify-between", children: [ /* @__PURE__ */ r("div", { children: [ /* @__PURE__ */ e("p", { className: "text-sm font-medium", children: "Two-factor authentication" }), /* @__PURE__ */ e("p", { className: "text-xs text-default-500", children: "Add an extra layer of security to your account" }) ] }), /* @__PURE__ */ e( A, { isSelected: f.mfaEnabled, size: "sm", isDisabled: t } ) ] }), /* @__PURE__ */ r("div", { className: "flex items-center justify-between", children: [ /* @__PURE__ */ r("div", { children: [ /* @__PURE__ */ e("p", { className: "text-sm font-medium", children: "Session timeout" }), /* @__PURE__ */ e("p", { className: "text-xs text-default-500", children: "Automatically sign out after inactivity" }) ] }), /* @__PURE__ */ e(A, { defaultSelected: !0, size: "sm", isDisabled: t }) ] }) ] }) }) ] }), x.map( (s) => !m.includes(s.key) && /* @__PURE__ */ r(C, { variant: u, children: [ /* @__PURE__ */ e(b, { children: /* @__PURE__ */ r("div", { className: "flex items-center gap-3", children: [ s.icon && /* @__PURE__ */ e("div", { className: "flex items-center justify-center w-10 h-10 bg-default/10 rounded-lg", children: s.icon }), /* @__PURE__ */ r("div", { children: [ /* @__PURE__ */ e("h4", { className: "text-md font-semibold", children: s.title }), s.description && /* @__PURE__ */ e("p", { className: "text-sm text-default-500", children: s.description }) ] }) ] }) }), /* @__PURE__ */ e(L, {}), /* @__PURE__ */ e(M, { children: s.content }) ] }, s.key) ), /* @__PURE__ */ e( I, { isOpen: p.isOpen, onOpenChange: p.onOpenChange, placement: "center", size: "md", children: /* @__PURE__ */ e(W, { children: (s) => /* @__PURE__ */ r(U, { children: [ /* @__PURE__ */ e(D, { children: /* @__PURE__ */ e("h3", { className: "text-lg font-semibold", children: "Change Password" }) }), /* @__PURE__ */ e(V, { children: /* @__PURE__ */ e( Q, { onSuccess: (o) => { n?.(o), s(); }, onError: (o) => l?.(o), onClose: s, isOpen: p.isOpen } ) }) ] }) }) } ) ] }) : null; } export { ne as SecurityPanel }; //# sourceMappingURL=security-panel.js.map