@burdenoff/website-sdk 0.0.0-dev → 2026.511.1
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/dist/analytics/index.js +0 -1
- package/dist/analytics/index.js.map +1 -1
- package/dist/analytics/index.mjs +0 -1
- package/dist/analytics/index.mjs.map +1 -1
- package/dist/client/index.d.mts +2 -5
- package/dist/client/index.d.ts +2 -5
- package/dist/client/index.js +2 -11
- package/dist/client/index.js.map +1 -1
- package/dist/client/index.mjs +2 -11
- package/dist/client/index.mjs.map +1 -1
- package/dist/components/contact.d.mts +2 -77
- package/dist/components/contact.d.ts +2 -77
- package/dist/components/contact.js +9 -112
- package/dist/components/contact.js.map +1 -1
- package/dist/components/contact.mjs +10 -112
- package/dist/components/contact.mjs.map +1 -1
- package/dist/components/newsletter.js +6 -17
- package/dist/components/newsletter.js.map +1 -1
- package/dist/components/newsletter.mjs +6 -17
- package/dist/components/newsletter.mjs.map +1 -1
- package/dist/components/pricing.d.mts +0 -38
- package/dist/components/pricing.d.ts +0 -38
- package/dist/components/pricing.js +84 -267
- package/dist/components/pricing.js.map +1 -1
- package/dist/components/pricing.mjs +84 -267
- package/dist/components/pricing.mjs.map +1 -1
- package/dist/components/waitlist.js +2 -6
- package/dist/components/waitlist.js.map +1 -1
- package/dist/components/waitlist.mjs +2 -6
- package/dist/components/waitlist.mjs.map +1 -1
- package/dist/content/index.d.mts +9 -181
- package/dist/content/index.d.ts +9 -181
- package/dist/content/index.js +16 -2177
- package/dist/content/index.js.map +1 -1
- package/dist/content/index.mjs +6 -2141
- package/dist/content/index.mjs.map +1 -1
- package/dist/index.d.mts +5 -43
- package/dist/index.d.ts +5 -43
- package/dist/index.js +373 -3919
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +378 -3905
- package/dist/index.mjs.map +1 -1
- package/dist/product/index.d.mts +0 -8
- package/dist/product/index.d.ts +0 -8
- package/dist/product/index.js +4 -24
- package/dist/product/index.js.map +1 -1
- package/dist/product/index.mjs +4 -24
- package/dist/product/index.mjs.map +1 -1
- package/package.json +3 -5
- package/dist/components/electron-download.d.mts +0 -32
- package/dist/components/electron-download.d.ts +0 -32
- package/dist/components/electron-download.js +0 -131
- package/dist/components/electron-download.js.map +0 -1
- package/dist/components/electron-download.mjs +0 -108
- package/dist/components/electron-download.mjs.map +0 -1
- package/dist/components/launch-countdown.d.mts +0 -15
- package/dist/components/launch-countdown.d.ts +0 -15
- package/dist/components/launch-countdown.js +0 -89
- package/dist/components/launch-countdown.js.map +0 -1
- package/dist/components/launch-countdown.mjs +0 -87
- package/dist/components/launch-countdown.mjs.map +0 -1
- package/dist/components/partners.d.mts +0 -88
- package/dist/components/partners.d.ts +0 -88
- package/dist/components/partners.js +0 -1169
- package/dist/components/partners.js.map +0 -1
- package/dist/components/partners.mjs +0 -1143
- package/dist/components/partners.mjs.map +0 -1
- package/dist/components/problems-solved.d.mts +0 -37
- package/dist/components/problems-solved.d.ts +0 -37
- package/dist/components/problems-solved.js +0 -157
- package/dist/components/problems-solved.js.map +0 -1
- package/dist/components/problems-solved.mjs +0 -135
- package/dist/components/problems-solved.mjs.map +0 -1
- package/dist/components/resource-links.d.mts +0 -28
- package/dist/components/resource-links.d.ts +0 -28
- package/dist/components/resource-links.js +0 -229
- package/dist/components/resource-links.js.map +0 -1
- package/dist/components/resource-links.mjs +0 -227
- package/dist/components/resource-links.mjs.map +0 -1
- package/dist/components/social-links.d.mts +0 -37
- package/dist/components/social-links.d.ts +0 -37
- package/dist/components/social-links.js +0 -268
- package/dist/components/social-links.js.map +0 -1
- package/dist/components/social-links.mjs +0 -266
- package/dist/components/social-links.mjs.map +0 -1
- package/dist/components/website-switcher.d.mts +0 -27
- package/dist/components/website-switcher.d.ts +0 -27
- package/dist/components/website-switcher.js +0 -168
- package/dist/components/website-switcher.js.map +0 -1
- package/dist/components/website-switcher.mjs +0 -146
- package/dist/components/website-switcher.mjs.map +0 -1
package/dist/index.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
var React = require('react');
|
|
4
4
|
var jsxRuntime = require('react/jsx-runtime');
|
|
5
5
|
var lucideReact = require('lucide-react');
|
|
6
|
-
var
|
|
6
|
+
var ReCAPTCHA2 = require('react-google-recaptcha');
|
|
7
7
|
var sonner = require('sonner');
|
|
8
8
|
var reactHelmetAsync = require('react-helmet-async');
|
|
9
9
|
var reactSlot = require('@radix-ui/react-slot');
|
|
@@ -14,7 +14,6 @@ var LabelPrimitive = require('@radix-ui/react-label');
|
|
|
14
14
|
var zod$1 = require('@hookform/resolvers/zod');
|
|
15
15
|
var reactHookForm = require('react-hook-form');
|
|
16
16
|
var zod = require('zod');
|
|
17
|
-
var DropdownMenu = require('@radix-ui/react-dropdown-menu');
|
|
18
17
|
|
|
19
18
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
20
19
|
|
|
@@ -37,9 +36,8 @@ function _interopNamespace(e) {
|
|
|
37
36
|
}
|
|
38
37
|
|
|
39
38
|
var React__namespace = /*#__PURE__*/_interopNamespace(React);
|
|
40
|
-
var
|
|
39
|
+
var ReCAPTCHA2__default = /*#__PURE__*/_interopDefault(ReCAPTCHA2);
|
|
41
40
|
var LabelPrimitive__namespace = /*#__PURE__*/_interopNamespace(LabelPrimitive);
|
|
42
|
-
var DropdownMenu__namespace = /*#__PURE__*/_interopNamespace(DropdownMenu);
|
|
43
41
|
|
|
44
42
|
var __defProp = Object.defineProperty;
|
|
45
43
|
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
@@ -194,7 +192,6 @@ function getLocalFallback() {
|
|
|
194
192
|
}
|
|
195
193
|
var PRODUCT_DOMAINS = [
|
|
196
194
|
"burdenoff.com",
|
|
197
|
-
"algoshred.com",
|
|
198
195
|
"vibecontrols.com",
|
|
199
196
|
"fluidgrids.com",
|
|
200
197
|
"botlit.ai",
|
|
@@ -239,25 +236,17 @@ function isAlphaHost(hostname) {
|
|
|
239
236
|
);
|
|
240
237
|
}
|
|
241
238
|
function resolveGatewayUrl(explicitUrl) {
|
|
242
|
-
if (explicitUrl &&
|
|
239
|
+
if (explicitUrl && !isLocalhostUrl(explicitUrl)) {
|
|
243
240
|
return explicitUrl;
|
|
244
241
|
}
|
|
245
242
|
if (typeof window === "undefined") {
|
|
246
|
-
return explicitUrl
|
|
243
|
+
return explicitUrl || getLocalFallback();
|
|
247
244
|
}
|
|
248
245
|
const hostname = window.location.hostname.toLowerCase();
|
|
249
246
|
if (isProductionHost(hostname)) return PROD_GATEWAY;
|
|
250
247
|
if (isAlphaHost(hostname)) return ALPHA_GATEWAY;
|
|
251
248
|
return getLocalFallback();
|
|
252
249
|
}
|
|
253
|
-
function isAbsoluteUrl(url) {
|
|
254
|
-
try {
|
|
255
|
-
new URL(url);
|
|
256
|
-
return true;
|
|
257
|
-
} catch {
|
|
258
|
-
return false;
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
250
|
function isLocalhostUrl(url) {
|
|
262
251
|
try {
|
|
263
252
|
const parsed = new URL(url);
|
|
@@ -289,125 +278,6 @@ function useWebSDKConfig() {
|
|
|
289
278
|
const client = useWebSDK();
|
|
290
279
|
return client.getConfig();
|
|
291
280
|
}
|
|
292
|
-
var PRODUCT_FIELDS_BASE = `
|
|
293
|
-
id
|
|
294
|
-
name
|
|
295
|
-
slug
|
|
296
|
-
description
|
|
297
|
-
status
|
|
298
|
-
logoUrl
|
|
299
|
-
website
|
|
300
|
-
appUrl
|
|
301
|
-
docsUrl
|
|
302
|
-
contactEmail
|
|
303
|
-
supportEmail
|
|
304
|
-
contactPhone
|
|
305
|
-
contactAddress
|
|
306
|
-
seoTitle
|
|
307
|
-
seoDescription
|
|
308
|
-
seoKeywords
|
|
309
|
-
ogImage
|
|
310
|
-
socialLinks
|
|
311
|
-
recaptchaSiteKey
|
|
312
|
-
rybbitSiteId
|
|
313
|
-
notificationEmail
|
|
314
|
-
metadata
|
|
315
|
-
`;
|
|
316
|
-
var PUBLIC_PRODUCT_QUERY = `
|
|
317
|
-
query PublicProduct($slug: String!) {
|
|
318
|
-
publicProduct(slug: $slug) {
|
|
319
|
-
${PRODUCT_FIELDS_BASE}
|
|
320
|
-
resourceLinks
|
|
321
|
-
}
|
|
322
|
-
}
|
|
323
|
-
`;
|
|
324
|
-
var PUBLIC_PRODUCT_QUERY_FALLBACK = `
|
|
325
|
-
query PublicProductFallback($slug: String!) {
|
|
326
|
-
publicProduct(slug: $slug) {
|
|
327
|
-
${PRODUCT_FIELDS_BASE}
|
|
328
|
-
}
|
|
329
|
-
}
|
|
330
|
-
`;
|
|
331
|
-
var ProductContext = React.createContext({
|
|
332
|
-
product: null,
|
|
333
|
-
loading: true,
|
|
334
|
-
error: null,
|
|
335
|
-
hasProvider: false,
|
|
336
|
-
refetch: () => {
|
|
337
|
-
}
|
|
338
|
-
});
|
|
339
|
-
function ProductProvider({ children, fallback }) {
|
|
340
|
-
const client = useWebSDK();
|
|
341
|
-
const config = useWebSDKConfig();
|
|
342
|
-
const [product, setProduct] = React.useState(
|
|
343
|
-
fallback ? fallback : null
|
|
344
|
-
);
|
|
345
|
-
const [loading, setLoading] = React.useState(true);
|
|
346
|
-
const [error, setError] = React.useState(null);
|
|
347
|
-
const fetchProduct = React.useCallback(async () => {
|
|
348
|
-
if (!config.productSlug) {
|
|
349
|
-
setLoading(false);
|
|
350
|
-
return;
|
|
351
|
-
}
|
|
352
|
-
setLoading(true);
|
|
353
|
-
setError(null);
|
|
354
|
-
try {
|
|
355
|
-
let result = await client.query(
|
|
356
|
-
PUBLIC_PRODUCT_QUERY,
|
|
357
|
-
{ slug: config.productSlug }
|
|
358
|
-
);
|
|
359
|
-
const schemaMissingResourceLinks = result.errors?.some(
|
|
360
|
-
(e) => /Cannot query field ["']?resourceLinks["']?/i.test(e?.message ?? "")
|
|
361
|
-
);
|
|
362
|
-
if (schemaMissingResourceLinks) {
|
|
363
|
-
result = await client.query(
|
|
364
|
-
PUBLIC_PRODUCT_QUERY_FALLBACK,
|
|
365
|
-
{ slug: config.productSlug }
|
|
366
|
-
);
|
|
367
|
-
}
|
|
368
|
-
if (result.errors?.length) {
|
|
369
|
-
setError(result.errors[0]?.message ?? "Unknown error");
|
|
370
|
-
} else if (result.data?.publicProduct) {
|
|
371
|
-
setProduct(result.data.publicProduct);
|
|
372
|
-
}
|
|
373
|
-
} catch (err) {
|
|
374
|
-
setError(err instanceof Error ? err.message : "Failed to fetch product");
|
|
375
|
-
} finally {
|
|
376
|
-
setLoading(false);
|
|
377
|
-
}
|
|
378
|
-
}, [client, config.productSlug]);
|
|
379
|
-
React.useLayoutEffect(() => {
|
|
380
|
-
fetchProduct();
|
|
381
|
-
}, [fetchProduct]);
|
|
382
|
-
const value = React.useMemo(
|
|
383
|
-
() => ({
|
|
384
|
-
product,
|
|
385
|
-
loading,
|
|
386
|
-
error,
|
|
387
|
-
hasProvider: true,
|
|
388
|
-
refetch: fetchProduct
|
|
389
|
-
}),
|
|
390
|
-
[product, loading, error, fetchProduct]
|
|
391
|
-
);
|
|
392
|
-
return /* @__PURE__ */ jsxRuntime.jsx(ProductContext.Provider, { value, children });
|
|
393
|
-
}
|
|
394
|
-
function useProduct() {
|
|
395
|
-
return React.useContext(ProductContext);
|
|
396
|
-
}
|
|
397
|
-
function useProductConfig() {
|
|
398
|
-
const { product } = useProduct();
|
|
399
|
-
const config = useWebSDKConfig();
|
|
400
|
-
return {
|
|
401
|
-
// Prefer product-level config, fall back to SDK config
|
|
402
|
-
recaptchaSiteKey: product?.recaptchaSiteKey ?? config.recaptchaSiteKey ?? null,
|
|
403
|
-
rybbitSiteId: product?.rybbitSiteId ?? config.rybbitSiteId ?? null,
|
|
404
|
-
contactEmail: product?.contactEmail ?? null,
|
|
405
|
-
supportEmail: product?.supportEmail ?? null,
|
|
406
|
-
notificationEmail: product?.notificationEmail ?? product?.contactEmail ?? null,
|
|
407
|
-
productName: product?.name ?? null,
|
|
408
|
-
logoUrl: product?.logoUrl ?? null
|
|
409
|
-
};
|
|
410
|
-
}
|
|
411
281
|
function PageHead({
|
|
412
282
|
title,
|
|
413
283
|
description,
|
|
@@ -531,28 +401,6 @@ var Label = React__namespace.forwardRef(({ className, ...props }, ref) => /* @__
|
|
|
531
401
|
}
|
|
532
402
|
));
|
|
533
403
|
Label.displayName = LabelPrimitive__namespace.Root.displayName;
|
|
534
|
-
var Select = React__namespace.forwardRef(({ className, children, ...props }, ref) => {
|
|
535
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
536
|
-
"select",
|
|
537
|
-
{
|
|
538
|
-
className: cn(
|
|
539
|
-
"flex h-9 w-full appearance-none rounded-md border border-input bg-transparent bg-[length:1rem_1rem] bg-[right_0.75rem_center] bg-no-repeat px-3 py-1 pr-8 text-base shadow-sm transition-colors 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",
|
|
540
|
-
// Inline SVG chevron: SVG inside a `background-image: url(data:…)`
|
|
541
|
-
// does NOT inherit `currentColor` from the host element (the SVG is
|
|
542
|
-
// its own document with no parent CSS context), so the stroke is
|
|
543
|
-
// hardcoded to a muted slate-400 (#94a3b8). Matches the muted
|
|
544
|
-
// foreground token visually in both light and dark themes well
|
|
545
|
-
// enough for a passive chevron — addresses #9 review feedback.
|
|
546
|
-
`bg-[url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%2394a3b8' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><polyline points='6 9 12 15 18 9'/></svg>")]`,
|
|
547
|
-
className
|
|
548
|
-
),
|
|
549
|
-
ref,
|
|
550
|
-
...props,
|
|
551
|
-
children
|
|
552
|
-
}
|
|
553
|
-
);
|
|
554
|
-
});
|
|
555
|
-
Select.displayName = "Select";
|
|
556
404
|
var Textarea = React__namespace.forwardRef(({ className, ...props }, ref) => {
|
|
557
405
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
558
406
|
"textarea",
|
|
@@ -567,15 +415,6 @@ var Textarea = React__namespace.forwardRef(({ className, ...props }, ref) => {
|
|
|
567
415
|
);
|
|
568
416
|
});
|
|
569
417
|
Textarea.displayName = "Textarea";
|
|
570
|
-
var DEFAULT_CONTACT_CATEGORIES = [
|
|
571
|
-
{ value: "general", label: "General enquiry" },
|
|
572
|
-
{ value: "sales", label: "Sales" },
|
|
573
|
-
{ value: "support", label: "Support" },
|
|
574
|
-
{ value: "partnership", label: "Partnership" },
|
|
575
|
-
{ value: "press", label: "Press / media" },
|
|
576
|
-
{ value: "careers", label: "Careers" },
|
|
577
|
-
{ value: "security", label: "Security disclosure" }
|
|
578
|
-
];
|
|
579
418
|
var SUBMIT_CONTACT_INQUIRY_MUTATION = (
|
|
580
419
|
/* GraphQL */
|
|
581
420
|
`
|
|
@@ -597,12 +436,6 @@ async function submitContactForm(client, data, productId) {
|
|
|
597
436
|
subject: data.subject,
|
|
598
437
|
message: data.message,
|
|
599
438
|
productId,
|
|
600
|
-
// Send `category` only when the user actually picked one. Older
|
|
601
|
-
// backend deployments without the `category` field in the
|
|
602
|
-
// `SubmitContactInquiryInput` schema would reject any unknown
|
|
603
|
-
// field; spreading conditionally keeps the SDK forward-
|
|
604
|
-
// compatible.
|
|
605
|
-
...data.category ? { category: data.category } : {},
|
|
606
439
|
...data.recaptchaToken ? { recaptchaToken: data.recaptchaToken } : {}
|
|
607
440
|
}
|
|
608
441
|
});
|
|
@@ -641,17 +474,6 @@ function ContactPage({
|
|
|
641
474
|
responseTime = "We typically respond within 24 hours",
|
|
642
475
|
heroTitle = "Get in Touch",
|
|
643
476
|
heroDescription = "Have questions about {productName}? We're here to help. Reach out to our team and we'll get back to you as soon as possible.",
|
|
644
|
-
headingLevel = 1,
|
|
645
|
-
// Backward-compatibility: default to `[]` so existing consumers
|
|
646
|
-
// (fluidgrids/burdenoff/algoshred websites) that don't pass
|
|
647
|
-
// `categories` continue to render with NO dropdown — same behavior
|
|
648
|
-
// they had before this SDK version. To opt in, pass an explicit
|
|
649
|
-
// `categories={…}` array (or the exported `DEFAULT_CONTACT_CATEGORIES`
|
|
650
|
-
// constant for the sane Burdenoff-generic vocabulary).
|
|
651
|
-
categories = [],
|
|
652
|
-
categoryLabel = "Category",
|
|
653
|
-
categoryPlaceholder = "Select a category",
|
|
654
|
-
categoryRequired = true,
|
|
655
477
|
additionalOptions = [
|
|
656
478
|
{
|
|
657
479
|
title: "Sales Inquiries",
|
|
@@ -676,10 +498,8 @@ function ContactPage({
|
|
|
676
498
|
}) {
|
|
677
499
|
const client = useWebSDK();
|
|
678
500
|
const config = useWebSDKConfig();
|
|
679
|
-
const
|
|
680
|
-
const
|
|
681
|
-
const displayName = productName ?? product?.name ?? config.productId;
|
|
682
|
-
const effectiveRecaptchaSiteKey = recaptchaSiteKey ?? product?.recaptchaSiteKey ?? config.recaptchaSiteKey ?? "";
|
|
501
|
+
const displayName = productName ?? config.productId;
|
|
502
|
+
const effectiveRecaptchaSiteKey = recaptchaSiteKey ?? config.recaptchaSiteKey ?? "";
|
|
683
503
|
const recaptchaRef = React.useRef(null);
|
|
684
504
|
const [isSubmitting, setIsSubmitting] = React.useState(false);
|
|
685
505
|
const [submitStatus, setSubmitStatus] = React.useState("idle");
|
|
@@ -695,19 +515,11 @@ function ContactPage({
|
|
|
695
515
|
const form = e.currentTarget;
|
|
696
516
|
try {
|
|
697
517
|
const formData = new FormData(form);
|
|
698
|
-
const showCategory = categories.length > 0;
|
|
699
|
-
const rawCategory = showCategory ? (formData.get("category") ?? "").trim() : "";
|
|
700
|
-
if (showCategory && categoryRequired && !rawCategory) {
|
|
701
|
-
sonner.toast.error("Please select a category");
|
|
702
|
-
setIsSubmitting(false);
|
|
703
|
-
return;
|
|
704
|
-
}
|
|
705
518
|
const contactData = {
|
|
706
519
|
firstName: formData.get("firstName"),
|
|
707
520
|
lastName: formData.get("lastName"),
|
|
708
521
|
email: formData.get("email"),
|
|
709
522
|
company: formData.get("company"),
|
|
710
|
-
category: rawCategory || void 0,
|
|
711
523
|
subject: formData.get("subject"),
|
|
712
524
|
message: formData.get("message"),
|
|
713
525
|
recaptchaToken
|
|
@@ -715,7 +527,7 @@ function ContactPage({
|
|
|
715
527
|
const result = await submitContactForm(
|
|
716
528
|
client,
|
|
717
529
|
contactData,
|
|
718
|
-
|
|
530
|
+
config.productId
|
|
719
531
|
);
|
|
720
532
|
if (result.success) {
|
|
721
533
|
setSubmitStatus("success");
|
|
@@ -754,16 +566,10 @@ function ContactPage({
|
|
|
754
566
|
url: seo.url
|
|
755
567
|
}
|
|
756
568
|
),
|
|
757
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
758
|
-
"
|
|
759
|
-
{
|
|
760
|
-
|
|
761
|
-
children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "max-w-7xl mx-auto px-4 sm:px-6 lg:px-8", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center max-w-3xl mx-auto", children: [
|
|
762
|
-
headingLevel === 2 ? /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-2xl md:text-3xl font-bold mb-3", children: heroTitle }) : /* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-3xl sm:text-4xl md:text-5xl font-bold mb-4 sm:mb-6", children: heroTitle }),
|
|
763
|
-
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-lg sm:text-xl text-muted-foreground leading-relaxed", children: replacePlaceholders(heroDescription, displayName, contactEmail) })
|
|
764
|
-
] }) })
|
|
765
|
-
}
|
|
766
|
-
),
|
|
569
|
+
/* @__PURE__ */ jsxRuntime.jsx("section", { className: "bg-gradient-to-b from-background to-muted/20 py-12 sm:py-16 md:py-20 lg:py-24", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "max-w-7xl mx-auto px-4 sm:px-6 lg:px-8", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center max-w-3xl mx-auto", children: [
|
|
570
|
+
/* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-3xl sm:text-4xl md:text-5xl font-bold mb-4 sm:mb-6", children: heroTitle }),
|
|
571
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-lg sm:text-xl text-muted-foreground leading-relaxed", children: replacePlaceholders(heroDescription, displayName, contactEmail) })
|
|
572
|
+
] }) }) }),
|
|
767
573
|
/* @__PURE__ */ jsxRuntime.jsx("section", { className: "py-12 sm:py-16 md:py-20 lg:py-24", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "max-w-7xl mx-auto px-4 sm:px-6 lg:px-8", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid sm:grid-cols-1 lg:grid-cols-3 gap-8 sm:gap-10 lg:gap-12", children: [
|
|
768
574
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "lg:col-span-1 order-2 lg:order-1", children: [
|
|
769
575
|
/* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-xl sm:text-2xl font-bold mb-4 sm:mb-6", children: "Contact Information" }),
|
|
@@ -775,7 +581,7 @@ function ContactPage({
|
|
|
775
581
|
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-muted-foreground text-sm sm:text-base break-all", children: contactEmail })
|
|
776
582
|
] })
|
|
777
583
|
] }),
|
|
778
|
-
|
|
584
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start space-x-3 sm:space-x-4", children: [
|
|
779
585
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-8 h-8 sm:w-10 sm:h-10 bg-primary/10 rounded-lg flex items-center justify-center flex-shrink-0", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Phone, { className: "h-4 w-4 sm:h-5 sm:w-5 text-primary" }) }),
|
|
780
586
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0 flex-1", children: [
|
|
781
587
|
/* @__PURE__ */ jsxRuntime.jsx("h3", { className: "font-semibold mb-1 text-sm sm:text-base", children: "Phone" }),
|
|
@@ -882,33 +688,6 @@ function ContactPage({
|
|
|
882
688
|
}
|
|
883
689
|
)
|
|
884
690
|
] }),
|
|
885
|
-
categories.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
|
|
886
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
887
|
-
Label,
|
|
888
|
-
{
|
|
889
|
-
htmlFor: "category",
|
|
890
|
-
className: "text-sm sm:text-base",
|
|
891
|
-
children: [
|
|
892
|
-
categoryLabel,
|
|
893
|
-
categoryRequired ? " *" : ""
|
|
894
|
-
]
|
|
895
|
-
}
|
|
896
|
-
),
|
|
897
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
898
|
-
Select,
|
|
899
|
-
{
|
|
900
|
-
id: "category",
|
|
901
|
-
name: "category",
|
|
902
|
-
required: categoryRequired,
|
|
903
|
-
defaultValue: "",
|
|
904
|
-
className: "text-sm sm:text-base",
|
|
905
|
-
children: [
|
|
906
|
-
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "", disabled: categoryRequired, children: categoryPlaceholder }),
|
|
907
|
-
categories.map((c) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: c.value, children: c.label }, c.value))
|
|
908
|
-
]
|
|
909
|
-
}
|
|
910
|
-
)
|
|
911
|
-
] }),
|
|
912
691
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
|
|
913
692
|
/* @__PURE__ */ jsxRuntime.jsx(Label, { htmlFor: "subject", className: "text-sm sm:text-base", children: "Subject *" }),
|
|
914
693
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -938,7 +717,7 @@ function ContactPage({
|
|
|
938
717
|
] }),
|
|
939
718
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-4", children: [
|
|
940
719
|
effectiveRecaptchaSiteKey && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex justify-center sm:justify-start", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
941
|
-
|
|
720
|
+
ReCAPTCHA2__default.default,
|
|
942
721
|
{
|
|
943
722
|
ref: recaptchaRef,
|
|
944
723
|
sitekey: effectiveRecaptchaSiteKey
|
|
@@ -979,7 +758,7 @@ function ContactPage({
|
|
|
979
758
|
)
|
|
980
759
|
] }) })
|
|
981
760
|
] }) }) }),
|
|
982
|
-
|
|
761
|
+
/* @__PURE__ */ jsxRuntime.jsx("section", { className: "py-12 sm:py-16 md:py-20 lg:py-24 bg-muted/30", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "max-w-7xl mx-auto px-4 sm:px-6 lg:px-8", children: [
|
|
983
762
|
/* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-2xl sm:text-3xl font-bold text-center mb-8 sm:mb-12", children: "More Ways to Connect" }),
|
|
984
763
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid sm:grid-cols-2 lg:grid-cols-3 gap-6 sm:gap-8 max-w-5xl mx-auto", children: additionalOptions.map((option, index) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
985
764
|
"div",
|
|
@@ -1008,999 +787,187 @@ function ContactPage({
|
|
|
1008
787
|
] }) })
|
|
1009
788
|
] });
|
|
1010
789
|
}
|
|
1011
|
-
var
|
|
1012
|
-
query
|
|
1013
|
-
|
|
1014
|
-
id
|
|
1015
|
-
title
|
|
1016
|
-
slug
|
|
1017
|
-
content
|
|
1018
|
-
contentFormat
|
|
1019
|
-
seoTitle
|
|
1020
|
-
seoDescription
|
|
1021
|
-
seoKeywords
|
|
1022
|
-
lastUpdated
|
|
1023
|
-
version
|
|
1024
|
-
excerpt
|
|
1025
|
-
thumbnail
|
|
1026
|
-
author
|
|
1027
|
-
publishedAt
|
|
1028
|
-
readingTime
|
|
1029
|
-
featured
|
|
1030
|
-
category
|
|
1031
|
-
}
|
|
1032
|
-
}
|
|
1033
|
-
`;
|
|
1034
|
-
var PUBLIC_RESOLVED_CONTENT_PAGE_QUERY = `
|
|
1035
|
-
query PublicResolvedContentPage($slug: String!, $productId: String!) {
|
|
1036
|
-
publicResolvedContentPage(slug: $slug, productId: $productId) {
|
|
790
|
+
var PUBLIC_PRODUCT_QUERY = `
|
|
791
|
+
query PublicProduct($slug: String!) {
|
|
792
|
+
publicProduct(slug: $slug) {
|
|
1037
793
|
id
|
|
1038
|
-
|
|
794
|
+
name
|
|
1039
795
|
slug
|
|
1040
|
-
|
|
1041
|
-
|
|
796
|
+
description
|
|
797
|
+
status
|
|
798
|
+
logoUrl
|
|
799
|
+
website
|
|
800
|
+
appUrl
|
|
801
|
+
docsUrl
|
|
802
|
+
contactEmail
|
|
803
|
+
supportEmail
|
|
804
|
+
contactPhone
|
|
805
|
+
contactAddress
|
|
1042
806
|
seoTitle
|
|
1043
807
|
seoDescription
|
|
1044
808
|
seoKeywords
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
readingTime
|
|
1052
|
-
featured
|
|
1053
|
-
category
|
|
809
|
+
ogImage
|
|
810
|
+
socialLinks
|
|
811
|
+
recaptchaSiteKey
|
|
812
|
+
rybbitSiteId
|
|
813
|
+
notificationEmail
|
|
814
|
+
metadata
|
|
1054
815
|
}
|
|
1055
816
|
}
|
|
1056
817
|
`;
|
|
1057
|
-
var
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
slug
|
|
1064
|
-
lastUpdated
|
|
1065
|
-
tags
|
|
1066
|
-
seoDescription
|
|
1067
|
-
excerpt
|
|
1068
|
-
thumbnail
|
|
1069
|
-
author
|
|
1070
|
-
publishedAt
|
|
1071
|
-
readingTime
|
|
1072
|
-
featured
|
|
1073
|
-
category
|
|
1074
|
-
}
|
|
1075
|
-
total
|
|
1076
|
-
hasMore
|
|
1077
|
-
}
|
|
818
|
+
var ProductContext = React.createContext({
|
|
819
|
+
product: null,
|
|
820
|
+
loading: true,
|
|
821
|
+
error: null,
|
|
822
|
+
hasProvider: false,
|
|
823
|
+
refetch: () => {
|
|
1078
824
|
}
|
|
1079
|
-
|
|
1080
|
-
function
|
|
825
|
+
});
|
|
826
|
+
function ProductProvider({ children, fallback }) {
|
|
1081
827
|
const client = useWebSDK();
|
|
1082
828
|
const config = useWebSDKConfig();
|
|
1083
|
-
const
|
|
1084
|
-
|
|
1085
|
-
|
|
829
|
+
const [product, setProduct] = React.useState(
|
|
830
|
+
fallback ? fallback : null
|
|
831
|
+
);
|
|
1086
832
|
const [loading, setLoading] = React.useState(true);
|
|
1087
833
|
const [error, setError] = React.useState(null);
|
|
1088
|
-
const
|
|
1089
|
-
if (!
|
|
834
|
+
const fetchProduct = React.useCallback(async () => {
|
|
835
|
+
if (!config.productSlug) {
|
|
836
|
+
setLoading(false);
|
|
837
|
+
return;
|
|
838
|
+
}
|
|
1090
839
|
setLoading(true);
|
|
1091
840
|
setError(null);
|
|
1092
841
|
try {
|
|
1093
|
-
const query = options?.resolve ? PUBLIC_RESOLVED_CONTENT_PAGE_QUERY : PUBLIC_CONTENT_PAGE_QUERY;
|
|
1094
|
-
const queryName = options?.resolve ? "publicResolvedContentPage" : "publicContentPage";
|
|
1095
842
|
const result = await client.query(
|
|
1096
|
-
|
|
1097
|
-
{
|
|
1098
|
-
slug,
|
|
1099
|
-
productId: resolvedProductId
|
|
1100
|
-
}
|
|
843
|
+
PUBLIC_PRODUCT_QUERY,
|
|
844
|
+
{ slug: config.productSlug }
|
|
1101
845
|
);
|
|
1102
846
|
if (result.errors?.length) {
|
|
1103
847
|
setError(result.errors[0]?.message ?? "Unknown error");
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
setData(result.data?.[queryName] ?? null);
|
|
848
|
+
} else if (result.data?.publicProduct) {
|
|
849
|
+
setProduct(result.data.publicProduct);
|
|
1107
850
|
}
|
|
1108
851
|
} catch (err) {
|
|
1109
|
-
setError(err instanceof Error ? err.message : "Failed to fetch
|
|
1110
|
-
setData(null);
|
|
852
|
+
setError(err instanceof Error ? err.message : "Failed to fetch product");
|
|
1111
853
|
} finally {
|
|
1112
854
|
setLoading(false);
|
|
1113
855
|
}
|
|
1114
|
-
}, [client,
|
|
1115
|
-
React.
|
|
1116
|
-
|
|
1117
|
-
}, [
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
setLoading(true);
|
|
1128
|
-
setError(null);
|
|
1129
|
-
try {
|
|
1130
|
-
const result = await client.query(PUBLIC_CONTENT_PAGES_QUERY, {
|
|
1131
|
-
productId: config.productId,
|
|
1132
|
-
limit: options?.limit ?? 50,
|
|
1133
|
-
offset: options?.offset ?? 0
|
|
1134
|
-
});
|
|
1135
|
-
if (result.errors?.length) {
|
|
1136
|
-
setError(result.errors[0]?.message ?? "Unknown error");
|
|
1137
|
-
setData(null);
|
|
1138
|
-
} else {
|
|
1139
|
-
setData(result.data?.publicContentPages ?? null);
|
|
1140
|
-
}
|
|
1141
|
-
} catch (err) {
|
|
1142
|
-
setError(
|
|
1143
|
-
err instanceof Error ? err.message : "Failed to fetch content pages"
|
|
1144
|
-
);
|
|
1145
|
-
setData(null);
|
|
1146
|
-
} finally {
|
|
1147
|
-
setLoading(false);
|
|
1148
|
-
}
|
|
1149
|
-
}, [client, config.productId, options?.limit, options?.offset]);
|
|
1150
|
-
React.useEffect(() => {
|
|
1151
|
-
fetchPages();
|
|
1152
|
-
}, [fetchPages]);
|
|
1153
|
-
return { data, loading, error, refetch: fetchPages };
|
|
1154
|
-
}
|
|
1155
|
-
function renderMarkdown(md) {
|
|
1156
|
-
let html = md;
|
|
1157
|
-
html = html.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
1158
|
-
html = html.replace(/```(\w*)\n([\s\S]*?)```/g, (_m, _lang, code) => {
|
|
1159
|
-
return `<pre><code>${code.trim()}</code></pre>`;
|
|
1160
|
-
});
|
|
1161
|
-
html = html.replace(/`([^`]+)`/g, "<code>$1</code>");
|
|
1162
|
-
html = html.replace(/^###### (.+)$/gm, "<h6>$1</h6>");
|
|
1163
|
-
html = html.replace(/^##### (.+)$/gm, "<h5>$1</h5>");
|
|
1164
|
-
html = html.replace(/^#### (.+)$/gm, "<h4>$1</h4>");
|
|
1165
|
-
html = html.replace(/^### (.+)$/gm, "<h3>$1</h3>");
|
|
1166
|
-
html = html.replace(/^## (.+)$/gm, "<h2>$1</h2>");
|
|
1167
|
-
html = html.replace(/^# (.+)$/gm, "<h1>$1</h1>");
|
|
1168
|
-
html = html.replace(/\*\*\*(.+?)\*\*\*/g, "<strong><em>$1</em></strong>");
|
|
1169
|
-
html = html.replace(/\*\*(.+?)\*\*/g, "<strong>$1</strong>");
|
|
1170
|
-
html = html.replace(/\*(.+?)\*/g, "<em>$1</em>");
|
|
1171
|
-
html = html.replace(
|
|
1172
|
-
/\[([^\]]+)\]\(([^)]+)\)/g,
|
|
1173
|
-
'<a href="$2" target="_blank" rel="noopener noreferrer">$1</a>'
|
|
856
|
+
}, [client, config.productSlug]);
|
|
857
|
+
React.useLayoutEffect(() => {
|
|
858
|
+
fetchProduct();
|
|
859
|
+
}, [fetchProduct]);
|
|
860
|
+
const value = React.useMemo(
|
|
861
|
+
() => ({
|
|
862
|
+
product,
|
|
863
|
+
loading,
|
|
864
|
+
error,
|
|
865
|
+
hasProvider: true,
|
|
866
|
+
refetch: fetchProduct
|
|
867
|
+
}),
|
|
868
|
+
[product, loading, error, fetchProduct]
|
|
1174
869
|
);
|
|
1175
|
-
|
|
1176
|
-
html = html.replace(/(^- .+$(\n^- .+$)*)/gm, (block) => {
|
|
1177
|
-
const items = block.split("\n").map((line) => `<li>${line.replace(/^- /, "")}</li>`).join("");
|
|
1178
|
-
return `<ul>${items}</ul>`;
|
|
1179
|
-
});
|
|
1180
|
-
html = html.replace(/(^\d+\. .+$(\n^\d+\. .+$)*)/gm, (block) => {
|
|
1181
|
-
const items = block.split("\n").map((line) => `<li>${line.replace(/^\d+\. /, "")}</li>`).join("");
|
|
1182
|
-
return `<ol>${items}</ol>`;
|
|
1183
|
-
});
|
|
1184
|
-
html = html.replace(/^---$/gm, "<hr />");
|
|
1185
|
-
html = html.split(/\n\n+/).map((block) => {
|
|
1186
|
-
const trimmed = block.trim();
|
|
1187
|
-
if (!trimmed) return "";
|
|
1188
|
-
if (/^<(h[1-6]|ul|ol|pre|blockquote|hr)/.test(trimmed)) return trimmed;
|
|
1189
|
-
return `<p>${trimmed.replace(/\n/g, "<br />")}</p>`;
|
|
1190
|
-
}).join("\n");
|
|
1191
|
-
return html;
|
|
1192
|
-
}
|
|
1193
|
-
function ContentRenderer({
|
|
1194
|
-
content,
|
|
1195
|
-
format = "markdown",
|
|
1196
|
-
className
|
|
1197
|
-
}) {
|
|
1198
|
-
if (format === "markdown") {
|
|
1199
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1200
|
-
"div",
|
|
1201
|
-
{
|
|
1202
|
-
className,
|
|
1203
|
-
dangerouslySetInnerHTML: { __html: renderMarkdown(content) }
|
|
1204
|
-
}
|
|
1205
|
-
);
|
|
1206
|
-
}
|
|
1207
|
-
if (format === "html") {
|
|
1208
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1209
|
-
"div",
|
|
1210
|
-
{
|
|
1211
|
-
className,
|
|
1212
|
-
dangerouslySetInnerHTML: { __html: content }
|
|
1213
|
-
}
|
|
1214
|
-
);
|
|
1215
|
-
}
|
|
1216
|
-
return /* @__PURE__ */ jsxRuntime.jsx("div", { className, children: /* @__PURE__ */ jsxRuntime.jsx("pre", { style: { whiteSpace: "pre-wrap", fontFamily: "inherit" }, children: content }) });
|
|
1217
|
-
}
|
|
1218
|
-
var PARTNER_CONTENT_QUERY = `
|
|
1219
|
-
query PartnerContent($productId: String!, $limit: Int) {
|
|
1220
|
-
publicContentPages(productId: $productId, limit: $limit, offset: 0) {
|
|
1221
|
-
items {
|
|
1222
|
-
id
|
|
1223
|
-
title
|
|
1224
|
-
slug
|
|
1225
|
-
excerpt
|
|
1226
|
-
content
|
|
1227
|
-
contentFormat
|
|
1228
|
-
thumbnail
|
|
1229
|
-
tags
|
|
1230
|
-
publishedAt
|
|
1231
|
-
lastUpdated
|
|
1232
|
-
}
|
|
1233
|
-
}
|
|
1234
|
-
}
|
|
1235
|
-
`;
|
|
1236
|
-
var PARTNER_DOCUMENTS_QUERY = `
|
|
1237
|
-
query PartnerDocuments($productId: String!, $limit: Int) {
|
|
1238
|
-
publicPressKitAssets(productId: $productId, limit: $limit, offset: 0) {
|
|
1239
|
-
items {
|
|
1240
|
-
id
|
|
1241
|
-
category
|
|
1242
|
-
title
|
|
1243
|
-
description
|
|
1244
|
-
fileUrl
|
|
1245
|
-
fileName
|
|
1246
|
-
fileSize
|
|
1247
|
-
mimeType
|
|
1248
|
-
version
|
|
1249
|
-
tags
|
|
1250
|
-
sortOrder
|
|
1251
|
-
updatedAt
|
|
1252
|
-
}
|
|
1253
|
-
}
|
|
1254
|
-
}
|
|
1255
|
-
`;
|
|
1256
|
-
function formatFileSize(bytes) {
|
|
1257
|
-
if (!bytes || bytes <= 0) return "";
|
|
1258
|
-
const units = ["B", "KB", "MB", "GB"];
|
|
1259
|
-
let i = 0;
|
|
1260
|
-
let n = bytes;
|
|
1261
|
-
while (n >= 1024 && i < units.length - 1) {
|
|
1262
|
-
n /= 1024;
|
|
1263
|
-
i++;
|
|
1264
|
-
}
|
|
1265
|
-
return `${n.toFixed(n >= 10 || i === 0 ? 0 : 1)} ${units[i]}`;
|
|
1266
|
-
}
|
|
1267
|
-
async function downloadDocument(e, doc) {
|
|
1268
|
-
e.preventDefault();
|
|
1269
|
-
const name = doc.fileName || doc.fileUrl.split("/").pop()?.split("?")[0] || doc.title || "partner-document";
|
|
1270
|
-
const openInTab = () => window.open(doc.fileUrl, "_blank", "noopener,noreferrer");
|
|
1271
|
-
try {
|
|
1272
|
-
const res = await fetch(doc.fileUrl, { mode: "cors" });
|
|
1273
|
-
if (!res.ok) {
|
|
1274
|
-
openInTab();
|
|
1275
|
-
return;
|
|
1276
|
-
}
|
|
1277
|
-
const blob = await res.blob();
|
|
1278
|
-
const objUrl = URL.createObjectURL(blob);
|
|
1279
|
-
const a = document.createElement("a");
|
|
1280
|
-
a.href = objUrl;
|
|
1281
|
-
a.download = name;
|
|
1282
|
-
document.body.appendChild(a);
|
|
1283
|
-
a.click();
|
|
1284
|
-
a.remove();
|
|
1285
|
-
URL.revokeObjectURL(objUrl);
|
|
1286
|
-
} catch {
|
|
1287
|
-
openInTab();
|
|
1288
|
-
}
|
|
1289
|
-
}
|
|
1290
|
-
function PartnerHero({
|
|
1291
|
-
title,
|
|
1292
|
-
subtitle
|
|
1293
|
-
}) {
|
|
1294
|
-
return /* @__PURE__ */ jsxRuntime.jsx("section", { className: "bg-gradient-to-b from-background to-muted/20 py-16 md:py-24", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "container mx-auto px-4 max-w-4xl text-center", children: [
|
|
1295
|
-
/* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-4xl md:text-5xl font-bold tracking-tight text-foreground mb-4", children: title }),
|
|
1296
|
-
subtitle && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-lg md:text-xl text-muted-foreground max-w-2xl mx-auto", children: subtitle })
|
|
1297
|
-
] }) });
|
|
1298
|
-
}
|
|
1299
|
-
function stripLeadingHeading(content, format) {
|
|
1300
|
-
if (format === "html") {
|
|
1301
|
-
return content.replace(/^\s*<h1\b[^>]*>[\s\S]*?<\/h1>\s*/i, "");
|
|
1302
|
-
}
|
|
1303
|
-
return content.replace(/^\s*#\s+.*(?:\r?\n)*/, "");
|
|
1304
|
-
}
|
|
1305
|
-
function PartnerContentSection({
|
|
1306
|
-
page
|
|
1307
|
-
}) {
|
|
1308
|
-
return /* @__PURE__ */ jsxRuntime.jsx("section", { className: "py-12 md:py-16", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
1309
|
-
"div",
|
|
1310
|
-
{
|
|
1311
|
-
className: [
|
|
1312
|
-
"mx-auto max-w-3xl px-4",
|
|
1313
|
-
// Headings
|
|
1314
|
-
"[&_h1]:text-3xl [&_h1]:font-bold [&_h1]:tracking-tight [&_h1]:mt-0 [&_h1]:mb-6",
|
|
1315
|
-
"[&_h2]:text-2xl [&_h2]:font-bold [&_h2]:tracking-tight [&_h2]:mt-10 [&_h2]:mb-4",
|
|
1316
|
-
"[&_h3]:text-xl [&_h3]:font-semibold [&_h3]:mt-7 [&_h3]:mb-3",
|
|
1317
|
-
"[&_h4]:text-lg [&_h4]:font-semibold [&_h4]:mt-6 [&_h4]:mb-2",
|
|
1318
|
-
// Block elements
|
|
1319
|
-
"[&_p]:my-4 [&_p]:leading-relaxed [&_p]:text-base",
|
|
1320
|
-
"[&_ul]:my-4 [&_ul]:pl-6 [&_ul]:list-disc [&_ul]:space-y-1",
|
|
1321
|
-
"[&_ol]:my-4 [&_ol]:pl-6 [&_ol]:list-decimal [&_ol]:space-y-1",
|
|
1322
|
-
"[&_li]:leading-relaxed",
|
|
1323
|
-
"[&_li>p]:my-1",
|
|
1324
|
-
"[&_blockquote]:border-l-4 [&_blockquote]:border-border [&_blockquote]:pl-4 [&_blockquote]:italic [&_blockquote]:my-4 [&_blockquote]:text-muted-foreground",
|
|
1325
|
-
"[&_hr]:my-8 [&_hr]:border-border",
|
|
1326
|
-
// Inline elements
|
|
1327
|
-
"[&_strong]:font-semibold [&_strong]:text-foreground",
|
|
1328
|
-
"[&_em]:italic",
|
|
1329
|
-
"[&_a]:underline [&_a]:underline-offset-2 [&_a]:text-accent hover:[&_a]:text-accent/80 [&_a]:transition-colors",
|
|
1330
|
-
"[&_code]:rounded [&_code]:bg-muted [&_code]:px-1.5 [&_code]:py-0.5 [&_code]:text-sm [&_code]:font-mono",
|
|
1331
|
-
"[&_pre]:my-4 [&_pre]:overflow-x-auto [&_pre]:rounded-lg [&_pre]:bg-muted [&_pre]:p-4 [&_pre]:text-sm",
|
|
1332
|
-
"[&_pre>code]:bg-transparent [&_pre>code]:p-0"
|
|
1333
|
-
].join(" "),
|
|
1334
|
-
children: page.content && /* @__PURE__ */ jsxRuntime.jsx(
|
|
1335
|
-
ContentRenderer,
|
|
1336
|
-
{
|
|
1337
|
-
content: stripLeadingHeading(page.content, page.contentFormat),
|
|
1338
|
-
format: (
|
|
1339
|
-
// Validate before passing; unknown formats fall through to
|
|
1340
|
-
// ContentRenderer's default behavior (markdown rendering)
|
|
1341
|
-
// rather than getting silently force-cast to an
|
|
1342
|
-
// unsupported variant.
|
|
1343
|
-
page.contentFormat === "markdown" || page.contentFormat === "html" ? page.contentFormat : void 0
|
|
1344
|
-
)
|
|
1345
|
-
}
|
|
1346
|
-
)
|
|
1347
|
-
}
|
|
1348
|
-
) });
|
|
1349
|
-
}
|
|
1350
|
-
function PartnerDocumentCard({ doc }) {
|
|
1351
|
-
const sizeLabel = formatFileSize(doc.fileSize);
|
|
1352
|
-
const ext = doc.fileName?.split(".").pop()?.toUpperCase();
|
|
1353
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("article", { className: "flex flex-col gap-3 rounded-xl border border-border bg-card p-5 shadow-sm hover:shadow-md transition-shadow", children: [
|
|
1354
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start gap-3", children: [
|
|
1355
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "rounded-lg bg-muted p-2.5 shrink-0", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.FileText, { size: 20, className: "text-muted-foreground" }) }),
|
|
1356
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0 flex-1", children: [
|
|
1357
|
-
/* @__PURE__ */ jsxRuntime.jsx("h3", { className: "font-semibold text-foreground truncate", children: doc.title }),
|
|
1358
|
-
doc.version && /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-xs text-muted-foreground mt-0.5", children: [
|
|
1359
|
-
"Version ",
|
|
1360
|
-
doc.version
|
|
1361
|
-
] })
|
|
1362
|
-
] })
|
|
1363
|
-
] }),
|
|
1364
|
-
doc.description && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground line-clamp-3", children: doc.description }),
|
|
1365
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between gap-3 mt-auto pt-2 border-t border-border/60", children: [
|
|
1366
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-xs text-muted-foreground flex items-center gap-2", children: [
|
|
1367
|
-
ext && /* @__PURE__ */ jsxRuntime.jsx("span", { children: ext }),
|
|
1368
|
-
sizeLabel && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
1369
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": true, children: "\xB7" }),
|
|
1370
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { children: sizeLabel })
|
|
1371
|
-
] })
|
|
1372
|
-
] }),
|
|
1373
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
1374
|
-
"a",
|
|
1375
|
-
{
|
|
1376
|
-
href: doc.fileUrl,
|
|
1377
|
-
download: doc.fileName ?? void 0,
|
|
1378
|
-
onClick: (e) => void downloadDocument(e, doc),
|
|
1379
|
-
className: "inline-flex items-center gap-1.5 rounded-md border border-border bg-background px-3 py-1.5 text-sm font-medium text-foreground hover:bg-muted transition-colors",
|
|
1380
|
-
children: [
|
|
1381
|
-
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Download, { size: 14 }),
|
|
1382
|
-
"Download"
|
|
1383
|
-
]
|
|
1384
|
-
}
|
|
1385
|
-
)
|
|
1386
|
-
] })
|
|
1387
|
-
] });
|
|
870
|
+
return /* @__PURE__ */ jsxRuntime.jsx(ProductContext.Provider, { value, children });
|
|
1388
871
|
}
|
|
1389
|
-
function
|
|
1390
|
-
|
|
1391
|
-
subtitle,
|
|
1392
|
-
docs
|
|
1393
|
-
}) {
|
|
1394
|
-
return /* @__PURE__ */ jsxRuntime.jsx("section", { className: "py-12 md:py-16 bg-muted/20", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "container mx-auto px-4 max-w-5xl", children: [
|
|
1395
|
-
/* @__PURE__ */ jsxRuntime.jsxs("header", { className: "mb-8 md:mb-10 text-center", children: [
|
|
1396
|
-
/* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-2xl md:text-3xl font-bold text-foreground mb-2", children: title }),
|
|
1397
|
-
subtitle && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-muted-foreground max-w-2xl mx-auto", children: subtitle })
|
|
1398
|
-
] }),
|
|
1399
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4", children: docs.map((doc) => /* @__PURE__ */ jsxRuntime.jsx(PartnerDocumentCard, { doc }, doc.id)) })
|
|
1400
|
-
] }) });
|
|
872
|
+
function useProduct() {
|
|
873
|
+
return React.useContext(ProductContext);
|
|
1401
874
|
}
|
|
1402
|
-
|
|
1403
|
-
value: "partnership",
|
|
1404
|
-
label: "Partner program"
|
|
1405
|
-
};
|
|
1406
|
-
function PartnersPage(props) {
|
|
1407
|
-
const {
|
|
1408
|
-
productName: productNameProp,
|
|
1409
|
-
heroTitle = "Partner with us",
|
|
1410
|
-
heroSubtitle,
|
|
1411
|
-
contentTag = "partner",
|
|
1412
|
-
contentSlug,
|
|
1413
|
-
documentsTag = "partner",
|
|
1414
|
-
documentsTitle = "Partner Documents",
|
|
1415
|
-
documentsSubtitle,
|
|
1416
|
-
showContactForm = true,
|
|
1417
|
-
contactCategory,
|
|
1418
|
-
formTitle = "Become a partner",
|
|
1419
|
-
formSubtitle,
|
|
1420
|
-
contactEmail,
|
|
1421
|
-
contactPhone,
|
|
1422
|
-
officeAddress,
|
|
1423
|
-
responseTime,
|
|
1424
|
-
recaptchaSiteKey,
|
|
1425
|
-
additionalOptions,
|
|
1426
|
-
seoTitle,
|
|
1427
|
-
seoDescription,
|
|
1428
|
-
seoKeywords,
|
|
1429
|
-
className
|
|
1430
|
-
} = props;
|
|
1431
|
-
const client = useWebSDK();
|
|
1432
|
-
const config = useWebSDKConfig();
|
|
875
|
+
function useProductConfig() {
|
|
1433
876
|
const { product } = useProduct();
|
|
1434
|
-
const
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
}
|
|
1446
|
-
let cancelled = false;
|
|
1447
|
-
setLoading(true);
|
|
1448
|
-
void Promise.allSettled([
|
|
1449
|
-
client.query(PARTNER_CONTENT_QUERY, { productId: resolvedProductId, limit: 50 }),
|
|
1450
|
-
client.query(
|
|
1451
|
-
PARTNER_DOCUMENTS_QUERY,
|
|
1452
|
-
{ productId: resolvedProductId, limit: 50 }
|
|
1453
|
-
)
|
|
1454
|
-
]).then(([contentResult, docsResult]) => {
|
|
1455
|
-
if (cancelled) return;
|
|
1456
|
-
if (contentResult.status === "rejected") {
|
|
1457
|
-
setContentPage(null);
|
|
1458
|
-
} else if (contentResult.status === "fulfilled") {
|
|
1459
|
-
const items = contentResult.value.data?.publicContentPages?.items ?? [];
|
|
1460
|
-
const match = contentSlug ? items.find((p) => p.slug === contentSlug) : items.find((p) => p.tags?.includes(contentTag));
|
|
1461
|
-
setContentPage(match ?? null);
|
|
1462
|
-
}
|
|
1463
|
-
if (docsResult.status === "rejected") {
|
|
1464
|
-
setDocs([]);
|
|
1465
|
-
} else if (docsResult.status === "fulfilled") {
|
|
1466
|
-
const items = docsResult.value.data?.publicPressKitAssets?.items ?? [];
|
|
1467
|
-
const filtered = items.filter(
|
|
1468
|
-
(d) => d.category === "DOCUMENT" && d.tags?.includes(documentsTag) && !!d.fileUrl
|
|
1469
|
-
).sort(
|
|
1470
|
-
(a, b) => a.sortOrder - b.sortOrder
|
|
1471
|
-
);
|
|
1472
|
-
setDocs(filtered);
|
|
1473
|
-
}
|
|
1474
|
-
setLoading(false);
|
|
1475
|
-
});
|
|
1476
|
-
return () => {
|
|
1477
|
-
cancelled = true;
|
|
1478
|
-
};
|
|
1479
|
-
}, [client, resolvedProductId, contentSlug, contentTag, documentsTag]);
|
|
1480
|
-
let partnerCategories;
|
|
1481
|
-
if (contactCategory === false) {
|
|
1482
|
-
partnerCategories = [];
|
|
1483
|
-
} else if (Array.isArray(contactCategory)) {
|
|
1484
|
-
partnerCategories = contactCategory.length > 0 ? contactCategory : [DEFAULT_PARTNER_CATEGORY];
|
|
1485
|
-
} else {
|
|
1486
|
-
partnerCategories = [contactCategory ?? DEFAULT_PARTNER_CATEGORY];
|
|
1487
|
-
}
|
|
1488
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className, children: [
|
|
1489
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1490
|
-
PageHead,
|
|
1491
|
-
{
|
|
1492
|
-
title: seoTitle ?? `${heroTitle} \u2014 ${resolvedProductName}`,
|
|
1493
|
-
description: seoDescription ?? heroSubtitle ?? `Join the ${resolvedProductName} partner programme.`,
|
|
1494
|
-
keywords: seoKeywords
|
|
1495
|
-
}
|
|
1496
|
-
),
|
|
1497
|
-
/* @__PURE__ */ jsxRuntime.jsx(PartnerHero, { title: heroTitle, subtitle: heroSubtitle }),
|
|
1498
|
-
!loading && contentPage && /* @__PURE__ */ jsxRuntime.jsx(PartnerContentSection, { page: contentPage }),
|
|
1499
|
-
!loading && docs.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(
|
|
1500
|
-
PartnerDocumentsSection,
|
|
1501
|
-
{
|
|
1502
|
-
title: documentsTitle,
|
|
1503
|
-
subtitle: documentsSubtitle,
|
|
1504
|
-
docs
|
|
1505
|
-
}
|
|
1506
|
-
),
|
|
1507
|
-
showContactForm && /* @__PURE__ */ jsxRuntime.jsx(
|
|
1508
|
-
ContactPage,
|
|
1509
|
-
{
|
|
1510
|
-
productName: resolvedProductName,
|
|
1511
|
-
heroTitle: formTitle,
|
|
1512
|
-
headingLevel: 2,
|
|
1513
|
-
heroDescription: formSubtitle ?? `Tell us about your organisation and a partner manager from ${resolvedProductName} will be in touch.`,
|
|
1514
|
-
contactEmail,
|
|
1515
|
-
contactPhone,
|
|
1516
|
-
officeAddress,
|
|
1517
|
-
responseTime,
|
|
1518
|
-
recaptchaSiteKey,
|
|
1519
|
-
additionalOptions,
|
|
1520
|
-
categories: partnerCategories,
|
|
1521
|
-
categoryLabel: "Partnership type",
|
|
1522
|
-
categoryRequired: partnerCategories.length > 0
|
|
1523
|
-
}
|
|
1524
|
-
)
|
|
1525
|
-
] });
|
|
1526
|
-
}
|
|
1527
|
-
var ALLOWED_RESUME_TYPES = {
|
|
1528
|
-
"application/pdf": "PDF",
|
|
1529
|
-
"application/msword": "DOC",
|
|
1530
|
-
"application/vnd.openxmlformats-officedocument.wordprocessingml.document": "DOCX"
|
|
1531
|
-
};
|
|
1532
|
-
var EXT_TO_MIME = {
|
|
1533
|
-
pdf: "application/pdf",
|
|
1534
|
-
doc: "application/msword",
|
|
1535
|
-
docx: "application/vnd.openxmlformats-officedocument.wordprocessingml.document"
|
|
1536
|
-
};
|
|
1537
|
-
var DEFAULT_MAX_RESUME_BYTES = 10 * 1024 * 1024;
|
|
1538
|
-
function resolveResumeType(file) {
|
|
1539
|
-
if (ALLOWED_RESUME_TYPES[file.type]) return file.type;
|
|
1540
|
-
const ext = file.name.split(".").pop()?.toLowerCase();
|
|
1541
|
-
return ext && EXT_TO_MIME[ext] ? EXT_TO_MIME[ext] : null;
|
|
877
|
+
const config = useWebSDKConfig();
|
|
878
|
+
return {
|
|
879
|
+
// Prefer product-level config, fall back to SDK config
|
|
880
|
+
recaptchaSiteKey: product?.recaptchaSiteKey ?? config.recaptchaSiteKey ?? null,
|
|
881
|
+
rybbitSiteId: product?.rybbitSiteId ?? config.rybbitSiteId ?? null,
|
|
882
|
+
contactEmail: product?.contactEmail ?? null,
|
|
883
|
+
supportEmail: product?.supportEmail ?? null,
|
|
884
|
+
notificationEmail: product?.notificationEmail ?? product?.contactEmail ?? null,
|
|
885
|
+
productName: product?.name ?? null,
|
|
886
|
+
logoUrl: product?.logoUrl ?? null
|
|
887
|
+
};
|
|
1542
888
|
}
|
|
1543
|
-
var
|
|
889
|
+
var newsletterSchema = zod.z.object({
|
|
890
|
+
email: zod.z.string().email("Please enter a valid email address")
|
|
891
|
+
});
|
|
892
|
+
var SUBSCRIBE_MUTATION = (
|
|
1544
893
|
/* GraphQL */
|
|
1545
894
|
`
|
|
1546
|
-
mutation
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
895
|
+
mutation Subscribe($input: SubscribeInput!) {
|
|
896
|
+
subscribe(input: $input) {
|
|
897
|
+
id
|
|
898
|
+
email
|
|
899
|
+
alreadySubscribed
|
|
900
|
+
status
|
|
901
|
+
subscribedAt
|
|
1550
902
|
}
|
|
1551
903
|
}
|
|
1552
904
|
`
|
|
1553
905
|
);
|
|
1554
|
-
function
|
|
1555
|
-
return new Promise((resolve, reject) => {
|
|
1556
|
-
const reader = new FileReader();
|
|
1557
|
-
reader.onload = () => {
|
|
1558
|
-
const result = typeof reader.result === "string" ? reader.result : "";
|
|
1559
|
-
const comma = result.indexOf(",");
|
|
1560
|
-
resolve(comma >= 0 ? result.slice(comma + 1) : result);
|
|
1561
|
-
};
|
|
1562
|
-
reader.onerror = () => reject(new Error("Failed to read the resume file"));
|
|
1563
|
-
reader.readAsDataURL(file);
|
|
1564
|
-
});
|
|
1565
|
-
}
|
|
1566
|
-
async function submitApplication(client, data, productId) {
|
|
906
|
+
async function subscribeNewsletter(client, email, productId, recaptchaToken) {
|
|
1567
907
|
try {
|
|
1568
|
-
const result = await client.mutate(
|
|
908
|
+
const result = await client.mutate(SUBSCRIBE_MUTATION, {
|
|
1569
909
|
input: {
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
...data.phone ? { phone: data.phone } : {},
|
|
1579
|
-
...data.linkedinUrl ? { linkedinUrl: data.linkedinUrl } : {},
|
|
1580
|
-
...data.portfolioUrl ? { portfolioUrl: data.portfolioUrl } : {},
|
|
1581
|
-
...data.coverLetter ? { coverLetter: data.coverLetter } : {},
|
|
1582
|
-
...data.recaptchaToken ? { recaptchaToken: data.recaptchaToken } : {}
|
|
910
|
+
email: email.trim().toLowerCase(),
|
|
911
|
+
platformProductId: productId,
|
|
912
|
+
metadata: {
|
|
913
|
+
source: "website",
|
|
914
|
+
referrer: typeof window !== "undefined" ? window.location.href : "",
|
|
915
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
916
|
+
...recaptchaToken ? { recaptchaToken } : {}
|
|
917
|
+
}
|
|
1583
918
|
}
|
|
1584
919
|
});
|
|
1585
920
|
if (result.errors?.length) {
|
|
921
|
+
const errorMsg = result.errors[0]?.message ?? "Subscription failed";
|
|
922
|
+
const code = result.errors[0]?.extensions?.code;
|
|
923
|
+
if (code === "DUPLICATE_SUBSCRIBER" || errorMsg.toLowerCase().includes("already subscribed")) {
|
|
924
|
+
return {
|
|
925
|
+
success: true,
|
|
926
|
+
message: "You are already part of this newsletter."
|
|
927
|
+
};
|
|
928
|
+
}
|
|
929
|
+
return { success: false, message: errorMsg };
|
|
930
|
+
}
|
|
931
|
+
if (result.data?.subscribe?.alreadySubscribed) {
|
|
1586
932
|
return {
|
|
1587
|
-
success:
|
|
1588
|
-
message:
|
|
933
|
+
success: true,
|
|
934
|
+
message: "You are already part of this newsletter."
|
|
1589
935
|
};
|
|
1590
936
|
}
|
|
1591
|
-
|
|
1592
|
-
return response?.success ? { success: true, message: response.message || "Application submitted!" } : {
|
|
1593
|
-
success: false,
|
|
1594
|
-
message: response?.message || "Failed to submit application"
|
|
1595
|
-
};
|
|
937
|
+
return { success: true };
|
|
1596
938
|
} catch (error) {
|
|
1597
|
-
console.error("
|
|
939
|
+
console.error("Newsletter subscription error:", error);
|
|
1598
940
|
return { success: false, message: "Network error. Please try again." };
|
|
1599
941
|
}
|
|
1600
942
|
}
|
|
1601
|
-
function
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
}) {
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
const { product } = useProduct();
|
|
1615
|
-
const resolvedProductId = product?.id || config.productId;
|
|
1616
|
-
const displayName = productName ?? product?.name ?? config.productId;
|
|
1617
|
-
const effectiveRecaptchaSiteKey = recaptchaSiteKey ?? product?.recaptchaSiteKey ?? config.recaptchaSiteKey ?? "";
|
|
1618
|
-
const recaptchaRef = React.useRef(null);
|
|
1619
|
-
const [isSubmitting, setIsSubmitting] = React.useState(false);
|
|
1620
|
-
const [submitted, setSubmitted] = React.useState(false);
|
|
1621
|
-
const [resumeFile, setResumeFile] = React.useState(null);
|
|
1622
|
-
const [resumeType, setResumeType] = React.useState(null);
|
|
1623
|
-
const [resumeError, setResumeError] = React.useState(null);
|
|
1624
|
-
const maxMb = Math.floor(maxResumeBytes / (1024 * 1024));
|
|
1625
|
-
const clearResume = () => {
|
|
1626
|
-
setResumeFile(null);
|
|
1627
|
-
setResumeType(null);
|
|
1628
|
-
};
|
|
1629
|
-
const handleFileChange = (e) => {
|
|
1630
|
-
setResumeError(null);
|
|
1631
|
-
const file = e.target.files?.[0] ?? null;
|
|
1632
|
-
if (!file) {
|
|
1633
|
-
clearResume();
|
|
1634
|
-
return;
|
|
943
|
+
function useRateLimit(maxAttempts, windowMinutes) {
|
|
944
|
+
const rateLimitRef = React.useRef(null);
|
|
945
|
+
const [isLimited, setIsLimited] = React.useState(false);
|
|
946
|
+
const [resetTime, setResetTime] = React.useState(null);
|
|
947
|
+
React.useEffect(() => {
|
|
948
|
+
const state = rateLimitRef.current;
|
|
949
|
+
if (state && Date.now() > state.resetTime) {
|
|
950
|
+
rateLimitRef.current = null;
|
|
951
|
+
setIsLimited(false);
|
|
952
|
+
setResetTime(null);
|
|
953
|
+
} else if (state && state.attempts >= maxAttempts) {
|
|
954
|
+
setIsLimited(true);
|
|
955
|
+
setResetTime(new Date(state.resetTime));
|
|
1635
956
|
}
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
957
|
+
}, [maxAttempts]);
|
|
958
|
+
function recordAttempt() {
|
|
959
|
+
const now = Date.now();
|
|
960
|
+
const state = rateLimitRef.current;
|
|
961
|
+
if (state && now > state.resetTime) {
|
|
962
|
+
rateLimitRef.current = null;
|
|
1641
963
|
}
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
};
|
|
1650
|
-
const handleSubmit = async (e) => {
|
|
1651
|
-
e.preventDefault();
|
|
1652
|
-
const recaptchaToken = recaptchaRef.current?.getValue() ?? "";
|
|
1653
|
-
if (effectiveRecaptchaSiteKey && !recaptchaToken) {
|
|
1654
|
-
sonner.toast.error("Please complete the reCAPTCHA verification");
|
|
1655
|
-
return;
|
|
1656
|
-
}
|
|
1657
|
-
if (!resumeFile) {
|
|
1658
|
-
setResumeError("Please attach your resume (PDF, DOC, or DOCX).");
|
|
1659
|
-
return;
|
|
1660
|
-
}
|
|
1661
|
-
if (!resolvedProductId) {
|
|
1662
|
-
console.warn(
|
|
1663
|
-
"[CareersApplyForm] No productId resolved \u2014 submission will not be attributed to a product."
|
|
1664
|
-
);
|
|
1665
|
-
}
|
|
1666
|
-
const form = e.currentTarget;
|
|
1667
|
-
setIsSubmitting(true);
|
|
1668
|
-
try {
|
|
1669
|
-
const formData = new FormData(form);
|
|
1670
|
-
const resumeBase64 = await fileToBase64(resumeFile);
|
|
1671
|
-
const result = await submitApplication(
|
|
1672
|
-
client,
|
|
1673
|
-
{
|
|
1674
|
-
firstName: formData.get("firstName") ?? "",
|
|
1675
|
-
lastName: formData.get("lastName") ?? "",
|
|
1676
|
-
email: formData.get("email") ?? "",
|
|
1677
|
-
phone: formData.get("phone") || void 0,
|
|
1678
|
-
position: formData.get("position") ?? "",
|
|
1679
|
-
linkedinUrl: formData.get("linkedinUrl") || void 0,
|
|
1680
|
-
portfolioUrl: formData.get("portfolioUrl") || void 0,
|
|
1681
|
-
coverLetter: formData.get("coverLetter") || void 0,
|
|
1682
|
-
resumeBase64,
|
|
1683
|
-
resumeFileName: resumeFile.name,
|
|
1684
|
-
// Use the resolved type (handles browsers that report an empty
|
|
1685
|
-
// file.type for .doc/.docx).
|
|
1686
|
-
resumeContentType: resumeType ?? resumeFile.type,
|
|
1687
|
-
recaptchaToken
|
|
1688
|
-
},
|
|
1689
|
-
resolvedProductId
|
|
1690
|
-
);
|
|
1691
|
-
if (result.success) {
|
|
1692
|
-
setSubmitted(true);
|
|
1693
|
-
sonner.toast.success(result.message ?? "Application submitted!");
|
|
1694
|
-
} else {
|
|
1695
|
-
sonner.toast.error(result.message ?? "Failed to submit application");
|
|
1696
|
-
recaptchaRef.current?.reset();
|
|
1697
|
-
}
|
|
1698
|
-
} catch (err) {
|
|
1699
|
-
console.error("Careers form error:", err);
|
|
1700
|
-
sonner.toast.error("Something went wrong. Please try again.");
|
|
1701
|
-
recaptchaRef.current?.reset();
|
|
1702
|
-
} finally {
|
|
1703
|
-
setIsSubmitting(false);
|
|
1704
|
-
}
|
|
1705
|
-
};
|
|
1706
|
-
const Heading = headingLevel === 2 ? "h2" : "h1";
|
|
1707
|
-
const headingClass = headingLevel === 2 ? "text-2xl md:text-3xl font-bold mb-3" : "text-3xl sm:text-4xl md:text-5xl font-bold mb-4 sm:mb-6";
|
|
1708
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className, children: [
|
|
1709
|
-
seo && /* @__PURE__ */ jsxRuntime.jsx(
|
|
1710
|
-
PageHead,
|
|
1711
|
-
{
|
|
1712
|
-
title: seo.title ?? `Careers \u2014 ${displayName}`,
|
|
1713
|
-
description: seo.description ?? heroDescription,
|
|
1714
|
-
keywords: seo.keywords
|
|
1715
|
-
}
|
|
1716
|
-
),
|
|
1717
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1718
|
-
"section",
|
|
1719
|
-
{
|
|
1720
|
-
className: headingLevel === 2 ? "py-12 md:py-16" : "bg-gradient-to-b from-background to-muted/20 py-12 sm:py-16 md:py-20 lg:py-24",
|
|
1721
|
-
children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "max-w-3xl mx-auto px-4 sm:px-6 lg:px-8", children: [
|
|
1722
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center mb-8 sm:mb-10", children: [
|
|
1723
|
-
/* @__PURE__ */ jsxRuntime.jsx(Heading, { className: headingClass, children: heroTitle }),
|
|
1724
|
-
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-lg text-muted-foreground leading-relaxed", children: heroDescription })
|
|
1725
|
-
] }),
|
|
1726
|
-
submitted ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center rounded-xl border border-border bg-card p-10", children: [
|
|
1727
|
-
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.CheckCircle, { className: "mx-auto mb-4 h-12 w-12 text-accent" }),
|
|
1728
|
-
/* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-xl font-semibold text-foreground mb-2", children: "Application received" }),
|
|
1729
|
-
/* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-muted-foreground", children: [
|
|
1730
|
-
"Thanks for applying to ",
|
|
1731
|
-
displayName,
|
|
1732
|
-
". We've emailed you a confirmation and our team will be in touch."
|
|
1733
|
-
] })
|
|
1734
|
-
] }) : /* @__PURE__ */ jsxRuntime.jsxs("form", { onSubmit: handleSubmit, className: "space-y-5", children: [
|
|
1735
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-1 sm:grid-cols-2 gap-5", children: [
|
|
1736
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
1737
|
-
/* @__PURE__ */ jsxRuntime.jsx(Label, { htmlFor: "firstName", children: "First name *" }),
|
|
1738
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1739
|
-
Input,
|
|
1740
|
-
{
|
|
1741
|
-
id: "firstName",
|
|
1742
|
-
name: "firstName",
|
|
1743
|
-
required: true,
|
|
1744
|
-
placeholder: "Ada"
|
|
1745
|
-
}
|
|
1746
|
-
)
|
|
1747
|
-
] }),
|
|
1748
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
1749
|
-
/* @__PURE__ */ jsxRuntime.jsx(Label, { htmlFor: "lastName", children: "Last name *" }),
|
|
1750
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1751
|
-
Input,
|
|
1752
|
-
{
|
|
1753
|
-
id: "lastName",
|
|
1754
|
-
name: "lastName",
|
|
1755
|
-
required: true,
|
|
1756
|
-
placeholder: "Lovelace"
|
|
1757
|
-
}
|
|
1758
|
-
)
|
|
1759
|
-
] })
|
|
1760
|
-
] }),
|
|
1761
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-1 sm:grid-cols-2 gap-5", children: [
|
|
1762
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
1763
|
-
/* @__PURE__ */ jsxRuntime.jsx(Label, { htmlFor: "email", children: "Email *" }),
|
|
1764
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1765
|
-
Input,
|
|
1766
|
-
{
|
|
1767
|
-
id: "email",
|
|
1768
|
-
name: "email",
|
|
1769
|
-
type: "email",
|
|
1770
|
-
required: true,
|
|
1771
|
-
placeholder: "ada@example.com"
|
|
1772
|
-
}
|
|
1773
|
-
)
|
|
1774
|
-
] }),
|
|
1775
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
1776
|
-
/* @__PURE__ */ jsxRuntime.jsx(Label, { htmlFor: "phone", children: "Phone" }),
|
|
1777
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1778
|
-
Input,
|
|
1779
|
-
{
|
|
1780
|
-
id: "phone",
|
|
1781
|
-
name: "phone",
|
|
1782
|
-
placeholder: "+1 555 010 0100"
|
|
1783
|
-
}
|
|
1784
|
-
)
|
|
1785
|
-
] })
|
|
1786
|
-
] }),
|
|
1787
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
1788
|
-
/* @__PURE__ */ jsxRuntime.jsx(Label, { htmlFor: "position", children: "Position *" }),
|
|
1789
|
-
positions.length > 0 ? /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1790
|
-
"select",
|
|
1791
|
-
{
|
|
1792
|
-
id: "position",
|
|
1793
|
-
name: "position",
|
|
1794
|
-
required: true,
|
|
1795
|
-
defaultValue: "",
|
|
1796
|
-
className: "flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-ring",
|
|
1797
|
-
children: [
|
|
1798
|
-
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "", disabled: true, children: "Select a role" }),
|
|
1799
|
-
positions.map((p) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: p, children: p }, p))
|
|
1800
|
-
]
|
|
1801
|
-
}
|
|
1802
|
-
) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
1803
|
-
Input,
|
|
1804
|
-
{
|
|
1805
|
-
id: "position",
|
|
1806
|
-
name: "position",
|
|
1807
|
-
required: true,
|
|
1808
|
-
placeholder: "Role you're applying for"
|
|
1809
|
-
}
|
|
1810
|
-
)
|
|
1811
|
-
] }),
|
|
1812
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-1 sm:grid-cols-2 gap-5", children: [
|
|
1813
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
1814
|
-
/* @__PURE__ */ jsxRuntime.jsx(Label, { htmlFor: "linkedinUrl", children: "LinkedIn" }),
|
|
1815
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1816
|
-
Input,
|
|
1817
|
-
{
|
|
1818
|
-
id: "linkedinUrl",
|
|
1819
|
-
name: "linkedinUrl",
|
|
1820
|
-
type: "url",
|
|
1821
|
-
placeholder: "https://linkedin.com/in/\u2026"
|
|
1822
|
-
}
|
|
1823
|
-
)
|
|
1824
|
-
] }),
|
|
1825
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
1826
|
-
/* @__PURE__ */ jsxRuntime.jsx(Label, { htmlFor: "portfolioUrl", children: "Portfolio / GitHub" }),
|
|
1827
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1828
|
-
Input,
|
|
1829
|
-
{
|
|
1830
|
-
id: "portfolioUrl",
|
|
1831
|
-
name: "portfolioUrl",
|
|
1832
|
-
type: "url",
|
|
1833
|
-
placeholder: "https://\u2026"
|
|
1834
|
-
}
|
|
1835
|
-
)
|
|
1836
|
-
] })
|
|
1837
|
-
] }),
|
|
1838
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
1839
|
-
/* @__PURE__ */ jsxRuntime.jsx(Label, { htmlFor: "coverLetter", children: "Cover letter" }),
|
|
1840
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1841
|
-
Textarea,
|
|
1842
|
-
{
|
|
1843
|
-
id: "coverLetter",
|
|
1844
|
-
name: "coverLetter",
|
|
1845
|
-
rows: 5,
|
|
1846
|
-
placeholder: "Tell us why you'd be a great fit\u2026"
|
|
1847
|
-
}
|
|
1848
|
-
)
|
|
1849
|
-
] }),
|
|
1850
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
1851
|
-
/* @__PURE__ */ jsxRuntime.jsxs(Label, { htmlFor: "resume", children: [
|
|
1852
|
-
"Resume * (PDF, DOC, or DOCX \u2014 max ",
|
|
1853
|
-
maxMb,
|
|
1854
|
-
"MB)"
|
|
1855
|
-
] }),
|
|
1856
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
1857
|
-
"label",
|
|
1858
|
-
{
|
|
1859
|
-
htmlFor: "resume",
|
|
1860
|
-
className: "mt-1 flex cursor-pointer items-center gap-3 rounded-md border border-dashed border-input bg-background px-4 py-3 text-sm hover:bg-muted/40 transition-colors",
|
|
1861
|
-
children: [
|
|
1862
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1863
|
-
lucideReact.Upload,
|
|
1864
|
-
{
|
|
1865
|
-
className: "h-5 w-5 text-muted-foreground",
|
|
1866
|
-
"aria-hidden": "true"
|
|
1867
|
-
}
|
|
1868
|
-
),
|
|
1869
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-foreground", children: resumeFile ? resumeFile.name : "Choose a file\u2026" }),
|
|
1870
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1871
|
-
"input",
|
|
1872
|
-
{
|
|
1873
|
-
id: "resume",
|
|
1874
|
-
name: "resume",
|
|
1875
|
-
type: "file",
|
|
1876
|
-
accept: ".pdf,.doc,.docx,application/pdf,application/msword,application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
1877
|
-
className: "sr-only",
|
|
1878
|
-
"aria-describedby": resumeError ? "resume-error" : void 0,
|
|
1879
|
-
"aria-invalid": resumeError ? true : void 0,
|
|
1880
|
-
onChange: handleFileChange
|
|
1881
|
-
}
|
|
1882
|
-
)
|
|
1883
|
-
]
|
|
1884
|
-
}
|
|
1885
|
-
),
|
|
1886
|
-
resumeError && /* @__PURE__ */ jsxRuntime.jsx(
|
|
1887
|
-
"p",
|
|
1888
|
-
{
|
|
1889
|
-
id: "resume-error",
|
|
1890
|
-
role: "alert",
|
|
1891
|
-
className: "mt-1 text-sm text-destructive",
|
|
1892
|
-
children: resumeError
|
|
1893
|
-
}
|
|
1894
|
-
)
|
|
1895
|
-
] }),
|
|
1896
|
-
effectiveRecaptchaSiteKey && /* @__PURE__ */ jsxRuntime.jsx(
|
|
1897
|
-
ReCAPTCHA3__default.default,
|
|
1898
|
-
{
|
|
1899
|
-
ref: recaptchaRef,
|
|
1900
|
-
sitekey: effectiveRecaptchaSiteKey
|
|
1901
|
-
}
|
|
1902
|
-
),
|
|
1903
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1904
|
-
Button,
|
|
1905
|
-
{
|
|
1906
|
-
type: "submit",
|
|
1907
|
-
size: "lg",
|
|
1908
|
-
disabled: isSubmitting,
|
|
1909
|
-
className: "w-full sm:w-auto",
|
|
1910
|
-
children: isSubmitting ? "Submitting\u2026" : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
1911
|
-
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Send, { className: "mr-2 h-4 w-4" }),
|
|
1912
|
-
" Submit application"
|
|
1913
|
-
] })
|
|
1914
|
-
}
|
|
1915
|
-
)
|
|
1916
|
-
] })
|
|
1917
|
-
] })
|
|
1918
|
-
}
|
|
1919
|
-
)
|
|
1920
|
-
] });
|
|
1921
|
-
}
|
|
1922
|
-
var newsletterSchema = zod.z.object({
|
|
1923
|
-
email: zod.z.string().email("Please enter a valid email address")
|
|
1924
|
-
});
|
|
1925
|
-
var SUBSCRIBE_MUTATION = (
|
|
1926
|
-
/* GraphQL */
|
|
1927
|
-
`
|
|
1928
|
-
mutation Subscribe($input: SubscribeInput!) {
|
|
1929
|
-
subscribe(input: $input) {
|
|
1930
|
-
id
|
|
1931
|
-
email
|
|
1932
|
-
alreadySubscribed
|
|
1933
|
-
status
|
|
1934
|
-
subscribedAt
|
|
1935
|
-
}
|
|
1936
|
-
}
|
|
1937
|
-
`
|
|
1938
|
-
);
|
|
1939
|
-
async function subscribeNewsletter(client, email, productId, recaptchaToken) {
|
|
1940
|
-
try {
|
|
1941
|
-
const result = await client.mutate(SUBSCRIBE_MUTATION, {
|
|
1942
|
-
input: {
|
|
1943
|
-
email: email.trim().toLowerCase(),
|
|
1944
|
-
platformProductId: productId,
|
|
1945
|
-
metadata: {
|
|
1946
|
-
source: "website",
|
|
1947
|
-
referrer: typeof window !== "undefined" ? window.location.href : "",
|
|
1948
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1949
|
-
...recaptchaToken ? { recaptchaToken } : {}
|
|
1950
|
-
}
|
|
1951
|
-
}
|
|
1952
|
-
});
|
|
1953
|
-
if (result.errors?.length) {
|
|
1954
|
-
const errorMsg = result.errors[0]?.message ?? "Subscription failed";
|
|
1955
|
-
const code = result.errors[0]?.extensions?.code;
|
|
1956
|
-
if (code === "DUPLICATE_SUBSCRIBER" || errorMsg.toLowerCase().includes("already subscribed")) {
|
|
1957
|
-
return {
|
|
1958
|
-
success: true,
|
|
1959
|
-
message: "You are already part of this newsletter."
|
|
1960
|
-
};
|
|
1961
|
-
}
|
|
1962
|
-
return { success: false, message: errorMsg };
|
|
1963
|
-
}
|
|
1964
|
-
if (result.data?.subscribe?.alreadySubscribed) {
|
|
1965
|
-
return {
|
|
1966
|
-
success: true,
|
|
1967
|
-
message: "You are already part of this newsletter."
|
|
1968
|
-
};
|
|
1969
|
-
}
|
|
1970
|
-
return { success: true };
|
|
1971
|
-
} catch (error) {
|
|
1972
|
-
console.error("Newsletter subscription error:", error);
|
|
1973
|
-
return { success: false, message: "Network error. Please try again." };
|
|
1974
|
-
}
|
|
1975
|
-
}
|
|
1976
|
-
function useRateLimit(maxAttempts, windowMinutes) {
|
|
1977
|
-
const rateLimitRef = React.useRef(null);
|
|
1978
|
-
const [isLimited, setIsLimited] = React.useState(false);
|
|
1979
|
-
const [resetTime, setResetTime] = React.useState(null);
|
|
1980
|
-
React.useEffect(() => {
|
|
1981
|
-
const state = rateLimitRef.current;
|
|
1982
|
-
if (state && Date.now() > state.resetTime) {
|
|
1983
|
-
rateLimitRef.current = null;
|
|
1984
|
-
setIsLimited(false);
|
|
1985
|
-
setResetTime(null);
|
|
1986
|
-
} else if (state && state.attempts >= maxAttempts) {
|
|
1987
|
-
setIsLimited(true);
|
|
1988
|
-
setResetTime(new Date(state.resetTime));
|
|
1989
|
-
}
|
|
1990
|
-
}, [maxAttempts]);
|
|
1991
|
-
function recordAttempt() {
|
|
1992
|
-
const now = Date.now();
|
|
1993
|
-
const state = rateLimitRef.current;
|
|
1994
|
-
if (state && now > state.resetTime) {
|
|
1995
|
-
rateLimitRef.current = null;
|
|
1996
|
-
}
|
|
1997
|
-
const current = rateLimitRef.current;
|
|
1998
|
-
if (!current) {
|
|
1999
|
-
rateLimitRef.current = {
|
|
2000
|
-
attempts: 1,
|
|
2001
|
-
resetTime: now + windowMinutes * 60 * 1e3
|
|
2002
|
-
};
|
|
2003
|
-
return false;
|
|
964
|
+
const current = rateLimitRef.current;
|
|
965
|
+
if (!current) {
|
|
966
|
+
rateLimitRef.current = {
|
|
967
|
+
attempts: 1,
|
|
968
|
+
resetTime: now + windowMinutes * 60 * 1e3
|
|
969
|
+
};
|
|
970
|
+
return false;
|
|
2004
971
|
}
|
|
2005
972
|
current.attempts += 1;
|
|
2006
973
|
if (current.attempts >= maxAttempts) {
|
|
@@ -2208,7 +1175,7 @@ function NewsletterPage({
|
|
|
2208
1175
|
errors.email && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-destructive", children: errors.email.message })
|
|
2209
1176
|
] }),
|
|
2210
1177
|
effectiveRecaptchaSiteKey && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
2211
|
-
|
|
1178
|
+
ReCAPTCHA2__default.default,
|
|
2212
1179
|
{
|
|
2213
1180
|
ref: recaptchaRef,
|
|
2214
1181
|
sitekey: effectiveRecaptchaSiteKey
|
|
@@ -2260,7 +1227,7 @@ function NewsletterPage({
|
|
|
2260
1227
|
errors.email && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-destructive", children: errors.email.message })
|
|
2261
1228
|
] }),
|
|
2262
1229
|
effectiveRecaptchaSiteKey && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex justify-center sm:justify-start", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
2263
|
-
|
|
1230
|
+
ReCAPTCHA2__default.default,
|
|
2264
1231
|
{
|
|
2265
1232
|
ref: recaptchaRef,
|
|
2266
1233
|
sitekey: effectiveRecaptchaSiteKey
|
|
@@ -2428,25 +1395,14 @@ function NewsletterForm({
|
|
|
2428
1395
|
background: rgba(0,0,0,0.3) !important;
|
|
2429
1396
|
}
|
|
2430
1397
|
` }),
|
|
2431
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2432
|
-
|
|
1398
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { style: { position: "relative", zIndex: 50 }, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
1399
|
+
ReCAPTCHA2__default.default,
|
|
2433
1400
|
{
|
|
2434
|
-
|
|
2435
|
-
|
|
2436
|
-
|
|
2437
|
-
display: "flex",
|
|
2438
|
-
justifyContent: "flex-start"
|
|
2439
|
-
},
|
|
2440
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
2441
|
-
ReCAPTCHA3__default.default,
|
|
2442
|
-
{
|
|
2443
|
-
ref: recaptchaRef,
|
|
2444
|
-
sitekey: effectiveRecaptchaSiteKey,
|
|
2445
|
-
size: "normal"
|
|
2446
|
-
}
|
|
2447
|
-
)
|
|
1401
|
+
ref: recaptchaRef,
|
|
1402
|
+
sitekey: effectiveRecaptchaSiteKey,
|
|
1403
|
+
size: "compact"
|
|
2448
1404
|
}
|
|
2449
|
-
)
|
|
1405
|
+
) })
|
|
2450
1406
|
] }),
|
|
2451
1407
|
effectiveRecaptchaSiteKey && /* @__PURE__ */ jsxRuntime.jsx(Button, { type: "submit", disabled: isLoading, className: "w-full", children: isLoading ? "Subscribing..." : buttonText })
|
|
2452
1408
|
]
|
|
@@ -2454,9 +1410,9 @@ function NewsletterForm({
|
|
|
2454
1410
|
)
|
|
2455
1411
|
] });
|
|
2456
1412
|
}
|
|
2457
|
-
var
|
|
2458
|
-
query
|
|
2459
|
-
|
|
1413
|
+
var GET_ALL_PLANS_QUERY = `
|
|
1414
|
+
query GetAllPlans {
|
|
1415
|
+
getAllPlans {
|
|
2460
1416
|
id
|
|
2461
1417
|
name
|
|
2462
1418
|
price
|
|
@@ -2501,15 +1457,11 @@ function formatInterval(duration, billingIntervals) {
|
|
|
2501
1457
|
const intervals = billingIntervals || defaultIntervals;
|
|
2502
1458
|
return intervals[duration] || `/${duration}`;
|
|
2503
1459
|
}
|
|
2504
|
-
function isEnterprisePlan(planName) {
|
|
2505
|
-
const name = planName.toLowerCase();
|
|
2506
|
-
return name.includes("team") || name.includes("business") || name.includes("enterprise");
|
|
2507
|
-
}
|
|
2508
1460
|
function detectPlanTier(planName) {
|
|
2509
1461
|
const name = planName.toLowerCase();
|
|
2510
|
-
if (name.includes("
|
|
1462
|
+
if (name.includes("basic") || name.includes("starter")) {
|
|
2511
1463
|
return "basic";
|
|
2512
|
-
} else if (name.includes("pro") || name.includes("professional") || name.includes("premium")
|
|
1464
|
+
} else if (name.includes("pro") || name.includes("professional") || name.includes("premium")) {
|
|
2513
1465
|
return "professional";
|
|
2514
1466
|
} else if (name.includes("enterprise") || name.includes("business")) {
|
|
2515
1467
|
return "enterprise";
|
|
@@ -2529,8 +1481,7 @@ function getMetadataForPlan(planName, planMetadata) {
|
|
|
2529
1481
|
defaultDescription: meta.defaultDescription || "Choose this plan",
|
|
2530
1482
|
popular: meta.popular ?? false,
|
|
2531
1483
|
ctaText: meta.ctaText || "Get Started",
|
|
2532
|
-
ctaType: meta.ctaType || "signup"
|
|
2533
|
-
features: meta.features
|
|
1484
|
+
ctaType: meta.ctaType || "signup"
|
|
2534
1485
|
};
|
|
2535
1486
|
}
|
|
2536
1487
|
const tier = detectPlanTier(planName);
|
|
@@ -2553,54 +1504,37 @@ function getMetadataForPlan(planName, planMetadata) {
|
|
|
2553
1504
|
ctaType: "signup"
|
|
2554
1505
|
};
|
|
2555
1506
|
}
|
|
2556
|
-
function humanizeQuotaName(name) {
|
|
2557
|
-
const parts = name.split(".");
|
|
2558
|
-
const resource = parts.length >= 3 ? parts[parts.length - 2] : parts[parts.length - 1];
|
|
2559
|
-
return resource.replace(/[-_]/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
|
|
2560
|
-
}
|
|
2561
1507
|
function transformQuotaToFeature(quota) {
|
|
2562
1508
|
const { name, limits } = quota;
|
|
2563
1509
|
if (!limits || typeof limits !== "object") {
|
|
2564
|
-
return
|
|
1510
|
+
return name;
|
|
2565
1511
|
}
|
|
2566
1512
|
const limitsObj = limits;
|
|
2567
1513
|
const value = limitsObj.value;
|
|
2568
1514
|
const unit = limitsObj.unit;
|
|
2569
1515
|
const type = limitsObj.type;
|
|
2570
1516
|
const description = limitsObj.description;
|
|
2571
|
-
if (
|
|
2572
|
-
|
|
2573
|
-
|
|
2574
|
-
|
|
2575
|
-
return description;
|
|
2576
|
-
}
|
|
2577
|
-
if (typeof unit === "string" && unit) {
|
|
2578
|
-
if (typeof value === "number") {
|
|
2579
|
-
return `${value.toLocaleString()} ${unit}`;
|
|
2580
|
-
}
|
|
2581
|
-
return `${unit}`;
|
|
2582
|
-
}
|
|
2583
|
-
const label = humanizeQuotaName(name);
|
|
2584
|
-
if (type === "boolean") {
|
|
2585
|
-
return label;
|
|
1517
|
+
if (type === "number" && typeof value === "number") {
|
|
1518
|
+
const formattedValue = value.toLocaleString();
|
|
1519
|
+
return `${formattedValue} ${unit || ""}${description ? " - " + description : ""}`.trim();
|
|
1520
|
+
} else if (type === "boolean") {
|
|
1521
|
+
return typeof description === "string" && description || name;
|
|
2586
1522
|
}
|
|
2587
1523
|
if (typeof value === "number") {
|
|
2588
|
-
|
|
1524
|
+
const formattedValue = value.toLocaleString();
|
|
1525
|
+
return `${name}: ${formattedValue} ${unit || ""}`.trim();
|
|
2589
1526
|
}
|
|
2590
|
-
return
|
|
1527
|
+
return name;
|
|
2591
1528
|
}
|
|
2592
1529
|
function extractDisplayFeatures(features, planName, fallbackFeatures) {
|
|
2593
|
-
const tier = detectPlanTier(planName);
|
|
2594
|
-
if (tier) {
|
|
2595
|
-
const tierFeatures = getFallbackFeaturesForTier(tier, fallbackFeatures);
|
|
2596
|
-
if (tierFeatures.length > 0) {
|
|
2597
|
-
return tierFeatures;
|
|
2598
|
-
}
|
|
2599
|
-
}
|
|
2600
1530
|
const quotaFeatures = features.filter((f) => f.quota).map((f) => transformQuotaToFeature(f.quota));
|
|
2601
1531
|
if (quotaFeatures.length > 0) {
|
|
2602
1532
|
return quotaFeatures;
|
|
2603
1533
|
}
|
|
1534
|
+
const tier = detectPlanTier(planName);
|
|
1535
|
+
if (tier) {
|
|
1536
|
+
return getFallbackFeaturesForTier(tier, fallbackFeatures);
|
|
1537
|
+
}
|
|
2604
1538
|
return [];
|
|
2605
1539
|
}
|
|
2606
1540
|
function transformToDisplayCard(plan, props) {
|
|
@@ -2611,7 +1545,7 @@ function transformToDisplayCard(plan, props) {
|
|
|
2611
1545
|
props.currencies
|
|
2612
1546
|
);
|
|
2613
1547
|
const interval = formatInterval(plan.duration, props.billingIntervals);
|
|
2614
|
-
const features =
|
|
1548
|
+
const features = extractDisplayFeatures(
|
|
2615
1549
|
plan.features,
|
|
2616
1550
|
plan.name,
|
|
2617
1551
|
props.fallbackFeatures
|
|
@@ -2649,25 +1583,22 @@ function PricingSection(props) {
|
|
|
2649
1583
|
const [billingCycle, setBillingCycle] = React.useState(
|
|
2650
1584
|
"monthly"
|
|
2651
1585
|
);
|
|
2652
|
-
const [audience, setAudience] = React.useState(
|
|
2653
|
-
"individual"
|
|
2654
|
-
);
|
|
2655
1586
|
React.useEffect(() => {
|
|
2656
1587
|
let cancelled = false;
|
|
2657
1588
|
async function loadPlans() {
|
|
2658
1589
|
try {
|
|
2659
1590
|
setLoading(true);
|
|
2660
|
-
const response = await client.query(
|
|
2661
|
-
productId: resolvedProductId
|
|
2662
|
-
});
|
|
1591
|
+
const response = await client.query(GET_ALL_PLANS_QUERY);
|
|
2663
1592
|
if (cancelled) return;
|
|
2664
1593
|
if (response.errors && response.errors.length > 0) {
|
|
2665
1594
|
throw new Error(response.errors[0].message);
|
|
2666
1595
|
}
|
|
2667
|
-
if (!response.data?.
|
|
1596
|
+
if (!response.data?.getAllPlans) {
|
|
2668
1597
|
throw new Error("No plans data received");
|
|
2669
1598
|
}
|
|
2670
|
-
const cards = response.data.
|
|
1599
|
+
const cards = response.data.getAllPlans.filter(
|
|
1600
|
+
(plan) => plan.isActive && (!resolvedProductId || plan.productID === resolvedProductId)
|
|
1601
|
+
).map((plan) => transformToDisplayCard(plan, props)).sort((a, b) => a.priceRaw - b.priceRaw);
|
|
2671
1602
|
setAllPlans(cards);
|
|
2672
1603
|
setError(null);
|
|
2673
1604
|
} catch (err) {
|
|
@@ -2685,56 +1616,13 @@ function PricingSection(props) {
|
|
|
2685
1616
|
cancelled = true;
|
|
2686
1617
|
};
|
|
2687
1618
|
}, [client, resolvedProductId]);
|
|
2688
|
-
const
|
|
2689
|
-
const enterpriseApiPlans = allPlans.filter((p) => isEnterprisePlan(p.name));
|
|
2690
|
-
const individualPlans = individualAllPlans.filter(
|
|
1619
|
+
const plans = allPlans.filter(
|
|
2691
1620
|
(plan) => billingCycle === "monthly" ? plan.duration === "monthly" : plan.duration === "yearly"
|
|
2692
1621
|
);
|
|
2693
|
-
const enterpriseCards = enterpriseApiPlans.map((p) => {
|
|
2694
|
-
if (p.priceRaw === 0 && p.name.toLowerCase().includes("enterprise")) {
|
|
2695
|
-
return {
|
|
2696
|
-
...p,
|
|
2697
|
-
price: "Custom",
|
|
2698
|
-
interval: "",
|
|
2699
|
-
ctaText: "Contact Sales",
|
|
2700
|
-
ctaLink: props.contactUrl || "/contact"
|
|
2701
|
-
};
|
|
2702
|
-
}
|
|
2703
|
-
return p;
|
|
2704
|
-
});
|
|
2705
|
-
const hasApiEnterprisePlans = enterpriseCards.length > 0;
|
|
2706
|
-
const defaultEnterpriseCard = {
|
|
2707
|
-
name: "Enterprise",
|
|
2708
|
-
subtitle: "For organizations that need more",
|
|
2709
|
-
description: "For organizations that need more",
|
|
2710
|
-
price: "Custom",
|
|
2711
|
-
period: "",
|
|
2712
|
-
interval: "",
|
|
2713
|
-
button: "Contact Sales",
|
|
2714
|
-
buttonLink: props.contactUrl || "/contact",
|
|
2715
|
-
ctaText: "Contact Sales",
|
|
2716
|
-
ctaLink: props.contactUrl || "/contact",
|
|
2717
|
-
popular: false,
|
|
2718
|
-
features: props.fallbackFeatures?.enterprise || [
|
|
2719
|
-
"Unlimited everything",
|
|
2720
|
-
"SSO & SAML",
|
|
2721
|
-
"Audit logs",
|
|
2722
|
-
"Dedicated support",
|
|
2723
|
-
"Self-hosted option"
|
|
2724
|
-
]
|
|
2725
|
-
};
|
|
2726
|
-
const resolvedEnterprisePlans = hasApiEnterprisePlans ? enterpriseCards : props.enterprisePlans ? props.enterprisePlans : [defaultEnterpriseCard];
|
|
2727
|
-
const plans = props.showAudienceTabs && audience === "enterprise" ? [] : individualPlans;
|
|
2728
1622
|
if (loading) {
|
|
2729
1623
|
return /* @__PURE__ */ jsxRuntime.jsx("section", { id, className: `py-12 sm:py-16 md:py-20 ${className}`, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "container mx-auto px-4 sm:px-6", children: [
|
|
2730
1624
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center mb-8 sm:mb-12", children: [
|
|
2731
|
-
|
|
2732
|
-
// Visually hidden but DOM-present heading so axe's heading-order
|
|
2733
|
-
// rule still sees an h2 between the consumer's hero h1 and the
|
|
2734
|
-
// h3 plan-card titles. See `getMetadataForPlan` for why a falsy
|
|
2735
|
-
// sectionHeading is the canonical "no visible heading" signal.
|
|
2736
|
-
/* @__PURE__ */ jsxRuntime.jsx("h2", { className: "sr-only", children: "Pricing" })
|
|
2737
|
-
),
|
|
1625
|
+
/* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-2xl sm:text-3xl md:text-4xl font-bold mb-3 sm:mb-4", children: sectionHeading }),
|
|
2738
1626
|
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-muted-foreground text-sm sm:text-base md:text-lg", children: "Loading pricing information..." })
|
|
2739
1627
|
] }),
|
|
2740
1628
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid md:grid-cols-3 gap-6 sm:gap-8", children: [1, 2, 3].map((i) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
@@ -2755,205 +1643,90 @@ function PricingSection(props) {
|
|
|
2755
1643
|
}
|
|
2756
1644
|
if (error && plans.length === 0) {
|
|
2757
1645
|
return /* @__PURE__ */ jsxRuntime.jsx("section", { id, className: `py-12 sm:py-16 md:py-20 ${className}`, children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "container mx-auto px-4 sm:px-6", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center", children: [
|
|
2758
|
-
|
|
1646
|
+
/* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-2xl sm:text-3xl md:text-4xl font-bold mb-4", children: sectionHeading }),
|
|
2759
1647
|
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-muted-foreground mb-8", children: error }),
|
|
2760
1648
|
/* @__PURE__ */ jsxRuntime.jsx(Button, { onClick: () => window.location.reload(), children: "Retry" })
|
|
2761
1649
|
] }) }) });
|
|
2762
1650
|
}
|
|
2763
1651
|
return /* @__PURE__ */ jsxRuntime.jsx("section", { id, className: `py-12 sm:py-16 md:py-20 ${className}`, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "container mx-auto px-4 sm:px-6", children: [
|
|
2764
1652
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center mb-8 sm:mb-12", children: [
|
|
2765
|
-
|
|
1653
|
+
/* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-2xl sm:text-3xl md:text-4xl font-bold mb-3 sm:mb-4", children: sectionHeading }),
|
|
2766
1654
|
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-muted-foreground text-sm sm:text-base md:text-lg mb-6", children: sectionSubtitle }),
|
|
2767
|
-
|
|
1655
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "inline-flex items-center gap-3 bg-muted p-1.5 rounded-lg", children: [
|
|
2768
1656
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2769
1657
|
"button",
|
|
2770
1658
|
{
|
|
2771
|
-
type: "button",
|
|
2772
|
-
onClick: () => setAudience("individual"),
|
|
2773
|
-
className: `px-6 sm:px-8 py-2.5 rounded-lg text-sm font-semibold transition-all ${audience === "individual" ? "bg-background shadow-sm text-foreground" : "text-muted-foreground hover:text-foreground"}`,
|
|
2774
|
-
children: "Individual"
|
|
2775
|
-
}
|
|
2776
|
-
),
|
|
2777
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2778
|
-
"button",
|
|
2779
|
-
{
|
|
2780
|
-
type: "button",
|
|
2781
|
-
onClick: () => setAudience("enterprise"),
|
|
2782
|
-
className: `px-6 sm:px-8 py-2.5 rounded-lg text-sm font-semibold transition-all ${audience === "enterprise" ? "bg-background shadow-sm text-foreground" : "text-muted-foreground hover:text-foreground"}`,
|
|
2783
|
-
children: "Enterprise"
|
|
2784
|
-
}
|
|
2785
|
-
)
|
|
2786
|
-
] }) }),
|
|
2787
|
-
(audience === "individual" || !props.showAudienceTabs) && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex justify-center", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "inline-flex items-center bg-muted/60 p-1.5 rounded-xl", children: [
|
|
2788
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2789
|
-
"button",
|
|
2790
|
-
{
|
|
2791
|
-
type: "button",
|
|
2792
1659
|
onClick: () => setBillingCycle("monthly"),
|
|
2793
|
-
className: `px-
|
|
1660
|
+
className: `px-6 py-2 rounded-md text-sm font-medium transition-all ${billingCycle === "monthly" ? "bg-background shadow-sm text-foreground" : "text-muted-foreground hover:text-foreground"}`,
|
|
2794
1661
|
children: "Monthly"
|
|
2795
1662
|
}
|
|
2796
1663
|
),
|
|
2797
1664
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2798
1665
|
"button",
|
|
2799
1666
|
{
|
|
2800
|
-
type: "button",
|
|
2801
1667
|
onClick: () => setBillingCycle("yearly"),
|
|
2802
|
-
className: `px-
|
|
1668
|
+
className: `px-6 py-2 rounded-md text-sm font-medium transition-all ${billingCycle === "yearly" ? "bg-background shadow-sm text-foreground" : "text-muted-foreground hover:text-foreground"}`,
|
|
2803
1669
|
children: "Yearly"
|
|
2804
1670
|
}
|
|
2805
1671
|
)
|
|
2806
|
-
] })
|
|
2807
|
-
] }),
|
|
2808
|
-
props.showAudienceTabs && audience === "enterprise" && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mx-auto max-w-3xl text-center mb-8", children: [
|
|
2809
|
-
/* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-xl sm:text-2xl font-bold text-foreground", children: props.enterpriseHeading || "Enterprise Solutions" }),
|
|
2810
|
-
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-3 text-muted-foreground", children: props.enterpriseSubtitle || "Built for teams, agencies, and organizations that need advanced collaboration, security, support, and scale." })
|
|
1672
|
+
] })
|
|
2811
1673
|
] }),
|
|
2812
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid md:grid-cols-3 gap-6 sm:gap-8 max-w-6xl mx-auto items-stretch", children:
|
|
2813
|
-
|
|
2814
|
-
|
|
2815
|
-
|
|
2816
|
-
|
|
2817
|
-
|
|
2818
|
-
|
|
2819
|
-
|
|
2820
|
-
|
|
2821
|
-
|
|
2822
|
-
|
|
2823
|
-
|
|
2824
|
-
|
|
2825
|
-
|
|
2826
|
-
{
|
|
2827
|
-
|
|
2828
|
-
children:
|
|
2829
|
-
|
|
2830
|
-
|
|
2831
|
-
|
|
2832
|
-
|
|
2833
|
-
|
|
2834
|
-
|
|
2835
|
-
|
|
2836
|
-
|
|
2837
|
-
|
|
2838
|
-
|
|
2839
|
-
note && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-2 text-sm font-medium text-primary", children: note })
|
|
2840
|
-
] }),
|
|
2841
|
-
/* @__PURE__ */ jsxRuntime.jsx("ul", { className: "space-y-3 mb-8 mt-6 flex-1", children: features.map((feature) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
2842
|
-
"li",
|
|
2843
|
-
{
|
|
2844
|
-
className: "text-sm flex items-start gap-2",
|
|
2845
|
-
children: [
|
|
2846
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-primary mt-0.5 flex-shrink-0", children: "\u2713" }),
|
|
2847
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { children: feature })
|
|
2848
|
-
]
|
|
2849
|
-
},
|
|
2850
|
-
feature
|
|
2851
|
-
)) }),
|
|
2852
|
-
btnLink.startsWith("/") ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
2853
|
-
Button,
|
|
1674
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid md:grid-cols-3 gap-6 sm:gap-8 max-w-6xl mx-auto items-stretch", children: plans.map((plan) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1675
|
+
"div",
|
|
1676
|
+
{
|
|
1677
|
+
className: `bg-card rounded-xl p-6 sm:p-8 transition-all duration-300 flex flex-col ${plan.popular ? "border-2 border-primary shadow-xl ring-1 ring-primary/20" : "border hover:shadow-lg hover:border-primary/50"}`,
|
|
1678
|
+
children: [
|
|
1679
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center mb-6", children: [
|
|
1680
|
+
plan.popular && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "inline-block bg-primary text-primary-foreground px-4 py-1 rounded-full text-xs font-semibold mb-3", children: "Most Popular" }),
|
|
1681
|
+
/* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-xl sm:text-2xl font-bold mb-2", children: plan.name }),
|
|
1682
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-muted-foreground text-sm", children: plan.description })
|
|
1683
|
+
] }),
|
|
1684
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-center mb-8", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-baseline justify-center gap-1", children: [
|
|
1685
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-4xl sm:text-5xl font-bold", children: plan.price }),
|
|
1686
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground text-lg", children: plan.interval })
|
|
1687
|
+
] }) }),
|
|
1688
|
+
/* @__PURE__ */ jsxRuntime.jsx("ul", { className: "space-y-3 mb-8 flex-1", children: plan.features.map((feature, index) => /* @__PURE__ */ jsxRuntime.jsxs("li", { className: "text-sm flex items-start gap-2", children: [
|
|
1689
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-primary mt-0.5 flex-shrink-0", children: "\u2713" }),
|
|
1690
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: feature })
|
|
1691
|
+
] }, index)) }),
|
|
1692
|
+
plan.ctaLink.startsWith("/") ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
1693
|
+
Button,
|
|
1694
|
+
{
|
|
1695
|
+
className: "w-full h-11",
|
|
1696
|
+
variant: plan.popular ? "default" : "outline",
|
|
1697
|
+
size: "lg",
|
|
1698
|
+
asChild: true,
|
|
1699
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
1700
|
+
"a",
|
|
2854
1701
|
{
|
|
2855
|
-
|
|
2856
|
-
|
|
2857
|
-
|
|
2858
|
-
asChild: true,
|
|
2859
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
2860
|
-
"a",
|
|
2861
|
-
{
|
|
2862
|
-
href: btnLink,
|
|
2863
|
-
onClick: () => window.scrollTo({ top: 0, behavior: "smooth" }),
|
|
2864
|
-
children: btnText
|
|
2865
|
-
}
|
|
2866
|
-
)
|
|
1702
|
+
href: plan.ctaLink,
|
|
1703
|
+
onClick: () => window.scrollTo({ top: 0, behavior: "smooth" }),
|
|
1704
|
+
children: plan.ctaText
|
|
2867
1705
|
}
|
|
2868
|
-
)
|
|
2869
|
-
|
|
1706
|
+
)
|
|
1707
|
+
}
|
|
1708
|
+
) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
1709
|
+
Button,
|
|
1710
|
+
{
|
|
1711
|
+
className: "w-full h-11",
|
|
1712
|
+
variant: plan.popular ? "default" : "outline",
|
|
1713
|
+
size: "lg",
|
|
1714
|
+
asChild: true,
|
|
1715
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
1716
|
+
"a",
|
|
2870
1717
|
{
|
|
2871
|
-
|
|
2872
|
-
|
|
2873
|
-
|
|
2874
|
-
|
|
2875
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
2876
|
-
"a",
|
|
2877
|
-
{
|
|
2878
|
-
href: btnLink,
|
|
2879
|
-
target: "_blank",
|
|
2880
|
-
rel: "noopener noreferrer",
|
|
2881
|
-
children: btnText
|
|
2882
|
-
}
|
|
2883
|
-
)
|
|
1718
|
+
href: plan.ctaLink,
|
|
1719
|
+
target: "_blank",
|
|
1720
|
+
rel: "noopener noreferrer",
|
|
1721
|
+
children: plan.ctaText
|
|
2884
1722
|
}
|
|
2885
1723
|
)
|
|
2886
|
-
|
|
2887
|
-
|
|
2888
|
-
|
|
2889
|
-
|
|
2890
|
-
|
|
2891
|
-
)
|
|
2892
|
-
/* Individual API-fetched cards */
|
|
2893
|
-
plans.map((plan) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
2894
|
-
"div",
|
|
2895
|
-
{
|
|
2896
|
-
className: `bg-card rounded-xl p-6 sm:p-8 transition-all duration-300 flex flex-col ${plan.popular ? "border-2 border-primary shadow-xl ring-1 ring-primary/20" : "border hover:shadow-lg hover:border-primary/50"}`,
|
|
2897
|
-
children: [
|
|
2898
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center mb-6", children: [
|
|
2899
|
-
plan.popular && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "inline-block bg-primary text-primary-foreground px-4 py-1 rounded-full text-xs font-semibold mb-3", children: "Most Popular" }),
|
|
2900
|
-
/* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-xl sm:text-2xl font-bold mb-2", children: plan.name }),
|
|
2901
|
-
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-muted-foreground text-sm", children: plan.description })
|
|
2902
|
-
] }),
|
|
2903
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-center mb-8", children: plan.price === "Custom" ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-3xl sm:text-4xl font-bold", children: "Custom" }) : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-baseline justify-center gap-1", children: [
|
|
2904
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-4xl sm:text-5xl font-bold", children: plan.price }),
|
|
2905
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground text-lg", children: plan.interval })
|
|
2906
|
-
] }) }),
|
|
2907
|
-
/* @__PURE__ */ jsxRuntime.jsx("ul", { className: "space-y-3 mb-8 flex-1", children: plan.features.map((feature, index) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
2908
|
-
"li",
|
|
2909
|
-
{
|
|
2910
|
-
className: "text-sm flex items-start gap-2",
|
|
2911
|
-
children: [
|
|
2912
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-primary mt-0.5 flex-shrink-0", children: "\u2713" }),
|
|
2913
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { children: feature })
|
|
2914
|
-
]
|
|
2915
|
-
},
|
|
2916
|
-
index
|
|
2917
|
-
)) }),
|
|
2918
|
-
plan.ctaLink.startsWith("/") ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
2919
|
-
Button,
|
|
2920
|
-
{
|
|
2921
|
-
className: "w-full h-11",
|
|
2922
|
-
variant: plan.popular ? "default" : "outline",
|
|
2923
|
-
size: "lg",
|
|
2924
|
-
asChild: true,
|
|
2925
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
2926
|
-
"a",
|
|
2927
|
-
{
|
|
2928
|
-
href: plan.ctaLink,
|
|
2929
|
-
onClick: () => window.scrollTo({ top: 0, behavior: "smooth" }),
|
|
2930
|
-
children: plan.ctaText
|
|
2931
|
-
}
|
|
2932
|
-
)
|
|
2933
|
-
}
|
|
2934
|
-
) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
2935
|
-
Button,
|
|
2936
|
-
{
|
|
2937
|
-
className: "w-full h-11",
|
|
2938
|
-
variant: plan.popular ? "default" : "outline",
|
|
2939
|
-
size: "lg",
|
|
2940
|
-
asChild: true,
|
|
2941
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
2942
|
-
"a",
|
|
2943
|
-
{
|
|
2944
|
-
href: plan.ctaLink,
|
|
2945
|
-
target: "_blank",
|
|
2946
|
-
rel: "noopener noreferrer",
|
|
2947
|
-
children: plan.ctaText
|
|
2948
|
-
}
|
|
2949
|
-
)
|
|
2950
|
-
}
|
|
2951
|
-
)
|
|
2952
|
-
]
|
|
2953
|
-
},
|
|
2954
|
-
plan.id
|
|
2955
|
-
))
|
|
2956
|
-
) })
|
|
1724
|
+
}
|
|
1725
|
+
)
|
|
1726
|
+
]
|
|
1727
|
+
},
|
|
1728
|
+
plan.id
|
|
1729
|
+
)) })
|
|
2957
1730
|
] }) });
|
|
2958
1731
|
}
|
|
2959
1732
|
function PricingPage(props) {
|
|
@@ -3251,11 +2024,7 @@ function WaitlistPage({
|
|
|
3251
2024
|
}
|
|
3252
2025
|
setIsSubmitting(true);
|
|
3253
2026
|
try {
|
|
3254
|
-
const result = await joinWaitlist(
|
|
3255
|
-
client,
|
|
3256
|
-
data,
|
|
3257
|
-
product?.id || config.productId
|
|
3258
|
-
);
|
|
2027
|
+
const result = await joinWaitlist(client, data, config.productId);
|
|
3259
2028
|
if (result.success) {
|
|
3260
2029
|
sonner.toast.success(result.message ?? successMessage);
|
|
3261
2030
|
reset();
|
|
@@ -3404,7 +2173,7 @@ function WaitlistForm({
|
|
|
3404
2173
|
const result = await joinWaitlist(
|
|
3405
2174
|
client,
|
|
3406
2175
|
{ email, name: name || void 0 },
|
|
3407
|
-
|
|
2176
|
+
config.productId
|
|
3408
2177
|
);
|
|
3409
2178
|
if (result.success) {
|
|
3410
2179
|
sonner.toast.success(result.message ?? "You have been added to the waitlist!");
|
|
@@ -3758,2511 +2527,196 @@ function UnsubscribePage({
|
|
|
3758
2527
|
)
|
|
3759
2528
|
}
|
|
3760
2529
|
) }),
|
|
3761
|
-
/* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-2xl font-bold text-foreground mb-3", children: "Invalid Unsubscribe Link" }),
|
|
3762
|
-
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-muted-foreground", children: "This unsubscribe link is invalid or missing. If you want to unsubscribe, please use the link from your newsletter email." })
|
|
3763
|
-
] })
|
|
3764
|
-
] })
|
|
3765
|
-
]
|
|
3766
|
-
}
|
|
3767
|
-
);
|
|
3768
|
-
}
|
|
3769
|
-
function DiscordIcon({ className }) {
|
|
3770
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
3771
|
-
"svg",
|
|
3772
|
-
{
|
|
3773
|
-
role: "img",
|
|
3774
|
-
viewBox: "0 0 24 24",
|
|
3775
|
-
fill: "currentColor",
|
|
3776
|
-
xmlns: "http://www.w3.org/2000/svg",
|
|
3777
|
-
className,
|
|
3778
|
-
"aria-hidden": "true",
|
|
3779
|
-
children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M20.317 4.369A19.79 19.79 0 0 0 16.558 3a.075.075 0 0 0-.079.037c-.34.6-.717 1.382-.98 1.99a18.27 18.27 0 0 0-5.998 0 12.51 12.51 0 0 0-.99-1.99.077.077 0 0 0-.079-.037 19.74 19.74 0 0 0-3.76 1.369.07.07 0 0 0-.032.028C.533 8.045-.32 11.6.099 15.114a.082.082 0 0 0 .031.056 19.9 19.9 0 0 0 5.993 3.03.078.078 0 0 0 .084-.027 14.09 14.09 0 0 0 1.226-1.994.076.076 0 0 0-.041-.105 13.1 13.1 0 0 1-1.872-.892.077.077 0 0 1-.008-.128c.126-.094.252-.192.372-.291a.074.074 0 0 1 .078-.01c3.927 1.793 8.18 1.793 12.061 0a.074.074 0 0 1 .079.009c.12.099.246.198.372.292a.077.077 0 0 1-.006.127 12.3 12.3 0 0 1-1.873.892.077.077 0 0 0-.041.106c.36.698.772 1.362 1.225 1.993a.076.076 0 0 0 .084.028 19.84 19.84 0 0 0 6.002-3.03.077.077 0 0 0 .032-.055c.5-4.063-.838-7.587-3.549-10.717a.061.061 0 0 0-.031-.028zM8.02 13.001c-1.182 0-2.157-1.085-2.157-2.419 0-1.333.956-2.418 2.157-2.418 1.21 0 2.176 1.094 2.157 2.418 0 1.334-.956 2.419-2.157 2.419zm7.974 0c-1.182 0-2.157-1.085-2.157-2.419 0-1.333.956-2.418 2.157-2.418 1.21 0 2.176 1.094 2.157 2.418 0 1.334-.946 2.419-2.157 2.419z" })
|
|
3780
|
-
}
|
|
3781
|
-
);
|
|
3782
|
-
}
|
|
3783
|
-
function PinterestIcon({ className }) {
|
|
3784
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
3785
|
-
"svg",
|
|
3786
|
-
{
|
|
3787
|
-
role: "img",
|
|
3788
|
-
viewBox: "0 0 24 24",
|
|
3789
|
-
fill: "currentColor",
|
|
3790
|
-
xmlns: "http://www.w3.org/2000/svg",
|
|
3791
|
-
className,
|
|
3792
|
-
"aria-hidden": "true",
|
|
3793
|
-
children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M12.017 0C5.396 0 .029 5.367.029 11.987c0 5.079 3.158 9.417 7.618 11.162-.105-.949-.199-2.403.041-3.439.219-.937 1.406-5.957 1.406-5.957s-.359-.72-.359-1.781c0-1.663.967-2.911 2.168-2.911 1.024 0 1.518.769 1.518 1.688 0 1.029-.653 2.567-.992 3.992-.285 1.193.6 2.165 1.775 2.165 2.128 0 3.768-2.245 3.768-5.487 0-2.861-2.063-4.869-5.008-4.869-3.41 0-5.409 2.562-5.409 5.199 0 1.033.394 2.143.889 2.741.099.12.112.225.085.345-.09.375-.293 1.199-.334 1.363-.053.225-.172.271-.401.165-1.495-.69-2.433-2.878-2.433-4.646 0-3.776 2.748-7.252 7.92-7.252 4.158 0 7.392 2.967 7.392 6.923 0 4.135-2.607 7.462-6.233 7.462-1.214 0-2.357-.629-2.75-1.378l-.748 2.853c-.271 1.043-1.002 2.35-1.492 3.146C9.57 23.812 10.763 24 12.017 24c6.624 0 11.99-5.367 11.99-11.987C24.007 5.367 18.641 0 12.017 0z" })
|
|
3794
|
-
}
|
|
3795
|
-
);
|
|
3796
|
-
}
|
|
3797
|
-
var PLATFORMS = {
|
|
3798
|
-
twitter: { Icon: lucideReact.Twitter, label: "Twitter / X", aliases: ["x"] },
|
|
3799
|
-
linkedin: { Icon: lucideReact.Linkedin, label: "LinkedIn" },
|
|
3800
|
-
github: { Icon: lucideReact.Github, label: "GitHub" },
|
|
3801
|
-
facebook: { Icon: lucideReact.Facebook, label: "Facebook" },
|
|
3802
|
-
youtube: { Icon: lucideReact.Youtube, label: "YouTube" },
|
|
3803
|
-
instagram: { Icon: lucideReact.Instagram, label: "Instagram" },
|
|
3804
|
-
discord: { Icon: DiscordIcon, label: "Discord" },
|
|
3805
|
-
pinterest: { Icon: PinterestIcon, label: "Pinterest" }
|
|
3806
|
-
};
|
|
3807
|
-
var KEY_TO_PLATFORM = (() => {
|
|
3808
|
-
const map = {};
|
|
3809
|
-
for (const [key, def] of Object.entries(PLATFORMS)) {
|
|
3810
|
-
map[key] = key;
|
|
3811
|
-
for (const alias of def.aliases ?? []) {
|
|
3812
|
-
map[alias] = key;
|
|
3813
|
-
}
|
|
3814
|
-
}
|
|
3815
|
-
return map;
|
|
3816
|
-
})();
|
|
3817
|
-
var DEFAULT_ITEM_CLASS = "text-muted-foreground hover:text-foreground transition-colors";
|
|
3818
|
-
function isSafeUrl(value) {
|
|
3819
|
-
const trimmed = value.trim();
|
|
3820
|
-
if (/^[/?#]/.test(trimmed)) return true;
|
|
3821
|
-
return /^(https?:|mailto:|tel:)/i.test(trimmed);
|
|
3822
|
-
}
|
|
3823
|
-
function SocialLinks({
|
|
3824
|
-
links: linksProp,
|
|
3825
|
-
email: emailProp,
|
|
3826
|
-
platforms: platformAllow,
|
|
3827
|
-
showEmail,
|
|
3828
|
-
iconClassName = "h-5 w-5",
|
|
3829
|
-
itemClassName = DEFAULT_ITEM_CLASS,
|
|
3830
|
-
className = "flex space-x-4"
|
|
3831
|
-
}) {
|
|
3832
|
-
const { product } = useProduct();
|
|
3833
|
-
const links = linksProp ?? product?.socialLinks ?? {};
|
|
3834
|
-
const email = emailProp !== void 0 ? emailProp : product?.contactEmail ?? null;
|
|
3835
|
-
const resolved = /* @__PURE__ */ new Map();
|
|
3836
|
-
for (const [rawKey, url] of Object.entries(links)) {
|
|
3837
|
-
if (!url || typeof url !== "string") continue;
|
|
3838
|
-
if (!isSafeUrl(url)) continue;
|
|
3839
|
-
const platform = KEY_TO_PLATFORM[rawKey.toLowerCase()];
|
|
3840
|
-
if (!platform) continue;
|
|
3841
|
-
resolved.set(platform, url);
|
|
3842
|
-
}
|
|
3843
|
-
const orderedPlatforms = platformAllow ? platformAllow.filter((p) => resolved.has(p)) : Array.from(resolved.keys());
|
|
3844
|
-
const wantEmail = (showEmail ?? !!email) && !!email;
|
|
3845
|
-
if (orderedPlatforms.length === 0 && !wantEmail) return null;
|
|
3846
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className, children: [
|
|
3847
|
-
orderedPlatforms.map((platform) => {
|
|
3848
|
-
const { Icon, label } = PLATFORMS[platform];
|
|
3849
|
-
const url = resolved.get(platform);
|
|
3850
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
3851
|
-
"a",
|
|
3852
|
-
{
|
|
3853
|
-
href: url,
|
|
3854
|
-
target: "_blank",
|
|
3855
|
-
rel: "noopener noreferrer",
|
|
3856
|
-
"aria-label": label,
|
|
3857
|
-
className: itemClassName,
|
|
3858
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { className: iconClassName })
|
|
3859
|
-
},
|
|
3860
|
-
platform
|
|
3861
|
-
);
|
|
3862
|
-
}),
|
|
3863
|
-
wantEmail && /* @__PURE__ */ jsxRuntime.jsx(
|
|
3864
|
-
"a",
|
|
3865
|
-
{
|
|
3866
|
-
href: `mailto:${email}`,
|
|
3867
|
-
"aria-label": "Email",
|
|
3868
|
-
className: itemClassName,
|
|
3869
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Mail, { className: iconClassName })
|
|
3870
|
-
},
|
|
3871
|
-
"email"
|
|
3872
|
-
)
|
|
3873
|
-
] });
|
|
3874
|
-
}
|
|
3875
|
-
var DEFAULT_LABELS = {
|
|
3876
|
-
docs: "Docs",
|
|
3877
|
-
community: "Community",
|
|
3878
|
-
careers: "Careers",
|
|
3879
|
-
team: "Team",
|
|
3880
|
-
companies: "Sister Companies"
|
|
3881
|
-
};
|
|
3882
|
-
var BURDENOFF_GROUP_DEFAULTS = {
|
|
3883
|
-
docs: null,
|
|
3884
|
-
// per-product → read from `Product.docsUrl`
|
|
3885
|
-
community: null,
|
|
3886
|
-
// per-product → caller supplies via prop until CMS field exists
|
|
3887
|
-
careers: "https://burdenoff.com/careers",
|
|
3888
|
-
team: "https://burdenoff.com/team",
|
|
3889
|
-
companies: "https://burdenoff.com/companies"
|
|
3890
|
-
};
|
|
3891
|
-
var DEFAULT_ORDER = [
|
|
3892
|
-
"docs",
|
|
3893
|
-
"community",
|
|
3894
|
-
"careers",
|
|
3895
|
-
"team",
|
|
3896
|
-
"companies"
|
|
3897
|
-
];
|
|
3898
|
-
var DEFAULT_ITEM_CLASS2 = "text-sm text-muted-foreground hover:text-foreground transition-colors";
|
|
3899
|
-
function isSafeUrl2(value) {
|
|
3900
|
-
const trimmed = value.trim();
|
|
3901
|
-
if (/^[/?#]/.test(trimmed)) return true;
|
|
3902
|
-
return /^(https?:|mailto:|tel:)/i.test(trimmed);
|
|
3903
|
-
}
|
|
3904
|
-
function ResourceLinks({
|
|
3905
|
-
links: linksProp,
|
|
3906
|
-
resources = DEFAULT_ORDER,
|
|
3907
|
-
labels = {},
|
|
3908
|
-
className = "space-y-2",
|
|
3909
|
-
itemClassName = DEFAULT_ITEM_CLASS2
|
|
3910
|
-
}) {
|
|
3911
|
-
const { product } = useProduct();
|
|
3912
|
-
function resolve(resource) {
|
|
3913
|
-
if (linksProp && resource in linksProp) {
|
|
3914
|
-
const override = linksProp[resource];
|
|
3915
|
-
if (override === void 0) ; else {
|
|
3916
|
-
return override;
|
|
3917
|
-
}
|
|
3918
|
-
}
|
|
3919
|
-
const fromCms = product?.resourceLinks?.[resource];
|
|
3920
|
-
if (typeof fromCms === "string" && fromCms.length > 0) {
|
|
3921
|
-
return fromCms;
|
|
3922
|
-
}
|
|
3923
|
-
if (resource === "docs" && product?.docsUrl) {
|
|
3924
|
-
return product.docsUrl;
|
|
3925
|
-
}
|
|
3926
|
-
return BURDENOFF_GROUP_DEFAULTS[resource];
|
|
3927
|
-
}
|
|
3928
|
-
const items = resources.map((r) => ({ resource: r, url: resolve(r) })).filter(
|
|
3929
|
-
(x) => !!x.url && isSafeUrl2(x.url)
|
|
3930
|
-
);
|
|
3931
|
-
if (items.length === 0) return null;
|
|
3932
|
-
return /* @__PURE__ */ jsxRuntime.jsx("ul", { className, children: items.map(({ resource, url }) => /* @__PURE__ */ jsxRuntime.jsx("li", { children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
3933
|
-
"a",
|
|
3934
|
-
{
|
|
3935
|
-
href: url,
|
|
3936
|
-
target: "_blank",
|
|
3937
|
-
rel: "noopener noreferrer",
|
|
3938
|
-
className: itemClassName,
|
|
3939
|
-
children: labels[resource] ?? DEFAULT_LABELS[resource]
|
|
3940
|
-
}
|
|
3941
|
-
) }, resource)) });
|
|
3942
|
-
}
|
|
3943
|
-
function normalizeSearch(value) {
|
|
3944
|
-
return value.toLowerCase().trim();
|
|
3945
|
-
}
|
|
3946
|
-
function filterProducts(products, query) {
|
|
3947
|
-
const normalized = normalizeSearch(query);
|
|
3948
|
-
if (!normalized) return products;
|
|
3949
|
-
return products.filter((p) => normalizeSearch(p.name).includes(normalized));
|
|
3950
|
-
}
|
|
3951
|
-
function WebsiteSwitcher({
|
|
3952
|
-
products,
|
|
3953
|
-
triggerLabel = "Explore products",
|
|
3954
|
-
searchPlaceholder = "Find a product\u2026",
|
|
3955
|
-
emptyText = "No products match your search.",
|
|
3956
|
-
className
|
|
3957
|
-
}) {
|
|
3958
|
-
const [open, setOpen] = React.useState(false);
|
|
3959
|
-
const [query, setQuery] = React.useState("");
|
|
3960
|
-
const filtered = React.useMemo(
|
|
3961
|
-
() => filterProducts(products, query),
|
|
3962
|
-
[products, query]
|
|
3963
|
-
);
|
|
3964
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(DropdownMenu__namespace.Root, { open, onOpenChange: setOpen, children: [
|
|
3965
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
3966
|
-
DropdownMenu__namespace.Trigger,
|
|
3967
|
-
{
|
|
3968
|
-
className: cn(
|
|
3969
|
-
"inline-flex items-center gap-1.5 text-sm font-medium transition-colors",
|
|
3970
|
-
"text-muted-foreground hover:text-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring rounded-md",
|
|
3971
|
-
className
|
|
3972
|
-
),
|
|
3973
|
-
"aria-label": triggerLabel,
|
|
3974
|
-
children: [
|
|
3975
|
-
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Globe, { className: "h-4 w-4", "aria-hidden": "true" }),
|
|
3976
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "hidden sm:inline", children: triggerLabel }),
|
|
3977
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3978
|
-
lucideReact.ChevronDown,
|
|
3979
|
-
{
|
|
3980
|
-
className: cn(
|
|
3981
|
-
"h-3.5 w-3.5 transition-transform duration-200",
|
|
3982
|
-
open && "rotate-180"
|
|
3983
|
-
),
|
|
3984
|
-
"aria-hidden": "true"
|
|
3985
|
-
}
|
|
3986
|
-
)
|
|
3987
|
-
]
|
|
3988
|
-
}
|
|
3989
|
-
),
|
|
3990
|
-
/* @__PURE__ */ jsxRuntime.jsx(DropdownMenu__namespace.Portal, { children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3991
|
-
DropdownMenu__namespace.Content,
|
|
3992
|
-
{
|
|
3993
|
-
className: "z-50 min-w-[16rem] max-w-[20rem] rounded-lg border border-border/60 bg-popover p-2 shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2",
|
|
3994
|
-
sideOffset: 8,
|
|
3995
|
-
align: "end",
|
|
3996
|
-
avoidCollisions: true,
|
|
3997
|
-
children: [
|
|
3998
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "px-2 pb-2 pt-1", children: [
|
|
3999
|
-
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs font-medium text-muted-foreground mb-2", children: "Burdenoff ecosystem" }),
|
|
4000
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", children: [
|
|
4001
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4002
|
-
lucideReact.Search,
|
|
4003
|
-
{
|
|
4004
|
-
className: "absolute left-2.5 top-1/2 -translate-y-1/2 h-3.5 w-3.5 text-muted-foreground",
|
|
4005
|
-
"aria-hidden": "true"
|
|
4006
|
-
}
|
|
4007
|
-
),
|
|
4008
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4009
|
-
Input,
|
|
4010
|
-
{
|
|
4011
|
-
type: "search",
|
|
4012
|
-
value: query,
|
|
4013
|
-
onChange: (e) => setQuery(e.target.value),
|
|
4014
|
-
placeholder: searchPlaceholder,
|
|
4015
|
-
className: "h-8 pl-8 text-sm",
|
|
4016
|
-
"aria-label": searchPlaceholder,
|
|
4017
|
-
onKeyDown: (e) => {
|
|
4018
|
-
if (e.key === "Escape") {
|
|
4019
|
-
setQuery("");
|
|
4020
|
-
}
|
|
4021
|
-
}
|
|
4022
|
-
}
|
|
4023
|
-
)
|
|
4024
|
-
] })
|
|
4025
|
-
] }),
|
|
4026
|
-
/* @__PURE__ */ jsxRuntime.jsx(DropdownMenu__namespace.Separator, { className: "my-1 h-px bg-border/60" }),
|
|
4027
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "max-h-[min(60vh,24rem)] overflow-y-auto py-1", children: filtered.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-3 py-4 text-center text-sm text-muted-foreground", children: emptyText }) : filtered.map((product) => /* @__PURE__ */ jsxRuntime.jsx(DropdownMenu__namespace.Item, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
4028
|
-
"a",
|
|
4029
|
-
{
|
|
4030
|
-
href: product.websiteUrl,
|
|
4031
|
-
target: "_blank",
|
|
4032
|
-
rel: "noopener noreferrer",
|
|
4033
|
-
className: cn(
|
|
4034
|
-
"flex items-center gap-3 rounded-md px-2 py-2 text-sm outline-none",
|
|
4035
|
-
"text-foreground hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground"
|
|
4036
|
-
),
|
|
4037
|
-
children: [
|
|
4038
|
-
product.logoUrl ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
4039
|
-
"img",
|
|
4040
|
-
{
|
|
4041
|
-
src: product.logoUrl,
|
|
4042
|
-
alt: "",
|
|
4043
|
-
"aria-hidden": "true",
|
|
4044
|
-
className: "h-5 w-5 shrink-0 rounded object-contain"
|
|
4045
|
-
}
|
|
4046
|
-
) : /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex h-5 w-5 shrink-0 items-center justify-center rounded bg-primary/10 text-[10px] font-bold text-primary", children: product.name.charAt(0) }),
|
|
4047
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate", children: product.name })
|
|
4048
|
-
]
|
|
4049
|
-
}
|
|
4050
|
-
) }, product.slug)) }),
|
|
4051
|
-
/* @__PURE__ */ jsxRuntime.jsx(DropdownMenu__namespace.Arrow, { className: "fill-popover" })
|
|
4052
|
-
]
|
|
4053
|
-
}
|
|
4054
|
-
) })
|
|
4055
|
-
] });
|
|
4056
|
-
}
|
|
4057
|
-
function splitRemaining(ms) {
|
|
4058
|
-
const clamped = Math.max(0, ms);
|
|
4059
|
-
const totalSeconds = Math.floor(clamped / 1e3);
|
|
4060
|
-
return {
|
|
4061
|
-
days: Math.floor(totalSeconds / 86400),
|
|
4062
|
-
hours: Math.floor(totalSeconds % 86400 / 3600),
|
|
4063
|
-
minutes: Math.floor(totalSeconds % 3600 / 60),
|
|
4064
|
-
seconds: totalSeconds % 60
|
|
4065
|
-
};
|
|
4066
|
-
}
|
|
4067
|
-
function padTwo(n) {
|
|
4068
|
-
return String(n).padStart(2, "0");
|
|
4069
|
-
}
|
|
4070
|
-
function LaunchCountdown({
|
|
4071
|
-
launchDate,
|
|
4072
|
-
launchLabel,
|
|
4073
|
-
launchedMessage = "We have launched!",
|
|
4074
|
-
className
|
|
4075
|
-
}) {
|
|
4076
|
-
const [now, setNow] = React.useState(() => Date.now());
|
|
4077
|
-
const remainingMs = launchDate - now;
|
|
4078
|
-
const hasLaunched = remainingMs <= 0;
|
|
4079
|
-
React.useEffect(() => {
|
|
4080
|
-
if (hasLaunched) return;
|
|
4081
|
-
const interval = setInterval(() => {
|
|
4082
|
-
setNow(Date.now());
|
|
4083
|
-
}, 1e3);
|
|
4084
|
-
return () => clearInterval(interval);
|
|
4085
|
-
}, [hasLaunched]);
|
|
4086
|
-
const { days, hours, minutes, seconds } = splitRemaining(remainingMs);
|
|
4087
|
-
const cells = [
|
|
4088
|
-
{ label: "Days", value: padTwo(Math.min(days, 99)) },
|
|
4089
|
-
{ label: "Hours", value: padTwo(hours) },
|
|
4090
|
-
{ label: "Minutes", value: padTwo(minutes) },
|
|
4091
|
-
{ label: "Seconds", value: padTwo(seconds) }
|
|
4092
|
-
];
|
|
4093
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
4094
|
-
"div",
|
|
4095
|
-
{
|
|
4096
|
-
className: cn("inline-flex flex-col gap-3", className),
|
|
4097
|
-
role: "timer",
|
|
4098
|
-
"aria-label": launchLabel ?? (hasLaunched ? launchedMessage : `Countdown to launch`),
|
|
4099
|
-
children: [
|
|
4100
|
-
!hasLaunched && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-mono uppercase tracking-wider text-muted-foreground", children: "Launching in" }),
|
|
4101
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex gap-2 sm:gap-3", children: hasLaunched ? /* @__PURE__ */ jsxRuntime.jsxs(
|
|
4102
|
-
"div",
|
|
4103
|
-
{
|
|
4104
|
-
className: cn(
|
|
4105
|
-
"flex min-w-[15rem] items-center justify-center gap-3 rounded-lg border border-primary/30 bg-primary/10 px-6 py-4",
|
|
4106
|
-
"text-center"
|
|
4107
|
-
),
|
|
4108
|
-
children: [
|
|
4109
|
-
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Rocket, { className: "h-5 w-5 text-primary", "aria-hidden": "true" }),
|
|
4110
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-lg font-semibold text-foreground", children: launchedMessage })
|
|
4111
|
-
]
|
|
4112
|
-
}
|
|
4113
|
-
) : cells.map((cell) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
4114
|
-
"div",
|
|
4115
|
-
{
|
|
4116
|
-
className: cn(
|
|
4117
|
-
"flex min-w-[3.75rem] flex-col items-center rounded-lg border border-border/60 bg-card px-3 py-2 sm:min-w-[4.5rem] sm:px-4 sm:py-3"
|
|
4118
|
-
),
|
|
4119
|
-
children: [
|
|
4120
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-mono text-2xl font-bold tabular-nums text-foreground sm:text-3xl", children: cell.value }),
|
|
4121
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "mt-1 text-[10px] font-medium uppercase tracking-wide text-muted-foreground sm:text-xs", children: cell.label })
|
|
4122
|
-
]
|
|
4123
|
-
},
|
|
4124
|
-
cell.label
|
|
4125
|
-
)) }),
|
|
4126
|
-
!hasLaunched && launchLabel && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm text-muted-foreground", children: launchLabel })
|
|
4127
|
-
]
|
|
4128
|
-
}
|
|
4129
|
-
);
|
|
4130
|
-
}
|
|
4131
|
-
function ProblemsSolvedSection({
|
|
4132
|
-
eyebrow,
|
|
4133
|
-
title,
|
|
4134
|
-
subtitle,
|
|
4135
|
-
problems,
|
|
4136
|
-
storyBasePath = "/use-cases",
|
|
4137
|
-
storyLabel = "See the story",
|
|
4138
|
-
className,
|
|
4139
|
-
children
|
|
4140
|
-
}) {
|
|
4141
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
4142
|
-
"section",
|
|
4143
|
-
{
|
|
4144
|
-
className: cn(
|
|
4145
|
-
"py-16 md:py-24 px-4 sm:px-6 lg:px-8 bg-muted/30",
|
|
4146
|
-
className
|
|
4147
|
-
),
|
|
4148
|
-
children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "max-w-7xl mx-auto", children: [
|
|
4149
|
-
(eyebrow || title || subtitle) && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "max-w-3xl mx-auto text-center mb-12 md:mb-16", children: [
|
|
4150
|
-
eyebrow && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs font-mono uppercase tracking-widest text-primary mb-4", children: eyebrow }),
|
|
4151
|
-
title && /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-3xl sm:text-4xl font-bold text-foreground mb-4", children: title }),
|
|
4152
|
-
subtitle && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-lg text-muted-foreground", children: subtitle })
|
|
4153
|
-
] }),
|
|
4154
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6", children: problems.map((item, index) => {
|
|
4155
|
-
const Icon = item.icon;
|
|
4156
|
-
const storyHref = item.storySlug ? `${storyBasePath.replace(/\/$/, "")}/${item.storySlug}` : null;
|
|
4157
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
4158
|
-
"div",
|
|
4159
|
-
{
|
|
4160
|
-
className: cn(
|
|
4161
|
-
"group relative flex flex-col rounded-2xl border border-border/40 bg-card p-6 transition-colors",
|
|
4162
|
-
"hover:border-primary/30"
|
|
4163
|
-
),
|
|
4164
|
-
children: [
|
|
4165
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4166
|
-
"div",
|
|
4167
|
-
{
|
|
4168
|
-
className: cn(
|
|
4169
|
-
"mb-5 inline-flex h-12 w-12 items-center justify-center rounded-xl",
|
|
4170
|
-
"bg-primary/10 text-primary"
|
|
4171
|
-
),
|
|
4172
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { className: "h-6 w-6", "aria-hidden": true })
|
|
4173
|
-
}
|
|
4174
|
-
),
|
|
4175
|
-
/* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-lg font-semibold text-foreground mb-2", children: item.problem }),
|
|
4176
|
-
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground leading-relaxed mb-4 flex-1", children: item.solution }),
|
|
4177
|
-
storyHref && /* @__PURE__ */ jsxRuntime.jsx(
|
|
4178
|
-
Button,
|
|
4179
|
-
{
|
|
4180
|
-
variant: "ghost",
|
|
4181
|
-
className: "justify-start px-0 h-auto py-0 text-sm group/link",
|
|
4182
|
-
asChild: true,
|
|
4183
|
-
children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
4184
|
-
"a",
|
|
4185
|
-
{
|
|
4186
|
-
href: storyHref,
|
|
4187
|
-
className: "inline-flex items-center gap-1 text-primary hover:underline",
|
|
4188
|
-
children: [
|
|
4189
|
-
storyLabel,
|
|
4190
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4191
|
-
lucideReact.ArrowRight,
|
|
4192
|
-
{
|
|
4193
|
-
className: "h-3.5 w-3.5 transition-transform group-hover/link:translate-x-0.5",
|
|
4194
|
-
"aria-hidden": "true"
|
|
4195
|
-
}
|
|
4196
|
-
)
|
|
4197
|
-
]
|
|
4198
|
-
}
|
|
4199
|
-
)
|
|
4200
|
-
}
|
|
4201
|
-
)
|
|
4202
|
-
]
|
|
4203
|
-
},
|
|
4204
|
-
`${item.problem}-${index}`
|
|
4205
|
-
);
|
|
4206
|
-
}) }),
|
|
4207
|
-
children && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-12", children })
|
|
4208
|
-
] })
|
|
4209
|
-
}
|
|
4210
|
-
);
|
|
4211
|
-
}
|
|
4212
|
-
function detectElectronPlatform(userAgent = typeof navigator !== "undefined" ? navigator.userAgent : "") {
|
|
4213
|
-
const ua = userAgent.toLowerCase();
|
|
4214
|
-
if (ua.includes("win")) return "win";
|
|
4215
|
-
if (ua.includes("mac")) return "mac";
|
|
4216
|
-
if (ua.includes("linux")) return "linux";
|
|
4217
|
-
return "unknown";
|
|
4218
|
-
}
|
|
4219
|
-
function buildDownloadUrl(base, artifactName, platform, platformMap) {
|
|
4220
|
-
const normalizedBase = base.replace(/\/$/, "");
|
|
4221
|
-
const platformKey = platformMap[platform] ?? platform;
|
|
4222
|
-
const fileName = artifactName.includes("{platform}") ? artifactName.replace(/\{platform\}/g, platformKey) : artifactName;
|
|
4223
|
-
return `${normalizedBase}/${fileName}`;
|
|
4224
|
-
}
|
|
4225
|
-
function ElectronDownloadLink({
|
|
4226
|
-
downloadBaseUrl,
|
|
4227
|
-
artifactName,
|
|
4228
|
-
hidden = true,
|
|
4229
|
-
label = "Download desktop app",
|
|
4230
|
-
downloadLabel = label,
|
|
4231
|
-
platformMap = {},
|
|
4232
|
-
className,
|
|
4233
|
-
variant = "default",
|
|
4234
|
-
size = "default"
|
|
4235
|
-
}) {
|
|
4236
|
-
if (hidden) return null;
|
|
4237
|
-
const platform = detectElectronPlatform();
|
|
4238
|
-
const href = buildDownloadUrl(
|
|
4239
|
-
downloadBaseUrl,
|
|
4240
|
-
artifactName,
|
|
4241
|
-
platform,
|
|
4242
|
-
platformMap
|
|
4243
|
-
);
|
|
4244
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
4245
|
-
Button,
|
|
4246
|
-
{
|
|
4247
|
-
variant,
|
|
4248
|
-
size,
|
|
4249
|
-
className: cn("inline-flex items-center gap-2", className),
|
|
4250
|
-
asChild: true,
|
|
4251
|
-
children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
4252
|
-
"a",
|
|
4253
|
-
{
|
|
4254
|
-
href,
|
|
4255
|
-
download: true,
|
|
4256
|
-
"aria-label": `${downloadLabel}${platform !== "unknown" ? ` for ${platform}` : ""}`,
|
|
4257
|
-
children: [
|
|
4258
|
-
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Download, { className: "h-4 w-4", "aria-hidden": "true" }),
|
|
4259
|
-
label
|
|
4260
|
-
]
|
|
4261
|
-
}
|
|
4262
|
-
)
|
|
4263
|
-
}
|
|
4264
|
-
);
|
|
4265
|
-
}
|
|
4266
|
-
function RybbitAnalytics({
|
|
4267
|
-
siteId,
|
|
4268
|
-
scriptUrl = "https://analytics.burdenoff.com/api/script.js",
|
|
4269
|
-
enabled = true
|
|
4270
|
-
}) {
|
|
4271
|
-
React.useEffect(() => {
|
|
4272
|
-
if (!enabled || !siteId) return;
|
|
4273
|
-
const existingScript = document.querySelector(
|
|
4274
|
-
`script[data-site-id="${siteId}"]`
|
|
4275
|
-
);
|
|
4276
|
-
if (existingScript) return;
|
|
4277
|
-
const script = document.createElement("script");
|
|
4278
|
-
script.src = scriptUrl;
|
|
4279
|
-
script.defer = true;
|
|
4280
|
-
script.crossOrigin = "anonymous";
|
|
4281
|
-
script.setAttribute("data-site-id", siteId);
|
|
4282
|
-
document.head.appendChild(script);
|
|
4283
|
-
return () => {
|
|
4284
|
-
const scriptToRemove = document.querySelector(
|
|
4285
|
-
`script[data-site-id="${siteId}"]`
|
|
4286
|
-
);
|
|
4287
|
-
if (scriptToRemove) {
|
|
4288
|
-
document.head.removeChild(scriptToRemove);
|
|
4289
|
-
}
|
|
4290
|
-
};
|
|
4291
|
-
}, [siteId, scriptUrl, enabled]);
|
|
4292
|
-
return null;
|
|
4293
|
-
}
|
|
4294
|
-
var BLOG_POSTS_QUERY = `
|
|
4295
|
-
query BlogPosts($productId: String!, $limit: Int, $offset: Int) {
|
|
4296
|
-
publicContentPages(productId: $productId, limit: $limit, offset: $offset) {
|
|
4297
|
-
items {
|
|
4298
|
-
id
|
|
4299
|
-
title
|
|
4300
|
-
slug
|
|
4301
|
-
lastUpdated
|
|
4302
|
-
tags
|
|
4303
|
-
seoDescription
|
|
4304
|
-
excerpt
|
|
4305
|
-
thumbnail
|
|
4306
|
-
author
|
|
4307
|
-
publishedAt
|
|
4308
|
-
readingTime
|
|
4309
|
-
featured
|
|
4310
|
-
category
|
|
4311
|
-
}
|
|
4312
|
-
total
|
|
4313
|
-
hasMore
|
|
4314
|
-
}
|
|
4315
|
-
}
|
|
4316
|
-
`;
|
|
4317
|
-
var BLOG_POST_QUERY = `
|
|
4318
|
-
query BlogPost($slug: String!, $productId: String) {
|
|
4319
|
-
publicContentPage(slug: $slug, productId: $productId) {
|
|
4320
|
-
id
|
|
4321
|
-
title
|
|
4322
|
-
slug
|
|
4323
|
-
content
|
|
4324
|
-
contentFormat
|
|
4325
|
-
seoTitle
|
|
4326
|
-
seoDescription
|
|
4327
|
-
seoKeywords
|
|
4328
|
-
lastUpdated
|
|
4329
|
-
tags
|
|
4330
|
-
excerpt
|
|
4331
|
-
thumbnail
|
|
4332
|
-
author
|
|
4333
|
-
publishedAt
|
|
4334
|
-
readingTime
|
|
4335
|
-
featured
|
|
4336
|
-
category
|
|
4337
|
-
}
|
|
4338
|
-
}
|
|
4339
|
-
`;
|
|
4340
|
-
function BlogCard({
|
|
4341
|
-
post,
|
|
4342
|
-
basePath = "/blog"
|
|
4343
|
-
}) {
|
|
4344
|
-
const date = post.publishedAt || post.lastUpdated;
|
|
4345
|
-
const formattedDate = date ? new Date(date).toLocaleDateString("en-US", {
|
|
4346
|
-
year: "numeric",
|
|
4347
|
-
month: "long",
|
|
4348
|
-
day: "numeric"
|
|
4349
|
-
}) : "";
|
|
4350
|
-
const [imgError, setImgError] = React.useState(false);
|
|
4351
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
4352
|
-
"a",
|
|
4353
|
-
{
|
|
4354
|
-
href: `${basePath}/${post.slug}`,
|
|
4355
|
-
className: "group block bg-card border rounded-xl overflow-hidden hover:shadow-lg hover:border-primary/30 transition-all duration-300",
|
|
4356
|
-
children: [
|
|
4357
|
-
post.thumbnail && !imgError ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "aspect-[16/9] overflow-hidden", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
4358
|
-
"img",
|
|
4359
|
-
{
|
|
4360
|
-
src: post.thumbnail,
|
|
4361
|
-
alt: post.title,
|
|
4362
|
-
className: "w-full h-full object-cover group-hover:scale-105 transition-transform duration-300",
|
|
4363
|
-
loading: "lazy",
|
|
4364
|
-
onError: () => setImgError(true)
|
|
4365
|
-
}
|
|
4366
|
-
) }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "aspect-[16/9] bg-muted/50 flex items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
4367
|
-
"svg",
|
|
4368
|
-
{
|
|
4369
|
-
className: "w-12 h-12 text-muted-foreground/20",
|
|
4370
|
-
fill: "none",
|
|
4371
|
-
viewBox: "0 0 24 24",
|
|
4372
|
-
stroke: "currentColor",
|
|
4373
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
4374
|
-
"path",
|
|
4375
|
-
{
|
|
4376
|
-
strokeLinecap: "round",
|
|
4377
|
-
strokeLinejoin: "round",
|
|
4378
|
-
strokeWidth: 1.5,
|
|
4379
|
-
d: "M12 6.253v13m0-13C10.832 5.477 9.246 5 7.5 5S4.168 5.477 3 6.253v13C4.168 18.477 5.754 18 7.5 18s3.332.477 4.5 1.253m0-13C13.168 5.477 14.754 5 16.5 5c1.747 0 3.332.477 4.5 1.253v13C19.832 18.477 18.247 18 16.5 18c-1.746 0-3.332.477-4.5 1.253"
|
|
4380
|
-
}
|
|
4381
|
-
)
|
|
4382
|
-
}
|
|
4383
|
-
) }),
|
|
4384
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-5 sm:p-6", children: [
|
|
4385
|
-
post.category && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "inline-block text-xs font-medium text-primary bg-primary/10 px-2.5 py-1 rounded-full mb-3", children: post.category }),
|
|
4386
|
-
/* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-lg font-semibold text-foreground mb-2 group-hover:text-primary transition-colors line-clamp-2", children: post.title }),
|
|
4387
|
-
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground mb-4 line-clamp-3", children: post.excerpt || post.seoDescription || "" }),
|
|
4388
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3 text-xs text-muted-foreground", children: [
|
|
4389
|
-
post.author && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium", children: post.author }),
|
|
4390
|
-
post.author && formattedDate && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground/40", children: "\xB7" }),
|
|
4391
|
-
formattedDate && /* @__PURE__ */ jsxRuntime.jsx("span", { children: formattedDate }),
|
|
4392
|
-
post.readingTime && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
4393
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground/40", children: "\xB7" }),
|
|
4394
|
-
/* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
|
|
4395
|
-
post.readingTime,
|
|
4396
|
-
" min read"
|
|
4397
|
-
] })
|
|
4398
|
-
] })
|
|
4399
|
-
] })
|
|
4400
|
-
] })
|
|
4401
|
-
]
|
|
4402
|
-
}
|
|
4403
|
-
);
|
|
4404
|
-
}
|
|
4405
|
-
function BlogListPage(props) {
|
|
4406
|
-
const {
|
|
4407
|
-
productName,
|
|
4408
|
-
heroTitle = "Blog",
|
|
4409
|
-
heroSubtitle = "Latest updates, engineering insights, and product news",
|
|
4410
|
-
blogPostBasePath = "/blog",
|
|
4411
|
-
className = "",
|
|
4412
|
-
seoTitle,
|
|
4413
|
-
seoDescription,
|
|
4414
|
-
seoKeywords
|
|
4415
|
-
} = props;
|
|
4416
|
-
const client = useWebSDK();
|
|
4417
|
-
const config = useWebSDKConfig();
|
|
4418
|
-
const { product } = useProduct();
|
|
4419
|
-
const resolvedProductId = product?.id || config.productId;
|
|
4420
|
-
const [posts, setPosts] = React.useState([]);
|
|
4421
|
-
const [total, setTotal] = React.useState(0);
|
|
4422
|
-
const [loading, setLoading] = React.useState(true);
|
|
4423
|
-
const [error, setError] = React.useState(null);
|
|
4424
|
-
const [offset, setOffset] = React.useState(0);
|
|
4425
|
-
const limit = 12;
|
|
4426
|
-
const fetchPosts = React.useCallback(async () => {
|
|
4427
|
-
if (!resolvedProductId) return;
|
|
4428
|
-
setLoading(true);
|
|
4429
|
-
try {
|
|
4430
|
-
const result = await client.query(BLOG_POSTS_QUERY, {
|
|
4431
|
-
productId: resolvedProductId,
|
|
4432
|
-
limit,
|
|
4433
|
-
offset
|
|
4434
|
-
});
|
|
4435
|
-
if (result.errors?.length) {
|
|
4436
|
-
setError(result.errors[0]?.message ?? "Failed to load posts");
|
|
4437
|
-
return;
|
|
4438
|
-
}
|
|
4439
|
-
const blogPosts = (result.data?.publicContentPages?.items || []).filter(
|
|
4440
|
-
(p) => p.tags?.includes("blog")
|
|
4441
|
-
);
|
|
4442
|
-
setPosts(blogPosts);
|
|
4443
|
-
setTotal(result.data?.publicContentPages?.total || 0);
|
|
4444
|
-
setError(null);
|
|
4445
|
-
} catch {
|
|
4446
|
-
setError("Failed to load blog posts");
|
|
4447
|
-
} finally {
|
|
4448
|
-
setLoading(false);
|
|
4449
|
-
}
|
|
4450
|
-
}, [client, resolvedProductId, offset]);
|
|
4451
|
-
React.useEffect(() => {
|
|
4452
|
-
fetchPosts();
|
|
4453
|
-
}, [fetchPosts]);
|
|
4454
|
-
const featuredPosts = posts.filter((p) => p.featured);
|
|
4455
|
-
const regularPosts = posts.filter((p) => !p.featured);
|
|
4456
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `w-full ${className}`, children: [
|
|
4457
|
-
seoTitle && /* @__PURE__ */ jsxRuntime.jsx(
|
|
4458
|
-
PageHead,
|
|
4459
|
-
{
|
|
4460
|
-
title: seoTitle,
|
|
4461
|
-
description: seoDescription || heroSubtitle,
|
|
4462
|
-
productName,
|
|
4463
|
-
keywords: seoKeywords
|
|
4464
|
-
}
|
|
4465
|
-
),
|
|
4466
|
-
/* @__PURE__ */ jsxRuntime.jsx("section", { className: "py-16 sm:py-20 md:py-24", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "max-w-7xl mx-auto px-4 sm:px-6 lg:px-8", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center max-w-3xl mx-auto", children: [
|
|
4467
|
-
/* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-3xl sm:text-4xl md:text-5xl font-bold tracking-tight text-foreground", children: heroTitle }),
|
|
4468
|
-
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-4 text-lg text-muted-foreground", children: heroSubtitle })
|
|
4469
|
-
] }) }) }),
|
|
4470
|
-
/* @__PURE__ */ jsxRuntime.jsx("section", { className: "pb-16 sm:pb-20", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "max-w-7xl mx-auto px-4 sm:px-6 lg:px-8", children: loading ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid md:grid-cols-2 lg:grid-cols-3 gap-6 sm:gap-8", children: [1, 2, 3].map((i) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
4471
|
-
"div",
|
|
4472
|
-
{
|
|
4473
|
-
className: "bg-card border rounded-xl overflow-hidden animate-pulse",
|
|
4474
|
-
children: [
|
|
4475
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "aspect-[16/9] bg-muted" }),
|
|
4476
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-6 space-y-3", children: [
|
|
4477
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-4 bg-muted rounded w-1/4" }),
|
|
4478
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-6 bg-muted rounded w-3/4" }),
|
|
4479
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-4 bg-muted rounded" }),
|
|
4480
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-4 bg-muted rounded w-1/2" })
|
|
4481
|
-
] })
|
|
4482
|
-
]
|
|
4483
|
-
},
|
|
4484
|
-
i
|
|
4485
|
-
)) }) : error ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-center py-12", children: /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-muted-foreground", children: error }) }) : posts.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-center py-12", children: /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-muted-foreground", children: "No blog posts yet. Check back soon!" }) }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
4486
|
-
featuredPosts.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid md:grid-cols-2 gap-6 sm:gap-8 mb-8", children: featuredPosts.map((post) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
4487
|
-
BlogCard,
|
|
4488
|
-
{
|
|
4489
|
-
post,
|
|
4490
|
-
basePath: blogPostBasePath
|
|
4491
|
-
},
|
|
4492
|
-
post.id
|
|
4493
|
-
)) }),
|
|
4494
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid md:grid-cols-2 lg:grid-cols-3 gap-6 sm:gap-8", children: regularPosts.map((post) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
4495
|
-
BlogCard,
|
|
4496
|
-
{
|
|
4497
|
-
post,
|
|
4498
|
-
basePath: blogPostBasePath
|
|
4499
|
-
},
|
|
4500
|
-
post.id
|
|
4501
|
-
)) }),
|
|
4502
|
-
total > limit && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-center gap-4 mt-12", children: [
|
|
4503
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4504
|
-
"button",
|
|
4505
|
-
{
|
|
4506
|
-
onClick: () => setOffset(Math.max(0, offset - limit)),
|
|
4507
|
-
disabled: offset === 0,
|
|
4508
|
-
className: "px-5 py-2.5 text-sm font-medium border rounded-lg disabled:opacity-40 hover:bg-muted transition-colors",
|
|
4509
|
-
children: "Previous"
|
|
4510
|
-
}
|
|
4511
|
-
),
|
|
4512
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4513
|
-
"button",
|
|
4514
|
-
{
|
|
4515
|
-
onClick: () => setOffset(offset + limit),
|
|
4516
|
-
disabled: offset + limit >= total,
|
|
4517
|
-
className: "px-5 py-2.5 text-sm font-medium border rounded-lg disabled:opacity-40 hover:bg-muted transition-colors",
|
|
4518
|
-
children: "Next"
|
|
4519
|
-
}
|
|
4520
|
-
)
|
|
4521
|
-
] })
|
|
4522
|
-
] }) }) })
|
|
4523
|
-
] });
|
|
4524
|
-
}
|
|
4525
|
-
function BlogPostPage(props) {
|
|
4526
|
-
const {
|
|
4527
|
-
productName,
|
|
4528
|
-
blogListUrl = "/blog",
|
|
4529
|
-
backLabel = "Back to Blog",
|
|
4530
|
-
className = ""
|
|
4531
|
-
} = props;
|
|
4532
|
-
const client = useWebSDK();
|
|
4533
|
-
const config = useWebSDKConfig();
|
|
4534
|
-
const { product } = useProduct();
|
|
4535
|
-
const resolvedProductId = product?.id || config.productId;
|
|
4536
|
-
const [post, setPost] = React.useState(null);
|
|
4537
|
-
const [loading, setLoading] = React.useState(true);
|
|
4538
|
-
const [error, setError] = React.useState(null);
|
|
4539
|
-
const slug = typeof window !== "undefined" ? window.location.pathname.split("/").pop() || "" : "";
|
|
4540
|
-
React.useEffect(() => {
|
|
4541
|
-
if (!slug || !resolvedProductId) return;
|
|
4542
|
-
async function fetchPost() {
|
|
4543
|
-
setLoading(true);
|
|
4544
|
-
try {
|
|
4545
|
-
const result = await client.query(BLOG_POST_QUERY, {
|
|
4546
|
-
slug,
|
|
4547
|
-
productId: resolvedProductId
|
|
4548
|
-
});
|
|
4549
|
-
if (result.errors?.length) {
|
|
4550
|
-
setError(result.errors[0]?.message ?? "Failed to load post");
|
|
4551
|
-
return;
|
|
4552
|
-
}
|
|
4553
|
-
setPost(result.data?.publicContentPage ?? null);
|
|
4554
|
-
setError(null);
|
|
4555
|
-
} catch {
|
|
4556
|
-
setError("Failed to load blog post");
|
|
4557
|
-
} finally {
|
|
4558
|
-
setLoading(false);
|
|
4559
|
-
}
|
|
4560
|
-
}
|
|
4561
|
-
fetchPost();
|
|
4562
|
-
}, [client, slug, resolvedProductId]);
|
|
4563
|
-
if (loading) {
|
|
4564
|
-
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: `w-full ${className}`, children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-24", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "animate-pulse space-y-6", children: [
|
|
4565
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-8 bg-muted rounded w-2/3" }),
|
|
4566
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-4 bg-muted rounded w-1/3" }),
|
|
4567
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "aspect-[16/9] bg-muted rounded-xl" }),
|
|
4568
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-3", children: [1, 2, 3, 4, 5].map((i) => /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-4 bg-muted rounded" }, i)) })
|
|
4569
|
-
] }) }) });
|
|
4570
|
-
}
|
|
4571
|
-
if (error || !post) {
|
|
4572
|
-
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: `w-full ${className}`, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-24 text-center", children: [
|
|
4573
|
-
/* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-2xl font-bold mb-4", children: "Post not found" }),
|
|
4574
|
-
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-muted-foreground mb-6", children: error || "This blog post doesn't exist or has been removed." }),
|
|
4575
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
4576
|
-
"a",
|
|
4577
|
-
{
|
|
4578
|
-
href: blogListUrl,
|
|
4579
|
-
className: "text-primary hover:underline font-medium",
|
|
4580
|
-
children: [
|
|
4581
|
-
"\u2190 ",
|
|
4582
|
-
backLabel
|
|
4583
|
-
]
|
|
4584
|
-
}
|
|
4585
|
-
)
|
|
4586
|
-
] }) });
|
|
4587
|
-
}
|
|
4588
|
-
const date = post.publishedAt || post.lastUpdated;
|
|
4589
|
-
const formattedDate = date ? new Date(date).toLocaleDateString("en-US", {
|
|
4590
|
-
year: "numeric",
|
|
4591
|
-
month: "long",
|
|
4592
|
-
day: "numeric"
|
|
4593
|
-
}) : "";
|
|
4594
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `w-full ${className}`, children: [
|
|
4595
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4596
|
-
PageHead,
|
|
4597
|
-
{
|
|
4598
|
-
title: post.seoTitle || post.title,
|
|
4599
|
-
description: post.seoDescription || post.excerpt || "",
|
|
4600
|
-
productName,
|
|
4601
|
-
keywords: post.seoKeywords || ""
|
|
4602
|
-
}
|
|
4603
|
-
),
|
|
4604
|
-
/* @__PURE__ */ jsxRuntime.jsxs("article", { className: "max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-16 sm:py-20", children: [
|
|
4605
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
4606
|
-
"a",
|
|
4607
|
-
{
|
|
4608
|
-
href: blogListUrl,
|
|
4609
|
-
className: "inline-flex items-center gap-1 text-sm text-muted-foreground hover:text-foreground mb-8 transition-colors",
|
|
4610
|
-
children: [
|
|
4611
|
-
"\u2190 ",
|
|
4612
|
-
backLabel
|
|
4613
|
-
]
|
|
4614
|
-
}
|
|
4615
|
-
),
|
|
4616
|
-
post.category && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "inline-block text-xs font-medium text-primary bg-primary/10 px-2.5 py-1 rounded-full mb-4", children: post.category }),
|
|
4617
|
-
/* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-3xl sm:text-4xl md:text-5xl font-bold tracking-tight text-foreground mb-4", children: post.title }),
|
|
4618
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3 text-sm text-muted-foreground mb-8", children: [
|
|
4619
|
-
post.author && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium text-foreground", children: post.author }),
|
|
4620
|
-
post.author && formattedDate && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground/40", children: "\xB7" }),
|
|
4621
|
-
formattedDate && /* @__PURE__ */ jsxRuntime.jsx("span", { children: formattedDate }),
|
|
4622
|
-
post.readingTime && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
4623
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground/40", children: "\xB7" }),
|
|
4624
|
-
/* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
|
|
4625
|
-
post.readingTime,
|
|
4626
|
-
" min read"
|
|
4627
|
-
] })
|
|
4628
|
-
] })
|
|
4629
|
-
] }),
|
|
4630
|
-
post.thumbnail && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "aspect-[16/9] rounded-xl overflow-hidden mb-10", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
4631
|
-
"img",
|
|
4632
|
-
{
|
|
4633
|
-
src: post.thumbnail,
|
|
4634
|
-
alt: post.title,
|
|
4635
|
-
className: "w-full h-full object-cover"
|
|
4636
|
-
}
|
|
4637
|
-
) }),
|
|
4638
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "prose prose-gray dark:prose-invert max-w-none", children: /* @__PURE__ */ jsxRuntime.jsx(ContentRenderer, { content: post.content, format: post.contentFormat }) })
|
|
4639
|
-
] })
|
|
4640
|
-
] });
|
|
4641
|
-
}
|
|
4642
|
-
var CASE_STUDY_TAG = "case-study";
|
|
4643
|
-
var CASE_STUDIES_QUERY = `
|
|
4644
|
-
query CaseStudies($productId: String!, $limit: Int, $offset: Int) {
|
|
4645
|
-
publicContentPages(productId: $productId, limit: $limit, offset: $offset) {
|
|
4646
|
-
items {
|
|
4647
|
-
id
|
|
4648
|
-
title
|
|
4649
|
-
slug
|
|
4650
|
-
lastUpdated
|
|
4651
|
-
tags
|
|
4652
|
-
seoDescription
|
|
4653
|
-
excerpt
|
|
4654
|
-
thumbnail
|
|
4655
|
-
author
|
|
4656
|
-
publishedAt
|
|
4657
|
-
readingTime
|
|
4658
|
-
featured
|
|
4659
|
-
category
|
|
4660
|
-
}
|
|
4661
|
-
total
|
|
4662
|
-
hasMore
|
|
4663
|
-
}
|
|
4664
|
-
}
|
|
4665
|
-
`;
|
|
4666
|
-
var CASE_STUDY_QUERY = `
|
|
4667
|
-
query CaseStudy($slug: String!, $productId: String) {
|
|
4668
|
-
publicContentPage(slug: $slug, productId: $productId) {
|
|
4669
|
-
id
|
|
4670
|
-
title
|
|
4671
|
-
slug
|
|
4672
|
-
content
|
|
4673
|
-
contentFormat
|
|
4674
|
-
seoTitle
|
|
4675
|
-
seoDescription
|
|
4676
|
-
seoKeywords
|
|
4677
|
-
lastUpdated
|
|
4678
|
-
tags
|
|
4679
|
-
excerpt
|
|
4680
|
-
thumbnail
|
|
4681
|
-
author
|
|
4682
|
-
publishedAt
|
|
4683
|
-
readingTime
|
|
4684
|
-
featured
|
|
4685
|
-
category
|
|
4686
|
-
}
|
|
4687
|
-
}
|
|
4688
|
-
`;
|
|
4689
|
-
function CaseStudyCard({
|
|
4690
|
-
post,
|
|
4691
|
-
basePath = "/case-studies"
|
|
4692
|
-
}) {
|
|
4693
|
-
const date = post.publishedAt || post.lastUpdated;
|
|
4694
|
-
const formattedDate = date ? new Date(date).toLocaleDateString("en-US", {
|
|
4695
|
-
year: "numeric",
|
|
4696
|
-
month: "long",
|
|
4697
|
-
day: "numeric"
|
|
4698
|
-
}) : "";
|
|
4699
|
-
const [imgError, setImgError] = React.useState(false);
|
|
4700
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
4701
|
-
"a",
|
|
4702
|
-
{
|
|
4703
|
-
href: `${basePath}/${post.slug}`,
|
|
4704
|
-
className: "group block bg-card border rounded-xl overflow-hidden hover:shadow-lg hover:border-primary/30 transition-all duration-300",
|
|
4705
|
-
children: [
|
|
4706
|
-
post.thumbnail && !imgError ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "aspect-[16/9] overflow-hidden", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
4707
|
-
"img",
|
|
4708
|
-
{
|
|
4709
|
-
src: post.thumbnail,
|
|
4710
|
-
alt: post.title,
|
|
4711
|
-
className: "w-full h-full object-cover group-hover:scale-105 transition-transform duration-300",
|
|
4712
|
-
loading: "lazy",
|
|
4713
|
-
onError: () => setImgError(true)
|
|
4714
|
-
}
|
|
4715
|
-
) }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "aspect-[16/9] bg-muted/50 flex items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
4716
|
-
"svg",
|
|
4717
|
-
{
|
|
4718
|
-
className: "w-12 h-12 text-muted-foreground/20",
|
|
4719
|
-
fill: "none",
|
|
4720
|
-
viewBox: "0 0 24 24",
|
|
4721
|
-
stroke: "currentColor",
|
|
4722
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
4723
|
-
"path",
|
|
4724
|
-
{
|
|
4725
|
-
strokeLinecap: "round",
|
|
4726
|
-
strokeLinejoin: "round",
|
|
4727
|
-
strokeWidth: 1.5,
|
|
4728
|
-
d: "M12 6.253v13m0-13C10.832 5.477 9.246 5 7.5 5S4.168 5.477 3 6.253v13C4.168 18.477 5.754 18 7.5 18s3.332.477 4.5 1.253m0-13C13.168 5.477 14.754 5 16.5 5c1.747 0 3.332.477 4.5 1.253v13C19.832 18.477 18.247 18 16.5 18c-1.746 0-3.332.477-4.5 1.253"
|
|
4729
|
-
}
|
|
4730
|
-
)
|
|
4731
|
-
}
|
|
4732
|
-
) }),
|
|
4733
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-5 sm:p-6", children: [
|
|
4734
|
-
post.category && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "inline-block text-xs font-medium text-primary bg-primary/10 px-2.5 py-1 rounded-full mb-3", children: post.category }),
|
|
4735
|
-
/* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-lg font-semibold text-foreground mb-2 group-hover:text-primary transition-colors line-clamp-2", children: post.title }),
|
|
4736
|
-
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground mb-4 line-clamp-3", children: post.excerpt || post.seoDescription || "" }),
|
|
4737
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3 text-xs text-muted-foreground", children: [
|
|
4738
|
-
post.author && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium", children: post.author }),
|
|
4739
|
-
post.author && formattedDate && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground/40", children: "\xB7" }),
|
|
4740
|
-
formattedDate && /* @__PURE__ */ jsxRuntime.jsx("span", { children: formattedDate }),
|
|
4741
|
-
post.readingTime && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
4742
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground/40", children: "\xB7" }),
|
|
4743
|
-
/* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
|
|
4744
|
-
post.readingTime,
|
|
4745
|
-
" min read"
|
|
4746
|
-
] })
|
|
4747
|
-
] })
|
|
4748
|
-
] })
|
|
4749
|
-
] })
|
|
4750
|
-
]
|
|
4751
|
-
}
|
|
4752
|
-
);
|
|
4753
|
-
}
|
|
4754
|
-
function CaseStudiesListPage(props) {
|
|
4755
|
-
const {
|
|
4756
|
-
productName,
|
|
4757
|
-
heroTitle = "Case Studies",
|
|
4758
|
-
heroSubtitle = "Real-world stories of teams building with our platform",
|
|
4759
|
-
caseStudyBasePath = "/case-studies",
|
|
4760
|
-
className = "",
|
|
4761
|
-
seoTitle,
|
|
4762
|
-
seoDescription,
|
|
4763
|
-
seoKeywords
|
|
4764
|
-
} = props;
|
|
4765
|
-
const client = useWebSDK();
|
|
4766
|
-
const config = useWebSDKConfig();
|
|
4767
|
-
const { product } = useProduct();
|
|
4768
|
-
const resolvedProductId = product?.id || config.productId;
|
|
4769
|
-
const [posts, setPosts] = React.useState([]);
|
|
4770
|
-
const [total, setTotal] = React.useState(0);
|
|
4771
|
-
const [loading, setLoading] = React.useState(true);
|
|
4772
|
-
const [error, setError] = React.useState(null);
|
|
4773
|
-
const [offset, setOffset] = React.useState(0);
|
|
4774
|
-
const limit = 12;
|
|
4775
|
-
const fetchPosts = React.useCallback(async () => {
|
|
4776
|
-
if (!resolvedProductId) return;
|
|
4777
|
-
setLoading(true);
|
|
4778
|
-
try {
|
|
4779
|
-
const result = await client.query(CASE_STUDIES_QUERY, {
|
|
4780
|
-
productId: resolvedProductId,
|
|
4781
|
-
limit,
|
|
4782
|
-
offset
|
|
4783
|
-
});
|
|
4784
|
-
if (result.errors?.length) {
|
|
4785
|
-
setError(result.errors[0]?.message ?? "Failed to load case studies");
|
|
4786
|
-
return;
|
|
4787
|
-
}
|
|
4788
|
-
const caseStudies = (result.data?.publicContentPages?.items || []).filter(
|
|
4789
|
-
(p) => p.tags?.includes(CASE_STUDY_TAG)
|
|
4790
|
-
);
|
|
4791
|
-
setPosts(caseStudies);
|
|
4792
|
-
setTotal(result.data?.publicContentPages?.total || 0);
|
|
4793
|
-
setError(null);
|
|
4794
|
-
} catch {
|
|
4795
|
-
setError("Failed to load case studies");
|
|
4796
|
-
} finally {
|
|
4797
|
-
setLoading(false);
|
|
4798
|
-
}
|
|
4799
|
-
}, [client, resolvedProductId, offset]);
|
|
4800
|
-
React.useEffect(() => {
|
|
4801
|
-
fetchPosts();
|
|
4802
|
-
}, [fetchPosts]);
|
|
4803
|
-
const featuredPosts = posts.filter((p) => p.featured);
|
|
4804
|
-
const regularPosts = posts.filter((p) => !p.featured);
|
|
4805
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `w-full ${className}`, children: [
|
|
4806
|
-
seoTitle && /* @__PURE__ */ jsxRuntime.jsx(
|
|
4807
|
-
PageHead,
|
|
4808
|
-
{
|
|
4809
|
-
title: seoTitle,
|
|
4810
|
-
description: seoDescription || heroSubtitle,
|
|
4811
|
-
productName,
|
|
4812
|
-
keywords: seoKeywords
|
|
4813
|
-
}
|
|
4814
|
-
),
|
|
4815
|
-
/* @__PURE__ */ jsxRuntime.jsx("section", { className: "py-16 sm:py-20 md:py-24", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "max-w-7xl mx-auto px-4 sm:px-6 lg:px-8", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center max-w-3xl mx-auto", children: [
|
|
4816
|
-
/* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-3xl sm:text-4xl md:text-5xl font-bold tracking-tight text-foreground", children: heroTitle }),
|
|
4817
|
-
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-4 text-lg text-muted-foreground", children: heroSubtitle })
|
|
4818
|
-
] }) }) }),
|
|
4819
|
-
/* @__PURE__ */ jsxRuntime.jsx("section", { className: "pb-16 sm:pb-20", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "max-w-7xl mx-auto px-4 sm:px-6 lg:px-8", children: loading ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid md:grid-cols-2 lg:grid-cols-3 gap-6 sm:gap-8", children: [1, 2, 3].map((i) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
4820
|
-
"div",
|
|
4821
|
-
{
|
|
4822
|
-
className: "bg-card border rounded-xl overflow-hidden animate-pulse",
|
|
4823
|
-
children: [
|
|
4824
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "aspect-[16/9] bg-muted" }),
|
|
4825
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-6 space-y-3", children: [
|
|
4826
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-4 bg-muted rounded w-1/4" }),
|
|
4827
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-6 bg-muted rounded w-3/4" }),
|
|
4828
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-4 bg-muted rounded" }),
|
|
4829
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-4 bg-muted rounded w-1/2" })
|
|
4830
|
-
] })
|
|
4831
|
-
]
|
|
4832
|
-
},
|
|
4833
|
-
i
|
|
4834
|
-
)) }) : error ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-center py-12", children: /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-muted-foreground", children: error }) }) : posts.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-center py-12", children: /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-muted-foreground", children: "No case studies yet. Check back soon!" }) }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
4835
|
-
featuredPosts.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid md:grid-cols-2 gap-6 sm:gap-8 mb-8", children: featuredPosts.map((post) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
4836
|
-
CaseStudyCard,
|
|
4837
|
-
{
|
|
4838
|
-
post,
|
|
4839
|
-
basePath: caseStudyBasePath
|
|
4840
|
-
},
|
|
4841
|
-
post.id
|
|
4842
|
-
)) }),
|
|
4843
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid md:grid-cols-2 lg:grid-cols-3 gap-6 sm:gap-8", children: regularPosts.map((post) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
4844
|
-
CaseStudyCard,
|
|
4845
|
-
{
|
|
4846
|
-
post,
|
|
4847
|
-
basePath: caseStudyBasePath
|
|
4848
|
-
},
|
|
4849
|
-
post.id
|
|
4850
|
-
)) }),
|
|
4851
|
-
total > limit && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-center gap-4 mt-12", children: [
|
|
4852
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4853
|
-
"button",
|
|
4854
|
-
{
|
|
4855
|
-
onClick: () => setOffset(Math.max(0, offset - limit)),
|
|
4856
|
-
disabled: offset === 0,
|
|
4857
|
-
className: "px-5 py-2.5 text-sm font-medium border rounded-lg disabled:opacity-40 hover:bg-muted transition-colors",
|
|
4858
|
-
children: "Previous"
|
|
4859
|
-
}
|
|
4860
|
-
),
|
|
4861
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4862
|
-
"button",
|
|
4863
|
-
{
|
|
4864
|
-
onClick: () => setOffset(offset + limit),
|
|
4865
|
-
disabled: offset + limit >= total,
|
|
4866
|
-
className: "px-5 py-2.5 text-sm font-medium border rounded-lg disabled:opacity-40 hover:bg-muted transition-colors",
|
|
4867
|
-
children: "Next"
|
|
4868
|
-
}
|
|
4869
|
-
)
|
|
4870
|
-
] })
|
|
4871
|
-
] }) }) })
|
|
4872
|
-
] });
|
|
4873
|
-
}
|
|
4874
|
-
function CaseStudyPage(props) {
|
|
4875
|
-
const {
|
|
4876
|
-
productName,
|
|
4877
|
-
caseStudiesListUrl = "/case-studies",
|
|
4878
|
-
backLabel = "Back to Case Studies",
|
|
4879
|
-
className = ""
|
|
4880
|
-
} = props;
|
|
4881
|
-
const client = useWebSDK();
|
|
4882
|
-
const config = useWebSDKConfig();
|
|
4883
|
-
const { product } = useProduct();
|
|
4884
|
-
const resolvedProductId = product?.id || config.productId;
|
|
4885
|
-
const [post, setPost] = React.useState(null);
|
|
4886
|
-
const [loading, setLoading] = React.useState(true);
|
|
4887
|
-
const [error, setError] = React.useState(null);
|
|
4888
|
-
const slug = typeof window !== "undefined" ? window.location.pathname.split("/").pop() || "" : "";
|
|
4889
|
-
React.useEffect(() => {
|
|
4890
|
-
if (!slug || !resolvedProductId) return;
|
|
4891
|
-
async function fetchPost() {
|
|
4892
|
-
setLoading(true);
|
|
4893
|
-
try {
|
|
4894
|
-
const result = await client.query(CASE_STUDY_QUERY, {
|
|
4895
|
-
slug,
|
|
4896
|
-
productId: resolvedProductId
|
|
4897
|
-
});
|
|
4898
|
-
if (result.errors?.length) {
|
|
4899
|
-
setError(result.errors[0]?.message ?? "Failed to load case study");
|
|
4900
|
-
return;
|
|
4901
|
-
}
|
|
4902
|
-
setPost(result.data?.publicContentPage ?? null);
|
|
4903
|
-
setError(null);
|
|
4904
|
-
} catch {
|
|
4905
|
-
setError("Failed to load case study");
|
|
4906
|
-
} finally {
|
|
4907
|
-
setLoading(false);
|
|
4908
|
-
}
|
|
4909
|
-
}
|
|
4910
|
-
fetchPost();
|
|
4911
|
-
}, [client, slug, resolvedProductId]);
|
|
4912
|
-
if (loading) {
|
|
4913
|
-
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: `w-full ${className}`, children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-24", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "animate-pulse space-y-6", children: [
|
|
4914
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-8 bg-muted rounded w-2/3" }),
|
|
4915
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-4 bg-muted rounded w-1/3" }),
|
|
4916
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "aspect-[16/9] bg-muted rounded-xl" }),
|
|
4917
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-3", children: [1, 2, 3, 4, 5].map((i) => /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-4 bg-muted rounded" }, i)) })
|
|
4918
|
-
] }) }) });
|
|
4919
|
-
}
|
|
4920
|
-
if (error || !post) {
|
|
4921
|
-
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: `w-full ${className}`, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-24 text-center", children: [
|
|
4922
|
-
/* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-2xl font-bold mb-4", children: "Case study not found" }),
|
|
4923
|
-
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-muted-foreground mb-6", children: error || "This case study doesn't exist or has been removed." }),
|
|
4924
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
4925
|
-
"a",
|
|
4926
|
-
{
|
|
4927
|
-
href: caseStudiesListUrl,
|
|
4928
|
-
className: "text-primary hover:underline font-medium",
|
|
4929
|
-
children: [
|
|
4930
|
-
"\u2190 ",
|
|
4931
|
-
backLabel
|
|
4932
|
-
]
|
|
4933
|
-
}
|
|
4934
|
-
)
|
|
4935
|
-
] }) });
|
|
4936
|
-
}
|
|
4937
|
-
const date = post.publishedAt || post.lastUpdated;
|
|
4938
|
-
const formattedDate = date ? new Date(date).toLocaleDateString("en-US", {
|
|
4939
|
-
year: "numeric",
|
|
4940
|
-
month: "long",
|
|
4941
|
-
day: "numeric"
|
|
4942
|
-
}) : "";
|
|
4943
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `w-full ${className}`, children: [
|
|
4944
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4945
|
-
PageHead,
|
|
4946
|
-
{
|
|
4947
|
-
title: post.seoTitle || post.title,
|
|
4948
|
-
description: post.seoDescription || post.excerpt || "",
|
|
4949
|
-
productName,
|
|
4950
|
-
keywords: post.seoKeywords || ""
|
|
4951
|
-
}
|
|
4952
|
-
),
|
|
4953
|
-
/* @__PURE__ */ jsxRuntime.jsxs("article", { className: "max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-16 sm:py-20", children: [
|
|
4954
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
4955
|
-
"a",
|
|
4956
|
-
{
|
|
4957
|
-
href: caseStudiesListUrl,
|
|
4958
|
-
className: "inline-flex items-center gap-1 text-sm text-muted-foreground hover:text-foreground mb-8 transition-colors",
|
|
4959
|
-
children: [
|
|
4960
|
-
"\u2190 ",
|
|
4961
|
-
backLabel
|
|
4962
|
-
]
|
|
4963
|
-
}
|
|
4964
|
-
),
|
|
4965
|
-
post.category && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "inline-block text-xs font-medium text-primary bg-primary/10 px-2.5 py-1 rounded-full mb-4", children: post.category }),
|
|
4966
|
-
/* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-3xl sm:text-4xl md:text-5xl font-bold tracking-tight text-foreground mb-4", children: post.title }),
|
|
4967
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3 text-sm text-muted-foreground mb-8", children: [
|
|
4968
|
-
post.author && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium text-foreground", children: post.author }),
|
|
4969
|
-
post.author && formattedDate && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground/40", children: "\xB7" }),
|
|
4970
|
-
formattedDate && /* @__PURE__ */ jsxRuntime.jsx("span", { children: formattedDate }),
|
|
4971
|
-
post.readingTime && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
4972
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground/40", children: "\xB7" }),
|
|
4973
|
-
/* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
|
|
4974
|
-
post.readingTime,
|
|
4975
|
-
" min read"
|
|
4976
|
-
] })
|
|
4977
|
-
] })
|
|
4978
|
-
] }),
|
|
4979
|
-
post.thumbnail && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "aspect-[16/9] rounded-xl overflow-hidden mb-10", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
4980
|
-
"img",
|
|
4981
|
-
{
|
|
4982
|
-
src: post.thumbnail,
|
|
4983
|
-
alt: post.title,
|
|
4984
|
-
className: "w-full h-full object-cover"
|
|
4985
|
-
}
|
|
4986
|
-
) }),
|
|
4987
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "prose prose-gray dark:prose-invert max-w-none", children: /* @__PURE__ */ jsxRuntime.jsx(ContentRenderer, { content: post.content, format: post.contentFormat }) })
|
|
4988
|
-
] })
|
|
4989
|
-
] });
|
|
4990
|
-
}
|
|
4991
|
-
var CHANGELOG_TAG = "changelog";
|
|
4992
|
-
var CHANGELOG_LIST_QUERY = `
|
|
4993
|
-
query ChangelogList($productId: String!, $limit: Int, $offset: Int) {
|
|
4994
|
-
publicContentPages(productId: $productId, limit: $limit, offset: $offset) {
|
|
4995
|
-
items {
|
|
4996
|
-
id
|
|
4997
|
-
title
|
|
4998
|
-
slug
|
|
4999
|
-
lastUpdated
|
|
5000
|
-
tags
|
|
5001
|
-
excerpt
|
|
5002
|
-
seoDescription
|
|
5003
|
-
thumbnail
|
|
5004
|
-
author
|
|
5005
|
-
publishedAt
|
|
5006
|
-
readingTime
|
|
5007
|
-
featured
|
|
5008
|
-
category
|
|
5009
|
-
}
|
|
5010
|
-
total
|
|
5011
|
-
hasMore
|
|
5012
|
-
}
|
|
5013
|
-
}
|
|
5014
|
-
`;
|
|
5015
|
-
var CHANGELOG_ENTRY_QUERY = `
|
|
5016
|
-
query ChangelogEntry($slug: String!, $productId: String) {
|
|
5017
|
-
publicContentPage(slug: $slug, productId: $productId) {
|
|
5018
|
-
content
|
|
5019
|
-
contentFormat
|
|
5020
|
-
}
|
|
5021
|
-
}
|
|
5022
|
-
`;
|
|
5023
|
-
function formatDate(value) {
|
|
5024
|
-
if (!value) return "";
|
|
5025
|
-
return new Date(value).toLocaleDateString("en-US", {
|
|
5026
|
-
year: "numeric",
|
|
5027
|
-
month: "long",
|
|
5028
|
-
day: "numeric"
|
|
5029
|
-
});
|
|
5030
|
-
}
|
|
5031
|
-
function sortByDateDesc(a, b) {
|
|
5032
|
-
const da = new Date(a.publishedAt || a.lastUpdated || 0).getTime();
|
|
5033
|
-
const db = new Date(b.publishedAt || b.lastUpdated || 0).getTime();
|
|
5034
|
-
return db - da;
|
|
5035
|
-
}
|
|
5036
|
-
function normalize(value) {
|
|
5037
|
-
return value.toLowerCase().trim();
|
|
5038
|
-
}
|
|
5039
|
-
function paginate(items, page, pageSize) {
|
|
5040
|
-
const start = (page - 1) * pageSize;
|
|
5041
|
-
return items.slice(start, start + pageSize);
|
|
5042
|
-
}
|
|
5043
|
-
function parseEntryDate(date) {
|
|
5044
|
-
return new Date(date).getTime();
|
|
5045
|
-
}
|
|
5046
|
-
function useCmsChangelog() {
|
|
5047
|
-
const client = useWebSDK();
|
|
5048
|
-
const config = useWebSDKConfig();
|
|
5049
|
-
const { product } = useProduct();
|
|
5050
|
-
const resolvedProductId = product?.id || config.productId;
|
|
5051
|
-
const [entries, setEntries] = React.useState([]);
|
|
5052
|
-
const [loading, setLoading] = React.useState(true);
|
|
5053
|
-
const [error, setError] = React.useState(null);
|
|
5054
|
-
const fetchEntries = React.useCallback(async () => {
|
|
5055
|
-
if (!resolvedProductId) {
|
|
5056
|
-
setLoading(false);
|
|
5057
|
-
return;
|
|
5058
|
-
}
|
|
5059
|
-
setLoading(true);
|
|
5060
|
-
try {
|
|
5061
|
-
const listResult = await client.query(CHANGELOG_LIST_QUERY, {
|
|
5062
|
-
productId: resolvedProductId,
|
|
5063
|
-
limit: 50,
|
|
5064
|
-
offset: 0
|
|
5065
|
-
});
|
|
5066
|
-
if (listResult.errors?.length) {
|
|
5067
|
-
setError(listResult.errors[0]?.message ?? "Failed to load changelog");
|
|
5068
|
-
return;
|
|
5069
|
-
}
|
|
5070
|
-
const changelogItems = (listResult.data?.publicContentPages?.items || []).filter((p) => p.tags?.includes(CHANGELOG_TAG)).sort(sortByDateDesc);
|
|
5071
|
-
const resolved = await Promise.all(
|
|
5072
|
-
changelogItems.map(async (item) => {
|
|
5073
|
-
try {
|
|
5074
|
-
const detail = await client.query(CHANGELOG_ENTRY_QUERY, {
|
|
5075
|
-
slug: item.slug,
|
|
5076
|
-
productId: resolvedProductId
|
|
5077
|
-
});
|
|
5078
|
-
const page = detail.data?.publicContentPage;
|
|
5079
|
-
return {
|
|
5080
|
-
...item,
|
|
5081
|
-
content: page?.content,
|
|
5082
|
-
contentFormat: page?.contentFormat
|
|
5083
|
-
};
|
|
5084
|
-
} catch {
|
|
5085
|
-
return { ...item };
|
|
5086
|
-
}
|
|
5087
|
-
})
|
|
5088
|
-
);
|
|
5089
|
-
setEntries(resolved);
|
|
5090
|
-
setError(null);
|
|
5091
|
-
} catch {
|
|
5092
|
-
setError("Failed to load changelog");
|
|
5093
|
-
} finally {
|
|
5094
|
-
setLoading(false);
|
|
5095
|
-
}
|
|
5096
|
-
}, [client, resolvedProductId]);
|
|
5097
|
-
React.useEffect(() => {
|
|
5098
|
-
fetchEntries();
|
|
5099
|
-
}, [fetchEntries]);
|
|
5100
|
-
return { entries, loading, error };
|
|
5101
|
-
}
|
|
5102
|
-
function CmsTimeline(props) {
|
|
5103
|
-
const { entries, loading, error } = props;
|
|
5104
|
-
if (loading) {
|
|
5105
|
-
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "max-w-3xl mx-auto space-y-12", children: [1, 2, 3].map((i) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
5106
|
-
"div",
|
|
5107
|
-
{
|
|
5108
|
-
className: "rounded-2xl border border-border/40 bg-card overflow-hidden animate-pulse",
|
|
5109
|
-
children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-6 sm:p-8 space-y-4", children: [
|
|
5110
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-3 bg-muted rounded w-1/3" }),
|
|
5111
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-6 bg-muted rounded w-2/3" }),
|
|
5112
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-4 bg-muted rounded" }),
|
|
5113
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-4 bg-muted rounded w-5/6" })
|
|
5114
|
-
] })
|
|
5115
|
-
},
|
|
5116
|
-
i
|
|
5117
|
-
)) });
|
|
5118
|
-
}
|
|
5119
|
-
if (error) {
|
|
5120
|
-
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-center py-12", children: /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-muted-foreground", children: error }) });
|
|
5121
|
-
}
|
|
5122
|
-
if (entries.length === 0) {
|
|
5123
|
-
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-center py-12", children: /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-muted-foreground", children: "No changelog entries yet. Check back soon!" }) });
|
|
5124
|
-
}
|
|
5125
|
-
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "max-w-3xl mx-auto space-y-12", children: entries.map((entry) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
5126
|
-
"article",
|
|
5127
|
-
{
|
|
5128
|
-
id: entry.slug,
|
|
5129
|
-
className: "rounded-2xl border border-border/40 bg-card overflow-hidden scroll-mt-24",
|
|
5130
|
-
children: [
|
|
5131
|
-
entry.thumbnail && /* @__PURE__ */ jsxRuntime.jsx(
|
|
5132
|
-
"img",
|
|
5133
|
-
{
|
|
5134
|
-
src: entry.thumbnail,
|
|
5135
|
-
alt: "",
|
|
5136
|
-
width: 1024,
|
|
5137
|
-
height: 576,
|
|
5138
|
-
loading: "lazy",
|
|
5139
|
-
decoding: "async",
|
|
5140
|
-
className: "w-full aspect-video object-cover border-b border-border/40"
|
|
5141
|
-
}
|
|
5142
|
-
),
|
|
5143
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-6 sm:p-8", children: [
|
|
5144
|
-
/* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-xs text-muted-foreground mb-2", children: [
|
|
5145
|
-
entry.publishedAt && /* @__PURE__ */ jsxRuntime.jsx("time", { dateTime: entry.publishedAt, children: formatDate(entry.publishedAt || entry.lastUpdated) }),
|
|
5146
|
-
entry.author && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
5147
|
-
" \xB7 ",
|
|
5148
|
-
entry.author
|
|
5149
|
-
] })
|
|
5150
|
-
] }),
|
|
5151
|
-
/* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-xl sm:text-2xl font-bold text-foreground mb-4", children: entry.title }),
|
|
5152
|
-
entry.content ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "prose prose-sm prose-gray dark:prose-invert max-w-none", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
5153
|
-
ContentRenderer,
|
|
5154
|
-
{
|
|
5155
|
-
content: entry.content,
|
|
5156
|
-
format: entry.contentFormat
|
|
5157
|
-
}
|
|
5158
|
-
) }) : entry.excerpt && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground leading-relaxed", children: entry.excerpt })
|
|
5159
|
-
] })
|
|
5160
|
-
]
|
|
5161
|
-
},
|
|
5162
|
-
entry.id
|
|
5163
|
-
)) });
|
|
5164
|
-
}
|
|
5165
|
-
function EntryContent({ content }) {
|
|
5166
|
-
if (typeof content === "string") {
|
|
5167
|
-
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "prose prose-sm prose-gray dark:prose-invert max-w-none", children: /* @__PURE__ */ jsxRuntime.jsx(ContentRenderer, { content, format: "markdown" }) });
|
|
5168
|
-
}
|
|
5169
|
-
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "prose prose-sm max-w-none", children: content });
|
|
5170
|
-
}
|
|
5171
|
-
function EntriesTimeline(props) {
|
|
5172
|
-
const { entries, pageSize, productName } = props;
|
|
5173
|
-
const [query, setQuery] = React.useState("");
|
|
5174
|
-
const [selectedCategory, setSelectedCategory] = React.useState(null);
|
|
5175
|
-
const [page, setPage] = React.useState(1);
|
|
5176
|
-
const categories = React.useMemo(
|
|
5177
|
-
() => Array.from(new Set(entries.map((e) => e.category))).sort(),
|
|
5178
|
-
[entries]
|
|
5179
|
-
);
|
|
5180
|
-
const filtered = React.useMemo(() => {
|
|
5181
|
-
const normalized = normalize(query);
|
|
5182
|
-
return entries.filter((entry) => {
|
|
5183
|
-
if (selectedCategory && entry.category !== selectedCategory) {
|
|
5184
|
-
return false;
|
|
5185
|
-
}
|
|
5186
|
-
if (!normalized) return true;
|
|
5187
|
-
const haystack = [
|
|
5188
|
-
entry.title,
|
|
5189
|
-
entry.version,
|
|
5190
|
-
entry.category,
|
|
5191
|
-
...entry.tags ?? [],
|
|
5192
|
-
typeof entry.content === "string" ? entry.content : ""
|
|
5193
|
-
].join(" ").toLowerCase();
|
|
5194
|
-
return haystack.includes(normalized);
|
|
5195
|
-
}).sort((a, b) => parseEntryDate(b.date) - parseEntryDate(a.date));
|
|
5196
|
-
}, [entries, query, selectedCategory]);
|
|
5197
|
-
const totalPages = Math.max(1, Math.ceil(filtered.length / pageSize));
|
|
5198
|
-
const safePage = Math.min(page, totalPages);
|
|
5199
|
-
const pageItems = paginate(filtered, safePage, pageSize);
|
|
5200
|
-
React.useEffect(() => {
|
|
5201
|
-
setPage(1);
|
|
5202
|
-
}, [query, selectedCategory]);
|
|
5203
|
-
if (entries.length === 0) {
|
|
5204
|
-
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-center py-12", children: /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-muted-foreground", children: "No changelog entries yet. Check back soon!" }) });
|
|
5205
|
-
}
|
|
5206
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "max-w-3xl mx-auto", children: [
|
|
5207
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mb-8 space-y-4", children: [
|
|
5208
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", children: [
|
|
5209
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
5210
|
-
lucideReact.Search,
|
|
5211
|
-
{
|
|
5212
|
-
className: "absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground",
|
|
5213
|
-
"aria-hidden": "true"
|
|
5214
|
-
}
|
|
5215
|
-
),
|
|
5216
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
5217
|
-
Input,
|
|
5218
|
-
{
|
|
5219
|
-
type: "search",
|
|
5220
|
-
placeholder: "Search releases, features, fixes\u2026",
|
|
5221
|
-
value: query,
|
|
5222
|
-
onChange: (e) => setQuery(e.target.value),
|
|
5223
|
-
className: "pl-9",
|
|
5224
|
-
"aria-label": "Search changelog"
|
|
5225
|
-
}
|
|
5226
|
-
)
|
|
5227
|
-
] }),
|
|
5228
|
-
categories.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap gap-2", children: [
|
|
5229
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
5230
|
-
"button",
|
|
5231
|
-
{
|
|
5232
|
-
type: "button",
|
|
5233
|
-
onClick: () => setSelectedCategory(null),
|
|
5234
|
-
className: cn(
|
|
5235
|
-
"px-3 py-1 rounded-full text-xs font-medium border transition-colors",
|
|
5236
|
-
selectedCategory === null ? "bg-primary text-primary-foreground border-primary" : "bg-background text-muted-foreground border-border/60 hover:border-primary/40"
|
|
5237
|
-
),
|
|
5238
|
-
children: "All"
|
|
5239
|
-
}
|
|
5240
|
-
),
|
|
5241
|
-
categories.map((category) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
5242
|
-
"button",
|
|
5243
|
-
{
|
|
5244
|
-
type: "button",
|
|
5245
|
-
onClick: () => setSelectedCategory(category),
|
|
5246
|
-
className: cn(
|
|
5247
|
-
"px-3 py-1 rounded-full text-xs font-medium border transition-colors",
|
|
5248
|
-
selectedCategory === category ? "bg-primary text-primary-foreground border-primary" : "bg-background text-muted-foreground border-border/60 hover:border-primary/40"
|
|
5249
|
-
),
|
|
5250
|
-
children: category
|
|
5251
|
-
},
|
|
5252
|
-
category
|
|
5253
|
-
))
|
|
5254
|
-
] })
|
|
5255
|
-
] }),
|
|
5256
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", children: [
|
|
5257
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
5258
|
-
"div",
|
|
5259
|
-
{
|
|
5260
|
-
className: "absolute left-[1.125rem] top-3 bottom-3 w-px bg-border/60 hidden sm:block",
|
|
5261
|
-
"aria-hidden": "true"
|
|
5262
|
-
}
|
|
5263
|
-
),
|
|
5264
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-8", children: pageItems.map((entry) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
5265
|
-
"article",
|
|
5266
|
-
{
|
|
5267
|
-
className: "relative sm:pl-12",
|
|
5268
|
-
children: [
|
|
5269
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
5270
|
-
"div",
|
|
5271
|
-
{
|
|
5272
|
-
className: "hidden sm:flex absolute left-2 top-2 h-5 w-5 items-center justify-center rounded-full border-2 border-background bg-primary ring-1 ring-border/40",
|
|
5273
|
-
"aria-hidden": "true",
|
|
5274
|
-
children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "h-2 w-2 rounded-full bg-primary-foreground" })
|
|
5275
|
-
}
|
|
5276
|
-
),
|
|
5277
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "rounded-2xl border border-border/40 bg-card overflow-hidden", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-6 sm:p-8", children: [
|
|
5278
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap items-center gap-2 mb-3", children: [
|
|
5279
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "inline-flex items-center rounded-md bg-primary/10 px-2 py-1 text-xs font-medium text-primary", children: entry.category }),
|
|
5280
|
-
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "inline-flex items-center rounded-md bg-muted px-2 py-1 text-xs font-mono font-medium text-muted-foreground", children: [
|
|
5281
|
-
"v",
|
|
5282
|
-
entry.version
|
|
5283
|
-
] }),
|
|
5284
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
5285
|
-
"time",
|
|
5286
|
-
{
|
|
5287
|
-
className: "text-xs text-muted-foreground",
|
|
5288
|
-
dateTime: entry.date,
|
|
5289
|
-
children: formatDate(entry.date)
|
|
5290
|
-
}
|
|
5291
|
-
)
|
|
5292
|
-
] }),
|
|
5293
|
-
entry.tags && entry.tags.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-wrap gap-1.5 mb-3", children: entry.tags.map((tag) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
5294
|
-
"span",
|
|
5295
|
-
{
|
|
5296
|
-
className: "text-[10px] uppercase tracking-wide text-muted-foreground bg-background border border-border/40 px-1.5 py-0.5 rounded",
|
|
5297
|
-
children: tag
|
|
5298
|
-
},
|
|
5299
|
-
tag
|
|
5300
|
-
)) }),
|
|
5301
|
-
/* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-xl sm:text-2xl font-bold text-foreground mb-4", children: entry.title }),
|
|
5302
|
-
/* @__PURE__ */ jsxRuntime.jsx(EntryContent, { content: entry.content })
|
|
5303
|
-
] }) })
|
|
5304
|
-
]
|
|
5305
|
-
},
|
|
5306
|
-
`${entry.version}-${entry.date}`
|
|
5307
|
-
)) })
|
|
5308
|
-
] }),
|
|
5309
|
-
pageItems.length === 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-center py-12", children: /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-muted-foreground", children: "No entries match your filters." }) }),
|
|
5310
|
-
totalPages > 1 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-10 flex items-center justify-between", children: [
|
|
5311
|
-
/* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-sm text-muted-foreground", children: [
|
|
5312
|
-
"Page ",
|
|
5313
|
-
safePage,
|
|
5314
|
-
" of ",
|
|
5315
|
-
totalPages,
|
|
5316
|
-
" \xB7 ",
|
|
5317
|
-
filtered.length,
|
|
5318
|
-
" entries"
|
|
5319
|
-
] }),
|
|
5320
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
|
|
5321
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
5322
|
-
Button,
|
|
5323
|
-
{
|
|
5324
|
-
variant: "outline",
|
|
5325
|
-
size: "sm",
|
|
5326
|
-
onClick: () => setPage((p) => Math.max(1, p - 1)),
|
|
5327
|
-
disabled: safePage <= 1,
|
|
5328
|
-
"aria-label": "Previous page",
|
|
5329
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronLeft, { className: "h-4 w-4" })
|
|
5330
|
-
}
|
|
5331
|
-
),
|
|
5332
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
5333
|
-
Button,
|
|
5334
|
-
{
|
|
5335
|
-
variant: "outline",
|
|
5336
|
-
size: "sm",
|
|
5337
|
-
onClick: () => setPage((p) => Math.min(totalPages, p + 1)),
|
|
5338
|
-
disabled: safePage >= totalPages,
|
|
5339
|
-
"aria-label": "Next page",
|
|
5340
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronRight, { className: "h-4 w-4" })
|
|
5341
|
-
}
|
|
5342
|
-
)
|
|
5343
|
-
] })
|
|
5344
|
-
] }),
|
|
5345
|
-
productName && pageItems.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("script", { type: "application/ld+json", children: JSON.stringify({
|
|
5346
|
-
"@context": "https://schema.org",
|
|
5347
|
-
"@type": "ItemList",
|
|
5348
|
-
name: `${productName} Changelog`,
|
|
5349
|
-
itemListElement: pageItems.map((entry, index) => ({
|
|
5350
|
-
"@type": "ListItem",
|
|
5351
|
-
position: (safePage - 1) * pageSize + index + 1,
|
|
5352
|
-
name: entry.title,
|
|
5353
|
-
description: typeof entry.content === "string" ? entry.content.slice(0, 200) : void 0,
|
|
5354
|
-
datePublished: entry.date
|
|
5355
|
-
}))
|
|
5356
|
-
}) })
|
|
5357
|
-
] });
|
|
5358
|
-
}
|
|
5359
|
-
function ChangelogPage(props) {
|
|
5360
|
-
const {
|
|
5361
|
-
productName,
|
|
5362
|
-
heroEyebrow = "What's new",
|
|
5363
|
-
heroTitle = "Changelog",
|
|
5364
|
-
heroSubtitle = "Platform updates, improvements, and announcements \u2014 newest first.",
|
|
5365
|
-
ctaTitle,
|
|
5366
|
-
ctaSubtitle,
|
|
5367
|
-
ctaHref,
|
|
5368
|
-
ctaLabel,
|
|
5369
|
-
className = "",
|
|
5370
|
-
seoTitle,
|
|
5371
|
-
seoDescription,
|
|
5372
|
-
seoKeywords,
|
|
5373
|
-
entries,
|
|
5374
|
-
pageSize = 10
|
|
5375
|
-
} = props;
|
|
5376
|
-
const { product } = useProduct();
|
|
5377
|
-
const resolvedProductName = productName || product?.name;
|
|
5378
|
-
const cms = useCmsChangelog();
|
|
5379
|
-
const showCta = Boolean(ctaHref && ctaLabel);
|
|
5380
|
-
const isPropDriven = entries != null;
|
|
5381
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `w-full ${className}`, children: [
|
|
5382
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
5383
|
-
PageHead,
|
|
5384
|
-
{
|
|
5385
|
-
title: seoTitle || heroTitle,
|
|
5386
|
-
description: seoDescription || heroSubtitle,
|
|
5387
|
-
productName: resolvedProductName,
|
|
5388
|
-
keywords: seoKeywords
|
|
5389
|
-
}
|
|
5390
|
-
),
|
|
5391
|
-
/* @__PURE__ */ jsxRuntime.jsx("section", { className: "py-16 md:py-20 px-4 sm:px-6 lg:px-8 text-center", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "max-w-3xl mx-auto", children: [
|
|
5392
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "inline-flex items-center gap-2 px-3 py-1.5 rounded-full bg-primary/10 border border-primary/20 text-xs font-medium text-primary mb-6", children: [
|
|
5393
|
-
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.History, { className: "h-3.5 w-3.5", "aria-hidden": "true" }),
|
|
5394
|
-
heroEyebrow
|
|
5395
|
-
] }),
|
|
5396
|
-
/* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-4xl sm:text-5xl font-bold text-foreground mb-4", children: heroTitle }),
|
|
5397
|
-
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-lg text-muted-foreground", children: heroSubtitle })
|
|
5398
|
-
] }) }),
|
|
5399
|
-
/* @__PURE__ */ jsxRuntime.jsx("section", { className: "px-4 sm:px-6 lg:px-8 pb-20", children: isPropDriven ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
5400
|
-
EntriesTimeline,
|
|
5401
|
-
{
|
|
5402
|
-
entries,
|
|
5403
|
-
pageSize,
|
|
5404
|
-
productName: resolvedProductName
|
|
5405
|
-
}
|
|
5406
|
-
) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
5407
|
-
CmsTimeline,
|
|
5408
|
-
{
|
|
5409
|
-
entries: cms.entries,
|
|
5410
|
-
loading: cms.loading,
|
|
5411
|
-
error: cms.error
|
|
5412
|
-
}
|
|
5413
|
-
) }),
|
|
5414
|
-
showCta && /* @__PURE__ */ jsxRuntime.jsx("section", { className: "py-14 px-4 sm:px-6 lg:px-8 bg-primary/5 border-t border-border/40", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "max-w-2xl mx-auto text-center", children: [
|
|
5415
|
-
ctaTitle && /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-2xl font-bold text-foreground mb-3", children: ctaTitle }),
|
|
5416
|
-
ctaSubtitle && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-muted-foreground mb-7", children: ctaSubtitle }),
|
|
5417
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
5418
|
-
"a",
|
|
5419
|
-
{
|
|
5420
|
-
href: ctaHref,
|
|
5421
|
-
className: "inline-flex items-center justify-center rounded-lg bg-primary px-6 py-3 text-sm font-medium text-primary-foreground hover:opacity-90 transition-opacity",
|
|
5422
|
-
children: ctaLabel
|
|
5423
|
-
}
|
|
5424
|
-
)
|
|
5425
|
-
] }) })
|
|
5426
|
-
] });
|
|
5427
|
-
}
|
|
5428
|
-
|
|
5429
|
-
// src/content/utils.ts
|
|
5430
|
-
function formatBytes(bytes) {
|
|
5431
|
-
if (bytes == null) return "";
|
|
5432
|
-
if (bytes < 1024) return `${bytes} B`;
|
|
5433
|
-
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(0)} KB`;
|
|
5434
|
-
return `${(bytes / 1024 / 1024).toFixed(1)} MB`;
|
|
5435
|
-
}
|
|
5436
|
-
function fileExt(file) {
|
|
5437
|
-
const name = file.fileName || file.fileUrl.split("/").pop()?.split("?")[0] || "";
|
|
5438
|
-
const ext = name.split(".").pop();
|
|
5439
|
-
return ext && ext !== name ? ext.toUpperCase() : "FILE";
|
|
5440
|
-
}
|
|
5441
|
-
async function downloadFile(e, file) {
|
|
5442
|
-
e.preventDefault();
|
|
5443
|
-
const openInTab = () => window.open(file.fileUrl, "_blank", "noopener,noreferrer");
|
|
5444
|
-
const name = file.fileName || file.fileUrl.split("/").pop()?.split("?")[0] || file.title || "download";
|
|
5445
|
-
try {
|
|
5446
|
-
const res = await fetch(file.fileUrl, { mode: "cors" });
|
|
5447
|
-
if (!res.ok) {
|
|
5448
|
-
openInTab();
|
|
5449
|
-
return;
|
|
5450
|
-
}
|
|
5451
|
-
const blob = await res.blob();
|
|
5452
|
-
const objUrl = URL.createObjectURL(blob);
|
|
5453
|
-
const a = document.createElement("a");
|
|
5454
|
-
a.href = objUrl;
|
|
5455
|
-
a.download = name;
|
|
5456
|
-
document.body.appendChild(a);
|
|
5457
|
-
a.click();
|
|
5458
|
-
a.remove();
|
|
5459
|
-
URL.revokeObjectURL(objUrl);
|
|
5460
|
-
} catch {
|
|
5461
|
-
openInTab();
|
|
5462
|
-
}
|
|
5463
|
-
}
|
|
5464
|
-
var PRESS_KIT_QUERY = `
|
|
5465
|
-
query PublicPressKit($productId: String!, $limit: Int, $offset: Int) {
|
|
5466
|
-
publicPressKitAssets(productId: $productId, limit: $limit, offset: $offset) {
|
|
5467
|
-
items {
|
|
5468
|
-
id
|
|
5469
|
-
category
|
|
5470
|
-
title
|
|
5471
|
-
slug
|
|
5472
|
-
description
|
|
5473
|
-
fileUrl
|
|
5474
|
-
fileName
|
|
5475
|
-
fileSize
|
|
5476
|
-
mimeType
|
|
5477
|
-
thumbnailUrl
|
|
5478
|
-
version
|
|
5479
|
-
tags
|
|
5480
|
-
sortOrder
|
|
5481
|
-
updatedAt
|
|
5482
|
-
}
|
|
5483
|
-
total
|
|
5484
|
-
hasMore
|
|
5485
|
-
}
|
|
5486
|
-
}
|
|
5487
|
-
`;
|
|
5488
|
-
var PRESS_RELEASES_QUERY = `
|
|
5489
|
-
query PublicPressReleases($productId: String!, $limit: Int, $offset: Int) {
|
|
5490
|
-
publicContentPages(productId: $productId, limit: $limit, offset: $offset) {
|
|
5491
|
-
items {
|
|
5492
|
-
id
|
|
5493
|
-
title
|
|
5494
|
-
slug
|
|
5495
|
-
lastUpdated
|
|
5496
|
-
tags
|
|
5497
|
-
seoDescription
|
|
5498
|
-
excerpt
|
|
5499
|
-
thumbnail
|
|
5500
|
-
author
|
|
5501
|
-
publishedAt
|
|
5502
|
-
readingTime
|
|
5503
|
-
featured
|
|
5504
|
-
category
|
|
5505
|
-
}
|
|
5506
|
-
total
|
|
5507
|
-
hasMore
|
|
5508
|
-
}
|
|
5509
|
-
}
|
|
5510
|
-
`;
|
|
5511
|
-
var CATEGORY_ORDER = [
|
|
5512
|
-
"LOGO",
|
|
5513
|
-
"BANNER",
|
|
5514
|
-
"SCREENSHOT",
|
|
5515
|
-
"BRAND_GUIDE",
|
|
5516
|
-
"BROCHURE",
|
|
5517
|
-
"DOCUMENT",
|
|
5518
|
-
"PRESS_RELEASE"
|
|
5519
|
-
];
|
|
5520
|
-
var CATEGORY_LABELS = {
|
|
5521
|
-
LOGO: "Logos",
|
|
5522
|
-
BANNER: "Banners",
|
|
5523
|
-
SCREENSHOT: "Screenshots",
|
|
5524
|
-
BRAND_GUIDE: "Brand Guides",
|
|
5525
|
-
BROCHURE: "Brochures",
|
|
5526
|
-
DOCUMENT: "Documents",
|
|
5527
|
-
PRESS_RELEASE: "Press Releases"
|
|
5528
|
-
};
|
|
5529
|
-
function isImageAsset(a) {
|
|
5530
|
-
return (a.mimeType || "").startsWith("image/");
|
|
5531
|
-
}
|
|
5532
|
-
function PressKitLightbox({
|
|
5533
|
-
asset,
|
|
5534
|
-
onClose
|
|
5535
|
-
}) {
|
|
5536
|
-
React.useEffect(() => {
|
|
5537
|
-
if (!asset) return;
|
|
5538
|
-
const onKey = (e) => {
|
|
5539
|
-
if (e.key === "Escape") onClose();
|
|
5540
|
-
};
|
|
5541
|
-
document.addEventListener("keydown", onKey);
|
|
5542
|
-
const prevOverflow = document.body.style.overflow;
|
|
5543
|
-
document.body.style.overflow = "hidden";
|
|
5544
|
-
return () => {
|
|
5545
|
-
document.removeEventListener("keydown", onKey);
|
|
5546
|
-
document.body.style.overflow = prevOverflow;
|
|
5547
|
-
};
|
|
5548
|
-
}, [asset, onClose]);
|
|
5549
|
-
if (!asset) return null;
|
|
5550
|
-
const ext = (asset.mimeType || "file").split("/").pop()?.toUpperCase() || "FILE";
|
|
5551
|
-
const size = formatBytes(asset.fileSize);
|
|
5552
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
5553
|
-
"div",
|
|
5554
|
-
{
|
|
5555
|
-
role: "dialog",
|
|
5556
|
-
"aria-modal": "true",
|
|
5557
|
-
"aria-label": `${asset.title} preview`,
|
|
5558
|
-
onClick: onClose,
|
|
5559
|
-
className: "fixed inset-0 z-50 flex items-center justify-center bg-black/80 p-4 sm:p-8",
|
|
5560
|
-
children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
5561
|
-
"div",
|
|
5562
|
-
{
|
|
5563
|
-
onClick: (e) => e.stopPropagation(),
|
|
5564
|
-
className: "relative flex max-h-full w-full max-w-4xl flex-col overflow-hidden rounded-xl border bg-card shadow-2xl",
|
|
5565
|
-
children: [
|
|
5566
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start justify-between gap-4 border-b p-4", children: [
|
|
5567
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0", children: [
|
|
5568
|
-
/* @__PURE__ */ jsxRuntime.jsx("h3", { className: "truncate text-sm font-semibold text-foreground", children: asset.title }),
|
|
5569
|
-
/* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-xs text-muted-foreground", children: [
|
|
5570
|
-
ext,
|
|
5571
|
-
size ? ` \xB7 ${size}` : ""
|
|
5572
|
-
] })
|
|
5573
|
-
] }),
|
|
5574
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
5575
|
-
"button",
|
|
5576
|
-
{
|
|
5577
|
-
type: "button",
|
|
5578
|
-
onClick: onClose,
|
|
5579
|
-
"aria-label": "Close preview",
|
|
5580
|
-
className: "shrink-0 rounded-md p-1.5 text-muted-foreground transition-colors hover:bg-muted hover:text-foreground",
|
|
5581
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
5582
|
-
"svg",
|
|
5583
|
-
{
|
|
5584
|
-
className: "h-5 w-5",
|
|
5585
|
-
fill: "none",
|
|
5586
|
-
viewBox: "0 0 24 24",
|
|
5587
|
-
stroke: "currentColor",
|
|
5588
|
-
strokeWidth: 2,
|
|
5589
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
5590
|
-
"path",
|
|
5591
|
-
{
|
|
5592
|
-
strokeLinecap: "round",
|
|
5593
|
-
strokeLinejoin: "round",
|
|
5594
|
-
d: "M6 18L18 6M6 6l12 12"
|
|
5595
|
-
}
|
|
5596
|
-
)
|
|
5597
|
-
}
|
|
5598
|
-
)
|
|
5599
|
-
}
|
|
5600
|
-
)
|
|
5601
|
-
] }),
|
|
5602
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-1 items-center justify-center overflow-auto bg-muted/20 p-4", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
5603
|
-
"img",
|
|
5604
|
-
{
|
|
5605
|
-
src: asset.fileUrl,
|
|
5606
|
-
alt: asset.title,
|
|
5607
|
-
className: "max-h-[70vh] w-auto max-w-full object-contain"
|
|
5608
|
-
}
|
|
5609
|
-
) }),
|
|
5610
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex justify-end border-t p-4", children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
5611
|
-
"a",
|
|
5612
|
-
{
|
|
5613
|
-
href: asset.fileUrl,
|
|
5614
|
-
target: "_blank",
|
|
5615
|
-
rel: "noopener noreferrer",
|
|
5616
|
-
download: true,
|
|
5617
|
-
onClick: (e) => {
|
|
5618
|
-
void downloadFile(e, asset);
|
|
5619
|
-
},
|
|
5620
|
-
className: "inline-flex items-center gap-1.5 rounded-lg bg-primary px-4 py-2 text-sm font-medium text-primary-foreground transition-opacity hover:opacity-90",
|
|
5621
|
-
children: [
|
|
5622
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
5623
|
-
"svg",
|
|
5624
|
-
{
|
|
5625
|
-
className: "h-4 w-4",
|
|
5626
|
-
fill: "none",
|
|
5627
|
-
viewBox: "0 0 24 24",
|
|
5628
|
-
stroke: "currentColor",
|
|
5629
|
-
strokeWidth: 2,
|
|
5630
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
5631
|
-
"path",
|
|
5632
|
-
{
|
|
5633
|
-
strokeLinecap: "round",
|
|
5634
|
-
strokeLinejoin: "round",
|
|
5635
|
-
d: "M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4"
|
|
5636
|
-
}
|
|
5637
|
-
)
|
|
5638
|
-
}
|
|
5639
|
-
),
|
|
5640
|
-
"Download"
|
|
5641
|
-
]
|
|
5642
|
-
}
|
|
5643
|
-
) })
|
|
5644
|
-
]
|
|
5645
|
-
}
|
|
5646
|
-
)
|
|
5647
|
-
}
|
|
5648
|
-
);
|
|
5649
|
-
}
|
|
5650
|
-
function AssetCard({
|
|
5651
|
-
asset,
|
|
5652
|
-
onPreview
|
|
5653
|
-
}) {
|
|
5654
|
-
const date = asset.updatedAt ? new Date(asset.updatedAt).toLocaleDateString("en-US", {
|
|
5655
|
-
year: "numeric",
|
|
5656
|
-
month: "short",
|
|
5657
|
-
day: "numeric"
|
|
5658
|
-
}) : "";
|
|
5659
|
-
const ext = (asset.mimeType || "file").split("/").pop()?.toUpperCase() || "FILE";
|
|
5660
|
-
const size = formatBytes(asset.fileSize);
|
|
5661
|
-
const [imgError, setImgError] = React.useState(false);
|
|
5662
|
-
const thumb = asset.thumbnailUrl && !imgError ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "aspect-[16/9] overflow-hidden bg-muted/30", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
5663
|
-
"img",
|
|
5664
|
-
{
|
|
5665
|
-
src: asset.thumbnailUrl,
|
|
5666
|
-
alt: asset.title,
|
|
5667
|
-
className: "w-full h-full object-contain group-hover:scale-105 transition-transform duration-300",
|
|
5668
|
-
loading: "lazy",
|
|
5669
|
-
onError: () => setImgError(true)
|
|
5670
|
-
}
|
|
5671
|
-
) }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "aspect-[16/9] bg-muted/50 flex items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
5672
|
-
"svg",
|
|
5673
|
-
{
|
|
5674
|
-
className: "w-12 h-12 text-muted-foreground/20",
|
|
5675
|
-
fill: "none",
|
|
5676
|
-
viewBox: "0 0 24 24",
|
|
5677
|
-
stroke: "currentColor",
|
|
5678
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
5679
|
-
"path",
|
|
5680
|
-
{
|
|
5681
|
-
strokeLinecap: "round",
|
|
5682
|
-
strokeLinejoin: "round",
|
|
5683
|
-
strokeWidth: 1.5,
|
|
5684
|
-
d: "M7 21h10a2 2 0 002-2V9.414a1 1 0 00-.293-.707l-5.414-5.414A1 1 0 0012.586 3H7a2 2 0 00-2 2v14a2 2 0 002 2z"
|
|
5685
|
-
}
|
|
5686
|
-
)
|
|
5687
|
-
}
|
|
5688
|
-
) });
|
|
5689
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "group flex flex-col bg-card border rounded-xl overflow-hidden hover:shadow-lg hover:border-primary/30 transition-all duration-300", children: [
|
|
5690
|
-
onPreview && isImageAsset(asset) ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
5691
|
-
"button",
|
|
5692
|
-
{
|
|
5693
|
-
type: "button",
|
|
5694
|
-
onClick: () => onPreview(asset),
|
|
5695
|
-
"aria-label": `Preview ${asset.title}`,
|
|
5696
|
-
className: "block w-full text-left cursor-zoom-in",
|
|
5697
|
-
children: thumb
|
|
5698
|
-
}
|
|
5699
|
-
) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
5700
|
-
"a",
|
|
5701
|
-
{
|
|
5702
|
-
href: asset.fileUrl,
|
|
5703
|
-
target: "_blank",
|
|
5704
|
-
rel: "noopener noreferrer",
|
|
5705
|
-
"aria-label": `Open ${asset.title}`,
|
|
5706
|
-
className: "block",
|
|
5707
|
-
children: thumb
|
|
5708
|
-
}
|
|
5709
|
-
),
|
|
5710
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col flex-1 p-5", children: [
|
|
5711
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "inline-block self-start text-xs font-medium text-primary bg-primary/10 px-2.5 py-1 rounded-full mb-3", children: CATEGORY_LABELS[asset.category] ?? asset.category }),
|
|
5712
|
-
/* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-base font-semibold text-foreground mb-1 line-clamp-2", children: asset.title }),
|
|
5713
|
-
asset.description && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground mb-3 line-clamp-2", children: asset.description }),
|
|
5714
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-auto flex items-center justify-between pt-3", children: [
|
|
5715
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 text-xs text-muted-foreground", children: [
|
|
5716
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium", children: ext }),
|
|
5717
|
-
size && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
5718
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground/40", children: "\xB7" }),
|
|
5719
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { children: size })
|
|
5720
|
-
] }),
|
|
5721
|
-
date && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
5722
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground/40", children: "\xB7" }),
|
|
5723
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { children: date })
|
|
5724
|
-
] })
|
|
5725
|
-
] }),
|
|
5726
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
5727
|
-
"a",
|
|
5728
|
-
{
|
|
5729
|
-
href: asset.fileUrl,
|
|
5730
|
-
target: "_blank",
|
|
5731
|
-
rel: "noopener noreferrer",
|
|
5732
|
-
download: true,
|
|
5733
|
-
onClick: (e) => {
|
|
5734
|
-
void downloadFile(e, asset);
|
|
5735
|
-
},
|
|
5736
|
-
className: "inline-flex items-center gap-1.5 text-sm font-medium text-primary hover:underline",
|
|
5737
|
-
children: [
|
|
5738
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
5739
|
-
"svg",
|
|
5740
|
-
{
|
|
5741
|
-
className: "w-4 h-4",
|
|
5742
|
-
fill: "none",
|
|
5743
|
-
viewBox: "0 0 24 24",
|
|
5744
|
-
stroke: "currentColor",
|
|
5745
|
-
strokeWidth: 2,
|
|
5746
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
5747
|
-
"path",
|
|
5748
|
-
{
|
|
5749
|
-
strokeLinecap: "round",
|
|
5750
|
-
strokeLinejoin: "round",
|
|
5751
|
-
d: "M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4"
|
|
5752
|
-
}
|
|
5753
|
-
)
|
|
5754
|
-
}
|
|
5755
|
-
),
|
|
5756
|
-
"Download"
|
|
5757
|
-
]
|
|
5758
|
-
}
|
|
5759
|
-
)
|
|
5760
|
-
] })
|
|
5761
|
-
] })
|
|
5762
|
-
] });
|
|
5763
|
-
}
|
|
5764
|
-
function PressReleaseCard({
|
|
5765
|
-
release,
|
|
5766
|
-
basePath
|
|
5767
|
-
}) {
|
|
5768
|
-
const date = release.publishedAt || release.lastUpdated;
|
|
5769
|
-
const formattedDate = date ? new Date(date).toLocaleDateString("en-US", {
|
|
5770
|
-
year: "numeric",
|
|
5771
|
-
month: "long",
|
|
5772
|
-
day: "numeric"
|
|
5773
|
-
}) : "";
|
|
5774
|
-
const [imgError, setImgError] = React.useState(false);
|
|
5775
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
5776
|
-
"a",
|
|
5777
|
-
{
|
|
5778
|
-
href: `${basePath}/${release.slug}`,
|
|
5779
|
-
className: "group flex flex-col bg-card border rounded-xl overflow-hidden hover:shadow-lg hover:border-primary/30 transition-all duration-300",
|
|
5780
|
-
children: [
|
|
5781
|
-
release.thumbnail && !imgError ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "aspect-[16/9] overflow-hidden bg-muted/30", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
5782
|
-
"img",
|
|
5783
|
-
{
|
|
5784
|
-
src: release.thumbnail,
|
|
5785
|
-
alt: release.title,
|
|
5786
|
-
className: "w-full h-full object-cover group-hover:scale-105 transition-transform duration-300",
|
|
5787
|
-
loading: "lazy",
|
|
5788
|
-
onError: () => setImgError(true)
|
|
5789
|
-
}
|
|
5790
|
-
) }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "aspect-[16/9] bg-muted/50 flex items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
5791
|
-
"svg",
|
|
5792
|
-
{
|
|
5793
|
-
className: "w-12 h-12 text-muted-foreground/20",
|
|
5794
|
-
fill: "none",
|
|
5795
|
-
viewBox: "0 0 24 24",
|
|
5796
|
-
stroke: "currentColor",
|
|
5797
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
5798
|
-
"path",
|
|
5799
|
-
{
|
|
5800
|
-
strokeLinecap: "round",
|
|
5801
|
-
strokeLinejoin: "round",
|
|
5802
|
-
strokeWidth: 1.5,
|
|
5803
|
-
d: "M19 20H5a2 2 0 01-2-2V6a2 2 0 012-2h10a2 2 0 012 2v1m-1 13a2 2 0 01-2-2V7m3 13a2 2 0 002-2V9a2 2 0 00-2-2h-2m-4-3H9M7 16h6M7 8h6v4H7V8z"
|
|
5804
|
-
}
|
|
5805
|
-
)
|
|
5806
|
-
}
|
|
5807
|
-
) }),
|
|
5808
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col flex-1 p-5 sm:p-6", children: [
|
|
5809
|
-
release.category && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "inline-block self-start text-xs font-medium text-primary bg-primary/10 px-2.5 py-1 rounded-full mb-3", children: release.category }),
|
|
5810
|
-
/* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-lg font-semibold text-foreground mb-2 group-hover:text-primary transition-colors line-clamp-2", children: release.title }),
|
|
5811
|
-
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground mb-4 line-clamp-3", children: release.excerpt || release.seoDescription || "" }),
|
|
5812
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-auto flex items-center gap-3 text-xs text-muted-foreground", children: [
|
|
5813
|
-
release.author && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium", children: release.author }),
|
|
5814
|
-
release.author && formattedDate && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground/40", children: "\xB7" }),
|
|
5815
|
-
formattedDate && /* @__PURE__ */ jsxRuntime.jsx("span", { children: formattedDate }),
|
|
5816
|
-
release.readingTime ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
5817
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground/40", children: "\xB7" }),
|
|
5818
|
-
/* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
|
|
5819
|
-
release.readingTime,
|
|
5820
|
-
" min read"
|
|
5821
|
-
] })
|
|
5822
|
-
] }) : null
|
|
2530
|
+
/* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-2xl font-bold text-foreground mb-3", children: "Invalid Unsubscribe Link" }),
|
|
2531
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-muted-foreground", children: "This unsubscribe link is invalid or missing. If you want to unsubscribe, please use the link from your newsletter email." })
|
|
5823
2532
|
] })
|
|
5824
2533
|
] })
|
|
5825
2534
|
]
|
|
5826
2535
|
}
|
|
5827
2536
|
);
|
|
5828
2537
|
}
|
|
5829
|
-
function
|
|
5830
|
-
|
|
5831
|
-
|
|
5832
|
-
|
|
5833
|
-
|
|
5834
|
-
mediaContactEmail,
|
|
5835
|
-
brandKitUrl,
|
|
5836
|
-
className = "",
|
|
5837
|
-
seoTitle,
|
|
5838
|
-
seoDescription,
|
|
5839
|
-
seoKeywords,
|
|
5840
|
-
showPressReleases = true,
|
|
5841
|
-
pressReleaseBasePath = "/press",
|
|
5842
|
-
pressReleasesTitle = "Press Releases"
|
|
5843
|
-
} = props;
|
|
5844
|
-
const client = useWebSDK();
|
|
5845
|
-
const config = useWebSDKConfig();
|
|
5846
|
-
const { product } = useProduct();
|
|
5847
|
-
const resolvedProductId = product?.id || config.productId;
|
|
5848
|
-
const contactEmail = mediaContactEmail || product?.contactEmail || void 0;
|
|
5849
|
-
const [assets, setAssets] = React.useState([]);
|
|
5850
|
-
const [loading, setLoading] = React.useState(true);
|
|
5851
|
-
const [error, setError] = React.useState(null);
|
|
5852
|
-
const [activeCategory, setActiveCategory] = React.useState("ALL");
|
|
5853
|
-
const [releases, setReleases] = React.useState([]);
|
|
5854
|
-
const [releasesLoading, setReleasesLoading] = React.useState(true);
|
|
5855
|
-
const [preview, setPreview] = React.useState(null);
|
|
5856
|
-
const fetchAssets = React.useCallback(async () => {
|
|
5857
|
-
if (!resolvedProductId) {
|
|
5858
|
-
setLoading(false);
|
|
5859
|
-
return;
|
|
5860
|
-
}
|
|
5861
|
-
setLoading(true);
|
|
5862
|
-
try {
|
|
5863
|
-
const PAGE_SIZE = 100;
|
|
5864
|
-
const MAX_PAGES = 10;
|
|
5865
|
-
const all = [];
|
|
5866
|
-
let offset = 0;
|
|
5867
|
-
for (let pageNum = 0; pageNum < MAX_PAGES; pageNum++) {
|
|
5868
|
-
const result = await client.query(PRESS_KIT_QUERY, {
|
|
5869
|
-
productId: resolvedProductId,
|
|
5870
|
-
limit: PAGE_SIZE,
|
|
5871
|
-
offset
|
|
5872
|
-
});
|
|
5873
|
-
if (result.errors?.length) {
|
|
5874
|
-
setError(result.errors[0]?.message ?? "Failed to load press kit");
|
|
5875
|
-
return;
|
|
5876
|
-
}
|
|
5877
|
-
const page = result.data?.publicPressKitAssets;
|
|
5878
|
-
const items = page?.items || [];
|
|
5879
|
-
all.push(...items);
|
|
5880
|
-
if (!page?.hasMore || items.length === 0) break;
|
|
5881
|
-
offset += PAGE_SIZE;
|
|
5882
|
-
}
|
|
5883
|
-
setAssets(all);
|
|
5884
|
-
setError(null);
|
|
5885
|
-
} catch {
|
|
5886
|
-
setError("Failed to load press kit");
|
|
5887
|
-
} finally {
|
|
5888
|
-
setLoading(false);
|
|
5889
|
-
}
|
|
5890
|
-
}, [client, resolvedProductId]);
|
|
5891
|
-
React.useEffect(() => {
|
|
5892
|
-
fetchAssets();
|
|
5893
|
-
}, [fetchAssets]);
|
|
5894
|
-
const fetchReleases = React.useCallback(async () => {
|
|
5895
|
-
if (!resolvedProductId || !showPressReleases) {
|
|
5896
|
-
setReleasesLoading(false);
|
|
5897
|
-
return;
|
|
5898
|
-
}
|
|
5899
|
-
setReleasesLoading(true);
|
|
5900
|
-
try {
|
|
5901
|
-
const PAGE_SIZE = 100;
|
|
5902
|
-
const MAX_PAGES = 10;
|
|
5903
|
-
const all = [];
|
|
5904
|
-
let offset = 0;
|
|
5905
|
-
for (let pageNum = 0; pageNum < MAX_PAGES; pageNum++) {
|
|
5906
|
-
const result = await client.query(PRESS_RELEASES_QUERY, {
|
|
5907
|
-
productId: resolvedProductId,
|
|
5908
|
-
limit: PAGE_SIZE,
|
|
5909
|
-
offset
|
|
5910
|
-
});
|
|
5911
|
-
if (result.errors?.length) {
|
|
5912
|
-
setReleases([]);
|
|
5913
|
-
return;
|
|
5914
|
-
}
|
|
5915
|
-
const page = result.data?.publicContentPages;
|
|
5916
|
-
const items = page?.items || [];
|
|
5917
|
-
all.push(...items);
|
|
5918
|
-
if (!page?.hasMore || items.length === 0) break;
|
|
5919
|
-
offset += PAGE_SIZE;
|
|
5920
|
-
}
|
|
5921
|
-
const pressOnly = all.filter((p) => p.tags?.includes("press")).sort((a, b) => {
|
|
5922
|
-
const da = new Date(a.publishedAt || a.lastUpdated || 0).getTime();
|
|
5923
|
-
const db = new Date(b.publishedAt || b.lastUpdated || 0).getTime();
|
|
5924
|
-
return db - da;
|
|
5925
|
-
});
|
|
5926
|
-
setReleases(pressOnly);
|
|
5927
|
-
} catch {
|
|
5928
|
-
setReleases([]);
|
|
5929
|
-
} finally {
|
|
5930
|
-
setReleasesLoading(false);
|
|
5931
|
-
}
|
|
5932
|
-
}, [client, resolvedProductId, showPressReleases]);
|
|
2538
|
+
function RybbitAnalytics({
|
|
2539
|
+
siteId,
|
|
2540
|
+
scriptUrl = "https://analytics.burdenoff.com/api/script.js",
|
|
2541
|
+
enabled = true
|
|
2542
|
+
}) {
|
|
5933
2543
|
React.useEffect(() => {
|
|
5934
|
-
|
|
5935
|
-
|
|
5936
|
-
|
|
5937
|
-
|
|
5938
|
-
|
|
5939
|
-
|
|
5940
|
-
|
|
5941
|
-
|
|
5942
|
-
|
|
5943
|
-
|
|
5944
|
-
|
|
5945
|
-
|
|
5946
|
-
|
|
5947
|
-
|
|
5948
|
-
|
|
5949
|
-
|
|
5950
|
-
productName,
|
|
5951
|
-
keywords: seoKeywords
|
|
2544
|
+
if (!enabled || !siteId) return;
|
|
2545
|
+
const existingScript = document.querySelector(
|
|
2546
|
+
`script[data-site-id="${siteId}"]`
|
|
2547
|
+
);
|
|
2548
|
+
if (existingScript) return;
|
|
2549
|
+
const script = document.createElement("script");
|
|
2550
|
+
script.src = scriptUrl;
|
|
2551
|
+
script.defer = true;
|
|
2552
|
+
script.setAttribute("data-site-id", siteId);
|
|
2553
|
+
document.head.appendChild(script);
|
|
2554
|
+
return () => {
|
|
2555
|
+
const scriptToRemove = document.querySelector(
|
|
2556
|
+
`script[data-site-id="${siteId}"]`
|
|
2557
|
+
);
|
|
2558
|
+
if (scriptToRemove) {
|
|
2559
|
+
document.head.removeChild(scriptToRemove);
|
|
5952
2560
|
}
|
|
5953
|
-
|
|
5954
|
-
|
|
5955
|
-
|
|
5956
|
-
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-4 text-lg text-muted-foreground", children: heroSubtitle }),
|
|
5957
|
-
(brandKitUrl || contactEmail) && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-8 flex flex-wrap items-center justify-center gap-4", children: [
|
|
5958
|
-
brandKitUrl && /* @__PURE__ */ jsxRuntime.jsx(
|
|
5959
|
-
"a",
|
|
5960
|
-
{
|
|
5961
|
-
href: brandKitUrl,
|
|
5962
|
-
target: "_blank",
|
|
5963
|
-
rel: "noopener noreferrer",
|
|
5964
|
-
className: "inline-flex items-center gap-2 px-6 py-3 rounded-lg bg-primary text-primary-foreground font-medium hover:opacity-90 transition-opacity",
|
|
5965
|
-
children: "Download brand kit"
|
|
5966
|
-
}
|
|
5967
|
-
),
|
|
5968
|
-
contactEmail && /* @__PURE__ */ jsxRuntime.jsx(
|
|
5969
|
-
"a",
|
|
5970
|
-
{
|
|
5971
|
-
href: `mailto:${contactEmail}`,
|
|
5972
|
-
className: "inline-flex items-center gap-2 px-6 py-3 rounded-lg border font-medium text-foreground hover:bg-muted transition-colors",
|
|
5973
|
-
children: "Media inquiries"
|
|
5974
|
-
}
|
|
5975
|
-
)
|
|
5976
|
-
] })
|
|
5977
|
-
] }) }) }),
|
|
5978
|
-
showPressReleases && (releasesLoading || releases.length > 0) && /* @__PURE__ */ jsxRuntime.jsx("section", { className: "pb-16 sm:pb-20", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "max-w-7xl mx-auto px-4 sm:px-6 lg:px-8", children: [
|
|
5979
|
-
/* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-xl font-semibold text-foreground mb-6", children: pressReleasesTitle }),
|
|
5980
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid sm:grid-cols-2 lg:grid-cols-3 gap-6", children: releasesLoading ? [1, 2, 3].map((i) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
5981
|
-
"div",
|
|
5982
|
-
{
|
|
5983
|
-
className: "bg-card border rounded-xl overflow-hidden animate-pulse",
|
|
5984
|
-
children: [
|
|
5985
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "aspect-[16/9] bg-muted" }),
|
|
5986
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-5 space-y-3", children: [
|
|
5987
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-4 bg-muted rounded w-1/4" }),
|
|
5988
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-5 bg-muted rounded w-3/4" }),
|
|
5989
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-4 bg-muted rounded w-1/2" })
|
|
5990
|
-
] })
|
|
5991
|
-
]
|
|
5992
|
-
},
|
|
5993
|
-
i
|
|
5994
|
-
)) : releases.map((r) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
5995
|
-
PressReleaseCard,
|
|
5996
|
-
{
|
|
5997
|
-
release: r,
|
|
5998
|
-
basePath: pressReleaseBasePath
|
|
5999
|
-
},
|
|
6000
|
-
r.id
|
|
6001
|
-
)) })
|
|
6002
|
-
] }) }),
|
|
6003
|
-
/* @__PURE__ */ jsxRuntime.jsx("section", { className: "pb-16 sm:pb-20", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "max-w-7xl mx-auto px-4 sm:px-6 lg:px-8", children: loading ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid sm:grid-cols-2 lg:grid-cols-3 gap-6", children: [1, 2, 3, 4, 5, 6].map((i) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
6004
|
-
"div",
|
|
6005
|
-
{
|
|
6006
|
-
className: "bg-card border rounded-xl overflow-hidden animate-pulse",
|
|
6007
|
-
children: [
|
|
6008
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "aspect-[16/9] bg-muted" }),
|
|
6009
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-5 space-y-3", children: [
|
|
6010
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-4 bg-muted rounded w-1/4" }),
|
|
6011
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-5 bg-muted rounded w-3/4" }),
|
|
6012
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-4 bg-muted rounded w-1/2" })
|
|
6013
|
-
] })
|
|
6014
|
-
]
|
|
6015
|
-
},
|
|
6016
|
-
i
|
|
6017
|
-
)) }) : error ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-center py-12", children: /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-muted-foreground", children: error }) }) : assets.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-center py-12", children: /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-muted-foreground", children: [
|
|
6018
|
-
"Press materials are coming soon. For media inquiries",
|
|
6019
|
-
contactEmail ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
6020
|
-
", contact ",
|
|
6021
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
6022
|
-
"a",
|
|
6023
|
-
{
|
|
6024
|
-
href: `mailto:${contactEmail}`,
|
|
6025
|
-
className: "text-primary hover:underline",
|
|
6026
|
-
children: contactEmail
|
|
6027
|
-
}
|
|
6028
|
-
)
|
|
6029
|
-
] }) : " please reach out to our team",
|
|
6030
|
-
"."
|
|
6031
|
-
] }) }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
6032
|
-
presentCategories.length > 1 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-wrap gap-2 mb-10 justify-center", children: ["ALL", ...presentCategories].map((c) => {
|
|
6033
|
-
const active = activeCategory === c;
|
|
6034
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
6035
|
-
"button",
|
|
6036
|
-
{
|
|
6037
|
-
type: "button",
|
|
6038
|
-
onClick: () => setActiveCategory(c),
|
|
6039
|
-
className: `px-4 py-2 rounded-full text-sm font-medium transition-colors ${active ? "bg-primary text-primary-foreground" : "bg-muted text-muted-foreground hover:text-foreground"}`,
|
|
6040
|
-
children: c === "ALL" ? "All" : CATEGORY_LABELS[c] ?? c
|
|
6041
|
-
},
|
|
6042
|
-
c
|
|
6043
|
-
);
|
|
6044
|
-
}) }),
|
|
6045
|
-
grouped.map((group) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mb-12", children: [
|
|
6046
|
-
/* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-xl font-semibold text-foreground mb-5", children: CATEGORY_LABELS[group.cat] ?? group.cat }),
|
|
6047
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid sm:grid-cols-2 lg:grid-cols-3 gap-6", children: group.items.map((a) => /* @__PURE__ */ jsxRuntime.jsx(AssetCard, { asset: a, onPreview: setPreview }, a.id)) })
|
|
6048
|
-
] }, group.cat)),
|
|
6049
|
-
contactEmail && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-8 border-t pt-10 text-center", children: [
|
|
6050
|
-
/* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-lg font-semibold text-foreground mb-2", children: "Media Contact" }),
|
|
6051
|
-
/* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-muted-foreground", children: [
|
|
6052
|
-
"For interviews, partnerships, and press inquiries:",
|
|
6053
|
-
" ",
|
|
6054
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
6055
|
-
"a",
|
|
6056
|
-
{
|
|
6057
|
-
href: `mailto:${contactEmail}`,
|
|
6058
|
-
className: "text-primary hover:underline",
|
|
6059
|
-
children: contactEmail
|
|
6060
|
-
}
|
|
6061
|
-
)
|
|
6062
|
-
] })
|
|
6063
|
-
] })
|
|
6064
|
-
] }) }) }),
|
|
6065
|
-
/* @__PURE__ */ jsxRuntime.jsx(PressKitLightbox, { asset: preview, onClose: () => setPreview(null) })
|
|
6066
|
-
] });
|
|
2561
|
+
};
|
|
2562
|
+
}, [siteId, scriptUrl, enabled]);
|
|
2563
|
+
return null;
|
|
6067
2564
|
}
|
|
6068
|
-
var
|
|
6069
|
-
query
|
|
6070
|
-
|
|
2565
|
+
var PUBLIC_CONTENT_PAGE_QUERY = `
|
|
2566
|
+
query PublicContentPage($slug: String!, $productId: String) {
|
|
2567
|
+
publicContentPage(slug: $slug, productId: $productId) {
|
|
2568
|
+
id
|
|
2569
|
+
title
|
|
2570
|
+
slug
|
|
2571
|
+
content
|
|
2572
|
+
contentFormat
|
|
2573
|
+
seoTitle
|
|
2574
|
+
seoDescription
|
|
2575
|
+
seoKeywords
|
|
2576
|
+
lastUpdated
|
|
2577
|
+
version
|
|
2578
|
+
}
|
|
2579
|
+
}
|
|
2580
|
+
`;
|
|
2581
|
+
var PUBLIC_RESOLVED_CONTENT_PAGE_QUERY = `
|
|
2582
|
+
query PublicResolvedContentPage($slug: String!, $productId: String!) {
|
|
2583
|
+
publicResolvedContentPage(slug: $slug, productId: $productId) {
|
|
2584
|
+
id
|
|
2585
|
+
title
|
|
2586
|
+
slug
|
|
2587
|
+
content
|
|
2588
|
+
contentFormat
|
|
2589
|
+
seoTitle
|
|
2590
|
+
seoDescription
|
|
2591
|
+
seoKeywords
|
|
2592
|
+
lastUpdated
|
|
2593
|
+
version
|
|
2594
|
+
}
|
|
2595
|
+
}
|
|
2596
|
+
`;
|
|
2597
|
+
var PUBLIC_CONTENT_PAGES_QUERY = `
|
|
2598
|
+
query PublicContentPages($productId: String!, $limit: Int, $offset: Int) {
|
|
2599
|
+
publicContentPages(productId: $productId, limit: $limit, offset: $offset) {
|
|
6071
2600
|
items {
|
|
6072
2601
|
id
|
|
6073
2602
|
title
|
|
6074
2603
|
slug
|
|
6075
|
-
|
|
6076
|
-
fileUrl
|
|
6077
|
-
fileName
|
|
6078
|
-
fileSize
|
|
6079
|
-
mimeType
|
|
6080
|
-
version
|
|
6081
|
-
tags
|
|
6082
|
-
sortOrder
|
|
6083
|
-
updatedAt
|
|
2604
|
+
lastUpdated
|
|
6084
2605
|
}
|
|
2606
|
+
total
|
|
6085
2607
|
hasMore
|
|
6086
2608
|
}
|
|
6087
2609
|
}
|
|
6088
2610
|
`;
|
|
6089
|
-
function
|
|
6090
|
-
const {
|
|
6091
|
-
productName,
|
|
6092
|
-
heroTitle = "Contracts & Legal Agreements",
|
|
6093
|
-
heroSubtitle = "Download our standard legal agreements and policies.",
|
|
6094
|
-
className = "",
|
|
6095
|
-
seoTitle,
|
|
6096
|
-
seoDescription,
|
|
6097
|
-
seoKeywords
|
|
6098
|
-
} = props;
|
|
2611
|
+
function useContentPage(slug, options) {
|
|
6099
2612
|
const client = useWebSDK();
|
|
6100
2613
|
const config = useWebSDKConfig();
|
|
6101
|
-
const
|
|
6102
|
-
const resolvedProductId = product?.id || config.productId;
|
|
6103
|
-
const displayName = productName ?? product?.name ?? config.productId;
|
|
6104
|
-
const [contracts, setContracts] = React.useState([]);
|
|
2614
|
+
const [data, setData] = React.useState(null);
|
|
6105
2615
|
const [loading, setLoading] = React.useState(true);
|
|
6106
2616
|
const [error, setError] = React.useState(null);
|
|
6107
|
-
const
|
|
6108
|
-
|
|
2617
|
+
const fetchPage = React.useCallback(async () => {
|
|
2618
|
+
setLoading(true);
|
|
2619
|
+
setError(null);
|
|
2620
|
+
try {
|
|
2621
|
+
const query = options?.resolve ? PUBLIC_RESOLVED_CONTENT_PAGE_QUERY : PUBLIC_CONTENT_PAGE_QUERY;
|
|
2622
|
+
const queryName = options?.resolve ? "publicResolvedContentPage" : "publicContentPage";
|
|
2623
|
+
const result = await client.query(
|
|
2624
|
+
query,
|
|
2625
|
+
{
|
|
2626
|
+
slug,
|
|
2627
|
+
productId: config.productId
|
|
2628
|
+
}
|
|
2629
|
+
);
|
|
2630
|
+
if (result.errors?.length) {
|
|
2631
|
+
setError(result.errors[0]?.message ?? "Unknown error");
|
|
2632
|
+
setData(null);
|
|
2633
|
+
} else {
|
|
2634
|
+
setData(result.data?.[queryName] ?? null);
|
|
2635
|
+
}
|
|
2636
|
+
} catch (err) {
|
|
2637
|
+
setError(err instanceof Error ? err.message : "Failed to fetch content");
|
|
2638
|
+
setData(null);
|
|
2639
|
+
} finally {
|
|
6109
2640
|
setLoading(false);
|
|
6110
|
-
return;
|
|
6111
2641
|
}
|
|
2642
|
+
}, [client, config.productId, slug, options?.resolve]);
|
|
2643
|
+
React.useEffect(() => {
|
|
2644
|
+
fetchPage();
|
|
2645
|
+
}, [fetchPage]);
|
|
2646
|
+
return { data, loading, error, refetch: fetchPage };
|
|
2647
|
+
}
|
|
2648
|
+
function useContentPages(options) {
|
|
2649
|
+
const client = useWebSDK();
|
|
2650
|
+
const config = useWebSDKConfig();
|
|
2651
|
+
const [data, setData] = React.useState(null);
|
|
2652
|
+
const [loading, setLoading] = React.useState(true);
|
|
2653
|
+
const [error, setError] = React.useState(null);
|
|
2654
|
+
const fetchPages = React.useCallback(async () => {
|
|
6112
2655
|
setLoading(true);
|
|
2656
|
+
setError(null);
|
|
6113
2657
|
try {
|
|
6114
|
-
const
|
|
6115
|
-
|
|
6116
|
-
|
|
6117
|
-
|
|
6118
|
-
|
|
6119
|
-
|
|
6120
|
-
|
|
6121
|
-
|
|
6122
|
-
|
|
6123
|
-
|
|
6124
|
-
if (result.errors?.length) {
|
|
6125
|
-
setError(result.errors[0]?.message ?? "Failed to load contracts");
|
|
6126
|
-
return;
|
|
6127
|
-
}
|
|
6128
|
-
const page = result.data?.publicPressKitAssets;
|
|
6129
|
-
const items = page?.items || [];
|
|
6130
|
-
all.push(...items);
|
|
6131
|
-
if (!page?.hasMore || items.length === 0) break;
|
|
6132
|
-
offset += PAGE_SIZE;
|
|
2658
|
+
const result = await client.query(PUBLIC_CONTENT_PAGES_QUERY, {
|
|
2659
|
+
productId: config.productId,
|
|
2660
|
+
limit: options?.limit ?? 50,
|
|
2661
|
+
offset: options?.offset ?? 0
|
|
2662
|
+
});
|
|
2663
|
+
if (result.errors?.length) {
|
|
2664
|
+
setError(result.errors[0]?.message ?? "Unknown error");
|
|
2665
|
+
setData(null);
|
|
2666
|
+
} else {
|
|
2667
|
+
setData(result.data?.publicContentPages ?? null);
|
|
6133
2668
|
}
|
|
6134
|
-
|
|
6135
|
-
|
|
2669
|
+
} catch (err) {
|
|
2670
|
+
setError(
|
|
2671
|
+
err instanceof Error ? err.message : "Failed to fetch content pages"
|
|
6136
2672
|
);
|
|
6137
|
-
|
|
6138
|
-
setError(null);
|
|
6139
|
-
} catch {
|
|
6140
|
-
setError("Failed to load contracts");
|
|
2673
|
+
setData(null);
|
|
6141
2674
|
} finally {
|
|
6142
2675
|
setLoading(false);
|
|
6143
2676
|
}
|
|
6144
|
-
}, [client,
|
|
2677
|
+
}, [client, config.productId, options?.limit, options?.offset]);
|
|
6145
2678
|
React.useEffect(() => {
|
|
6146
|
-
|
|
6147
|
-
}, [
|
|
6148
|
-
return
|
|
6149
|
-
|
|
6150
|
-
|
|
2679
|
+
fetchPages();
|
|
2680
|
+
}, [fetchPages]);
|
|
2681
|
+
return { data, loading, error, refetch: fetchPages };
|
|
2682
|
+
}
|
|
2683
|
+
function ContentRenderer({
|
|
2684
|
+
content,
|
|
2685
|
+
format = "markdown",
|
|
2686
|
+
className
|
|
2687
|
+
}) {
|
|
2688
|
+
if (format === "html" || format === "markdown") {
|
|
2689
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
2690
|
+
"div",
|
|
6151
2691
|
{
|
|
6152
|
-
|
|
6153
|
-
|
|
6154
|
-
productName,
|
|
6155
|
-
keywords: seoKeywords
|
|
2692
|
+
className,
|
|
2693
|
+
dangerouslySetInnerHTML: { __html: content }
|
|
6156
2694
|
}
|
|
6157
|
-
)
|
|
6158
|
-
|
|
6159
|
-
|
|
6160
|
-
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-lg text-muted-foreground max-w-2xl mx-auto", children: heroSubtitle })
|
|
6161
|
-
] }) }),
|
|
6162
|
-
/* @__PURE__ */ jsxRuntime.jsx("section", { className: "max-w-5xl mx-auto px-4 sm:px-6 lg:px-8 py-12", children: loading ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex justify-center py-20", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-8 w-8 animate-spin rounded-full border-2 border-primary border-t-transparent" }) }) : error ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-center py-16", children: /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground", children: error }) }) : contracts.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-center py-16", children: /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-base text-muted-foreground", children: "No contracts are available yet." }) }) : /* @__PURE__ */ jsxRuntime.jsx("ul", { className: "divide-y divide-border rounded-xl border border-border overflow-hidden bg-card", children: contracts.map((c) => {
|
|
6163
|
-
const ext = fileExt(c);
|
|
6164
|
-
const size = formatBytes(c.fileSize);
|
|
6165
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
6166
|
-
"li",
|
|
6167
|
-
{
|
|
6168
|
-
className: "flex items-center justify-between gap-4 p-5 hover:bg-muted/40 transition-colors",
|
|
6169
|
-
children: [
|
|
6170
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0", children: [
|
|
6171
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 flex-wrap", children: [
|
|
6172
|
-
/* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-base font-semibold text-foreground truncate", children: c.title }),
|
|
6173
|
-
c.version && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-medium text-primary bg-primary/10 px-2 py-0.5 rounded-full", children: c.version })
|
|
6174
|
-
] }),
|
|
6175
|
-
c.description && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground mt-1 line-clamp-2", children: c.description }),
|
|
6176
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-1 flex items-center gap-2 text-xs text-muted-foreground", children: [
|
|
6177
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium", children: ext }),
|
|
6178
|
-
size && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
6179
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground/40", children: "\xB7" }),
|
|
6180
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { children: size })
|
|
6181
|
-
] })
|
|
6182
|
-
] })
|
|
6183
|
-
] }),
|
|
6184
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
6185
|
-
"a",
|
|
6186
|
-
{
|
|
6187
|
-
href: c.fileUrl,
|
|
6188
|
-
target: "_blank",
|
|
6189
|
-
rel: "noopener noreferrer",
|
|
6190
|
-
download: true,
|
|
6191
|
-
"aria-label": `Download ${c.title}`,
|
|
6192
|
-
onClick: (e) => {
|
|
6193
|
-
void downloadFile(e, c);
|
|
6194
|
-
},
|
|
6195
|
-
className: "flex-shrink-0 inline-flex items-center gap-1.5 text-sm font-medium text-primary-foreground bg-primary hover:opacity-90 px-4 py-2 rounded-lg transition-opacity",
|
|
6196
|
-
children: [
|
|
6197
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
6198
|
-
"svg",
|
|
6199
|
-
{
|
|
6200
|
-
className: "w-4 h-4",
|
|
6201
|
-
fill: "none",
|
|
6202
|
-
viewBox: "0 0 24 24",
|
|
6203
|
-
stroke: "currentColor",
|
|
6204
|
-
"aria-hidden": "true",
|
|
6205
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
6206
|
-
"path",
|
|
6207
|
-
{
|
|
6208
|
-
strokeLinecap: "round",
|
|
6209
|
-
strokeLinejoin: "round",
|
|
6210
|
-
strokeWidth: 2,
|
|
6211
|
-
d: "M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4"
|
|
6212
|
-
}
|
|
6213
|
-
)
|
|
6214
|
-
}
|
|
6215
|
-
),
|
|
6216
|
-
"Download"
|
|
6217
|
-
]
|
|
6218
|
-
}
|
|
6219
|
-
)
|
|
6220
|
-
]
|
|
6221
|
-
},
|
|
6222
|
-
c.id
|
|
6223
|
-
);
|
|
6224
|
-
}) }) })
|
|
6225
|
-
] });
|
|
2695
|
+
);
|
|
2696
|
+
}
|
|
2697
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className, children: /* @__PURE__ */ jsxRuntime.jsx("pre", { style: { whiteSpace: "pre-wrap", fontFamily: "inherit" }, children: content }) });
|
|
6226
2698
|
}
|
|
6227
2699
|
|
|
6228
|
-
exports.BlogListPage = BlogListPage;
|
|
6229
|
-
exports.BlogPostPage = BlogPostPage;
|
|
6230
2700
|
exports.Button = Button;
|
|
6231
|
-
exports.CareersApplyForm = CareersApplyForm;
|
|
6232
|
-
exports.CaseStudiesListPage = CaseStudiesListPage;
|
|
6233
|
-
exports.CaseStudyPage = CaseStudyPage;
|
|
6234
|
-
exports.ChangelogPage = ChangelogPage;
|
|
6235
2701
|
exports.ContactPage = ContactPage;
|
|
6236
2702
|
exports.ContentRenderer = ContentRenderer;
|
|
6237
|
-
exports.ContractsPage = ContractsPage;
|
|
6238
|
-
exports.DEFAULT_CONTACT_CATEGORIES = DEFAULT_CONTACT_CATEGORIES;
|
|
6239
|
-
exports.ElectronDownloadLink = ElectronDownloadLink;
|
|
6240
2703
|
exports.Input = Input;
|
|
6241
2704
|
exports.Label = Label;
|
|
6242
|
-
exports.LaunchCountdown = LaunchCountdown;
|
|
6243
2705
|
exports.NewsletterForm = NewsletterForm;
|
|
6244
2706
|
exports.NewsletterPage = NewsletterPage;
|
|
6245
2707
|
exports.PageHead = PageHead;
|
|
6246
|
-
exports.PartnersPage = PartnersPage;
|
|
6247
|
-
exports.PressKitPage = PressKitPage;
|
|
6248
2708
|
exports.PricingPage = PricingPage;
|
|
6249
2709
|
exports.PricingSection = PricingSection;
|
|
6250
|
-
exports.ProblemsSolvedSection = ProblemsSolvedSection;
|
|
6251
2710
|
exports.ProductProvider = ProductProvider;
|
|
6252
|
-
exports.ResourceLinks = ResourceLinks;
|
|
6253
2711
|
exports.RybbitAnalytics = RybbitAnalytics;
|
|
6254
|
-
exports.Select = Select;
|
|
6255
|
-
exports.SocialLinks = SocialLinks;
|
|
6256
2712
|
exports.Textarea = Textarea;
|
|
6257
2713
|
exports.UnsubscribePage = UnsubscribePage;
|
|
6258
2714
|
exports.WaitlistForm = WaitlistForm;
|
|
6259
2715
|
exports.WaitlistPage = WaitlistPage;
|
|
6260
2716
|
exports.WebSDKClient = WebSDKClient;
|
|
6261
2717
|
exports.WebSDKProvider = WebSDKProvider;
|
|
6262
|
-
exports.WebsiteSwitcher = WebsiteSwitcher;
|
|
6263
2718
|
exports.buttonVariants = buttonVariants;
|
|
6264
2719
|
exports.cn = cn;
|
|
6265
|
-
exports.detectElectronPlatform = detectElectronPlatform;
|
|
6266
2720
|
exports.useContentPage = useContentPage;
|
|
6267
2721
|
exports.useContentPages = useContentPages;
|
|
6268
2722
|
exports.useProduct = useProduct;
|