@burdenoff/website-sdk 0.0.0-dev
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +24 -0
- package/README.md +357 -0
- package/dist/analytics/index.d.mts +27 -0
- package/dist/analytics/index.d.ts +27 -0
- package/dist/analytics/index.js +36 -0
- package/dist/analytics/index.js.map +1 -0
- package/dist/analytics/index.mjs +34 -0
- package/dist/analytics/index.mjs.map +1 -0
- package/dist/client/index.d.mts +29 -0
- package/dist/client/index.d.ts +29 -0
- package/dist/client/index.js +261 -0
- package/dist/client/index.js.map +1 -0
- package/dist/client/index.mjs +255 -0
- package/dist/client/index.mjs.map +1 -0
- package/dist/components/contact.d.mts +144 -0
- package/dist/components/contact.d.ts +144 -0
- package/dist/components/contact.js +806 -0
- package/dist/components/contact.js.map +1 -0
- package/dist/components/contact.mjs +780 -0
- package/dist/components/contact.mjs.map +1 -0
- package/dist/components/electron-download.d.mts +32 -0
- package/dist/components/electron-download.d.ts +32 -0
- package/dist/components/electron-download.js +131 -0
- package/dist/components/electron-download.js.map +1 -0
- package/dist/components/electron-download.mjs +108 -0
- package/dist/components/electron-download.mjs.map +1 -0
- package/dist/components/launch-countdown.d.mts +15 -0
- package/dist/components/launch-countdown.d.ts +15 -0
- package/dist/components/launch-countdown.js +89 -0
- package/dist/components/launch-countdown.js.map +1 -0
- package/dist/components/launch-countdown.mjs +87 -0
- package/dist/components/launch-countdown.mjs.map +1 -0
- package/dist/components/newsletter.d.mts +155 -0
- package/dist/components/newsletter.d.ts +155 -0
- package/dist/components/newsletter.js +880 -0
- package/dist/components/newsletter.js.map +1 -0
- package/dist/components/newsletter.mjs +854 -0
- package/dist/components/newsletter.mjs.map +1 -0
- package/dist/components/partners.d.mts +88 -0
- package/dist/components/partners.d.ts +88 -0
- package/dist/components/partners.js +1169 -0
- package/dist/components/partners.js.map +1 -0
- package/dist/components/partners.mjs +1143 -0
- package/dist/components/partners.mjs.map +1 -0
- package/dist/components/pricing.d.mts +194 -0
- package/dist/components/pricing.d.ts +194 -0
- package/dist/components/pricing.js +813 -0
- package/dist/components/pricing.js.map +1 -0
- package/dist/components/pricing.mjs +791 -0
- package/dist/components/pricing.mjs.map +1 -0
- package/dist/components/problems-solved.d.mts +37 -0
- package/dist/components/problems-solved.d.ts +37 -0
- package/dist/components/problems-solved.js +157 -0
- package/dist/components/problems-solved.js.map +1 -0
- package/dist/components/problems-solved.mjs +135 -0
- package/dist/components/problems-solved.mjs.map +1 -0
- package/dist/components/resource-links.d.mts +28 -0
- package/dist/components/resource-links.d.ts +28 -0
- package/dist/components/resource-links.js +229 -0
- package/dist/components/resource-links.js.map +1 -0
- package/dist/components/resource-links.mjs +227 -0
- package/dist/components/resource-links.mjs.map +1 -0
- package/dist/components/social-links.d.mts +37 -0
- package/dist/components/social-links.d.ts +37 -0
- package/dist/components/social-links.js +268 -0
- package/dist/components/social-links.js.map +1 -0
- package/dist/components/social-links.mjs +266 -0
- package/dist/components/social-links.mjs.map +1 -0
- package/dist/components/unsubscribe.d.mts +14 -0
- package/dist/components/unsubscribe.d.ts +14 -0
- package/dist/components/unsubscribe.js +538 -0
- package/dist/components/unsubscribe.js.map +1 -0
- package/dist/components/unsubscribe.mjs +517 -0
- package/dist/components/unsubscribe.mjs.map +1 -0
- package/dist/components/waitlist.d.mts +70 -0
- package/dist/components/waitlist.d.ts +70 -0
- package/dist/components/waitlist.js +864 -0
- package/dist/components/waitlist.js.map +1 -0
- package/dist/components/waitlist.mjs +841 -0
- package/dist/components/waitlist.mjs.map +1 -0
- package/dist/components/website-switcher.d.mts +27 -0
- package/dist/components/website-switcher.d.ts +27 -0
- package/dist/components/website-switcher.js +168 -0
- package/dist/components/website-switcher.js.map +1 -0
- package/dist/components/website-switcher.mjs +146 -0
- package/dist/components/website-switcher.mjs.map +1 -0
- package/dist/content/index.d.mts +247 -0
- package/dist/content/index.d.ts +247 -0
- package/dist/content/index.js +2458 -0
- package/dist/content/index.js.map +1 -0
- package/dist/content/index.mjs +2428 -0
- package/dist/content/index.mjs.map +1 -0
- package/dist/index.d.mts +72 -0
- package/dist/index.d.ts +72 -0
- package/dist/index.js +6273 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +6204 -0
- package/dist/index.mjs.map +1 -0
- package/dist/product/index.d.mts +94 -0
- package/dist/product/index.d.ts +94 -0
- package/dist/product/index.js +282 -0
- package/dist/product/index.js.map +1 -0
- package/dist/product/index.mjs +278 -0
- package/dist/product/index.mjs.map +1 -0
- package/dist/provider-D7Rij9UM.d.mts +86 -0
- package/dist/provider-D7Rij9UM.d.ts +86 -0
- package/dist/seo/index.d.mts +64 -0
- package/dist/seo/index.d.ts +64 -0
- package/dist/seo/index.js +61 -0
- package/dist/seo/index.js.map +1 -0
- package/dist/seo/index.mjs +59 -0
- package/dist/seo/index.mjs.map +1 -0
- package/dist/utils/index.d.mts +21 -0
- package/dist/utils/index.d.ts +21 -0
- package/dist/utils/index.js +12 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/index.mjs +10 -0
- package/dist/utils/index.mjs.map +1 -0
- package/package.json +168 -0
|
@@ -0,0 +1,880 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var React = require('react');
|
|
4
|
+
var zod$1 = require('@hookform/resolvers/zod');
|
|
5
|
+
var ReCAPTCHA = require('react-google-recaptcha');
|
|
6
|
+
var reactHookForm = require('react-hook-form');
|
|
7
|
+
var sonner = require('sonner');
|
|
8
|
+
var zod = require('zod');
|
|
9
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
10
|
+
var reactHelmetAsync = require('react-helmet-async');
|
|
11
|
+
var reactSlot = require('@radix-ui/react-slot');
|
|
12
|
+
var classVarianceAuthority = require('class-variance-authority');
|
|
13
|
+
var clsx = require('clsx');
|
|
14
|
+
var tailwindMerge = require('tailwind-merge');
|
|
15
|
+
var LabelPrimitive = require('@radix-ui/react-label');
|
|
16
|
+
|
|
17
|
+
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
18
|
+
|
|
19
|
+
function _interopNamespace(e) {
|
|
20
|
+
if (e && e.__esModule) return e;
|
|
21
|
+
var n = Object.create(null);
|
|
22
|
+
if (e) {
|
|
23
|
+
Object.keys(e).forEach(function (k) {
|
|
24
|
+
if (k !== 'default') {
|
|
25
|
+
var d = Object.getOwnPropertyDescriptor(e, k);
|
|
26
|
+
Object.defineProperty(n, k, d.get ? d : {
|
|
27
|
+
enumerable: true,
|
|
28
|
+
get: function () { return e[k]; }
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
n.default = e;
|
|
34
|
+
return Object.freeze(n);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
var React__namespace = /*#__PURE__*/_interopNamespace(React);
|
|
38
|
+
var ReCAPTCHA__default = /*#__PURE__*/_interopDefault(ReCAPTCHA);
|
|
39
|
+
var LabelPrimitive__namespace = /*#__PURE__*/_interopNamespace(LabelPrimitive);
|
|
40
|
+
|
|
41
|
+
var __defProp = Object.defineProperty;
|
|
42
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
43
|
+
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
44
|
+
|
|
45
|
+
// src/client/security.ts
|
|
46
|
+
var LOOPBACK_HOSTS = /* @__PURE__ */ new Set(["localhost", "127.0.0.1", "::1"]);
|
|
47
|
+
var RESERVED_HEADERS = /* @__PURE__ */ new Set([
|
|
48
|
+
"authorization",
|
|
49
|
+
"cookie",
|
|
50
|
+
"x-actor-id",
|
|
51
|
+
"x-actor-type",
|
|
52
|
+
"x-consent-granted-by",
|
|
53
|
+
"x-consent-scope",
|
|
54
|
+
"x-consent-granted-at",
|
|
55
|
+
"x-consent-expires-at"
|
|
56
|
+
]);
|
|
57
|
+
function validateGatewayUrl(value) {
|
|
58
|
+
let url;
|
|
59
|
+
try {
|
|
60
|
+
url = new URL(value);
|
|
61
|
+
} catch {
|
|
62
|
+
throw new Error("Gateway URL must be a valid absolute URL");
|
|
63
|
+
}
|
|
64
|
+
if (url.protocol === "https:") {
|
|
65
|
+
return url.toString();
|
|
66
|
+
}
|
|
67
|
+
if (url.protocol === "http:" && LOOPBACK_HOSTS.has(url.hostname.toLowerCase())) {
|
|
68
|
+
return url.toString();
|
|
69
|
+
}
|
|
70
|
+
throw new Error("Gateway URL must use HTTPS unless it targets localhost");
|
|
71
|
+
}
|
|
72
|
+
function sanitizeHeaders(headers) {
|
|
73
|
+
if (!headers) return void 0;
|
|
74
|
+
return Object.fromEntries(
|
|
75
|
+
Object.entries(headers).filter(
|
|
76
|
+
([key]) => !RESERVED_HEADERS.has(key.toLowerCase())
|
|
77
|
+
)
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// src/client/graphql-client.ts
|
|
82
|
+
var _WebSDKClient = class _WebSDKClient {
|
|
83
|
+
constructor(config) {
|
|
84
|
+
__publicField(this, "config");
|
|
85
|
+
__publicField(this, "queryCache", /* @__PURE__ */ new Map());
|
|
86
|
+
__publicField(this, "inflight", /* @__PURE__ */ new Map());
|
|
87
|
+
this.config = {
|
|
88
|
+
...config,
|
|
89
|
+
gatewayUrl: validateGatewayUrl(config.gatewayUrl),
|
|
90
|
+
headers: sanitizeHeaders(config.headers)
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
getConfig() {
|
|
94
|
+
return this.config;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Execute a GraphQL query (with deduplication and caching)
|
|
98
|
+
*/
|
|
99
|
+
async query(query, variables) {
|
|
100
|
+
const cacheKey = JSON.stringify({ query, variables });
|
|
101
|
+
const cached = this.queryCache.get(cacheKey);
|
|
102
|
+
if (cached) {
|
|
103
|
+
if (Date.now() - cached.ts < _WebSDKClient.CACHE_TTL) {
|
|
104
|
+
return cached.data;
|
|
105
|
+
}
|
|
106
|
+
this.queryCache.delete(cacheKey);
|
|
107
|
+
}
|
|
108
|
+
const existing = this.inflight.get(cacheKey);
|
|
109
|
+
if (existing) {
|
|
110
|
+
return existing;
|
|
111
|
+
}
|
|
112
|
+
const promise = this.request(query, variables).then((result) => {
|
|
113
|
+
if (result.data && !result.errors?.length) {
|
|
114
|
+
if (this.queryCache.size >= _WebSDKClient.MAX_CACHE_ENTRIES) {
|
|
115
|
+
const oldestEntry = this.queryCache.keys().next().value;
|
|
116
|
+
if (oldestEntry) {
|
|
117
|
+
this.queryCache.delete(oldestEntry);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
this.queryCache.set(cacheKey, { data: result, ts: Date.now() });
|
|
121
|
+
}
|
|
122
|
+
return result;
|
|
123
|
+
}).finally(() => {
|
|
124
|
+
this.inflight.delete(cacheKey);
|
|
125
|
+
});
|
|
126
|
+
this.inflight.set(cacheKey, promise);
|
|
127
|
+
return promise;
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Execute a GraphQL mutation
|
|
131
|
+
*/
|
|
132
|
+
async mutate(mutation, variables) {
|
|
133
|
+
return this.request(mutation, variables);
|
|
134
|
+
}
|
|
135
|
+
async request(query, variables) {
|
|
136
|
+
try {
|
|
137
|
+
const response = await fetch(this.config.gatewayUrl, {
|
|
138
|
+
method: "POST",
|
|
139
|
+
headers: {
|
|
140
|
+
"Content-Type": "application/json",
|
|
141
|
+
"x-product-id": this.config.productId,
|
|
142
|
+
...this.config.headers
|
|
143
|
+
},
|
|
144
|
+
body: JSON.stringify({ query, variables })
|
|
145
|
+
});
|
|
146
|
+
if (!response.ok) {
|
|
147
|
+
console.error(
|
|
148
|
+
"[WebSDK] GraphQL request failed",
|
|
149
|
+
response.status,
|
|
150
|
+
response.statusText
|
|
151
|
+
);
|
|
152
|
+
return {
|
|
153
|
+
errors: [
|
|
154
|
+
{
|
|
155
|
+
message: `HTTP ${response.status}: ${response.statusText}`,
|
|
156
|
+
extensions: { code: `HTTP_${response.status}` }
|
|
157
|
+
}
|
|
158
|
+
]
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
return await response.json();
|
|
162
|
+
} catch (error) {
|
|
163
|
+
console.error(
|
|
164
|
+
"[WebSDK] Network error",
|
|
165
|
+
error instanceof Error ? error.message : "unknown error"
|
|
166
|
+
);
|
|
167
|
+
return {
|
|
168
|
+
errors: [
|
|
169
|
+
{
|
|
170
|
+
message: error instanceof Error ? error.message : "Network error",
|
|
171
|
+
extensions: { code: "NETWORK_ERROR" }
|
|
172
|
+
}
|
|
173
|
+
]
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
};
|
|
178
|
+
__publicField(_WebSDKClient, "CACHE_TTL", 5 * 60 * 1e3);
|
|
179
|
+
// 5 minutes
|
|
180
|
+
__publicField(_WebSDKClient, "MAX_CACHE_ENTRIES", 100);
|
|
181
|
+
var WebSDKContext = React.createContext(null);
|
|
182
|
+
function useWebSDK() {
|
|
183
|
+
const client = React.useContext(WebSDKContext);
|
|
184
|
+
if (!client) {
|
|
185
|
+
throw new Error("useWebSDK must be used within a WebSDKProvider");
|
|
186
|
+
}
|
|
187
|
+
return client;
|
|
188
|
+
}
|
|
189
|
+
function useWebSDKConfig() {
|
|
190
|
+
const client = useWebSDK();
|
|
191
|
+
return client.getConfig();
|
|
192
|
+
}
|
|
193
|
+
var ProductContext = React.createContext({
|
|
194
|
+
product: null,
|
|
195
|
+
loading: true,
|
|
196
|
+
error: null,
|
|
197
|
+
hasProvider: false,
|
|
198
|
+
refetch: () => {
|
|
199
|
+
}
|
|
200
|
+
});
|
|
201
|
+
function useProduct() {
|
|
202
|
+
return React.useContext(ProductContext);
|
|
203
|
+
}
|
|
204
|
+
function useProductConfig() {
|
|
205
|
+
const { product } = useProduct();
|
|
206
|
+
const config = useWebSDKConfig();
|
|
207
|
+
return {
|
|
208
|
+
// Prefer product-level config, fall back to SDK config
|
|
209
|
+
recaptchaSiteKey: product?.recaptchaSiteKey ?? config.recaptchaSiteKey ?? null,
|
|
210
|
+
rybbitSiteId: product?.rybbitSiteId ?? config.rybbitSiteId ?? null,
|
|
211
|
+
contactEmail: product?.contactEmail ?? null,
|
|
212
|
+
supportEmail: product?.supportEmail ?? null,
|
|
213
|
+
notificationEmail: product?.notificationEmail ?? product?.contactEmail ?? null,
|
|
214
|
+
productName: product?.name ?? null,
|
|
215
|
+
logoUrl: product?.logoUrl ?? null
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
function PageHead({
|
|
219
|
+
title,
|
|
220
|
+
description,
|
|
221
|
+
productName,
|
|
222
|
+
keywords,
|
|
223
|
+
image,
|
|
224
|
+
url,
|
|
225
|
+
favicon,
|
|
226
|
+
robots = "index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1",
|
|
227
|
+
locale = "en_US",
|
|
228
|
+
twitterHandle,
|
|
229
|
+
themeColor = "#000000",
|
|
230
|
+
structuredData,
|
|
231
|
+
additionalMeta
|
|
232
|
+
}) {
|
|
233
|
+
const fullTitle = productName && !title.includes(productName) ? `${title} | ${productName}` : title;
|
|
234
|
+
const fullImageUrl = image && !image.startsWith("http") && url ? `${url.replace(/\/$/, "")}${image}` : image;
|
|
235
|
+
const structuredDataItems = structuredData ? Array.isArray(structuredData) ? structuredData : [structuredData] : [];
|
|
236
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(reactHelmetAsync.Helmet, { children: [
|
|
237
|
+
/* @__PURE__ */ jsxRuntime.jsx("title", { children: fullTitle }),
|
|
238
|
+
/* @__PURE__ */ jsxRuntime.jsx("meta", { name: "title", content: fullTitle }),
|
|
239
|
+
description && /* @__PURE__ */ jsxRuntime.jsx("meta", { name: "description", content: description }),
|
|
240
|
+
keywords && /* @__PURE__ */ jsxRuntime.jsx("meta", { name: "keywords", content: keywords }),
|
|
241
|
+
/* @__PURE__ */ jsxRuntime.jsx("meta", { name: "author", content: "Burdenoff Consultancy Services Pvt Ltd" }),
|
|
242
|
+
/* @__PURE__ */ jsxRuntime.jsx("meta", { name: "robots", content: robots }),
|
|
243
|
+
url && /* @__PURE__ */ jsxRuntime.jsx("link", { rel: "canonical", href: url }),
|
|
244
|
+
favicon && /* @__PURE__ */ jsxRuntime.jsx("link", { rel: "icon", href: favicon }),
|
|
245
|
+
/* @__PURE__ */ jsxRuntime.jsx("meta", { property: "og:type", content: "website" }),
|
|
246
|
+
/* @__PURE__ */ jsxRuntime.jsx("meta", { property: "og:title", content: fullTitle }),
|
|
247
|
+
description && /* @__PURE__ */ jsxRuntime.jsx("meta", { property: "og:description", content: description }),
|
|
248
|
+
fullImageUrl && /* @__PURE__ */ jsxRuntime.jsx("meta", { property: "og:image", content: fullImageUrl }),
|
|
249
|
+
url && /* @__PURE__ */ jsxRuntime.jsx("meta", { property: "og:url", content: url }),
|
|
250
|
+
/* @__PURE__ */ jsxRuntime.jsx("meta", { property: "og:locale", content: locale }),
|
|
251
|
+
productName && /* @__PURE__ */ jsxRuntime.jsx("meta", { property: "og:site_name", content: productName }),
|
|
252
|
+
/* @__PURE__ */ jsxRuntime.jsx("meta", { name: "twitter:card", content: "summary_large_image" }),
|
|
253
|
+
/* @__PURE__ */ jsxRuntime.jsx("meta", { name: "twitter:title", content: fullTitle }),
|
|
254
|
+
description && /* @__PURE__ */ jsxRuntime.jsx("meta", { name: "twitter:description", content: description }),
|
|
255
|
+
fullImageUrl && /* @__PURE__ */ jsxRuntime.jsx("meta", { name: "twitter:image", content: fullImageUrl }),
|
|
256
|
+
twitterHandle && /* @__PURE__ */ jsxRuntime.jsx("meta", { name: "twitter:creator", content: twitterHandle }),
|
|
257
|
+
/* @__PURE__ */ jsxRuntime.jsx("meta", { name: "theme-color", content: themeColor }),
|
|
258
|
+
structuredDataItems.map((data, index) => /* @__PURE__ */ jsxRuntime.jsx("script", { type: "application/ld+json", children: JSON.stringify(data) }, `ld-${index}`)),
|
|
259
|
+
additionalMeta?.map((meta, index) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
260
|
+
"meta",
|
|
261
|
+
{
|
|
262
|
+
...meta.name ? { name: meta.name } : {},
|
|
263
|
+
...meta.property ? { property: meta.property } : {},
|
|
264
|
+
content: meta.content
|
|
265
|
+
},
|
|
266
|
+
`meta-${index}`
|
|
267
|
+
))
|
|
268
|
+
] });
|
|
269
|
+
}
|
|
270
|
+
function cn(...inputs) {
|
|
271
|
+
return tailwindMerge.twMerge(clsx.clsx(inputs));
|
|
272
|
+
}
|
|
273
|
+
var buttonVariants = classVarianceAuthority.cva(
|
|
274
|
+
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
|
|
275
|
+
{
|
|
276
|
+
variants: {
|
|
277
|
+
variant: {
|
|
278
|
+
default: "bg-primary text-primary-foreground shadow hover:bg-primary/90",
|
|
279
|
+
destructive: "bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90",
|
|
280
|
+
outline: "border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground",
|
|
281
|
+
secondary: "bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80",
|
|
282
|
+
ghost: "hover:bg-accent hover:text-accent-foreground",
|
|
283
|
+
link: "text-primary underline-offset-4 hover:underline"
|
|
284
|
+
},
|
|
285
|
+
size: {
|
|
286
|
+
default: "h-9 px-4 py-2",
|
|
287
|
+
sm: "h-8 rounded-md px-3 text-xs",
|
|
288
|
+
lg: "h-10 rounded-md px-8",
|
|
289
|
+
icon: "h-9 w-9"
|
|
290
|
+
}
|
|
291
|
+
},
|
|
292
|
+
defaultVariants: {
|
|
293
|
+
variant: "default",
|
|
294
|
+
size: "default"
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
);
|
|
298
|
+
var Button = React__namespace.forwardRef(
|
|
299
|
+
({ className, variant, size, asChild = false, ...props }, ref) => {
|
|
300
|
+
const Comp = asChild ? reactSlot.Slot : "button";
|
|
301
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
302
|
+
Comp,
|
|
303
|
+
{
|
|
304
|
+
className: cn(buttonVariants({ variant, size, className })),
|
|
305
|
+
ref,
|
|
306
|
+
...props
|
|
307
|
+
}
|
|
308
|
+
);
|
|
309
|
+
}
|
|
310
|
+
);
|
|
311
|
+
Button.displayName = "Button";
|
|
312
|
+
var Input = React__namespace.forwardRef(
|
|
313
|
+
({ className, type, ...props }, ref) => {
|
|
314
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
315
|
+
"input",
|
|
316
|
+
{
|
|
317
|
+
type,
|
|
318
|
+
className: cn(
|
|
319
|
+
"flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-base shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
|
|
320
|
+
className
|
|
321
|
+
),
|
|
322
|
+
ref,
|
|
323
|
+
...props
|
|
324
|
+
}
|
|
325
|
+
);
|
|
326
|
+
}
|
|
327
|
+
);
|
|
328
|
+
Input.displayName = "Input";
|
|
329
|
+
var labelVariants = classVarianceAuthority.cva(
|
|
330
|
+
"text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
|
331
|
+
);
|
|
332
|
+
var Label = React__namespace.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
333
|
+
LabelPrimitive__namespace.Root,
|
|
334
|
+
{
|
|
335
|
+
ref,
|
|
336
|
+
className: cn(labelVariants(), className),
|
|
337
|
+
...props
|
|
338
|
+
}
|
|
339
|
+
));
|
|
340
|
+
Label.displayName = LabelPrimitive__namespace.Root.displayName;
|
|
341
|
+
var newsletterSchema = zod.z.object({
|
|
342
|
+
email: zod.z.string().email("Please enter a valid email address")
|
|
343
|
+
});
|
|
344
|
+
var SUBSCRIBE_MUTATION = (
|
|
345
|
+
/* GraphQL */
|
|
346
|
+
`
|
|
347
|
+
mutation Subscribe($input: SubscribeInput!) {
|
|
348
|
+
subscribe(input: $input) {
|
|
349
|
+
id
|
|
350
|
+
email
|
|
351
|
+
alreadySubscribed
|
|
352
|
+
status
|
|
353
|
+
subscribedAt
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
`
|
|
357
|
+
);
|
|
358
|
+
async function subscribeNewsletter(client, email, productId, recaptchaToken) {
|
|
359
|
+
try {
|
|
360
|
+
const result = await client.mutate(SUBSCRIBE_MUTATION, {
|
|
361
|
+
input: {
|
|
362
|
+
email: email.trim().toLowerCase(),
|
|
363
|
+
platformProductId: productId,
|
|
364
|
+
metadata: {
|
|
365
|
+
source: "website",
|
|
366
|
+
referrer: typeof window !== "undefined" ? window.location.href : "",
|
|
367
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
368
|
+
...recaptchaToken ? { recaptchaToken } : {}
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
});
|
|
372
|
+
if (result.errors?.length) {
|
|
373
|
+
const errorMsg = result.errors[0]?.message ?? "Subscription failed";
|
|
374
|
+
const code = result.errors[0]?.extensions?.code;
|
|
375
|
+
if (code === "DUPLICATE_SUBSCRIBER" || errorMsg.toLowerCase().includes("already subscribed")) {
|
|
376
|
+
return {
|
|
377
|
+
success: true,
|
|
378
|
+
message: "You are already part of this newsletter."
|
|
379
|
+
};
|
|
380
|
+
}
|
|
381
|
+
return { success: false, message: errorMsg };
|
|
382
|
+
}
|
|
383
|
+
if (result.data?.subscribe?.alreadySubscribed) {
|
|
384
|
+
return {
|
|
385
|
+
success: true,
|
|
386
|
+
message: "You are already part of this newsletter."
|
|
387
|
+
};
|
|
388
|
+
}
|
|
389
|
+
return { success: true };
|
|
390
|
+
} catch (error) {
|
|
391
|
+
console.error("Newsletter subscription error:", error);
|
|
392
|
+
return { success: false, message: "Network error. Please try again." };
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
function useRateLimit(maxAttempts, windowMinutes) {
|
|
396
|
+
const rateLimitRef = React.useRef(null);
|
|
397
|
+
const [isLimited, setIsLimited] = React.useState(false);
|
|
398
|
+
const [resetTime, setResetTime] = React.useState(null);
|
|
399
|
+
React.useEffect(() => {
|
|
400
|
+
const state = rateLimitRef.current;
|
|
401
|
+
if (state && Date.now() > state.resetTime) {
|
|
402
|
+
rateLimitRef.current = null;
|
|
403
|
+
setIsLimited(false);
|
|
404
|
+
setResetTime(null);
|
|
405
|
+
} else if (state && state.attempts >= maxAttempts) {
|
|
406
|
+
setIsLimited(true);
|
|
407
|
+
setResetTime(new Date(state.resetTime));
|
|
408
|
+
}
|
|
409
|
+
}, [maxAttempts]);
|
|
410
|
+
function recordAttempt() {
|
|
411
|
+
const now = Date.now();
|
|
412
|
+
const state = rateLimitRef.current;
|
|
413
|
+
if (state && now > state.resetTime) {
|
|
414
|
+
rateLimitRef.current = null;
|
|
415
|
+
}
|
|
416
|
+
const current = rateLimitRef.current;
|
|
417
|
+
if (!current) {
|
|
418
|
+
rateLimitRef.current = {
|
|
419
|
+
attempts: 1,
|
|
420
|
+
resetTime: now + windowMinutes * 60 * 1e3
|
|
421
|
+
};
|
|
422
|
+
return false;
|
|
423
|
+
}
|
|
424
|
+
current.attempts += 1;
|
|
425
|
+
if (current.attempts >= maxAttempts) {
|
|
426
|
+
setIsLimited(true);
|
|
427
|
+
setResetTime(new Date(current.resetTime));
|
|
428
|
+
return true;
|
|
429
|
+
}
|
|
430
|
+
return false;
|
|
431
|
+
}
|
|
432
|
+
function checkLimit() {
|
|
433
|
+
const state = rateLimitRef.current;
|
|
434
|
+
if (!state) return false;
|
|
435
|
+
if (Date.now() > state.resetTime) {
|
|
436
|
+
rateLimitRef.current = null;
|
|
437
|
+
setIsLimited(false);
|
|
438
|
+
setResetTime(null);
|
|
439
|
+
return false;
|
|
440
|
+
}
|
|
441
|
+
return state.attempts >= maxAttempts;
|
|
442
|
+
}
|
|
443
|
+
return { isLimited, resetTime, recordAttempt, checkLimit };
|
|
444
|
+
}
|
|
445
|
+
function NewsletterPage({
|
|
446
|
+
productName,
|
|
447
|
+
layout = "centered",
|
|
448
|
+
rateLimitAttempts = 3,
|
|
449
|
+
rateLimitWindowMinutes = 15,
|
|
450
|
+
heroHeading = "Stay Updated",
|
|
451
|
+
heroSubtitle = "Subscribe to our newsletter",
|
|
452
|
+
heroDescription,
|
|
453
|
+
features,
|
|
454
|
+
successMessage = "Successfully subscribed to newsletter!",
|
|
455
|
+
errorMessage = "Failed to subscribe. Please try again.",
|
|
456
|
+
rateLimitMessage,
|
|
457
|
+
className,
|
|
458
|
+
seoTitle,
|
|
459
|
+
seoDescription,
|
|
460
|
+
seoKeywords,
|
|
461
|
+
seoImage,
|
|
462
|
+
seoUrl,
|
|
463
|
+
recaptchaSiteKey
|
|
464
|
+
}) {
|
|
465
|
+
const client = useWebSDK();
|
|
466
|
+
const config = useWebSDKConfig();
|
|
467
|
+
const { product } = useProduct();
|
|
468
|
+
const productConfig = useProductConfig();
|
|
469
|
+
const effectiveProductId = product?.id || config.productId;
|
|
470
|
+
const displayName = productName ?? product?.name ?? config.productId;
|
|
471
|
+
const effectiveRecaptchaSiteKey = recaptchaSiteKey ?? productConfig.recaptchaSiteKey ?? config.recaptchaSiteKey ?? "";
|
|
472
|
+
const recaptchaRef = React.useRef(null);
|
|
473
|
+
const [isSubmitting, setIsSubmitting] = React.useState(false);
|
|
474
|
+
const { isLimited, resetTime, recordAttempt, checkLimit } = useRateLimit(
|
|
475
|
+
rateLimitAttempts,
|
|
476
|
+
rateLimitWindowMinutes
|
|
477
|
+
);
|
|
478
|
+
const {
|
|
479
|
+
register,
|
|
480
|
+
handleSubmit,
|
|
481
|
+
formState: { errors },
|
|
482
|
+
reset
|
|
483
|
+
} = reactHookForm.useForm({
|
|
484
|
+
resolver: zod$1.zodResolver(newsletterSchema)
|
|
485
|
+
});
|
|
486
|
+
const onSubmit = async (data) => {
|
|
487
|
+
const recaptchaToken = recaptchaRef.current?.getValue() ?? "";
|
|
488
|
+
if (effectiveRecaptchaSiteKey && !recaptchaToken) {
|
|
489
|
+
sonner.toast.error("Please complete the reCAPTCHA verification");
|
|
490
|
+
return;
|
|
491
|
+
}
|
|
492
|
+
if (checkLimit()) {
|
|
493
|
+
sonner.toast.error(
|
|
494
|
+
rateLimitMessage ?? `Too many attempts. Please try again after ${resetTime?.toLocaleTimeString()}`
|
|
495
|
+
);
|
|
496
|
+
return;
|
|
497
|
+
}
|
|
498
|
+
setIsSubmitting(true);
|
|
499
|
+
try {
|
|
500
|
+
const result = await subscribeNewsletter(
|
|
501
|
+
client,
|
|
502
|
+
data.email,
|
|
503
|
+
effectiveProductId,
|
|
504
|
+
recaptchaToken || void 0
|
|
505
|
+
);
|
|
506
|
+
if (result.success) {
|
|
507
|
+
if (result.message) {
|
|
508
|
+
sonner.toast.info(result.message);
|
|
509
|
+
} else {
|
|
510
|
+
sonner.toast.success(successMessage);
|
|
511
|
+
}
|
|
512
|
+
reset();
|
|
513
|
+
} else {
|
|
514
|
+
sonner.toast.error(result.message ?? errorMessage);
|
|
515
|
+
}
|
|
516
|
+
recordAttempt();
|
|
517
|
+
recaptchaRef.current?.reset();
|
|
518
|
+
} catch (_error) {
|
|
519
|
+
sonner.toast.error(errorMessage);
|
|
520
|
+
recaptchaRef.current?.reset();
|
|
521
|
+
} finally {
|
|
522
|
+
setIsSubmitting(false);
|
|
523
|
+
}
|
|
524
|
+
};
|
|
525
|
+
const defaultFeatures = [
|
|
526
|
+
{
|
|
527
|
+
icon: /* @__PURE__ */ jsxRuntime.jsx(
|
|
528
|
+
"svg",
|
|
529
|
+
{
|
|
530
|
+
className: "h-6 w-6",
|
|
531
|
+
fill: "none",
|
|
532
|
+
stroke: "currentColor",
|
|
533
|
+
viewBox: "0 0 24 24",
|
|
534
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
535
|
+
"path",
|
|
536
|
+
{
|
|
537
|
+
strokeLinecap: "round",
|
|
538
|
+
strokeLinejoin: "round",
|
|
539
|
+
strokeWidth: 2,
|
|
540
|
+
d: "M13 10V3L4 14h7v7l9-11h-7z"
|
|
541
|
+
}
|
|
542
|
+
)
|
|
543
|
+
}
|
|
544
|
+
),
|
|
545
|
+
title: "Latest Updates",
|
|
546
|
+
description: "Get the latest news and updates delivered to your inbox"
|
|
547
|
+
},
|
|
548
|
+
{
|
|
549
|
+
icon: /* @__PURE__ */ jsxRuntime.jsx(
|
|
550
|
+
"svg",
|
|
551
|
+
{
|
|
552
|
+
className: "h-6 w-6",
|
|
553
|
+
fill: "none",
|
|
554
|
+
stroke: "currentColor",
|
|
555
|
+
viewBox: "0 0 24 24",
|
|
556
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
557
|
+
"path",
|
|
558
|
+
{
|
|
559
|
+
strokeLinecap: "round",
|
|
560
|
+
strokeLinejoin: "round",
|
|
561
|
+
strokeWidth: 2,
|
|
562
|
+
d: "M12 6V4m0 2a2 2 0 100 4m0-4a2 2 0 110 4m-6 8a2 2 0 100-4m0 4a2 2 0 110-4m0 4v2m0-6V4m6 6v10m6-2a2 2 0 100-4m0 4a2 2 0 110-4m0 4v2m0-6V4"
|
|
563
|
+
}
|
|
564
|
+
)
|
|
565
|
+
}
|
|
566
|
+
),
|
|
567
|
+
title: "Exclusive Content",
|
|
568
|
+
description: "Access exclusive content and insights not available elsewhere"
|
|
569
|
+
},
|
|
570
|
+
{
|
|
571
|
+
icon: /* @__PURE__ */ jsxRuntime.jsx(
|
|
572
|
+
"svg",
|
|
573
|
+
{
|
|
574
|
+
className: "h-6 w-6",
|
|
575
|
+
fill: "none",
|
|
576
|
+
stroke: "currentColor",
|
|
577
|
+
viewBox: "0 0 24 24",
|
|
578
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
579
|
+
"path",
|
|
580
|
+
{
|
|
581
|
+
strokeLinecap: "round",
|
|
582
|
+
strokeLinejoin: "round",
|
|
583
|
+
strokeWidth: 2,
|
|
584
|
+
d: "M15 17h5l-1.405-1.405A2.032 2.032 0 0118 14.158V11a6.002 6.002 0 00-4-5.659V5a2 2 0 10-4 0v.341C7.67 6.165 6 8.388 6 11v3.159c0 .538-.214 1.055-.595 1.436L4 17h5m6 0v1a3 3 0 11-6 0v-1m6 0H9"
|
|
585
|
+
}
|
|
586
|
+
)
|
|
587
|
+
}
|
|
588
|
+
),
|
|
589
|
+
title: "Early Access",
|
|
590
|
+
description: "Be the first to know about new features and product launches"
|
|
591
|
+
}
|
|
592
|
+
];
|
|
593
|
+
const displayFeatures = features ?? defaultFeatures;
|
|
594
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
595
|
+
seoTitle && /* @__PURE__ */ jsxRuntime.jsx(
|
|
596
|
+
PageHead,
|
|
597
|
+
{
|
|
598
|
+
title: seoTitle,
|
|
599
|
+
description: seoDescription ?? heroDescription ?? heroSubtitle,
|
|
600
|
+
productName: displayName,
|
|
601
|
+
keywords: seoKeywords,
|
|
602
|
+
image: seoImage,
|
|
603
|
+
url: seoUrl
|
|
604
|
+
}
|
|
605
|
+
),
|
|
606
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className, children: layout === "centered" ? (
|
|
607
|
+
// Centered Layout
|
|
608
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "min-h-screen flex items-center justify-center px-4 py-12", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "max-w-md w-full space-y-8", children: [
|
|
609
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center", children: [
|
|
610
|
+
/* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-4xl font-bold tracking-tight", children: heroHeading }),
|
|
611
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-2 text-lg text-muted-foreground", children: heroSubtitle }),
|
|
612
|
+
heroDescription && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-4 text-sm text-muted-foreground", children: heroDescription })
|
|
613
|
+
] }),
|
|
614
|
+
/* @__PURE__ */ jsxRuntime.jsxs("form", { onSubmit: handleSubmit(onSubmit), className: "space-y-6", children: [
|
|
615
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
|
|
616
|
+
/* @__PURE__ */ jsxRuntime.jsx(Label, { htmlFor: "email", children: "Email address" }),
|
|
617
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
618
|
+
Input,
|
|
619
|
+
{
|
|
620
|
+
id: "email",
|
|
621
|
+
type: "email",
|
|
622
|
+
placeholder: "you@example.com",
|
|
623
|
+
disabled: isSubmitting || isLimited,
|
|
624
|
+
...register("email")
|
|
625
|
+
}
|
|
626
|
+
),
|
|
627
|
+
errors.email && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-destructive", children: errors.email.message })
|
|
628
|
+
] }),
|
|
629
|
+
effectiveRecaptchaSiteKey && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
630
|
+
ReCAPTCHA__default.default,
|
|
631
|
+
{
|
|
632
|
+
ref: recaptchaRef,
|
|
633
|
+
sitekey: effectiveRecaptchaSiteKey
|
|
634
|
+
}
|
|
635
|
+
) }),
|
|
636
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
637
|
+
Button,
|
|
638
|
+
{
|
|
639
|
+
type: "submit",
|
|
640
|
+
className: "w-full",
|
|
641
|
+
disabled: isSubmitting || isLimited,
|
|
642
|
+
children: isSubmitting ? "Subscribing..." : "Subscribe"
|
|
643
|
+
}
|
|
644
|
+
),
|
|
645
|
+
isLimited && resetTime && /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-sm text-muted-foreground text-center", children: [
|
|
646
|
+
"Rate limit reached. Try again after",
|
|
647
|
+
" ",
|
|
648
|
+
resetTime.toLocaleTimeString()
|
|
649
|
+
] })
|
|
650
|
+
] }),
|
|
651
|
+
/* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-xs text-muted-foreground text-center", children: [
|
|
652
|
+
"By subscribing, you agree to receive emails from ",
|
|
653
|
+
displayName,
|
|
654
|
+
". You can unsubscribe at any time."
|
|
655
|
+
] })
|
|
656
|
+
] }) })
|
|
657
|
+
) : (
|
|
658
|
+
// Split Layout
|
|
659
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-h-screen grid lg:grid-cols-2", children: [
|
|
660
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-center px-4 py-12 sm:px-6 lg:px-8", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "max-w-md w-full space-y-8", children: [
|
|
661
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
662
|
+
/* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-4xl font-bold tracking-tight", children: heroHeading }),
|
|
663
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-2 text-lg text-muted-foreground", children: heroSubtitle }),
|
|
664
|
+
heroDescription && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-4 text-sm text-muted-foreground", children: heroDescription })
|
|
665
|
+
] }),
|
|
666
|
+
/* @__PURE__ */ jsxRuntime.jsxs("form", { onSubmit: handleSubmit(onSubmit), className: "space-y-6", children: [
|
|
667
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
|
|
668
|
+
/* @__PURE__ */ jsxRuntime.jsx(Label, { htmlFor: "email", children: "Email address" }),
|
|
669
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
670
|
+
Input,
|
|
671
|
+
{
|
|
672
|
+
id: "email",
|
|
673
|
+
type: "email",
|
|
674
|
+
placeholder: "you@example.com",
|
|
675
|
+
disabled: isSubmitting || isLimited,
|
|
676
|
+
...register("email")
|
|
677
|
+
}
|
|
678
|
+
),
|
|
679
|
+
errors.email && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-destructive", children: errors.email.message })
|
|
680
|
+
] }),
|
|
681
|
+
effectiveRecaptchaSiteKey && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex justify-center sm:justify-start", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
682
|
+
ReCAPTCHA__default.default,
|
|
683
|
+
{
|
|
684
|
+
ref: recaptchaRef,
|
|
685
|
+
sitekey: effectiveRecaptchaSiteKey
|
|
686
|
+
}
|
|
687
|
+
) }),
|
|
688
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
689
|
+
Button,
|
|
690
|
+
{
|
|
691
|
+
type: "submit",
|
|
692
|
+
className: "w-full",
|
|
693
|
+
disabled: isSubmitting || isLimited,
|
|
694
|
+
children: isSubmitting ? "Subscribing..." : "Subscribe"
|
|
695
|
+
}
|
|
696
|
+
),
|
|
697
|
+
isLimited && resetTime && /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-sm text-muted-foreground text-center", children: [
|
|
698
|
+
"Rate limit reached. Try again after",
|
|
699
|
+
" ",
|
|
700
|
+
resetTime.toLocaleTimeString()
|
|
701
|
+
] })
|
|
702
|
+
] }),
|
|
703
|
+
/* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-xs text-muted-foreground", children: [
|
|
704
|
+
"By subscribing, you agree to receive emails from ",
|
|
705
|
+
displayName,
|
|
706
|
+
". You can unsubscribe at any time."
|
|
707
|
+
] })
|
|
708
|
+
] }) }),
|
|
709
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "hidden lg:flex items-center justify-center bg-muted px-4 py-12 sm:px-6 lg:px-8", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "max-w-md space-y-8", children: [
|
|
710
|
+
/* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-2xl font-bold", children: "Why Subscribe?" }),
|
|
711
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-6", children: displayFeatures.map((feature, index) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-4", children: [
|
|
712
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-shrink-0 text-primary", children: feature.icon }),
|
|
713
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
714
|
+
/* @__PURE__ */ jsxRuntime.jsx("h3", { className: "font-semibold", children: feature.title }),
|
|
715
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground", children: feature.description })
|
|
716
|
+
] })
|
|
717
|
+
] }, index)) })
|
|
718
|
+
] }) })
|
|
719
|
+
] })
|
|
720
|
+
) })
|
|
721
|
+
] });
|
|
722
|
+
}
|
|
723
|
+
function NewsletterForm({
|
|
724
|
+
variant = "inline",
|
|
725
|
+
showLabel = false,
|
|
726
|
+
showDescription = false,
|
|
727
|
+
description = "Subscribe to our newsletter for updates",
|
|
728
|
+
buttonText = "Subscribe",
|
|
729
|
+
className = "",
|
|
730
|
+
recaptchaSiteKey
|
|
731
|
+
}) {
|
|
732
|
+
const client = useWebSDK();
|
|
733
|
+
const config = useWebSDKConfig();
|
|
734
|
+
const { product } = useProduct();
|
|
735
|
+
const productConfig = useProductConfig();
|
|
736
|
+
const effectiveProductId = product?.id || config.productId;
|
|
737
|
+
const effectiveRecaptchaSiteKey = recaptchaSiteKey ?? productConfig.recaptchaSiteKey ?? config.recaptchaSiteKey ?? "";
|
|
738
|
+
const recaptchaRef = React.useRef(null);
|
|
739
|
+
const [email, setEmail] = React.useState("");
|
|
740
|
+
const [isLoading, setIsLoading] = React.useState(false);
|
|
741
|
+
const handleSubmit = async (e) => {
|
|
742
|
+
e.preventDefault();
|
|
743
|
+
if (!email || !email.includes("@")) {
|
|
744
|
+
sonner.toast.error("Please enter a valid email address");
|
|
745
|
+
return;
|
|
746
|
+
}
|
|
747
|
+
const recaptchaToken = recaptchaRef.current?.getValue() ?? "";
|
|
748
|
+
if (effectiveRecaptchaSiteKey && !recaptchaToken) {
|
|
749
|
+
sonner.toast.error("Please complete the reCAPTCHA verification");
|
|
750
|
+
return;
|
|
751
|
+
}
|
|
752
|
+
setIsLoading(true);
|
|
753
|
+
try {
|
|
754
|
+
const result = await subscribeNewsletter(
|
|
755
|
+
client,
|
|
756
|
+
email,
|
|
757
|
+
effectiveProductId,
|
|
758
|
+
recaptchaToken || void 0
|
|
759
|
+
);
|
|
760
|
+
if (result.success) {
|
|
761
|
+
if (result.message) {
|
|
762
|
+
sonner.toast.info(result.message);
|
|
763
|
+
} else {
|
|
764
|
+
sonner.toast.success("Successfully subscribed!");
|
|
765
|
+
}
|
|
766
|
+
setEmail("");
|
|
767
|
+
} else {
|
|
768
|
+
sonner.toast.error(result.message ?? "Subscription failed");
|
|
769
|
+
}
|
|
770
|
+
recaptchaRef.current?.reset();
|
|
771
|
+
} catch (_error) {
|
|
772
|
+
sonner.toast.error("An error occurred. Please try again.");
|
|
773
|
+
recaptchaRef.current?.reset();
|
|
774
|
+
} finally {
|
|
775
|
+
setIsLoading(false);
|
|
776
|
+
}
|
|
777
|
+
};
|
|
778
|
+
const isInline = variant === "inline";
|
|
779
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className, style: { overflow: "visible" }, children: [
|
|
780
|
+
showDescription && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground mb-3", children: description }),
|
|
781
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
782
|
+
"form",
|
|
783
|
+
{
|
|
784
|
+
onSubmit: handleSubmit,
|
|
785
|
+
className: isInline ? "flex flex-col gap-2" : "flex flex-col gap-2",
|
|
786
|
+
style: { overflow: "visible" },
|
|
787
|
+
children: [
|
|
788
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
789
|
+
"div",
|
|
790
|
+
{
|
|
791
|
+
className: isInline && !effectiveRecaptchaSiteKey ? "flex gap-2" : "flex flex-col gap-2",
|
|
792
|
+
children: [
|
|
793
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
794
|
+
"div",
|
|
795
|
+
{
|
|
796
|
+
className: isInline && !effectiveRecaptchaSiteKey ? "flex-1" : "w-full",
|
|
797
|
+
children: [
|
|
798
|
+
showLabel && /* @__PURE__ */ jsxRuntime.jsx(Label, { htmlFor: "newsletter-email", className: "text-sm mb-1 block", children: "Email" }),
|
|
799
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
800
|
+
Input,
|
|
801
|
+
{
|
|
802
|
+
id: "newsletter-email",
|
|
803
|
+
type: "email",
|
|
804
|
+
placeholder: "Enter your email",
|
|
805
|
+
value: email,
|
|
806
|
+
onChange: (e) => setEmail(e.target.value),
|
|
807
|
+
disabled: isLoading,
|
|
808
|
+
required: true,
|
|
809
|
+
className: "w-full"
|
|
810
|
+
}
|
|
811
|
+
)
|
|
812
|
+
]
|
|
813
|
+
}
|
|
814
|
+
),
|
|
815
|
+
!effectiveRecaptchaSiteKey && /* @__PURE__ */ jsxRuntime.jsx(
|
|
816
|
+
Button,
|
|
817
|
+
{
|
|
818
|
+
type: "submit",
|
|
819
|
+
disabled: isLoading,
|
|
820
|
+
className: isInline ? "whitespace-nowrap" : "w-full",
|
|
821
|
+
children: isLoading ? "Subscribing..." : buttonText
|
|
822
|
+
}
|
|
823
|
+
)
|
|
824
|
+
]
|
|
825
|
+
}
|
|
826
|
+
),
|
|
827
|
+
effectiveRecaptchaSiteKey && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
828
|
+
/* @__PURE__ */ jsxRuntime.jsx("style", { children: `
|
|
829
|
+
body > div[style*="position: absolute"] > div > iframe[title*="recaptcha challenge"],
|
|
830
|
+
body > div:not([class]):not([id]) > div > iframe[title*="recaptcha"] {
|
|
831
|
+
position: fixed !important;
|
|
832
|
+
top: 50% !important;
|
|
833
|
+
left: 50% !important;
|
|
834
|
+
transform: translate(-50%, -50%) !important;
|
|
835
|
+
z-index: 99999 !important;
|
|
836
|
+
}
|
|
837
|
+
body > div[style*="position: absolute"]:has(iframe[title*="recaptcha challenge"]) {
|
|
838
|
+
position: fixed !important;
|
|
839
|
+
top: 0 !important;
|
|
840
|
+
left: 0 !important;
|
|
841
|
+
width: 100vw !important;
|
|
842
|
+
height: 100vh !important;
|
|
843
|
+
display: flex !important;
|
|
844
|
+
align-items: center !important;
|
|
845
|
+
justify-content: center !important;
|
|
846
|
+
z-index: 99999 !important;
|
|
847
|
+
background: rgba(0,0,0,0.3) !important;
|
|
848
|
+
}
|
|
849
|
+
` }),
|
|
850
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
851
|
+
"div",
|
|
852
|
+
{
|
|
853
|
+
style: {
|
|
854
|
+
position: "relative",
|
|
855
|
+
zIndex: 50,
|
|
856
|
+
display: "flex",
|
|
857
|
+
justifyContent: "flex-start"
|
|
858
|
+
},
|
|
859
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
860
|
+
ReCAPTCHA__default.default,
|
|
861
|
+
{
|
|
862
|
+
ref: recaptchaRef,
|
|
863
|
+
sitekey: effectiveRecaptchaSiteKey,
|
|
864
|
+
size: "normal"
|
|
865
|
+
}
|
|
866
|
+
)
|
|
867
|
+
}
|
|
868
|
+
)
|
|
869
|
+
] }),
|
|
870
|
+
effectiveRecaptchaSiteKey && /* @__PURE__ */ jsxRuntime.jsx(Button, { type: "submit", disabled: isLoading, className: "w-full", children: isLoading ? "Subscribing..." : buttonText })
|
|
871
|
+
]
|
|
872
|
+
}
|
|
873
|
+
)
|
|
874
|
+
] });
|
|
875
|
+
}
|
|
876
|
+
|
|
877
|
+
exports.NewsletterForm = NewsletterForm;
|
|
878
|
+
exports.NewsletterPage = NewsletterPage;
|
|
879
|
+
//# sourceMappingURL=newsletter.js.map
|
|
880
|
+
//# sourceMappingURL=newsletter.js.map
|