UNPKG

payload-plugin-newsletter

Version:

Complete newsletter management plugin for Payload CMS with subscriber management, magic link authentication, and email service integration

899 lines (894 loc) 31.2 kB
"use strict"; "use client"; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // src/exports/client.ts var client_exports = {}; __export(client_exports, { MagicLinkVerify: () => MagicLinkVerify, NewsletterForm: () => NewsletterForm, PreferencesForm: () => PreferencesForm, createMagicLinkVerify: () => createMagicLinkVerify, createNewsletterForm: () => createNewsletterForm, createPreferencesForm: () => createPreferencesForm, useNewsletterAuth: () => useNewsletterAuth }); module.exports = __toCommonJS(client_exports); // src/components/NewsletterForm.tsx var import_react = require("react"); var import_jsx_runtime = require("react/jsx-runtime"); var defaultStyles = { form: { display: "flex", flexDirection: "column", gap: "1rem", maxWidth: "400px", margin: "0 auto" }, inputGroup: { display: "flex", flexDirection: "column", gap: "0.5rem" }, label: { fontSize: "0.875rem", fontWeight: "500", color: "#374151" }, input: { padding: "0.5rem 0.75rem", fontSize: "1rem", border: "1px solid #e5e7eb", borderRadius: "0.375rem", outline: "none", transition: "border-color 0.2s" }, button: { padding: "0.75rem 1.5rem", fontSize: "1rem", fontWeight: "500", color: "#ffffff", backgroundColor: "#3b82f6", border: "none", borderRadius: "0.375rem", cursor: "pointer", transition: "background-color 0.2s" }, buttonDisabled: { opacity: 0.5, cursor: "not-allowed" }, error: { fontSize: "0.875rem", color: "#ef4444", marginTop: "0.25rem" }, success: { fontSize: "0.875rem", color: "#10b981", marginTop: "0.25rem" }, checkbox: { display: "flex", alignItems: "center", gap: "0.5rem" }, checkboxInput: { width: "1rem", height: "1rem" }, checkboxLabel: { fontSize: "0.875rem", color: "#374151" } }; var NewsletterForm = ({ onSuccess, onError, showName = false, showPreferences = false, leadMagnet, className, styles: customStyles = {}, apiEndpoint = "/api/newsletter/subscribe", buttonText = "Subscribe", loadingText = "Subscribing...", successMessage = "Successfully subscribed!", placeholders = { email: "Enter your email", name: "Enter your name" }, labels = { email: "Email", name: "Name", newsletter: "Newsletter updates", announcements: "Product announcements" } }) => { const [email, setEmail] = (0, import_react.useState)(""); const [name, setName] = (0, import_react.useState)(""); const [preferences, setPreferences] = (0, import_react.useState)({ newsletter: true, announcements: true }); const [loading, setLoading] = (0, import_react.useState)(false); const [error, setError] = (0, import_react.useState)(null); const [success, setSuccess] = (0, import_react.useState)(false); const styles = { form: { ...defaultStyles.form, ...customStyles.form }, inputGroup: { ...defaultStyles.inputGroup, ...customStyles.inputGroup }, label: { ...defaultStyles.label, ...customStyles.label }, input: { ...defaultStyles.input, ...customStyles.input }, button: { ...defaultStyles.button, ...customStyles.button }, buttonDisabled: { ...defaultStyles.buttonDisabled, ...customStyles.buttonDisabled }, error: { ...defaultStyles.error, ...customStyles.error }, success: { ...defaultStyles.success, ...customStyles.success }, checkbox: { ...defaultStyles.checkbox, ...customStyles.checkbox }, checkboxInput: { ...defaultStyles.checkboxInput, ...customStyles.checkboxInput }, checkboxLabel: { ...defaultStyles.checkboxLabel, ...customStyles.checkboxLabel } }; const handleSubmit = async (e) => { e.preventDefault(); setError(null); setLoading(true); try { const payload = { email, ...showName && name && { name }, ...showPreferences && { preferences }, ...leadMagnet && { leadMagnet: leadMagnet.id }, metadata: { signupPage: window.location.href, ...typeof window !== "undefined" && window.location.search && { utmParams: Object.fromEntries(new URLSearchParams(window.location.search)) } } }; const response = await fetch(apiEndpoint, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(payload) }); const data = await response.json(); if (!response.ok) { throw new Error(data.error || data.errors?.join(", ") || "Subscription failed"); } setSuccess(true); setEmail(""); setName(""); if (onSuccess) { onSuccess(data.subscriber); } } catch (err) { const errorMessage = err instanceof Error ? err.message : "An error occurred"; setError(errorMessage); if (onError) { onError(new Error(errorMessage)); } } finally { setLoading(false); } }; if (success && !showPreferences) { return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className, style: styles.form, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: styles.success, children: successMessage }) }); } return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("form", { onSubmit: handleSubmit, className, style: styles.form, children: [ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: styles.inputGroup, children: [ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { htmlFor: "email", style: styles.label, children: labels.email }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)( "input", { id: "email", type: "email", value: email, onChange: (e) => setEmail(e.target.value), placeholder: placeholders.email, required: true, disabled: loading, style: { ...styles.input, ...loading && { opacity: 0.5 } } } ) ] }), showName && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: styles.inputGroup, children: [ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { htmlFor: "name", style: styles.label, children: labels.name }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)( "input", { id: "name", type: "text", value: name, onChange: (e) => setName(e.target.value), placeholder: placeholders.name, disabled: loading, style: { ...styles.input, ...loading && { opacity: 0.5 } } } ) ] }), showPreferences && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: styles.inputGroup, children: [ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { style: styles.label, children: "Email Preferences" }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: styles.checkbox, children: [ /* @__PURE__ */ (0, import_jsx_runtime.jsx)( "input", { id: "newsletter", type: "checkbox", checked: preferences.newsletter, onChange: (e) => setPreferences({ ...preferences, newsletter: e.target.checked }), disabled: loading, style: styles.checkboxInput } ), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { htmlFor: "newsletter", style: styles.checkboxLabel, children: labels.newsletter }) ] }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: styles.checkbox, children: [ /* @__PURE__ */ (0, import_jsx_runtime.jsx)( "input", { id: "announcements", type: "checkbox", checked: preferences.announcements, onChange: (e) => setPreferences({ ...preferences, announcements: e.target.checked }), disabled: loading, style: styles.checkboxInput } ), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { htmlFor: "announcements", style: styles.checkboxLabel, children: labels.announcements }) ] }) ] }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)( "button", { type: "submit", disabled: loading, style: { ...styles.button, ...loading && styles.buttonDisabled }, children: loading ? loadingText : buttonText } ), error && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: styles.error, children: error }), success && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: styles.success, children: successMessage }) ] }); }; function createNewsletterForm(defaultProps) { return (props) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(NewsletterForm, { ...defaultProps, ...props }); } // src/components/PreferencesForm.tsx var import_react2 = require("react"); var import_jsx_runtime2 = require("react/jsx-runtime"); var defaultStyles2 = { container: { maxWidth: "600px", margin: "0 auto", padding: "2rem" }, heading: { fontSize: "1.5rem", fontWeight: "600", marginBottom: "1.5rem", color: "#111827" }, form: { display: "flex", flexDirection: "column", gap: "1.5rem" }, section: { padding: "1.5rem", backgroundColor: "#f9fafb", borderRadius: "0.5rem", border: "1px solid #e5e7eb" }, sectionTitle: { fontSize: "1.125rem", fontWeight: "500", marginBottom: "1rem", color: "#111827" }, inputGroup: { display: "flex", flexDirection: "column", gap: "0.5rem" }, label: { fontSize: "0.875rem", fontWeight: "500", color: "#374151" }, input: { padding: "0.5rem 0.75rem", fontSize: "1rem", border: "1px solid #e5e7eb", borderRadius: "0.375rem", outline: "none", transition: "border-color 0.2s" }, select: { padding: "0.5rem 0.75rem", fontSize: "1rem", border: "1px solid #e5e7eb", borderRadius: "0.375rem", outline: "none", backgroundColor: "#ffffff" }, checkbox: { display: "flex", alignItems: "center", gap: "0.5rem", marginBottom: "0.5rem" }, checkboxInput: { width: "1rem", height: "1rem" }, checkboxLabel: { fontSize: "0.875rem", color: "#374151" }, buttonGroup: { display: "flex", gap: "1rem", marginTop: "1rem" }, button: { padding: "0.75rem 1.5rem", fontSize: "1rem", fontWeight: "500", borderRadius: "0.375rem", cursor: "pointer", transition: "all 0.2s", border: "none" }, primaryButton: { color: "#ffffff", backgroundColor: "#3b82f6" }, secondaryButton: { color: "#374151", backgroundColor: "#ffffff", border: "1px solid #e5e7eb" }, dangerButton: { color: "#ffffff", backgroundColor: "#ef4444" }, error: { fontSize: "0.875rem", color: "#ef4444", marginTop: "0.5rem" }, success: { fontSize: "0.875rem", color: "#10b981", marginTop: "0.5rem" }, info: { fontSize: "0.875rem", color: "#6b7280", marginTop: "0.5rem" } }; var PreferencesForm = ({ subscriber: initialSubscriber, onSuccess, onError, className, styles: customStyles = {}, sessionToken, apiEndpoint = "/api/newsletter/preferences", showUnsubscribe = true, locales = ["en"], labels = { title: "Newsletter Preferences", personalInfo: "Personal Information", emailPreferences: "Email Preferences", name: "Name", language: "Preferred Language", newsletter: "Newsletter updates", announcements: "Product announcements", saveButton: "Save Preferences", unsubscribeButton: "Unsubscribe", saving: "Saving...", saved: "Preferences saved successfully!", unsubscribeConfirm: "Are you sure you want to unsubscribe? This cannot be undone." } }) => { const [subscriber, setSubscriber] = (0, import_react2.useState)(initialSubscriber || {}); const [loading, setLoading] = (0, import_react2.useState)(false); const [loadingData, setLoadingData] = (0, import_react2.useState)(!initialSubscriber); const [error, setError] = (0, import_react2.useState)(null); const [success, setSuccess] = (0, import_react2.useState)(false); const styles = { container: { ...defaultStyles2.container, ...customStyles.container }, heading: { ...defaultStyles2.heading, ...customStyles.heading }, form: { ...defaultStyles2.form, ...customStyles.form }, section: { ...defaultStyles2.section, ...customStyles.section }, sectionTitle: { ...defaultStyles2.sectionTitle, ...customStyles.sectionTitle }, inputGroup: { ...defaultStyles2.inputGroup, ...customStyles.inputGroup }, label: { ...defaultStyles2.label, ...customStyles.label }, input: { ...defaultStyles2.input, ...customStyles.input }, select: { ...defaultStyles2.select, ...customStyles.select }, checkbox: { ...defaultStyles2.checkbox, ...customStyles.checkbox }, checkboxInput: { ...defaultStyles2.checkboxInput, ...customStyles.checkboxInput }, checkboxLabel: { ...defaultStyles2.checkboxLabel, ...customStyles.checkboxLabel }, buttonGroup: { ...defaultStyles2.buttonGroup, ...customStyles.buttonGroup }, button: { ...defaultStyles2.button, ...customStyles.button }, primaryButton: { ...defaultStyles2.primaryButton, ...customStyles.primaryButton }, secondaryButton: { ...defaultStyles2.secondaryButton, ...customStyles.secondaryButton }, dangerButton: { ...defaultStyles2.dangerButton, ...customStyles.dangerButton }, error: { ...defaultStyles2.error, ...customStyles.error }, success: { ...defaultStyles2.success, ...customStyles.success }, info: { ...defaultStyles2.info, ...customStyles.info } }; (0, import_react2.useEffect)(() => { if (!initialSubscriber && sessionToken) { fetchPreferences(); } }, []); const fetchPreferences = async () => { try { const response = await fetch(apiEndpoint, { headers: { "Authorization": `Bearer ${sessionToken}` } }); if (!response.ok) { throw new Error("Failed to load preferences"); } const data = await response.json(); setSubscriber(data.subscriber); } catch (err) { setError(err instanceof Error ? err.message : "Failed to load preferences"); if (onError) { onError(err instanceof Error ? err : new Error("Failed to load preferences")); } } finally { setLoadingData(false); } }; const handleSave = async (e) => { e.preventDefault(); setError(null); setSuccess(false); setLoading(true); try { const response = await fetch(apiEndpoint, { method: "POST", headers: { "Content-Type": "application/json", "Authorization": `Bearer ${sessionToken}` }, body: JSON.stringify({ name: subscriber.name, locale: subscriber.locale, emailPreferences: subscriber.emailPreferences }) }); const data = await response.json(); if (!response.ok) { throw new Error(data.error || "Failed to save preferences"); } setSubscriber(data.subscriber); setSuccess(true); if (onSuccess) { onSuccess(data.subscriber); } } catch (err) { const errorMessage = err instanceof Error ? err.message : "An error occurred"; setError(errorMessage); if (onError) { onError(new Error(errorMessage)); } } finally { setLoading(false); } }; const handleUnsubscribe = async () => { if (!window.confirm(labels.unsubscribeConfirm)) { return; } setLoading(true); setError(null); try { const response = await fetch("/api/newsletter/unsubscribe", { method: "POST", headers: { "Content-Type": "application/json", "Authorization": `Bearer ${sessionToken}` }, body: JSON.stringify({ email: subscriber.email }) }); if (!response.ok) { throw new Error("Failed to unsubscribe"); } setSubscriber({ ...subscriber, subscriptionStatus: "unsubscribed" }); if (onSuccess) { onSuccess({ ...subscriber, subscriptionStatus: "unsubscribed" }); } } catch (err) { setError("Failed to unsubscribe. Please try again."); if (onError) { onError(err instanceof Error ? err : new Error("Failed to unsubscribe")); } } finally { setLoading(false); } }; if (loadingData) { return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className, style: styles.container, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { style: styles.info, children: "Loading preferences..." }) }); } if (subscriber.subscriptionStatus === "unsubscribed") { return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className, style: styles.container, children: [ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h2", { style: styles.heading, children: "Unsubscribed" }), /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { style: styles.info, children: "You have been unsubscribed from all emails. To resubscribe, please sign up again." }) ] }); } return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className, style: styles.container, children: [ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h2", { style: styles.heading, children: labels.title }), /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("form", { onSubmit: handleSave, style: styles.form, children: [ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: styles.section, children: [ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h3", { style: styles.sectionTitle, children: labels.personalInfo }), /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: styles.inputGroup, children: [ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("label", { htmlFor: "name", style: styles.label, children: labels.name }), /* @__PURE__ */ (0, import_jsx_runtime2.jsx)( "input", { id: "name", type: "text", value: subscriber.name || "", onChange: (e) => setSubscriber({ ...subscriber, name: e.target.value }), disabled: loading, style: styles.input } ) ] }), locales.length > 1 && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: styles.inputGroup, children: [ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("label", { htmlFor: "locale", style: styles.label, children: labels.language }), /* @__PURE__ */ (0, import_jsx_runtime2.jsx)( "select", { id: "locale", value: subscriber.locale || locales[0], onChange: (e) => setSubscriber({ ...subscriber, locale: e.target.value }), disabled: loading, style: styles.select, children: locales.map((locale) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("option", { value: locale, children: locale.toUpperCase() }, locale)) } ) ] }) ] }), /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: styles.section, children: [ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h3", { style: styles.sectionTitle, children: labels.emailPreferences }), /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: styles.checkbox, children: [ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)( "input", { id: "pref-newsletter", type: "checkbox", checked: subscriber.emailPreferences?.newsletter ?? true, onChange: (e) => setSubscriber({ ...subscriber, emailPreferences: { ...subscriber.emailPreferences, newsletter: e.target.checked } }), disabled: loading, style: styles.checkboxInput } ), /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("label", { htmlFor: "pref-newsletter", style: styles.checkboxLabel, children: labels.newsletter }) ] }), /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: styles.checkbox, children: [ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)( "input", { id: "pref-announcements", type: "checkbox", checked: subscriber.emailPreferences?.announcements ?? true, onChange: (e) => setSubscriber({ ...subscriber, emailPreferences: { ...subscriber.emailPreferences, announcements: e.target.checked } }), disabled: loading, style: styles.checkboxInput } ), /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("label", { htmlFor: "pref-announcements", style: styles.checkboxLabel, children: labels.announcements }) ] }) ] }), /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: styles.buttonGroup, children: [ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)( "button", { type: "submit", disabled: loading, style: { ...styles.button, ...styles.primaryButton, ...loading && { opacity: 0.5, cursor: "not-allowed" } }, children: loading ? labels.saving : labels.saveButton } ), showUnsubscribe && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)( "button", { type: "button", onClick: handleUnsubscribe, disabled: loading, style: { ...styles.button, ...styles.dangerButton, ...loading && { opacity: 0.5, cursor: "not-allowed" } }, children: labels.unsubscribeButton } ) ] }), error && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { style: styles.error, children: error }), success && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { style: styles.success, children: labels.saved }) ] }) ] }); }; function createPreferencesForm(defaultProps) { return (props) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(PreferencesForm, { ...defaultProps, ...props }); } // src/components/MagicLinkVerify.tsx var import_react3 = require("react"); var import_jsx_runtime3 = require("react/jsx-runtime"); var defaultStyles3 = { container: { maxWidth: "400px", margin: "4rem auto", padding: "2rem", textAlign: "center" }, heading: { fontSize: "1.5rem", fontWeight: "600", marginBottom: "1rem", color: "#111827" }, message: { fontSize: "1rem", color: "#6b7280", marginBottom: "1.5rem" }, error: { fontSize: "1rem", color: "#ef4444", marginBottom: "1.5rem" }, button: { padding: "0.75rem 1.5rem", fontSize: "1rem", fontWeight: "500", color: "#ffffff", backgroundColor: "#3b82f6", border: "none", borderRadius: "0.375rem", cursor: "pointer", transition: "background-color 0.2s" } }; var MagicLinkVerify = ({ token: propToken, onSuccess, onError, apiEndpoint = "/api/newsletter/verify-magic-link", className, styles: customStyles = {}, labels = { verifying: "Verifying your magic link...", success: "Successfully verified! Redirecting...", error: "Failed to verify magic link", expired: "This magic link has expired. Please request a new one.", invalid: "This magic link is invalid. Please request a new one.", redirecting: "Redirecting to your preferences...", tryAgain: "Try Again" } }) => { const [status, setStatus] = (0, import_react3.useState)("verifying"); const [error, setError] = (0, import_react3.useState)(null); const [_sessionToken, setSessionToken] = (0, import_react3.useState)(null); const styles = { container: { ...defaultStyles3.container, ...customStyles.container }, heading: { ...defaultStyles3.heading, ...customStyles.heading }, message: { ...defaultStyles3.message, ...customStyles.message }, error: { ...defaultStyles3.error, ...customStyles.error }, button: { ...defaultStyles3.button, ...customStyles.button } }; (0, import_react3.useEffect)(() => { const token = propToken || new URLSearchParams(window.location.search).get("token"); if (token) { verifyToken(token); } else { setStatus("error"); setError(labels.invalid || "Invalid magic link"); } }, [propToken]); const verifyToken = async (token) => { try { const response = await fetch(apiEndpoint, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ token }) }); const data = await response.json(); if (!response.ok) { if (data.error?.includes("expired")) { throw new Error(labels.expired); } throw new Error(data.error || labels.error); } setStatus("success"); setSessionToken(data.sessionToken); if (typeof window !== "undefined" && data.sessionToken) { localStorage.setItem("newsletter_session", data.sessionToken); } if (onSuccess) { onSuccess(data.sessionToken, data.subscriber); } } catch (err) { setStatus("error"); const errorMessage = err instanceof Error ? err.message : labels.error || "Verification failed"; setError(errorMessage); if (onError) { onError(err instanceof Error ? err : new Error(errorMessage)); } } }; const handleTryAgain = () => { window.location.href = "/"; }; return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className, style: styles.container, children: [ status === "verifying" && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h2", { style: styles.heading, children: "Verifying" }), /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { style: styles.message, children: labels.verifying }) ] }), status === "success" && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h2", { style: styles.heading, children: "Success!" }), /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { style: styles.message, children: labels.success }) ] }), status === "error" && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h2", { style: styles.heading, children: "Verification Failed" }), /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { style: styles.error, children: error }), /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { onClick: handleTryAgain, style: styles.button, children: labels.tryAgain }) ] }) ] }); }; function createMagicLinkVerify(defaultProps) { return (props) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(MagicLinkVerify, { ...defaultProps, ...props }); } // src/hooks/useNewsletterAuth.ts var import_react4 = require("react"); function useNewsletterAuth(options = {}) { const { sessionTokenKey = "newsletter_session", apiEndpoint = "/api/newsletter/preferences" } = options; const [subscriber, setSubscriber] = (0, import_react4.useState)(null); const [loading, setLoading] = (0, import_react4.useState)(true); const [error, setError] = (0, import_react4.useState)(null); const getSessionToken = (0, import_react4.useCallback)(() => { if (typeof window === "undefined") return null; return localStorage.getItem(sessionTokenKey); }, [sessionTokenKey]); const setSessionToken = (0, import_react4.useCallback)((token) => { if (typeof window === "undefined") return; if (token) { localStorage.setItem(sessionTokenKey, token); } else { localStorage.removeItem(sessionTokenKey); } }, [sessionTokenKey]); const fetchSubscriber = (0, import_react4.useCallback)(async (token) => { try { const response = await fetch(apiEndpoint, { headers: { "Authorization": `Bearer ${token}` } }); if (!response.ok) { if (response.status === 401) { setSessionToken(null); throw new Error("Session expired"); } throw new Error("Failed to fetch subscriber"); } const data = await response.json(); setSubscriber(data.subscriber); setError(null); } catch (err) { setError(err instanceof Error ? err : new Error("An error occurred")); setSubscriber(null); throw err; } }, [apiEndpoint, setSessionToken]); (0, import_react4.useEffect)(() => { const token = getSessionToken(); if (token) { fetchSubscriber(token).catch(() => { }).finally(() => setLoading(false)); } else { setLoading(false); } }, []); const login = (0, import_react4.useCallback)(async (token) => { setLoading(true); setError(null); try { setSessionToken(token); await fetchSubscriber(token); } catch (err) { setSessionToken(null); throw err; } finally { setLoading(false); } }, [fetchSubscriber, setSessionToken]); const logout = (0, import_react4.useCallback)(() => { setSessionToken(null); setSubscriber(null); setError(null); }, [setSessionToken]); const refreshSubscriber = (0, import_react4.useCallback)(async () => { const token = getSessionToken(); if (!token) { throw new Error("Not authenticated"); } await fetchSubscriber(token); }, [fetchSubscriber, getSessionToken]); return { subscriber, loading, error, isAuthenticated: !!subscriber, login, logout, refreshSubscriber }; } // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { MagicLinkVerify, NewsletterForm, PreferencesForm, createMagicLinkVerify, createNewsletterForm, createPreferencesForm, useNewsletterAuth }); //# sourceMappingURL=client.cjs.map