@burdenoff/website-sdk 2026.1031.3 → 2026.1101.2
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/README.md +42 -8
- package/dist/analytics/index.js +19 -69
- package/dist/analytics/index.js.map +1 -1
- package/dist/analytics/index.mjs +3 -69
- package/dist/analytics/index.mjs.map +1 -1
- package/dist/avatar-live/index.js +1 -0
- package/dist/avatar-live/index.js.map +1 -1
- package/dist/avatar-live/index.mjs +1 -0
- package/dist/avatar-live/index.mjs.map +1 -1
- package/dist/avatar-live-view/index.js +10 -30
- package/dist/avatar-live-view/index.js.map +1 -1
- package/dist/avatar-live-view/index.mjs +4 -24
- package/dist/avatar-live-view/index.mjs.map +1 -1
- package/dist/chunk-23H4JUL4.js +41 -0
- package/dist/chunk-23H4JUL4.js.map +1 -0
- package/dist/chunk-34RX5P75.mjs +25 -0
- package/dist/chunk-34RX5P75.mjs.map +1 -0
- package/dist/chunk-3LFFY7AZ.js +184 -0
- package/dist/chunk-3LFFY7AZ.js.map +1 -0
- package/dist/chunk-3X26GE4S.js +2794 -0
- package/dist/chunk-3X26GE4S.js.map +1 -0
- package/dist/chunk-45GCA4GG.js +85 -0
- package/dist/chunk-45GCA4GG.js.map +1 -0
- package/dist/chunk-4SAGVLHN.js +876 -0
- package/dist/chunk-4SAGVLHN.js.map +1 -0
- package/dist/chunk-4U6YAII5.js +218 -0
- package/dist/chunk-4U6YAII5.js.map +1 -0
- package/dist/chunk-5GMTBOO6.js +21 -0
- package/dist/chunk-5GMTBOO6.js.map +1 -0
- package/dist/chunk-5JRJ57CL.mjs +329 -0
- package/dist/chunk-5JRJ57CL.mjs.map +1 -0
- package/dist/chunk-6C2DKIC7.js +4 -0
- package/dist/chunk-6C2DKIC7.js.map +1 -0
- package/dist/chunk-6IBFBWC5.mjs +17 -0
- package/dist/chunk-6IBFBWC5.mjs.map +1 -0
- package/dist/chunk-6KY2ZRVI.js +116 -0
- package/dist/chunk-6KY2ZRVI.js.map +1 -0
- package/dist/chunk-6VJT3SM4.mjs +182 -0
- package/dist/chunk-6VJT3SM4.mjs.map +1 -0
- package/dist/chunk-6YZUFMK2.mjs +40 -0
- package/dist/chunk-6YZUFMK2.mjs.map +1 -0
- package/dist/chunk-7A6R7CGG.js +47 -0
- package/dist/chunk-7A6R7CGG.js.map +1 -0
- package/dist/chunk-7PWL4V2R.js +4 -0
- package/dist/chunk-7PWL4V2R.js.map +1 -0
- package/dist/chunk-AA33WDYS.js +161 -0
- package/dist/chunk-AA33WDYS.js.map +1 -0
- package/dist/chunk-AHRGFXV2.js +525 -0
- package/dist/chunk-AHRGFXV2.js.map +1 -0
- package/dist/chunk-B63XLDZH.mjs +109 -0
- package/dist/chunk-B63XLDZH.mjs.map +1 -0
- package/dist/chunk-BX2USEWF.mjs +35 -0
- package/dist/chunk-BX2USEWF.mjs.map +1 -0
- package/dist/chunk-BYSZT35R.js +150 -0
- package/dist/chunk-BYSZT35R.js.map +1 -0
- package/dist/chunk-C6OJIHNO.js +12 -0
- package/dist/chunk-C6OJIHNO.js.map +1 -0
- package/dist/chunk-COOELBX7.mjs +63 -0
- package/dist/chunk-COOELBX7.mjs.map +1 -0
- package/dist/chunk-D2TW7MNC.mjs +2771 -0
- package/dist/chunk-D2TW7MNC.mjs.map +1 -0
- package/dist/chunk-D5KEZ6V6.mjs +3 -0
- package/dist/chunk-D5KEZ6V6.mjs.map +1 -0
- package/dist/chunk-E3GUGJMY.js +46 -0
- package/dist/chunk-E3GUGJMY.js.map +1 -0
- package/dist/chunk-EBZ2HGJ2.mjs +52 -0
- package/dist/chunk-EBZ2HGJ2.mjs.map +1 -0
- package/dist/chunk-EDLTUOIA.mjs +210 -0
- package/dist/chunk-EDLTUOIA.mjs.map +1 -0
- package/dist/chunk-EJQSOT4X.js +4 -0
- package/dist/chunk-EJQSOT4X.js.map +1 -0
- package/dist/chunk-EUPFS6MZ.mjs +2019 -0
- package/dist/chunk-EUPFS6MZ.mjs.map +1 -0
- package/dist/chunk-F3XWDC3B.js +77 -0
- package/dist/chunk-F3XWDC3B.js.map +1 -0
- package/dist/chunk-FFADMVIK.mjs +5127 -0
- package/dist/chunk-FFADMVIK.mjs.map +1 -0
- package/dist/chunk-FJWS7BKT.mjs +3 -0
- package/dist/chunk-FJWS7BKT.mjs.map +1 -0
- package/dist/chunk-FJZJDUOR.mjs +3 -0
- package/dist/chunk-FJZJDUOR.mjs.map +1 -0
- package/dist/chunk-GCFLCO2Z.mjs +145 -0
- package/dist/chunk-GCFLCO2Z.mjs.map +1 -0
- package/dist/chunk-GEXC4LRP.js +61 -0
- package/dist/chunk-GEXC4LRP.js.map +1 -0
- package/dist/chunk-GTRAXQGC.mjs +3 -0
- package/dist/chunk-GTRAXQGC.mjs.map +1 -0
- package/dist/chunk-IKOIRUKW.js +270 -0
- package/dist/chunk-IKOIRUKW.js.map +1 -0
- package/dist/chunk-IXTWOPRN.js +45 -0
- package/dist/chunk-IXTWOPRN.js.map +1 -0
- package/dist/chunk-JDXOXGOK.js +547 -0
- package/dist/chunk-JDXOXGOK.js.map +1 -0
- package/dist/chunk-JSLDWYV7.mjs +558 -0
- package/dist/chunk-JSLDWYV7.mjs.map +1 -0
- package/dist/chunk-JXLSTVBK.js +336 -0
- package/dist/chunk-JXLSTVBK.js.map +1 -0
- package/dist/chunk-KHYXK7FG.mjs +49 -0
- package/dist/chunk-KHYXK7FG.mjs.map +1 -0
- package/dist/chunk-KSMBOWQQ.js +2034 -0
- package/dist/chunk-KSMBOWQQ.js.map +1 -0
- package/dist/chunk-KU2PXHGB.mjs +83 -0
- package/dist/chunk-KU2PXHGB.mjs.map +1 -0
- package/dist/chunk-KZANIV3S.mjs +37 -0
- package/dist/chunk-KZANIV3S.mjs.map +1 -0
- package/dist/chunk-L4BP6YOB.js +20 -0
- package/dist/chunk-L4BP6YOB.js.map +1 -0
- package/dist/chunk-MRKA2OAF.mjs +75 -0
- package/dist/chunk-MRKA2OAF.mjs.map +1 -0
- package/dist/chunk-N5EC53MH.mjs +80 -0
- package/dist/chunk-N5EC53MH.mjs.map +1 -0
- package/dist/chunk-NZANQGDM.mjs +34 -0
- package/dist/chunk-NZANQGDM.mjs.map +1 -0
- package/dist/chunk-O4GS3XPZ.js +10 -0
- package/dist/chunk-O4GS3XPZ.js.map +1 -0
- package/dist/chunk-O76QDGRM.js +5154 -0
- package/dist/chunk-O76QDGRM.js.map +1 -0
- package/dist/chunk-OYEUNZJY.js +114 -0
- package/dist/chunk-OYEUNZJY.js.map +1 -0
- package/dist/chunk-PHXAGQNB.js +74 -0
- package/dist/chunk-PHXAGQNB.js.map +1 -0
- package/dist/chunk-PIHHPP7J.mjs +16 -0
- package/dist/chunk-PIHHPP7J.mjs.map +1 -0
- package/dist/chunk-PXRHVVOU.mjs +499 -0
- package/dist/chunk-PXRHVVOU.mjs.map +1 -0
- package/dist/chunk-QMA6U6AU.js +36 -0
- package/dist/chunk-QMA6U6AU.js.map +1 -0
- package/dist/chunk-R6J7IULF.js +44 -0
- package/dist/chunk-R6J7IULF.js.map +1 -0
- package/dist/chunk-R7G6TWIS.mjs +10 -0
- package/dist/chunk-R7G6TWIS.mjs.map +1 -0
- package/dist/chunk-RUMGFI6L.mjs +544 -0
- package/dist/chunk-RUMGFI6L.mjs.map +1 -0
- package/dist/chunk-S54JWSJF.js +565 -0
- package/dist/chunk-S54JWSJF.js.map +1 -0
- package/dist/chunk-SK5UGG6A.js +2041 -0
- package/dist/chunk-SK5UGG6A.js.map +1 -0
- package/dist/chunk-SKIYDKIS.mjs +70 -0
- package/dist/chunk-SKIYDKIS.mjs.map +1 -0
- package/dist/chunk-SZNS5IK6.mjs +322 -0
- package/dist/chunk-SZNS5IK6.mjs.map +1 -0
- package/dist/chunk-T7JIPZAC.js +66 -0
- package/dist/chunk-T7JIPZAC.js.map +1 -0
- package/dist/chunk-TGYQHO2E.mjs +864 -0
- package/dist/chunk-TGYQHO2E.mjs.map +1 -0
- package/dist/chunk-TT3MHN42.mjs +8 -0
- package/dist/chunk-TT3MHN42.mjs.map +1 -0
- package/dist/chunk-UM55U65W.mjs +268 -0
- package/dist/chunk-UM55U65W.mjs.map +1 -0
- package/dist/chunk-UQ2HALUD.js +72 -0
- package/dist/chunk-UQ2HALUD.js.map +1 -0
- package/dist/chunk-UXWT7G77.js +82 -0
- package/dist/chunk-UXWT7G77.js.map +1 -0
- package/dist/chunk-VNS5WDWO.mjs +7 -0
- package/dist/chunk-VNS5WDWO.mjs.map +1 -0
- package/dist/chunk-VRWZLGR7.js +4 -0
- package/dist/chunk-VRWZLGR7.js.map +1 -0
- package/dist/chunk-VZLDSNH4.mjs +22 -0
- package/dist/chunk-VZLDSNH4.mjs.map +1 -0
- package/dist/chunk-W6P3YETJ.js +92 -0
- package/dist/chunk-W6P3YETJ.js.map +1 -0
- package/dist/chunk-WPKFFQCR.mjs +90 -0
- package/dist/chunk-WPKFFQCR.mjs.map +1 -0
- package/dist/chunk-WTVTI5TO.mjs +22 -0
- package/dist/chunk-WTVTI5TO.mjs.map +1 -0
- package/dist/chunk-XJOBZ4RJ.js +44 -0
- package/dist/chunk-XJOBZ4RJ.js.map +1 -0
- package/dist/chunk-XP2JDNDU.js +325 -0
- package/dist/chunk-XP2JDNDU.js.map +1 -0
- package/dist/chunk-Y4V5CEWD.mjs +112 -0
- package/dist/chunk-Y4V5CEWD.mjs.map +1 -0
- package/dist/chunk-YERN2UWM.js +116 -0
- package/dist/chunk-YERN2UWM.js.map +1 -0
- package/dist/chunk-ZKJQWFAA.mjs +114 -0
- package/dist/chunk-ZKJQWFAA.mjs.map +1 -0
- package/dist/chunk-ZOON3S3Y.mjs +138 -0
- package/dist/chunk-ZOON3S3Y.mjs.map +1 -0
- package/dist/chunk-ZP6QRAYC.mjs +2024 -0
- package/dist/chunk-ZP6QRAYC.mjs.map +1 -0
- package/dist/chunk-ZSTFX7FV.js +9 -0
- package/dist/chunk-ZSTFX7FV.js.map +1 -0
- package/dist/client/index.js +23 -331
- package/dist/client/index.js.map +1 -1
- package/dist/client/index.mjs +3 -330
- package/dist/client/index.mjs.map +1 -1
- package/dist/comparison/index.js +28 -111
- package/dist/comparison/index.js.map +1 -1
- package/dist/comparison/index.mjs +2 -107
- package/dist/comparison/index.mjs.map +1 -1
- package/dist/components/contact.js +21 -931
- package/dist/components/contact.js.map +1 -1
- package/dist/components/contact.mjs +12 -909
- package/dist/components/contact.mjs.map +1 -1
- package/dist/components/electron-download.js +12 -124
- package/dist/components/electron-download.js.map +1 -1
- package/dist/components/electron-download.mjs +4 -106
- package/dist/components/electron-download.mjs.map +1 -1
- package/dist/components/explore-cta.js +8 -112
- package/dist/components/explore-cta.js.map +1 -1
- package/dist/components/explore-cta.mjs +3 -114
- package/dist/components/explore-cta.mjs.map +1 -1
- package/dist/components/explore.js +62 -7833
- package/dist/components/explore.js.map +1 -1
- package/dist/components/explore.mjs +13 -7800
- package/dist/components/explore.mjs.map +1 -1
- package/dist/components/launch-countdown.js +8 -83
- package/dist/components/launch-countdown.js.map +1 -1
- package/dist/components/launch-countdown.mjs +3 -85
- package/dist/components/launch-countdown.mjs.map +1 -1
- package/dist/components/newsletter.js +20 -993
- package/dist/components/newsletter.js.map +1 -1
- package/dist/components/newsletter.mjs +11 -971
- package/dist/components/newsletter.mjs.map +1 -1
- package/dist/components/partners.js +24 -1302
- package/dist/components/partners.js.map +1 -1
- package/dist/components/partners.mjs +15 -1276
- package/dist/components/partners.mjs.map +1 -1
- package/dist/components/pricing.js +49 -1249
- package/dist/components/pricing.js.map +1 -1
- package/dist/components/pricing.mjs +8 -1226
- package/dist/components/pricing.mjs.map +1 -1
- package/dist/components/problems-solved.js +8 -150
- package/dist/components/problems-solved.js.map +1 -1
- package/dist/components/problems-solved.mjs +4 -133
- package/dist/components/problems-solved.mjs.map +1 -1
- package/dist/components/resource-links.js +8 -304
- package/dist/components/resource-links.js.map +1 -1
- package/dist/components/resource-links.mjs +5 -308
- package/dist/components/resource-links.mjs.map +1 -1
- package/dist/components/social-links.js +8 -343
- package/dist/components/social-links.js.map +1 -1
- package/dist/components/social-links.mjs +5 -347
- package/dist/components/social-links.mjs.map +1 -1
- package/dist/components/unsubscribe.js +10 -627
- package/dist/components/unsubscribe.js.map +1 -1
- package/dist/components/unsubscribe.mjs +6 -611
- package/dist/components/unsubscribe.mjs.map +1 -1
- package/dist/components/waitlist.js +21 -1001
- package/dist/components/waitlist.js.map +1 -1
- package/dist/components/waitlist.mjs +12 -982
- package/dist/components/waitlist.mjs.map +1 -1
- package/dist/components/website-switcher.js +12 -175
- package/dist/components/website-switcher.js.map +1 -1
- package/dist/components/website-switcher.mjs +4 -157
- package/dist/components/website-switcher.mjs.map +1 -1
- package/dist/content/index.js +118 -3616
- package/dist/content/index.js.map +1 -1
- package/dist/content/index.mjs +12 -3567
- package/dist/content/index.mjs.map +1 -1
- package/dist/hooks/index.js +37 -2321
- package/dist/hooks/index.js.map +1 -1
- package/dist/hooks/index.mjs +6 -2318
- package/dist/hooks/index.mjs.map +1 -1
- package/dist/index.js +4145 -20599
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +3766 -20567
- package/dist/index.mjs.map +1 -1
- package/dist/product/index.js +20 -393
- package/dist/product/index.js.map +1 -1
- package/dist/product/index.mjs +5 -394
- package/dist/product/index.mjs.map +1 -1
- package/dist/seo/index.js +8 -76
- package/dist/seo/index.js.map +1 -1
- package/dist/seo/index.mjs +3 -78
- package/dist/seo/index.mjs.map +1 -1
- package/dist/store/index.js +51 -2650
- package/dist/store/index.js.map +1 -1
- package/dist/store/index.mjs +13 -2619
- package/dist/store/index.mjs.map +1 -1
- package/dist/utils/index.js +69 -134
- package/dist/utils/index.js.map +1 -1
- package/dist/utils/index.mjs +5 -122
- package/dist/utils/index.mjs.map +1 -1
- package/package.json +2 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/utils/recaptcha-v3.ts"],"names":[],"mappings":"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","file":"chunk-SKIYDKIS.mjs","sourcesContent":["/**\n * reCAPTCHA v3 (invisible, score-based) loader and token minter.\n *\n * Deliberately dependency-free: the SDK already ships the `react-google-recaptcha`\n * peer for the **v2 checkbox** used by `ContactPage`, and the two widgets cannot\n * share a script tag — v3 needs `?render=<siteKey>` on the URL, v2 must not have\n * it. This module injects its own v3 script and leaves the v2 integration alone.\n *\n * The Explore chat turns v3 on purely from the backend catalog\n * (`exploreCatalog.captchaRequired && recaptchaSiteKey`), so nothing is loaded\n * and no third-party request is made on sites where captcha is off.\n */\n\n/** The slice of the `grecaptcha` global this module uses. */\nexport interface RecaptchaV3 {\n /** Runs the callback once the API is fully initialised. */\n ready(callback: () => void): void;\n /** Mints a single-use token for `action`. */\n execute(siteKey: string, options: { action: string }): Promise<string>;\n}\n\ndeclare global {\n interface Window {\n grecaptcha?: RecaptchaV3;\n }\n}\n\nconst SCRIPT_ID = \"boff-recaptcha-v3\";\nconst SCRIPT_SRC = \"https://www.google.com/recaptcha/api.js?render=\";\n\n/** Default action name reported to reCAPTCHA for Explore chat submissions. */\nexport const EXPLORE_RECAPTCHA_ACTION = \"explore_chat\";\n\n/**\n * One in-flight/settled promise per site key. Guarantees the script tag is\n * injected at most once even when several components mount at the same time,\n * and lets later callers await the same load instead of racing it.\n */\nconst loaders = new Map<string, Promise<RecaptchaV3>>();\n\nfunction isBrowser(): boolean {\n return typeof window !== \"undefined\" && typeof document !== \"undefined\";\n}\n\n/**\n * Load the reCAPTCHA v3 script for `siteKey` and resolve once `grecaptcha` is\n * ready to mint tokens.\n *\n * - Injects the script at most once per site key (subsequent calls reuse the\n * same promise).\n * - Adopts an already-present script tag (e.g. injected by the host site)\n * rather than adding a second one.\n * - Rejects rather than hanging when the script fails to load, so the caller\n * can decide whether to submit without a token or surface an error.\n */\nexport function loadRecaptchaV3(siteKey: string): Promise<RecaptchaV3> {\n if (!siteKey) {\n return Promise.reject(new Error(\"reCAPTCHA site key is required\"));\n }\n if (!isBrowser()) {\n return Promise.reject(\n new Error(\"reCAPTCHA v3 can only be loaded in a browser\"),\n );\n }\n\n const existing = loaders.get(siteKey);\n if (existing) return existing;\n\n const promise = new Promise<RecaptchaV3>((resolve, reject) => {\n const ready = () => {\n const api = window.grecaptcha;\n if (!api) {\n reject(new Error(\"reCAPTCHA loaded but grecaptcha is unavailable\"));\n return;\n }\n // `ready` queues until the API has finished initialising internally;\n // calling `execute` before that throws.\n api.ready(() => resolve(api));\n };\n\n // Already loaded by this module, another SDK instance, or the host site.\n if (window.grecaptcha) {\n ready();\n return;\n }\n\n const scriptId = `${SCRIPT_ID}-${siteKey}`;\n const found = document.getElementById(scriptId);\n if (found) {\n found.addEventListener(\"load\", ready, { once: true });\n found.addEventListener(\n \"error\",\n () => reject(new Error(\"Failed to load reCAPTCHA v3\")),\n { once: true },\n );\n return;\n }\n\n const script = document.createElement(\"script\");\n script.id = scriptId;\n script.src = `${SCRIPT_SRC}${encodeURIComponent(siteKey)}`;\n script.async = true;\n script.defer = true;\n script.addEventListener(\"load\", ready, { once: true });\n script.addEventListener(\n \"error\",\n () => {\n // Drop the memo so a later attempt can retry a transient CDN failure\n // instead of being stuck with a permanently rejected promise.\n loaders.delete(siteKey);\n reject(new Error(\"Failed to load reCAPTCHA v3\"));\n },\n { once: true },\n );\n document.head.appendChild(script);\n });\n\n loaders.set(siteKey, promise);\n return promise;\n}\n\n/**\n * Mint a single-use reCAPTCHA v3 token, loading the script on first use.\n *\n * Tokens are valid for ~2 minutes and are consumed by one verification, so\n * this must be called per submission — never cached.\n */\nexport async function getRecaptchaV3Token(\n siteKey: string,\n action: string = EXPLORE_RECAPTCHA_ACTION,\n): Promise<string> {\n const api = await loadRecaptchaV3(siteKey);\n return api.execute(siteKey, { action });\n}\n"]}
|
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
import { ContentRenderer } from './chunk-EDLTUOIA.mjs';
|
|
2
|
+
import { ContactPage } from './chunk-PXRHVVOU.mjs';
|
|
3
|
+
import { PageHead } from './chunk-N5EC53MH.mjs';
|
|
4
|
+
import { useProduct } from './chunk-GCFLCO2Z.mjs';
|
|
5
|
+
import { useWebSDK, useWebSDKConfig } from './chunk-5JRJ57CL.mjs';
|
|
6
|
+
import { useState, useEffect } from 'react';
|
|
7
|
+
import { FileText, Download } from 'lucide-react';
|
|
8
|
+
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
9
|
+
|
|
10
|
+
var PARTNER_CONTENT_QUERY = `
|
|
11
|
+
query PartnerContent($productId: String!, $limit: Int) {
|
|
12
|
+
publicContentPages(productId: $productId, limit: $limit, offset: 0) {
|
|
13
|
+
items {
|
|
14
|
+
id
|
|
15
|
+
title
|
|
16
|
+
slug
|
|
17
|
+
excerpt
|
|
18
|
+
content
|
|
19
|
+
contentFormat
|
|
20
|
+
thumbnail
|
|
21
|
+
tags
|
|
22
|
+
publishedAt
|
|
23
|
+
lastUpdated
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
`;
|
|
28
|
+
var PARTNER_DOCUMENTS_QUERY = `
|
|
29
|
+
query PartnerDocuments($productId: String!, $limit: Int) {
|
|
30
|
+
publicPressKitAssets(productId: $productId, limit: $limit, offset: 0) {
|
|
31
|
+
items {
|
|
32
|
+
id
|
|
33
|
+
category
|
|
34
|
+
title
|
|
35
|
+
description
|
|
36
|
+
fileUrl
|
|
37
|
+
fileName
|
|
38
|
+
fileSize
|
|
39
|
+
mimeType
|
|
40
|
+
version
|
|
41
|
+
tags
|
|
42
|
+
sortOrder
|
|
43
|
+
updatedAt
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
`;
|
|
48
|
+
function formatFileSize(bytes) {
|
|
49
|
+
if (!bytes || bytes <= 0) return "";
|
|
50
|
+
const units = ["B", "KB", "MB", "GB"];
|
|
51
|
+
let i = 0;
|
|
52
|
+
let n = bytes;
|
|
53
|
+
while (n >= 1024 && i < units.length - 1) {
|
|
54
|
+
n /= 1024;
|
|
55
|
+
i++;
|
|
56
|
+
}
|
|
57
|
+
return `${n.toFixed(n >= 10 || i === 0 ? 0 : 1)} ${units[i]}`;
|
|
58
|
+
}
|
|
59
|
+
async function downloadDocument(e, doc) {
|
|
60
|
+
e.preventDefault();
|
|
61
|
+
const name = doc.fileName || doc.fileUrl.split("/").pop()?.split("?")[0] || doc.title || "partner-document";
|
|
62
|
+
const openInTab = () => window.open(doc.fileUrl, "_blank", "noopener,noreferrer");
|
|
63
|
+
try {
|
|
64
|
+
const res = await fetch(doc.fileUrl, { mode: "cors" });
|
|
65
|
+
if (!res.ok) {
|
|
66
|
+
openInTab();
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
const blob = await res.blob();
|
|
70
|
+
const objUrl = URL.createObjectURL(blob);
|
|
71
|
+
const a = document.createElement("a");
|
|
72
|
+
a.href = objUrl;
|
|
73
|
+
a.download = name;
|
|
74
|
+
document.body.appendChild(a);
|
|
75
|
+
a.click();
|
|
76
|
+
a.remove();
|
|
77
|
+
URL.revokeObjectURL(objUrl);
|
|
78
|
+
} catch {
|
|
79
|
+
openInTab();
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
function PartnerHero({
|
|
83
|
+
title,
|
|
84
|
+
subtitle
|
|
85
|
+
}) {
|
|
86
|
+
return /* @__PURE__ */ jsx("section", { className: "bg-gradient-to-b from-background to-muted/20 py-16 md:py-24", children: /* @__PURE__ */ jsxs("div", { className: "container mx-auto px-4 max-w-4xl text-center", children: [
|
|
87
|
+
/* @__PURE__ */ jsx("h1", { className: "text-4xl md:text-5xl font-bold tracking-tight text-foreground mb-4", children: title }),
|
|
88
|
+
subtitle && /* @__PURE__ */ jsx("p", { className: "text-lg md:text-xl text-muted-foreground max-w-2xl mx-auto", children: subtitle })
|
|
89
|
+
] }) });
|
|
90
|
+
}
|
|
91
|
+
function stripLeadingHeading(content, format) {
|
|
92
|
+
if (format === "html") {
|
|
93
|
+
return content.replace(/^\s*<h1\b[^>]*>[\s\S]*?<\/h1>\s*/i, "");
|
|
94
|
+
}
|
|
95
|
+
return content.replace(/^\s*#\s+.*(?:\r?\n)*/, "");
|
|
96
|
+
}
|
|
97
|
+
function PartnerContentSection({
|
|
98
|
+
page
|
|
99
|
+
}) {
|
|
100
|
+
return /* @__PURE__ */ jsx("section", { className: "py-12 md:py-16", children: /* @__PURE__ */ jsx(
|
|
101
|
+
"div",
|
|
102
|
+
{
|
|
103
|
+
className: [
|
|
104
|
+
"mx-auto max-w-3xl px-4",
|
|
105
|
+
// Headings
|
|
106
|
+
"[&_h1]:text-3xl [&_h1]:font-bold [&_h1]:tracking-tight [&_h1]:mt-0 [&_h1]:mb-6",
|
|
107
|
+
"[&_h2]:text-2xl [&_h2]:font-bold [&_h2]:tracking-tight [&_h2]:mt-10 [&_h2]:mb-4",
|
|
108
|
+
"[&_h3]:text-xl [&_h3]:font-semibold [&_h3]:mt-7 [&_h3]:mb-3",
|
|
109
|
+
"[&_h4]:text-lg [&_h4]:font-semibold [&_h4]:mt-6 [&_h4]:mb-2",
|
|
110
|
+
// Block elements
|
|
111
|
+
"[&_p]:my-4 [&_p]:leading-relaxed [&_p]:text-base",
|
|
112
|
+
"[&_ul]:my-4 [&_ul]:pl-6 [&_ul]:list-disc [&_ul]:space-y-1",
|
|
113
|
+
"[&_ol]:my-4 [&_ol]:pl-6 [&_ol]:list-decimal [&_ol]:space-y-1",
|
|
114
|
+
"[&_li]:leading-relaxed",
|
|
115
|
+
"[&_li>p]:my-1",
|
|
116
|
+
"[&_blockquote]:border-l-4 [&_blockquote]:border-border [&_blockquote]:pl-4 [&_blockquote]:italic [&_blockquote]:my-4 [&_blockquote]:text-muted-foreground",
|
|
117
|
+
"[&_hr]:my-8 [&_hr]:border-border",
|
|
118
|
+
// Inline elements
|
|
119
|
+
"[&_strong]:font-semibold [&_strong]:text-foreground",
|
|
120
|
+
"[&_em]:italic",
|
|
121
|
+
"[&_a]:underline [&_a]:underline-offset-2 [&_a]:text-accent hover:[&_a]:text-accent/80 [&_a]:transition-colors",
|
|
122
|
+
"[&_code]:rounded [&_code]:bg-muted [&_code]:px-1.5 [&_code]:py-0.5 [&_code]:text-sm [&_code]:font-mono",
|
|
123
|
+
"[&_pre]:my-4 [&_pre]:overflow-x-auto [&_pre]:rounded-lg [&_pre]:bg-muted [&_pre]:p-4 [&_pre]:text-sm",
|
|
124
|
+
"[&_pre>code]:bg-transparent [&_pre>code]:p-0"
|
|
125
|
+
].join(" "),
|
|
126
|
+
children: page.content && /* @__PURE__ */ jsx(
|
|
127
|
+
ContentRenderer,
|
|
128
|
+
{
|
|
129
|
+
content: stripLeadingHeading(page.content, page.contentFormat),
|
|
130
|
+
format: (
|
|
131
|
+
// Validate before passing; unknown formats fall through to
|
|
132
|
+
// ContentRenderer's default behavior (markdown rendering)
|
|
133
|
+
// rather than getting silently force-cast to an
|
|
134
|
+
// unsupported variant.
|
|
135
|
+
page.contentFormat === "markdown" || page.contentFormat === "html" ? page.contentFormat : void 0
|
|
136
|
+
)
|
|
137
|
+
}
|
|
138
|
+
)
|
|
139
|
+
}
|
|
140
|
+
) });
|
|
141
|
+
}
|
|
142
|
+
function PartnerDocumentCard({ doc }) {
|
|
143
|
+
const sizeLabel = formatFileSize(doc.fileSize);
|
|
144
|
+
const ext = doc.fileName?.split(".").pop()?.toUpperCase();
|
|
145
|
+
return /* @__PURE__ */ 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: [
|
|
146
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-start gap-3", children: [
|
|
147
|
+
/* @__PURE__ */ jsx("div", { className: "rounded-lg bg-muted p-2.5 shrink-0", children: /* @__PURE__ */ jsx(FileText, { size: 20, className: "text-muted-foreground" }) }),
|
|
148
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
|
|
149
|
+
/* @__PURE__ */ jsx("h3", { className: "font-semibold text-foreground truncate", children: doc.title }),
|
|
150
|
+
doc.version && /* @__PURE__ */ jsxs("p", { className: "text-xs text-muted-foreground mt-0.5", children: [
|
|
151
|
+
"Version ",
|
|
152
|
+
doc.version
|
|
153
|
+
] })
|
|
154
|
+
] })
|
|
155
|
+
] }),
|
|
156
|
+
doc.description && /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground line-clamp-3", children: doc.description }),
|
|
157
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-3 mt-auto pt-2 border-t border-border/60", children: [
|
|
158
|
+
/* @__PURE__ */ jsxs("div", { className: "text-xs text-muted-foreground flex items-center gap-2", children: [
|
|
159
|
+
ext && /* @__PURE__ */ jsx("span", { children: ext }),
|
|
160
|
+
sizeLabel && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
161
|
+
/* @__PURE__ */ jsx("span", { "aria-hidden": true, children: "\xB7" }),
|
|
162
|
+
/* @__PURE__ */ jsx("span", { children: sizeLabel })
|
|
163
|
+
] })
|
|
164
|
+
] }),
|
|
165
|
+
/* @__PURE__ */ jsxs(
|
|
166
|
+
"a",
|
|
167
|
+
{
|
|
168
|
+
href: doc.fileUrl,
|
|
169
|
+
download: doc.fileName ?? void 0,
|
|
170
|
+
onClick: (e) => void downloadDocument(e, doc),
|
|
171
|
+
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",
|
|
172
|
+
children: [
|
|
173
|
+
/* @__PURE__ */ jsx(Download, { size: 14 }),
|
|
174
|
+
"Download"
|
|
175
|
+
]
|
|
176
|
+
}
|
|
177
|
+
)
|
|
178
|
+
] })
|
|
179
|
+
] });
|
|
180
|
+
}
|
|
181
|
+
function PartnerDocumentsSection({
|
|
182
|
+
title,
|
|
183
|
+
subtitle,
|
|
184
|
+
docs
|
|
185
|
+
}) {
|
|
186
|
+
return /* @__PURE__ */ jsx("section", { className: "py-12 md:py-16 bg-muted/20", children: /* @__PURE__ */ jsxs("div", { className: "container mx-auto px-4 max-w-5xl", children: [
|
|
187
|
+
/* @__PURE__ */ jsxs("header", { className: "mb-8 md:mb-10 text-center", children: [
|
|
188
|
+
/* @__PURE__ */ jsx("h2", { className: "text-2xl md:text-3xl font-bold text-foreground mb-2", children: title }),
|
|
189
|
+
subtitle && /* @__PURE__ */ jsx("p", { className: "text-muted-foreground max-w-2xl mx-auto", children: subtitle })
|
|
190
|
+
] }),
|
|
191
|
+
/* @__PURE__ */ jsx("div", { className: "grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4", children: docs.map((doc) => /* @__PURE__ */ jsx(PartnerDocumentCard, { doc }, doc.id)) })
|
|
192
|
+
] }) });
|
|
193
|
+
}
|
|
194
|
+
var DEFAULT_PARTNER_CATEGORY = {
|
|
195
|
+
value: "partnership",
|
|
196
|
+
label: "Partner program"
|
|
197
|
+
};
|
|
198
|
+
function PartnersPage(props) {
|
|
199
|
+
const {
|
|
200
|
+
productName: productNameProp,
|
|
201
|
+
heroTitle = "Partner with us",
|
|
202
|
+
heroSubtitle,
|
|
203
|
+
contentTag = "partner",
|
|
204
|
+
contentSlug,
|
|
205
|
+
documentsTag = "partner",
|
|
206
|
+
documentsTitle = "Partner Documents",
|
|
207
|
+
documentsSubtitle,
|
|
208
|
+
showContactForm = true,
|
|
209
|
+
contactCategory,
|
|
210
|
+
formTitle = "Become a partner",
|
|
211
|
+
formSubtitle,
|
|
212
|
+
contactEmail,
|
|
213
|
+
contactPhone,
|
|
214
|
+
officeAddress,
|
|
215
|
+
responseTime,
|
|
216
|
+
recaptchaSiteKey,
|
|
217
|
+
additionalOptions,
|
|
218
|
+
seoTitle,
|
|
219
|
+
seoDescription,
|
|
220
|
+
seoKeywords,
|
|
221
|
+
className
|
|
222
|
+
} = props;
|
|
223
|
+
const client = useWebSDK();
|
|
224
|
+
const config = useWebSDKConfig();
|
|
225
|
+
const { product } = useProduct();
|
|
226
|
+
const resolvedProductId = product?.id ?? config?.productId ?? productNameProp ?? "";
|
|
227
|
+
const resolvedProductName = productNameProp ?? product?.name ?? config?.productId ?? "our team";
|
|
228
|
+
const [contentPage, setContentPage] = useState(null);
|
|
229
|
+
const [docs, setDocs] = useState([]);
|
|
230
|
+
const [loading, setLoading] = useState(true);
|
|
231
|
+
useEffect(() => {
|
|
232
|
+
if (!client || !resolvedProductId) {
|
|
233
|
+
setContentPage(null);
|
|
234
|
+
setDocs([]);
|
|
235
|
+
setLoading(false);
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
let cancelled = false;
|
|
239
|
+
setLoading(true);
|
|
240
|
+
void Promise.allSettled([
|
|
241
|
+
client.query(PARTNER_CONTENT_QUERY, { productId: resolvedProductId, limit: 50 }),
|
|
242
|
+
client.query(
|
|
243
|
+
PARTNER_DOCUMENTS_QUERY,
|
|
244
|
+
{ productId: resolvedProductId, limit: 50 }
|
|
245
|
+
)
|
|
246
|
+
]).then(([contentResult, docsResult]) => {
|
|
247
|
+
if (cancelled) return;
|
|
248
|
+
if (contentResult.status === "rejected") {
|
|
249
|
+
setContentPage(null);
|
|
250
|
+
} else if (contentResult.status === "fulfilled") {
|
|
251
|
+
const items = contentResult.value.data?.publicContentPages?.items ?? [];
|
|
252
|
+
const match = contentSlug ? items.find((p) => p.slug === contentSlug) : items.find((p) => p.tags?.includes(contentTag));
|
|
253
|
+
setContentPage(match ?? null);
|
|
254
|
+
}
|
|
255
|
+
if (docsResult.status === "rejected") {
|
|
256
|
+
setDocs([]);
|
|
257
|
+
} else if (docsResult.status === "fulfilled") {
|
|
258
|
+
const items = docsResult.value.data?.publicPressKitAssets?.items ?? [];
|
|
259
|
+
const filtered = items.filter(
|
|
260
|
+
(d) => d.category === "DOCUMENT" && d.tags?.includes(documentsTag) && !!d.fileUrl
|
|
261
|
+
).sort(
|
|
262
|
+
(a, b) => a.sortOrder - b.sortOrder
|
|
263
|
+
);
|
|
264
|
+
setDocs(filtered);
|
|
265
|
+
}
|
|
266
|
+
setLoading(false);
|
|
267
|
+
});
|
|
268
|
+
return () => {
|
|
269
|
+
cancelled = true;
|
|
270
|
+
};
|
|
271
|
+
}, [client, resolvedProductId, contentSlug, contentTag, documentsTag]);
|
|
272
|
+
let partnerCategories;
|
|
273
|
+
if (contactCategory === false) {
|
|
274
|
+
partnerCategories = [];
|
|
275
|
+
} else if (Array.isArray(contactCategory)) {
|
|
276
|
+
partnerCategories = contactCategory.length > 0 ? contactCategory : [DEFAULT_PARTNER_CATEGORY];
|
|
277
|
+
} else {
|
|
278
|
+
partnerCategories = [contactCategory ?? DEFAULT_PARTNER_CATEGORY];
|
|
279
|
+
}
|
|
280
|
+
return /* @__PURE__ */ jsxs("div", { className, children: [
|
|
281
|
+
/* @__PURE__ */ jsx(
|
|
282
|
+
PageHead,
|
|
283
|
+
{
|
|
284
|
+
title: seoTitle ?? `${heroTitle} \u2014 ${resolvedProductName}`,
|
|
285
|
+
description: seoDescription ?? heroSubtitle ?? `Join the ${resolvedProductName} partner programme.`,
|
|
286
|
+
keywords: seoKeywords
|
|
287
|
+
}
|
|
288
|
+
),
|
|
289
|
+
/* @__PURE__ */ jsx(PartnerHero, { title: heroTitle, subtitle: heroSubtitle }),
|
|
290
|
+
!loading && contentPage && /* @__PURE__ */ jsx(PartnerContentSection, { page: contentPage }),
|
|
291
|
+
!loading && docs.length > 0 && /* @__PURE__ */ jsx(
|
|
292
|
+
PartnerDocumentsSection,
|
|
293
|
+
{
|
|
294
|
+
title: documentsTitle,
|
|
295
|
+
subtitle: documentsSubtitle,
|
|
296
|
+
docs
|
|
297
|
+
}
|
|
298
|
+
),
|
|
299
|
+
showContactForm && /* @__PURE__ */ jsx(
|
|
300
|
+
ContactPage,
|
|
301
|
+
{
|
|
302
|
+
productName: resolvedProductName,
|
|
303
|
+
heroTitle: formTitle,
|
|
304
|
+
headingLevel: 2,
|
|
305
|
+
heroDescription: formSubtitle ?? `Tell us about your organisation and a partner manager from ${resolvedProductName} will be in touch.`,
|
|
306
|
+
contactEmail,
|
|
307
|
+
contactPhone,
|
|
308
|
+
officeAddress,
|
|
309
|
+
responseTime,
|
|
310
|
+
recaptchaSiteKey,
|
|
311
|
+
additionalOptions,
|
|
312
|
+
categories: partnerCategories,
|
|
313
|
+
categoryLabel: "Partnership type",
|
|
314
|
+
categoryRequired: partnerCategories.length > 0
|
|
315
|
+
}
|
|
316
|
+
)
|
|
317
|
+
] });
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
export { PartnersPage, stripLeadingHeading };
|
|
321
|
+
//# sourceMappingURL=chunk-SZNS5IK6.mjs.map
|
|
322
|
+
//# sourceMappingURL=chunk-SZNS5IK6.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/partners.tsx"],"names":[],"mappings":"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","file":"chunk-SZNS5IK6.mjs","sourcesContent":["\"use client\";\n\n/**\n * Partners Page Component\n *\n * CMS-powered public partner programme page. Composes three sources of truth\n * we already manage in the admin:\n *\n * 1. **Marketing copy** — a single `ProductContentPage` matched by `tag`\n * (default `\"partner\"`) or by `slug`. Rendered through `ContentRenderer`\n * so admins can use the full CMS editor — no hardcoded copy in this file.\n * 2. **Downloadable documents** — `ProductPressKitAsset`s with category\n * `\"DOCUMENT\"` and a matching `tag` (default `\"partner\"`). Typically the\n * Partner / Reseller Agreement PDF, deck, FAQ, etc. Self-hides when empty.\n * 3. **\"Become a partner\" contact form** — embedded `<ContactPage>` with the\n * inquiry category **locked** to `\"partnership\"` so submissions land in the\n * admin support inbox tagged for the partner-program lane (shared inbox\n * routing for v1; per-category email routing is a separate backend change).\n *\n * The page is intentionally section-based and gracefully degrades: if the\n * admin hasn't created a content page yet, only the hero + form render; if\n * no documents are uploaded, the document section disappears; if neither\n * exists, the page is just a form. This keeps the partner page useful from\n * day one without blocking on CMS data.\n *\n * Mirrors the `PressKitPage` + `BlogListPage` architecture; data fetching\n * goes through the same `WebSDKClient` GraphQL helpers everything else uses.\n *\n * @example\n * ```tsx\n * import { PartnersPage } from '@burdenoff/website-sdk'\n *\n * <Route\n * path=\"/partners\"\n * element={\n * <PartnersPage\n * productName=\"VibeControls\"\n * contactEmail=\"partnerships@vibecontrols.com\"\n * contactPhone=\"+91-XXX-XXX-XXXX\"\n * officeAddress={{ line1: \"…\", line2: \"\", line3: \"\", country: \"India\" }}\n * />\n * }\n * />\n * ```\n */\n\nimport { useEffect, useState } from \"react\";\n\nimport { Download, FileText } from \"lucide-react\";\n\nimport { ContactPage } from \"./contact\";\nimport { useWebSDK, useWebSDKConfig } from \"../client/provider\";\nimport { ContentRenderer } from \"../content/content-provider\";\nimport { useProduct } from \"../product/product-provider\";\nimport { PageHead } from \"../seo/page-head\";\n\nimport type { ContactCategory, ContactPageProps } from \"./contact\";\nimport type { ContentPageListItem } from \"../content/content-provider\";\n\n/**\n * Office address shape consumed by `<ContactPage>`. Mirrored here as a\n * local type so we don't widen `contact.tsx`'s public surface just for\n * this consumer — keeps the contact module's exports unchanged.\n */\ninterface OfficeAddress {\n line1: string;\n line2: string;\n line3: string;\n country: string;\n}\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * A downloadable document surfaced on the partners page. Same shape as\n * `PressKitAsset` but only the fields the card uses — keeps the type\n * surface tight and avoids a cross-file import of the press-kit type.\n */\ninterface PartnerDocument {\n id: string;\n category: string;\n title: string;\n description?: string | null;\n fileUrl: string;\n fileName?: string | null;\n fileSize?: number | null;\n mimeType?: string | null;\n version?: string | null;\n tags: string[];\n sortOrder: number;\n updatedAt: string;\n}\n\nexport interface PartnersPageProps {\n /** Product name (e.g. `\"VibeControls\"`). Falls back to SDK config. */\n productName?: string;\n\n // ── Hero ─────────────────────────────────────────────────────────────\n /** Hero headline. Default: `\"Partner with us\"`. */\n heroTitle?: string;\n /** Hero subtitle / pitch line. */\n heroSubtitle?: string;\n\n // ── CMS marketing copy section ───────────────────────────────────────\n /**\n * Tag filter for the marketing-copy body. Default: `\"partner\"`. The\n * component fetches the FIRST published `ProductContentPage` whose\n * tags array contains this tag and renders its body via\n * `ContentRenderer`. Self-hides if none exist.\n */\n contentTag?: string;\n /**\n * Pin a specific content page by slug. If set, overrides `contentTag`.\n * Useful when multiple partner pages exist and you want to render one\n * canonical version per route.\n */\n contentSlug?: string;\n\n // ── Documents section ────────────────────────────────────────────────\n /**\n * Tag filter for the downloadable documents. Default: `\"partner\"`.\n * Combined with the implicit `category === \"DOCUMENT\"` filter — only\n * Press Kit assets that match BOTH are rendered as cards. Self-hides\n * when empty.\n */\n documentsTag?: string;\n /** Documents-section heading. Default: `\"Partner Documents\"`. */\n documentsTitle?: string;\n /** Documents-section subtitle / lead. */\n documentsSubtitle?: string;\n\n // ── Contact form section ─────────────────────────────────────────────\n /**\n * Whether to render the \"Become a partner\" contact form. Default: `true`.\n * Set to `false` for a documents-only / read-only partner landing page.\n */\n showContactForm?: boolean;\n /**\n * Inquiry-category options for the embedded `<ContactPage>` dropdown.\n * Default: `{ value: \"partnership\", label: \"Partner program\" }` (a single,\n * required option so every submission is tagged for partner triage). Pass an\n * **array** to offer multiple selectable partner tracks (Referral, Reseller,\n * MSP, …). Pass `false` to disable the dropdown entirely.\n */\n contactCategory?: ContactCategory | ContactCategory[] | false;\n /** Heading shown above the form. Default: `\"Become a partner\"`. */\n formTitle?: string;\n /** Subtitle / lead above the form. */\n formSubtitle?: string;\n\n // ── Contact-form passthroughs (required for <ContactPage>) ───────────\n contactEmail: string;\n contactPhone: string;\n officeAddress: OfficeAddress;\n responseTime?: string;\n recaptchaSiteKey?: string;\n /** Forwarded to `<ContactPage additionalOptions>`. */\n additionalOptions?: ContactPageProps[\"additionalOptions\"];\n\n // ── SEO ──────────────────────────────────────────────────────────────\n seoTitle?: string;\n seoDescription?: string;\n seoKeywords?: string;\n\n className?: string;\n}\n\n// ============================================================================\n// Queries\n// ============================================================================\n\nconst PARTNER_CONTENT_QUERY = `\n query PartnerContent($productId: String!, $limit: Int) {\n publicContentPages(productId: $productId, limit: $limit, offset: 0) {\n items {\n id\n title\n slug\n excerpt\n content\n contentFormat\n thumbnail\n tags\n publishedAt\n lastUpdated\n }\n }\n }\n`;\n\nconst PARTNER_DOCUMENTS_QUERY = `\n query PartnerDocuments($productId: String!, $limit: Int) {\n publicPressKitAssets(productId: $productId, limit: $limit, offset: 0) {\n items {\n id\n category\n title\n description\n fileUrl\n fileName\n fileSize\n mimeType\n version\n tags\n sortOrder\n updatedAt\n }\n }\n }\n`;\n\n// ============================================================================\n// Helpers\n// ============================================================================\n\nfunction formatFileSize(bytes: number | null | undefined): string {\n if (!bytes || bytes <= 0) return \"\";\n const units = [\"B\", \"KB\", \"MB\", \"GB\"];\n let i = 0;\n let n = bytes;\n while (n >= 1024 && i < units.length - 1) {\n n /= 1024;\n i++;\n }\n return `${n.toFixed(n >= 10 || i === 0 ? 0 : 1)} ${units[i]}`;\n}\n\n/**\n * Download an asset as a file (vs opening in a tab). Same blob-fetch\n * pattern as press-kit `downloadAsset` — works wherever the bucket\n * allows cross-origin GET (verified for dev/alpha/prod presskit\n * buckets, 2026-05-20).\n */\nasync function downloadDocument(\n e: { preventDefault: () => void },\n doc: PartnerDocument,\n): Promise<void> {\n e.preventDefault();\n const name =\n doc.fileName ||\n doc.fileUrl.split(\"/\").pop()?.split(\"?\")[0] ||\n doc.title ||\n \"partner-document\";\n const openInTab = () =>\n window.open(doc.fileUrl, \"_blank\", \"noopener,noreferrer\");\n try {\n const res = await fetch(doc.fileUrl, { mode: \"cors\" });\n if (!res.ok) {\n openInTab();\n return;\n }\n const blob = await res.blob();\n const objUrl = URL.createObjectURL(blob);\n const a = document.createElement(\"a\");\n a.href = objUrl;\n a.download = name;\n document.body.appendChild(a);\n a.click();\n a.remove();\n URL.revokeObjectURL(objUrl);\n } catch {\n openInTab();\n }\n}\n\n// ============================================================================\n// Sub-components\n// ============================================================================\n\nfunction PartnerHero({\n title,\n subtitle,\n}: {\n title: string;\n subtitle?: string;\n}) {\n return (\n <section className=\"bg-gradient-to-b from-background to-muted/20 py-16 md:py-24\">\n <div className=\"container mx-auto px-4 max-w-4xl text-center\">\n <h1 className=\"text-4xl md:text-5xl font-bold tracking-tight text-foreground mb-4\">\n {title}\n </h1>\n {subtitle && (\n <p className=\"text-lg md:text-xl text-muted-foreground max-w-2xl mx-auto\">\n {subtitle}\n </p>\n )}\n </div>\n </section>\n );\n}\n\n/**\n * Strip a single leading top-level heading from CMS body content. The\n * `PartnerHero` already renders the page title, so a content body that also\n * opens with `# Title` (the natural way an author writes a page) would print\n * the title twice. Removing the body's leading heading keeps one visible\n * title and one `<h1>` per page. Only the *first* heading is touched — all\n * section headings below it are preserved.\n *\n * Exported for unit testing only; not re-exported from the package barrel.\n */\nexport function stripLeadingHeading(content: string, format?: string): string {\n if (format === \"html\") {\n return content.replace(/^\\s*<h1\\b[^>]*>[\\s\\S]*?<\\/h1>\\s*/i, \"\");\n }\n // markdown (default): drop a leading \"# Heading\" line + any trailing blanks.\n // Trailing newlines are optional (`*`, not `+`) so a body that is only\n // \"# Title\" with no trailing newline — common after editor/API trimming —\n // still gets stripped. `#\\s+` matches H1 only; `## Section` is left intact.\n return content.replace(/^\\s*#\\s+.*(?:\\r?\\n)*/, \"\");\n}\n\nfunction PartnerContentSection({\n page,\n}: {\n page: ContentPageListItem & { content?: string; contentFormat?: string };\n}) {\n // Restore typography for the markdown-rendered HTML — Tailwind preflight\n // strips browser defaults, and we don't want to assume consumer sites have\n // `@tailwindcss/typography` installed. Arbitrary-children selectors keep\n // this portable to every product website that consumes the SDK.\n return (\n <section className=\"py-12 md:py-16\">\n <div\n className={[\n \"mx-auto max-w-3xl px-4\",\n // Headings\n \"[&_h1]:text-3xl [&_h1]:font-bold [&_h1]:tracking-tight [&_h1]:mt-0 [&_h1]:mb-6\",\n \"[&_h2]:text-2xl [&_h2]:font-bold [&_h2]:tracking-tight [&_h2]:mt-10 [&_h2]:mb-4\",\n \"[&_h3]:text-xl [&_h3]:font-semibold [&_h3]:mt-7 [&_h3]:mb-3\",\n \"[&_h4]:text-lg [&_h4]:font-semibold [&_h4]:mt-6 [&_h4]:mb-2\",\n // Block elements\n \"[&_p]:my-4 [&_p]:leading-relaxed [&_p]:text-base\",\n \"[&_ul]:my-4 [&_ul]:pl-6 [&_ul]:list-disc [&_ul]:space-y-1\",\n \"[&_ol]:my-4 [&_ol]:pl-6 [&_ol]:list-decimal [&_ol]:space-y-1\",\n \"[&_li]:leading-relaxed\",\n \"[&_li>p]:my-1\",\n \"[&_blockquote]:border-l-4 [&_blockquote]:border-border [&_blockquote]:pl-4 [&_blockquote]:italic [&_blockquote]:my-4 [&_blockquote]:text-muted-foreground\",\n \"[&_hr]:my-8 [&_hr]:border-border\",\n // Inline elements\n \"[&_strong]:font-semibold [&_strong]:text-foreground\",\n \"[&_em]:italic\",\n \"[&_a]:underline [&_a]:underline-offset-2 [&_a]:text-accent hover:[&_a]:text-accent/80 [&_a]:transition-colors\",\n \"[&_code]:rounded [&_code]:bg-muted [&_code]:px-1.5 [&_code]:py-0.5 [&_code]:text-sm [&_code]:font-mono\",\n \"[&_pre]:my-4 [&_pre]:overflow-x-auto [&_pre]:rounded-lg [&_pre]:bg-muted [&_pre]:p-4 [&_pre]:text-sm\",\n \"[&_pre>code]:bg-transparent [&_pre>code]:p-0\",\n ].join(\" \")}\n >\n {page.content && (\n <ContentRenderer\n content={stripLeadingHeading(page.content, page.contentFormat)}\n format={\n // Validate before passing; unknown formats fall through to\n // ContentRenderer's default behavior (markdown rendering)\n // rather than getting silently force-cast to an\n // unsupported variant.\n page.contentFormat === \"markdown\" || page.contentFormat === \"html\"\n ? page.contentFormat\n : undefined\n }\n />\n )}\n </div>\n </section>\n );\n}\n\nfunction PartnerDocumentCard({ doc }: { doc: PartnerDocument }) {\n const sizeLabel = formatFileSize(doc.fileSize);\n const ext = doc.fileName?.split(\".\").pop()?.toUpperCase();\n return (\n <article className=\"flex flex-col gap-3 rounded-xl border border-border bg-card p-5 shadow-sm hover:shadow-md transition-shadow\">\n <div className=\"flex items-start gap-3\">\n <div className=\"rounded-lg bg-muted p-2.5 shrink-0\">\n <FileText size={20} className=\"text-muted-foreground\" />\n </div>\n <div className=\"min-w-0 flex-1\">\n <h3 className=\"font-semibold text-foreground truncate\">\n {doc.title}\n </h3>\n {doc.version && (\n <p className=\"text-xs text-muted-foreground mt-0.5\">\n Version {doc.version}\n </p>\n )}\n </div>\n </div>\n {doc.description && (\n <p className=\"text-sm text-muted-foreground line-clamp-3\">\n {doc.description}\n </p>\n )}\n <div className=\"flex items-center justify-between gap-3 mt-auto pt-2 border-t border-border/60\">\n <div className=\"text-xs text-muted-foreground flex items-center gap-2\">\n {ext && <span>{ext}</span>}\n {sizeLabel && (\n <>\n <span aria-hidden>·</span>\n <span>{sizeLabel}</span>\n </>\n )}\n </div>\n <a\n href={doc.fileUrl}\n download={doc.fileName ?? undefined}\n onClick={(e) => void downloadDocument(e, doc)}\n 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\"\n >\n <Download size={14} />\n Download\n </a>\n </div>\n </article>\n );\n}\n\nfunction PartnerDocumentsSection({\n title,\n subtitle,\n docs,\n}: {\n title: string;\n subtitle?: string;\n docs: PartnerDocument[];\n}) {\n return (\n <section className=\"py-12 md:py-16 bg-muted/20\">\n <div className=\"container mx-auto px-4 max-w-5xl\">\n <header className=\"mb-8 md:mb-10 text-center\">\n <h2 className=\"text-2xl md:text-3xl font-bold text-foreground mb-2\">\n {title}\n </h2>\n {subtitle && (\n <p className=\"text-muted-foreground max-w-2xl mx-auto\">\n {subtitle}\n </p>\n )}\n </header>\n <div className=\"grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4\">\n {docs.map((doc) => (\n <PartnerDocumentCard key={doc.id} doc={doc} />\n ))}\n </div>\n </div>\n </section>\n );\n}\n\n// ============================================================================\n// Main component\n// ============================================================================\n\nconst DEFAULT_PARTNER_CATEGORY: ContactCategory = {\n value: \"partnership\",\n label: \"Partner program\",\n};\n\nexport function PartnersPage(props: PartnersPageProps) {\n const {\n productName: productNameProp,\n heroTitle = \"Partner with us\",\n heroSubtitle,\n\n contentTag = \"partner\",\n contentSlug,\n\n documentsTag = \"partner\",\n documentsTitle = \"Partner Documents\",\n documentsSubtitle,\n\n showContactForm = true,\n contactCategory,\n formTitle = \"Become a partner\",\n formSubtitle,\n\n contactEmail,\n contactPhone,\n officeAddress,\n responseTime,\n recaptchaSiteKey,\n additionalOptions,\n\n seoTitle,\n seoDescription,\n seoKeywords,\n className,\n } = props;\n\n const client = useWebSDK();\n const config = useWebSDKConfig();\n const { product } = useProduct();\n\n const resolvedProductId =\n product?.id ?? config?.productId ?? productNameProp ?? \"\";\n const resolvedProductName =\n productNameProp ?? product?.name ?? config?.productId ?? \"our team\";\n\n const [contentPage, setContentPage] = useState<\n (ContentPageListItem & { content?: string; contentFormat?: string }) | null\n >(null);\n const [docs, setDocs] = useState<PartnerDocument[]>([]);\n const [loading, setLoading] = useState<boolean>(true);\n\n // Single fetch on mount/productId-change — small payloads, no pagination needed\n // on a partners page (one content body + a handful of docs at most).\n useEffect(() => {\n if (!client || !resolvedProductId) {\n // Reset section state when the product scope unsets (e.g. provider re-\n // resolution between routes). Without this, the page would keep\n // rendering the previous product's content + docs because the render\n // guards only check `!loading` plus existing state.\n setContentPage(null);\n setDocs([]);\n setLoading(false);\n return;\n }\n let cancelled = false;\n setLoading(true);\n\n void Promise.allSettled([\n client.query<{\n publicContentPages: {\n items: (ContentPageListItem & {\n content?: string;\n contentFormat?: string;\n })[];\n };\n }>(PARTNER_CONTENT_QUERY, { productId: resolvedProductId, limit: 50 }),\n client.query<{ publicPressKitAssets: { items: PartnerDocument[] } }>(\n PARTNER_DOCUMENTS_QUERY,\n { productId: resolvedProductId, limit: 50 },\n ),\n ]).then(([contentResult, docsResult]) => {\n if (cancelled) return;\n\n // Content page — match by slug if given, else first item carrying the tag.\n // Clear the previous match on a rejected request so stale data from an\n // earlier product / tag doesn't linger on screen across navigations.\n if (contentResult.status === \"rejected\") {\n setContentPage(null);\n } else if (contentResult.status === \"fulfilled\") {\n type ContentItem = ContentPageListItem & {\n content?: string;\n contentFormat?: string;\n };\n const items: ContentItem[] =\n contentResult.value.data?.publicContentPages?.items ?? [];\n const match: ContentItem | undefined = contentSlug\n ? items.find((p: ContentItem) => p.slug === contentSlug)\n : items.find((p: ContentItem) => p.tags?.includes(contentTag));\n setContentPage(match ?? null);\n }\n\n // Documents — only category === \"DOCUMENT\" AND tag matches AND has a fileUrl.\n // Same stale-clear behavior as the content section above.\n if (docsResult.status === \"rejected\") {\n setDocs([]);\n } else if (docsResult.status === \"fulfilled\") {\n const items: PartnerDocument[] =\n docsResult.value.data?.publicPressKitAssets?.items ?? [];\n const filtered = items\n .filter(\n (d: PartnerDocument) =>\n d.category === \"DOCUMENT\" &&\n d.tags?.includes(documentsTag) &&\n !!d.fileUrl,\n )\n .sort(\n (a: PartnerDocument, b: PartnerDocument) =>\n a.sortOrder - b.sortOrder,\n );\n setDocs(filtered);\n }\n\n setLoading(false);\n });\n\n return () => {\n cancelled = true;\n };\n }, [client, resolvedProductId, contentSlug, contentTag, documentsTag]);\n\n // Normalise the contact-category options for the dropdown:\n // - `false` → no dropdown (explicitly disabled)\n // - a non-empty array → those partner tracks\n // - a single value → that one option\n // - unset OR `[]` → the default partner category\n // An empty array is treated as \"use the default\", not \"disable\" — so an\n // accidental `contactCategory={[]}` doesn't silently drop the dropdown; pass\n // `false` to disable it intentionally.\n let partnerCategories: ContactCategory[];\n if (contactCategory === false) {\n partnerCategories = [];\n } else if (Array.isArray(contactCategory)) {\n partnerCategories =\n contactCategory.length > 0 ? contactCategory : [DEFAULT_PARTNER_CATEGORY];\n } else {\n partnerCategories = [contactCategory ?? DEFAULT_PARTNER_CATEGORY];\n }\n\n return (\n <div className={className}>\n <PageHead\n title={seoTitle ?? `${heroTitle} — ${resolvedProductName}`}\n description={\n seoDescription ??\n heroSubtitle ??\n `Join the ${resolvedProductName} partner programme.`\n }\n keywords={seoKeywords}\n />\n\n <PartnerHero title={heroTitle} subtitle={heroSubtitle} />\n\n {!loading && contentPage && <PartnerContentSection page={contentPage} />}\n\n {!loading && docs.length > 0 && (\n <PartnerDocumentsSection\n title={documentsTitle}\n subtitle={documentsSubtitle}\n docs={docs}\n />\n )}\n\n {showContactForm && (\n <ContactPage\n productName={resolvedProductName}\n heroTitle={formTitle}\n // Embedded inside PartnersPage, which already owns the page <h1>.\n // Render the form's \"Become a partner\" as an <h2> section header so\n // there's a single <h1> per page and no competing full-size hero.\n headingLevel={2}\n heroDescription={\n formSubtitle ??\n `Tell us about your organisation and a partner manager from ${resolvedProductName} will be in touch.`\n }\n contactEmail={contactEmail}\n contactPhone={contactPhone}\n officeAddress={officeAddress}\n responseTime={responseTime}\n recaptchaSiteKey={recaptchaSiteKey}\n additionalOptions={additionalOptions}\n // Partnership-type dropdown: one or more partner tracks so every\n // submission is tagged for the partner-program lane. Skipped when the\n // consumer passes `false`.\n categories={partnerCategories}\n categoryLabel=\"Partnership type\"\n categoryRequired={partnerCategories.length > 0}\n />\n )}\n </div>\n );\n}\n"]}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var chunkUQ2HALUD_js = require('./chunk-UQ2HALUD.js');
|
|
4
|
+
var chunkC6OJIHNO_js = require('./chunk-C6OJIHNO.js');
|
|
5
|
+
var lucideReact = require('lucide-react');
|
|
6
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
7
|
+
|
|
8
|
+
function detectElectronPlatform(userAgent = typeof navigator !== "undefined" ? navigator.userAgent : "") {
|
|
9
|
+
const ua = userAgent.toLowerCase();
|
|
10
|
+
if (ua.includes("win")) return "win";
|
|
11
|
+
if (ua.includes("mac")) return "mac";
|
|
12
|
+
if (ua.includes("linux")) return "linux";
|
|
13
|
+
return "unknown";
|
|
14
|
+
}
|
|
15
|
+
function buildDownloadUrl(base, artifactName, platform, platformMap) {
|
|
16
|
+
const normalizedBase = base.replace(/\/$/, "");
|
|
17
|
+
const platformKey = platformMap[platform] ?? platform;
|
|
18
|
+
const fileName = artifactName.includes("{platform}") ? artifactName.replace(/\{platform\}/g, platformKey) : artifactName;
|
|
19
|
+
return `${normalizedBase}/${fileName}`;
|
|
20
|
+
}
|
|
21
|
+
function ElectronDownloadLink({
|
|
22
|
+
downloadBaseUrl,
|
|
23
|
+
artifactName,
|
|
24
|
+
hidden = true,
|
|
25
|
+
label = "Download desktop app",
|
|
26
|
+
downloadLabel = label,
|
|
27
|
+
platformMap = {},
|
|
28
|
+
className,
|
|
29
|
+
variant = "default",
|
|
30
|
+
size = "default"
|
|
31
|
+
}) {
|
|
32
|
+
if (hidden) return null;
|
|
33
|
+
const platform = detectElectronPlatform();
|
|
34
|
+
const href = buildDownloadUrl(
|
|
35
|
+
downloadBaseUrl,
|
|
36
|
+
artifactName,
|
|
37
|
+
platform,
|
|
38
|
+
platformMap
|
|
39
|
+
);
|
|
40
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
41
|
+
chunkUQ2HALUD_js.Button,
|
|
42
|
+
{
|
|
43
|
+
variant,
|
|
44
|
+
size,
|
|
45
|
+
className: chunkC6OJIHNO_js.cn("inline-flex items-center gap-2", className),
|
|
46
|
+
asChild: true,
|
|
47
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
48
|
+
"a",
|
|
49
|
+
{
|
|
50
|
+
href,
|
|
51
|
+
download: true,
|
|
52
|
+
"aria-label": `${downloadLabel}${platform !== "unknown" ? ` for ${platform}` : ""}`,
|
|
53
|
+
children: [
|
|
54
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Download, { className: "h-4 w-4", "aria-hidden": "true" }),
|
|
55
|
+
label
|
|
56
|
+
]
|
|
57
|
+
}
|
|
58
|
+
)
|
|
59
|
+
}
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
exports.ElectronDownloadLink = ElectronDownloadLink;
|
|
64
|
+
exports.detectElectronPlatform = detectElectronPlatform;
|
|
65
|
+
//# sourceMappingURL=chunk-T7JIPZAC.js.map
|
|
66
|
+
//# sourceMappingURL=chunk-T7JIPZAC.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/electron-download.tsx"],"names":["jsx","Button","cn","jsxs","Download"],"mappings":";;;;;;;AAgEO,SAAS,uBACd,SAAA,GAAoB,OAAO,cAAc,WAAA,GACrC,SAAA,CAAU,YACV,EAAA,EACc;AAClB,EAAA,MAAM,EAAA,GAAK,UAAU,WAAA,EAAY;AAEjC,EAAA,IAAI,EAAA,CAAG,QAAA,CAAS,KAAK,CAAA,EAAG,OAAO,KAAA;AAC/B,EAAA,IAAI,EAAA,CAAG,QAAA,CAAS,KAAK,CAAA,EAAG,OAAO,KAAA;AAC/B,EAAA,IAAI,EAAA,CAAG,QAAA,CAAS,OAAO,CAAA,EAAG,OAAO,OAAA;AAEjC,EAAA,OAAO,SAAA;AACT;AAEA,SAAS,gBAAA,CACP,IAAA,EACA,YAAA,EACA,QAAA,EACA,WAAA,EACQ;AACR,EAAA,MAAM,cAAA,GAAiB,IAAA,CAAK,OAAA,CAAQ,KAAA,EAAO,EAAE,CAAA;AAC7C,EAAA,MAAM,WAAA,GAAc,WAAA,CAAY,QAAQ,CAAA,IAAK,QAAA;AAC7C,EAAA,MAAM,QAAA,GAAW,aAAa,QAAA,CAAS,YAAY,IAC/C,YAAA,CAAa,OAAA,CAAQ,eAAA,EAAiB,WAAW,CAAA,GACjD,YAAA;AACJ,EAAA,OAAO,CAAA,EAAG,cAAc,CAAA,CAAA,EAAI,QAAQ,CAAA,CAAA;AACtC;AAMO,SAAS,oBAAA,CAAqB;AAAA,EACnC,eAAA;AAAA,EACA,YAAA;AAAA,EACA,MAAA,GAAS,IAAA;AAAA,EACT,KAAA,GAAQ,sBAAA;AAAA,EACR,aAAA,GAAgB,KAAA;AAAA,EAChB,cAAc,EAAC;AAAA,EACf,SAAA;AAAA,EACA,OAAA,GAAU,SAAA;AAAA,EACV,IAAA,GAAO;AACT,CAAA,EAA8B;AAC5B,EAAA,IAAI,QAAQ,OAAO,IAAA;AAEnB,EAAA,MAAM,WAAW,sBAAA,EAAuB;AACxC,EAAA,MAAM,IAAA,GAAO,gBAAA;AAAA,IACX,eAAA;AAAA,IACA,YAAA;AAAA,IACA,QAAA;AAAA,IACA;AAAA,GACF;AAEA,EAAA,uBACEA,cAAA;AAAA,IAACC,uBAAA;AAAA,IAAA;AAAA,MACC,OAAA;AAAA,MACA,IAAA;AAAA,MACA,SAAA,EAAWC,mBAAA,CAAG,gCAAA,EAAkC,SAAS,CAAA;AAAA,MACzD,OAAA,EAAO,IAAA;AAAA,MAEP,QAAA,kBAAAC,eAAA;AAAA,QAAC,GAAA;AAAA,QAAA;AAAA,UACC,IAAA;AAAA,UACA,QAAA,EAAQ,IAAA;AAAA,UACR,YAAA,EAAY,GAAG,aAAa,CAAA,EAAG,aAAa,SAAA,GAAY,CAAA,KAAA,EAAQ,QAAQ,CAAA,CAAA,GAAK,EAAE,CAAA,CAAA;AAAA,UAE/E,QAAA,EAAA;AAAA,4BAAAH,cAAA,CAACI,oBAAA,EAAA,EAAS,SAAA,EAAU,SAAA,EAAU,aAAA,EAAY,MAAA,EAAO,CAAA;AAAA,YAChD;AAAA;AAAA;AAAA;AACH;AAAA,GACF;AAEJ","file":"chunk-T7JIPZAC.js","sourcesContent":["\"use client\";\n\n/**\n * ElectronDownloadLink Component\n *\n * A download link that auto-detects the visitor's operating system and points\n * to the matching Electron artifact. Hidden by default via the `hidden` prop so\n * it can be staged in pages and revealed once a desktop build is ready.\n *\n * The `artifactName` prop may contain a `{platform}` token which is replaced\n * with the detected platform key (`win`, `mac`, or `linux`).\n *\n * @example\n * ```tsx\n * import { ElectronDownloadLink } from '@burdenoff/website-sdk'\n *\n * <ElectronDownloadLink\n * downloadBaseUrl=\"https://releases.example.com/desktop\"\n * artifactName=\"MyApp-{platform}.zip\"\n * />\n * ```\n */\n\nimport { Download } from \"lucide-react\";\n\nimport { Button } from \"../ui/button\";\nimport { cn } from \"../utils/cn\";\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport type DetectedPlatform = \"win\" | \"mac\" | \"linux\" | \"unknown\";\n\nexport interface ElectronDownloadLinkProps {\n /** Base URL where artifacts are hosted (with or without trailing slash). */\n downloadBaseUrl: string;\n /** Artifact file name; may include a `{platform}` token. */\n artifactName: string;\n /** When true, the element is not rendered. Defaults to true (hidden). */\n hidden?: boolean;\n /** Link/button text. */\n label?: string;\n /** Accessible label for the download action. */\n downloadLabel?: string;\n /** Optional mapping from detected platform to artifact platform key. */\n platformMap?: Partial<Record<DetectedPlatform, string>>;\n /** Additional CSS class for the link. */\n className?: string;\n /** Variant for the rendered button. */\n variant?: \"default\" | \"outline\" | \"ghost\" | \"link\";\n /** Size for the rendered button. */\n size?: \"default\" | \"sm\" | \"lg\" | \"icon\";\n}\n\n// ============================================================================\n// Helpers\n// ============================================================================\n\n/**\n * Detect the visitor's desktop operating system from the user agent string.\n * Returns `win`, `mac`, `linux`, or `unknown`. This is intended for choosing\n * a download artifact; it intentionally does not distinguish mobile OSes.\n */\nexport function detectElectronPlatform(\n userAgent: string = typeof navigator !== \"undefined\"\n ? navigator.userAgent\n : \"\",\n): DetectedPlatform {\n const ua = userAgent.toLowerCase();\n\n if (ua.includes(\"win\")) return \"win\";\n if (ua.includes(\"mac\")) return \"mac\";\n if (ua.includes(\"linux\")) return \"linux\";\n\n return \"unknown\";\n}\n\nfunction buildDownloadUrl(\n base: string,\n artifactName: string,\n platform: DetectedPlatform,\n platformMap: Partial<Record<DetectedPlatform, string>>,\n): string {\n const normalizedBase = base.replace(/\\/$/, \"\");\n const platformKey = platformMap[platform] ?? platform;\n const fileName = artifactName.includes(\"{platform}\")\n ? artifactName.replace(/\\{platform\\}/g, platformKey)\n : artifactName;\n return `${normalizedBase}/${fileName}`;\n}\n\n// ============================================================================\n// Component\n// ============================================================================\n\nexport function ElectronDownloadLink({\n downloadBaseUrl,\n artifactName,\n hidden = true,\n label = \"Download desktop app\",\n downloadLabel = label,\n platformMap = {},\n className,\n variant = \"default\",\n size = \"default\",\n}: ElectronDownloadLinkProps) {\n if (hidden) return null;\n\n const platform = detectElectronPlatform();\n const href = buildDownloadUrl(\n downloadBaseUrl,\n artifactName,\n platform,\n platformMap,\n );\n\n return (\n <Button\n variant={variant}\n size={size}\n className={cn(\"inline-flex items-center gap-2\", className)}\n asChild\n >\n <a\n href={href}\n download\n aria-label={`${downloadLabel}${platform !== \"unknown\" ? ` for ${platform}` : \"\"}`}\n >\n <Download className=\"h-4 w-4\" aria-hidden=\"true\" />\n {label}\n </a>\n </Button>\n );\n}\n"]}
|