@burdenoff/website-sdk 2026.1101.1 → 2026.1101.3

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.
Files changed (276) hide show
  1. package/README.md +52 -11
  2. package/dist/analytics/index.js +19 -69
  3. package/dist/analytics/index.js.map +1 -1
  4. package/dist/analytics/index.mjs +3 -69
  5. package/dist/analytics/index.mjs.map +1 -1
  6. package/dist/avatar-live/index.js +1 -0
  7. package/dist/avatar-live/index.js.map +1 -1
  8. package/dist/avatar-live/index.mjs +1 -0
  9. package/dist/avatar-live/index.mjs.map +1 -1
  10. package/dist/avatar-live-view/index.js +25 -30
  11. package/dist/avatar-live-view/index.js.map +1 -1
  12. package/dist/avatar-live-view/index.mjs +20 -25
  13. package/dist/avatar-live-view/index.mjs.map +1 -1
  14. package/dist/chunk-23H4JUL4.js +41 -0
  15. package/dist/chunk-23H4JUL4.js.map +1 -0
  16. package/dist/chunk-34RX5P75.mjs +25 -0
  17. package/dist/chunk-34RX5P75.mjs.map +1 -0
  18. package/dist/chunk-3LFFY7AZ.js +184 -0
  19. package/dist/chunk-3LFFY7AZ.js.map +1 -0
  20. package/dist/chunk-3X26GE4S.js +2794 -0
  21. package/dist/chunk-3X26GE4S.js.map +1 -0
  22. package/dist/chunk-45GCA4GG.js +85 -0
  23. package/dist/chunk-45GCA4GG.js.map +1 -0
  24. package/dist/chunk-4SAGVLHN.js +876 -0
  25. package/dist/chunk-4SAGVLHN.js.map +1 -0
  26. package/dist/chunk-4U6YAII5.js +218 -0
  27. package/dist/chunk-4U6YAII5.js.map +1 -0
  28. package/dist/chunk-5GMTBOO6.js +21 -0
  29. package/dist/chunk-5GMTBOO6.js.map +1 -0
  30. package/dist/chunk-5JRJ57CL.mjs +329 -0
  31. package/dist/chunk-5JRJ57CL.mjs.map +1 -0
  32. package/dist/chunk-6C2DKIC7.js +4 -0
  33. package/dist/chunk-6C2DKIC7.js.map +1 -0
  34. package/dist/chunk-6IBFBWC5.mjs +17 -0
  35. package/dist/chunk-6IBFBWC5.mjs.map +1 -0
  36. package/dist/chunk-6KY2ZRVI.js +116 -0
  37. package/dist/chunk-6KY2ZRVI.js.map +1 -0
  38. package/dist/chunk-6VJT3SM4.mjs +182 -0
  39. package/dist/chunk-6VJT3SM4.mjs.map +1 -0
  40. package/dist/chunk-6YZUFMK2.mjs +40 -0
  41. package/dist/chunk-6YZUFMK2.mjs.map +1 -0
  42. package/dist/chunk-7A6R7CGG.js +47 -0
  43. package/dist/chunk-7A6R7CGG.js.map +1 -0
  44. package/dist/chunk-7PWL4V2R.js +4 -0
  45. package/dist/chunk-7PWL4V2R.js.map +1 -0
  46. package/dist/chunk-AA33WDYS.js +161 -0
  47. package/dist/chunk-AA33WDYS.js.map +1 -0
  48. package/dist/chunk-AHRGFXV2.js +525 -0
  49. package/dist/chunk-AHRGFXV2.js.map +1 -0
  50. package/dist/chunk-B63XLDZH.mjs +109 -0
  51. package/dist/chunk-B63XLDZH.mjs.map +1 -0
  52. package/dist/chunk-BX2USEWF.mjs +35 -0
  53. package/dist/chunk-BX2USEWF.mjs.map +1 -0
  54. package/dist/chunk-BYSZT35R.js +150 -0
  55. package/dist/chunk-BYSZT35R.js.map +1 -0
  56. package/dist/chunk-C6OJIHNO.js +12 -0
  57. package/dist/chunk-C6OJIHNO.js.map +1 -0
  58. package/dist/chunk-COOELBX7.mjs +63 -0
  59. package/dist/chunk-COOELBX7.mjs.map +1 -0
  60. package/dist/chunk-D2TW7MNC.mjs +2771 -0
  61. package/dist/chunk-D2TW7MNC.mjs.map +1 -0
  62. package/dist/chunk-D5KEZ6V6.mjs +3 -0
  63. package/dist/chunk-D5KEZ6V6.mjs.map +1 -0
  64. package/dist/chunk-E3GUGJMY.js +46 -0
  65. package/dist/chunk-E3GUGJMY.js.map +1 -0
  66. package/dist/chunk-EBZ2HGJ2.mjs +52 -0
  67. package/dist/chunk-EBZ2HGJ2.mjs.map +1 -0
  68. package/dist/chunk-EDLTUOIA.mjs +210 -0
  69. package/dist/chunk-EDLTUOIA.mjs.map +1 -0
  70. package/dist/chunk-EJQSOT4X.js +4 -0
  71. package/dist/chunk-EJQSOT4X.js.map +1 -0
  72. package/dist/chunk-EUPFS6MZ.mjs +2019 -0
  73. package/dist/chunk-EUPFS6MZ.mjs.map +1 -0
  74. package/dist/chunk-F3XWDC3B.js +77 -0
  75. package/dist/chunk-F3XWDC3B.js.map +1 -0
  76. package/dist/chunk-FFADMVIK.mjs +5127 -0
  77. package/dist/chunk-FFADMVIK.mjs.map +1 -0
  78. package/dist/chunk-FJWS7BKT.mjs +3 -0
  79. package/dist/chunk-FJWS7BKT.mjs.map +1 -0
  80. package/dist/chunk-FJZJDUOR.mjs +3 -0
  81. package/dist/chunk-FJZJDUOR.mjs.map +1 -0
  82. package/dist/chunk-GCFLCO2Z.mjs +145 -0
  83. package/dist/chunk-GCFLCO2Z.mjs.map +1 -0
  84. package/dist/chunk-GEXC4LRP.js +61 -0
  85. package/dist/chunk-GEXC4LRP.js.map +1 -0
  86. package/dist/chunk-GTRAXQGC.mjs +3 -0
  87. package/dist/chunk-GTRAXQGC.mjs.map +1 -0
  88. package/dist/chunk-IKOIRUKW.js +270 -0
  89. package/dist/chunk-IKOIRUKW.js.map +1 -0
  90. package/dist/chunk-IXTWOPRN.js +45 -0
  91. package/dist/chunk-IXTWOPRN.js.map +1 -0
  92. package/dist/chunk-JDXOXGOK.js +547 -0
  93. package/dist/chunk-JDXOXGOK.js.map +1 -0
  94. package/dist/chunk-JSLDWYV7.mjs +558 -0
  95. package/dist/chunk-JSLDWYV7.mjs.map +1 -0
  96. package/dist/chunk-JXLSTVBK.js +336 -0
  97. package/dist/chunk-JXLSTVBK.js.map +1 -0
  98. package/dist/chunk-KHYXK7FG.mjs +49 -0
  99. package/dist/chunk-KHYXK7FG.mjs.map +1 -0
  100. package/dist/chunk-KSMBOWQQ.js +2034 -0
  101. package/dist/chunk-KSMBOWQQ.js.map +1 -0
  102. package/dist/chunk-KU2PXHGB.mjs +83 -0
  103. package/dist/chunk-KU2PXHGB.mjs.map +1 -0
  104. package/dist/chunk-KZANIV3S.mjs +37 -0
  105. package/dist/chunk-KZANIV3S.mjs.map +1 -0
  106. package/dist/chunk-L4BP6YOB.js +20 -0
  107. package/dist/chunk-L4BP6YOB.js.map +1 -0
  108. package/dist/chunk-MRKA2OAF.mjs +75 -0
  109. package/dist/chunk-MRKA2OAF.mjs.map +1 -0
  110. package/dist/chunk-N5EC53MH.mjs +80 -0
  111. package/dist/chunk-N5EC53MH.mjs.map +1 -0
  112. package/dist/chunk-NZANQGDM.mjs +34 -0
  113. package/dist/chunk-NZANQGDM.mjs.map +1 -0
  114. package/dist/chunk-O4GS3XPZ.js +10 -0
  115. package/dist/chunk-O4GS3XPZ.js.map +1 -0
  116. package/dist/chunk-O76QDGRM.js +5154 -0
  117. package/dist/chunk-O76QDGRM.js.map +1 -0
  118. package/dist/chunk-OYEUNZJY.js +114 -0
  119. package/dist/chunk-OYEUNZJY.js.map +1 -0
  120. package/dist/chunk-PHXAGQNB.js +74 -0
  121. package/dist/chunk-PHXAGQNB.js.map +1 -0
  122. package/dist/chunk-PIHHPP7J.mjs +16 -0
  123. package/dist/chunk-PIHHPP7J.mjs.map +1 -0
  124. package/dist/chunk-PXRHVVOU.mjs +499 -0
  125. package/dist/chunk-PXRHVVOU.mjs.map +1 -0
  126. package/dist/chunk-QMA6U6AU.js +36 -0
  127. package/dist/chunk-QMA6U6AU.js.map +1 -0
  128. package/dist/chunk-R6J7IULF.js +44 -0
  129. package/dist/chunk-R6J7IULF.js.map +1 -0
  130. package/dist/chunk-R7G6TWIS.mjs +10 -0
  131. package/dist/chunk-R7G6TWIS.mjs.map +1 -0
  132. package/dist/chunk-RUMGFI6L.mjs +544 -0
  133. package/dist/chunk-RUMGFI6L.mjs.map +1 -0
  134. package/dist/chunk-S54JWSJF.js +565 -0
  135. package/dist/chunk-S54JWSJF.js.map +1 -0
  136. package/dist/chunk-SK5UGG6A.js +2041 -0
  137. package/dist/chunk-SK5UGG6A.js.map +1 -0
  138. package/dist/chunk-SKIYDKIS.mjs +70 -0
  139. package/dist/chunk-SKIYDKIS.mjs.map +1 -0
  140. package/dist/chunk-SZNS5IK6.mjs +322 -0
  141. package/dist/chunk-SZNS5IK6.mjs.map +1 -0
  142. package/dist/chunk-T7JIPZAC.js +66 -0
  143. package/dist/chunk-T7JIPZAC.js.map +1 -0
  144. package/dist/chunk-TGYQHO2E.mjs +864 -0
  145. package/dist/chunk-TGYQHO2E.mjs.map +1 -0
  146. package/dist/chunk-TT3MHN42.mjs +8 -0
  147. package/dist/chunk-TT3MHN42.mjs.map +1 -0
  148. package/dist/chunk-UM55U65W.mjs +268 -0
  149. package/dist/chunk-UM55U65W.mjs.map +1 -0
  150. package/dist/chunk-UQ2HALUD.js +72 -0
  151. package/dist/chunk-UQ2HALUD.js.map +1 -0
  152. package/dist/chunk-UXWT7G77.js +82 -0
  153. package/dist/chunk-UXWT7G77.js.map +1 -0
  154. package/dist/chunk-VNS5WDWO.mjs +7 -0
  155. package/dist/chunk-VNS5WDWO.mjs.map +1 -0
  156. package/dist/chunk-VRWZLGR7.js +4 -0
  157. package/dist/chunk-VRWZLGR7.js.map +1 -0
  158. package/dist/chunk-VZLDSNH4.mjs +22 -0
  159. package/dist/chunk-VZLDSNH4.mjs.map +1 -0
  160. package/dist/chunk-W6P3YETJ.js +92 -0
  161. package/dist/chunk-W6P3YETJ.js.map +1 -0
  162. package/dist/chunk-WPKFFQCR.mjs +90 -0
  163. package/dist/chunk-WPKFFQCR.mjs.map +1 -0
  164. package/dist/chunk-WTVTI5TO.mjs +22 -0
  165. package/dist/chunk-WTVTI5TO.mjs.map +1 -0
  166. package/dist/chunk-XJOBZ4RJ.js +44 -0
  167. package/dist/chunk-XJOBZ4RJ.js.map +1 -0
  168. package/dist/chunk-XP2JDNDU.js +325 -0
  169. package/dist/chunk-XP2JDNDU.js.map +1 -0
  170. package/dist/chunk-Y4V5CEWD.mjs +112 -0
  171. package/dist/chunk-Y4V5CEWD.mjs.map +1 -0
  172. package/dist/chunk-YERN2UWM.js +116 -0
  173. package/dist/chunk-YERN2UWM.js.map +1 -0
  174. package/dist/chunk-ZKJQWFAA.mjs +114 -0
  175. package/dist/chunk-ZKJQWFAA.mjs.map +1 -0
  176. package/dist/chunk-ZOON3S3Y.mjs +138 -0
  177. package/dist/chunk-ZOON3S3Y.mjs.map +1 -0
  178. package/dist/chunk-ZP6QRAYC.mjs +2024 -0
  179. package/dist/chunk-ZP6QRAYC.mjs.map +1 -0
  180. package/dist/chunk-ZSTFX7FV.js +9 -0
  181. package/dist/chunk-ZSTFX7FV.js.map +1 -0
  182. package/dist/client/index.js +23 -331
  183. package/dist/client/index.js.map +1 -1
  184. package/dist/client/index.mjs +3 -330
  185. package/dist/client/index.mjs.map +1 -1
  186. package/dist/comparison/index.js +28 -111
  187. package/dist/comparison/index.js.map +1 -1
  188. package/dist/comparison/index.mjs +2 -107
  189. package/dist/comparison/index.mjs.map +1 -1
  190. package/dist/components/contact.js +21 -931
  191. package/dist/components/contact.js.map +1 -1
  192. package/dist/components/contact.mjs +12 -909
  193. package/dist/components/contact.mjs.map +1 -1
  194. package/dist/components/electron-download.js +12 -124
  195. package/dist/components/electron-download.js.map +1 -1
  196. package/dist/components/electron-download.mjs +4 -106
  197. package/dist/components/electron-download.mjs.map +1 -1
  198. package/dist/components/explore-cta.js +8 -112
  199. package/dist/components/explore-cta.js.map +1 -1
  200. package/dist/components/explore-cta.mjs +3 -114
  201. package/dist/components/explore-cta.mjs.map +1 -1
  202. package/dist/components/explore.js +62 -7833
  203. package/dist/components/explore.js.map +1 -1
  204. package/dist/components/explore.mjs +13 -7800
  205. package/dist/components/explore.mjs.map +1 -1
  206. package/dist/components/launch-countdown.js +8 -83
  207. package/dist/components/launch-countdown.js.map +1 -1
  208. package/dist/components/launch-countdown.mjs +3 -85
  209. package/dist/components/launch-countdown.mjs.map +1 -1
  210. package/dist/components/newsletter.js +20 -993
  211. package/dist/components/newsletter.js.map +1 -1
  212. package/dist/components/newsletter.mjs +11 -971
  213. package/dist/components/newsletter.mjs.map +1 -1
  214. package/dist/components/partners.js +24 -1302
  215. package/dist/components/partners.js.map +1 -1
  216. package/dist/components/partners.mjs +15 -1276
  217. package/dist/components/partners.mjs.map +1 -1
  218. package/dist/components/pricing.js +49 -1249
  219. package/dist/components/pricing.js.map +1 -1
  220. package/dist/components/pricing.mjs +8 -1226
  221. package/dist/components/pricing.mjs.map +1 -1
  222. package/dist/components/problems-solved.js +8 -150
  223. package/dist/components/problems-solved.js.map +1 -1
  224. package/dist/components/problems-solved.mjs +4 -133
  225. package/dist/components/problems-solved.mjs.map +1 -1
  226. package/dist/components/resource-links.js +8 -304
  227. package/dist/components/resource-links.js.map +1 -1
  228. package/dist/components/resource-links.mjs +5 -308
  229. package/dist/components/resource-links.mjs.map +1 -1
  230. package/dist/components/social-links.js +8 -343
  231. package/dist/components/social-links.js.map +1 -1
  232. package/dist/components/social-links.mjs +5 -347
  233. package/dist/components/social-links.mjs.map +1 -1
  234. package/dist/components/unsubscribe.js +10 -627
  235. package/dist/components/unsubscribe.js.map +1 -1
  236. package/dist/components/unsubscribe.mjs +6 -611
  237. package/dist/components/unsubscribe.mjs.map +1 -1
  238. package/dist/components/waitlist.js +21 -1001
  239. package/dist/components/waitlist.js.map +1 -1
  240. package/dist/components/waitlist.mjs +12 -982
  241. package/dist/components/waitlist.mjs.map +1 -1
  242. package/dist/components/website-switcher.js +12 -175
  243. package/dist/components/website-switcher.js.map +1 -1
  244. package/dist/components/website-switcher.mjs +4 -157
  245. package/dist/components/website-switcher.mjs.map +1 -1
  246. package/dist/content/index.js +118 -3616
  247. package/dist/content/index.js.map +1 -1
  248. package/dist/content/index.mjs +12 -3567
  249. package/dist/content/index.mjs.map +1 -1
  250. package/dist/hooks/index.js +37 -2321
  251. package/dist/hooks/index.js.map +1 -1
  252. package/dist/hooks/index.mjs +6 -2318
  253. package/dist/hooks/index.mjs.map +1 -1
  254. package/dist/index.d.mts +1 -1
  255. package/dist/index.d.ts +1 -1
  256. package/dist/index.js +4145 -20599
  257. package/dist/index.js.map +1 -1
  258. package/dist/index.mjs +3766 -20567
  259. package/dist/index.mjs.map +1 -1
  260. package/dist/product/index.js +20 -393
  261. package/dist/product/index.js.map +1 -1
  262. package/dist/product/index.mjs +5 -394
  263. package/dist/product/index.mjs.map +1 -1
  264. package/dist/seo/index.js +8 -76
  265. package/dist/seo/index.js.map +1 -1
  266. package/dist/seo/index.mjs +3 -78
  267. package/dist/seo/index.mjs.map +1 -1
  268. package/dist/store/index.js +51 -2650
  269. package/dist/store/index.js.map +1 -1
  270. package/dist/store/index.mjs +13 -2619
  271. package/dist/store/index.mjs.map +1 -1
  272. package/dist/utils/index.js +69 -134
  273. package/dist/utils/index.js.map +1 -1
  274. package/dist/utils/index.mjs +5 -122
  275. package/dist/utils/index.mjs.map +1 -1
  276. package/package.json +2 -1
@@ -1,3621 +1,123 @@
1
1
  'use strict';
2
2
 
3
- var React = require('react');
4
- var DOMPurify = require('isomorphic-dompurify');
5
- var ReactMarkdown = require('react-markdown');
6
- var rehypeSanitize = require('rehype-sanitize');
7
- var remarkGfm = require('remark-gfm');
8
- var jsxRuntime = require('react/jsx-runtime');
9
- var reactHelmetAsync = require('react-helmet-async');
10
- var lucideReact = require('lucide-react');
11
- var reactSlot = require('@radix-ui/react-slot');
12
- var classVarianceAuthority = require('class-variance-authority');
13
- var clsx = require('clsx');
14
- var tailwindMerge = require('tailwind-merge');
15
-
16
- function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
17
-
18
- function _interopNamespace(e) {
19
- if (e && e.__esModule) return e;
20
- var n = Object.create(null);
21
- if (e) {
22
- Object.keys(e).forEach(function (k) {
23
- if (k !== 'default') {
24
- var d = Object.getOwnPropertyDescriptor(e, k);
25
- Object.defineProperty(n, k, d.get ? d : {
26
- enumerable: true,
27
- get: function () { return e[k]; }
28
- });
29
- }
30
- });
31
- }
32
- n.default = e;
33
- return Object.freeze(n);
34
- }
35
-
36
- var React__namespace = /*#__PURE__*/_interopNamespace(React);
37
- var DOMPurify__default = /*#__PURE__*/_interopDefault(DOMPurify);
38
- var ReactMarkdown__default = /*#__PURE__*/_interopDefault(ReactMarkdown);
39
- var rehypeSanitize__default = /*#__PURE__*/_interopDefault(rehypeSanitize);
40
- var remarkGfm__default = /*#__PURE__*/_interopDefault(remarkGfm);
41
-
42
- var __defProp = Object.defineProperty;
43
- var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
44
- var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
45
- function MarkdownLink({
46
- href,
47
- children,
48
- // react-markdown passes the hast node to every override; it is not a DOM attribute.
49
- node: _node,
50
- ...props
51
- }) {
52
- const isExternal = typeof href === "string" && (href.startsWith("http://") || href.startsWith("https://"));
53
- return /* @__PURE__ */ jsxRuntime.jsx(
54
- "a",
55
- {
56
- href,
57
- ...props,
58
- ...isExternal ? { target: "_blank", rel: "noopener noreferrer" } : { rel: "noopener noreferrer" },
59
- children
60
- }
61
- );
62
- }
63
- var defaultComponents = {
64
- a: MarkdownLink
65
- };
66
- function Markdown({
67
- children,
68
- className,
69
- inline,
70
- components,
71
- rehypePlugins
72
- }) {
73
- const content = /* @__PURE__ */ jsxRuntime.jsx(
74
- ReactMarkdown__default.default,
75
- {
76
- remarkPlugins: [remarkGfm__default.default],
77
- rehypePlugins: [rehypeSanitize__default.default, ...rehypePlugins ?? []],
78
- components: { ...defaultComponents, ...components },
79
- ...inline ? { disallowedElements: ["p"], unwrapDisallowed: true } : {},
80
- children
81
- }
82
- );
83
- if (inline) {
84
- return /* @__PURE__ */ jsxRuntime.jsx("span", { className, children: content });
85
- }
86
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className, children: content });
87
- }
88
-
89
- // src/client/security.ts
90
- var LOOPBACK_HOSTS = /* @__PURE__ */ new Set(["localhost", "127.0.0.1", "::1"]);
91
- var RESERVED_HEADERS = /* @__PURE__ */ new Set([
92
- "authorization",
93
- "cookie",
94
- "x-actor-id",
95
- "x-actor-type",
96
- "x-consent-granted-by",
97
- "x-consent-scope",
98
- "x-consent-granted-at",
99
- "x-consent-expires-at"
100
- ]);
101
- function validateGatewayUrl(value) {
102
- let url;
103
- try {
104
- url = new URL(value);
105
- } catch {
106
- throw new Error("Gateway URL must be a valid absolute URL");
107
- }
108
- if (url.protocol === "https:") {
109
- return url.toString();
110
- }
111
- if (url.protocol === "http:" && LOOPBACK_HOSTS.has(url.hostname.toLowerCase())) {
112
- return url.toString();
113
- }
114
- throw new Error("Gateway URL must use HTTPS unless it targets localhost");
115
- }
116
- function sanitizeHeaders(headers) {
117
- if (!headers) return void 0;
118
- return Object.fromEntries(
119
- Object.entries(headers).filter(
120
- ([key]) => !RESERVED_HEADERS.has(key.toLowerCase())
121
- )
122
- );
123
- }
124
- var CF_ACCESS_GATED_HOSTS = /* @__PURE__ */ new Set([
125
- "alphagraphql.burdenoff.com",
126
- "alphagraphqlworkspaces.burdenoff.com"
127
- ]);
128
- function cfAccessHeaders(gatewayUrl, cfAccess) {
129
- const clientId = cfAccess?.clientId?.trim();
130
- const clientSecret = cfAccess?.clientSecret?.trim();
131
- if (!clientId || !clientSecret) return {};
132
- let host;
133
- try {
134
- host = new URL(gatewayUrl).hostname.toLowerCase();
135
- } catch {
136
- return {};
137
- }
138
- if (!CF_ACCESS_GATED_HOSTS.has(host)) return {};
139
- return {
140
- "CF-Access-Client-Id": clientId,
141
- "CF-Access-Client-Secret": clientSecret
142
- };
143
- }
144
-
145
- // src/client/graphql-client.ts
146
- var _WebSDKClient = class _WebSDKClient {
147
- constructor(config) {
148
- __publicField(this, "config");
149
- __publicField(this, "queryCache", /* @__PURE__ */ new Map());
150
- __publicField(this, "inflight", /* @__PURE__ */ new Map());
151
- this.config = {
152
- ...config,
153
- gatewayUrl: validateGatewayUrl(config.gatewayUrl),
154
- headers: sanitizeHeaders(config.headers)
155
- };
156
- }
157
- getConfig() {
158
- return this.config;
159
- }
160
- /**
161
- * Execute a GraphQL query (with deduplication and caching).
162
- *
163
- * Pass `{ cache: false }` to bypass the cache entirely — required for poll
164
- * loops, where every tick must reach the network.
165
- */
166
- async query(query, variables, options) {
167
- if (options?.cache === false) {
168
- return this.request(query, variables, true, options.timeoutMs);
169
- }
170
- const cacheKey = JSON.stringify({ query, variables });
171
- const cached = this.queryCache.get(cacheKey);
172
- if (cached) {
173
- if (Date.now() - cached.ts < _WebSDKClient.CACHE_TTL) {
174
- return cached.data;
175
- }
176
- this.queryCache.delete(cacheKey);
177
- }
178
- const existing = this.inflight.get(cacheKey);
179
- if (existing) {
180
- return existing;
181
- }
182
- const promise = this.request(query, variables, true, options?.timeoutMs).then((result) => {
183
- if (result.data && !result.errors?.length) {
184
- if (this.queryCache.size >= _WebSDKClient.MAX_CACHE_ENTRIES) {
185
- const oldestEntry = this.queryCache.keys().next().value;
186
- if (oldestEntry) {
187
- this.queryCache.delete(oldestEntry);
188
- }
189
- }
190
- this.queryCache.set(cacheKey, { data: result, ts: Date.now() });
191
- }
192
- return result;
193
- }).finally(() => {
194
- this.inflight.delete(cacheKey);
195
- });
196
- this.inflight.set(cacheKey, promise);
197
- return promise;
198
- }
199
- /**
200
- * Execute a GraphQL mutation
201
- */
202
- async mutate(mutation, variables, options) {
203
- return this.request(mutation, variables, false, options?.timeoutMs);
204
- }
205
- /** HTTP statuses worth another attempt — transient server/edge conditions. */
206
- static isRetriableStatus(status) {
207
- return status === 429 || status === 408 || status >= 500 && status < 600;
208
- }
209
- static sleep(ms) {
210
- return new Promise((resolve) => setTimeout(resolve, ms));
211
- }
212
- /**
213
- * Perform the HTTP call with a hard timeout. Every outcome resolves to a
214
- * GraphQLResponse — callers never hang, so a UI's `loading` state always
215
- * settles into data or a renderable error.
216
- */
217
- async attempt(query, variables, timeoutMs) {
218
- const controller = new AbortController();
219
- const timer = setTimeout(() => controller.abort(), timeoutMs);
220
- try {
221
- const response = await fetch(this.config.gatewayUrl, {
222
- method: "POST",
223
- headers: {
224
- "Content-Type": "application/json",
225
- "x-product-id": this.config.productId,
226
- ...this.config.headers,
227
- ...cfAccessHeaders(this.config.gatewayUrl, this.config.cfAccess)
228
- },
229
- body: JSON.stringify({ query, variables }),
230
- signal: controller.signal
231
- });
232
- if (!response.ok) {
233
- console.error(
234
- "[WebSDK] GraphQL request failed",
235
- response.status,
236
- response.statusText
237
- );
238
- return {
239
- res: {
240
- errors: [
241
- {
242
- message: `HTTP ${response.status}: ${response.statusText}`,
243
- extensions: { code: `HTTP_${response.status}` }
244
- }
245
- ]
246
- },
247
- retriable: _WebSDKClient.isRetriableStatus(response.status)
248
- };
249
- }
250
- return {
251
- res: await response.json(),
252
- retriable: false
253
- };
254
- } catch (error) {
255
- const aborted = error instanceof Error && (error.name === "AbortError" || controller.signal.aborted);
256
- const message = aborted ? `Request timed out after ${timeoutMs}ms` : error instanceof Error ? error.message : "Network error";
257
- console.error("[WebSDK] Network error", message);
258
- return {
259
- res: {
260
- errors: [
261
- {
262
- message,
263
- extensions: { code: aborted ? "TIMEOUT" : "NETWORK_ERROR" }
264
- }
265
- ]
266
- },
267
- // Transport-level failures are worth one more try; a genuine outage
268
- // just fails twice quickly and still renders an error state.
269
- retriable: true
270
- };
271
- } finally {
272
- clearTimeout(timer);
273
- }
274
- }
275
- async request(query, variables, retry = true, timeoutOverrideMs) {
276
- const timeoutMs = timeoutOverrideMs ?? this.config.timeoutMs ?? _WebSDKClient.DEFAULT_TIMEOUT_MS;
277
- const maxRetries = retry ? this.config.maxRetries ?? _WebSDKClient.DEFAULT_MAX_RETRIES : 0;
278
- let last;
279
- for (let i = 0; i <= maxRetries; i++) {
280
- const { res, retriable } = await this.attempt(
281
- query,
282
- variables,
283
- timeoutMs
284
- );
285
- if (!retriable || i === maxRetries) return res;
286
- last = res;
287
- await _WebSDKClient.sleep(
288
- _WebSDKClient.RETRY_BASE_DELAY_MS * (i + 1) + Math.random() * 200
289
- );
290
- }
291
- return last;
292
- }
293
- };
294
- __publicField(_WebSDKClient, "CACHE_TTL", 5 * 60 * 1e3);
295
- // 5 minutes
296
- __publicField(_WebSDKClient, "MAX_CACHE_ENTRIES", 100);
297
- __publicField(_WebSDKClient, "DEFAULT_TIMEOUT_MS", 12e3);
298
- __publicField(_WebSDKClient, "DEFAULT_MAX_RETRIES", 1);
299
- __publicField(_WebSDKClient, "RETRY_BASE_DELAY_MS", 400);
300
- var WebSDKContext = React.createContext(null);
301
- function useWebSDK() {
302
- const client = React.useContext(WebSDKContext);
303
- if (!client) {
304
- throw new Error("useWebSDK must be used within a WebSDKProvider");
305
- }
306
- return client;
307
- }
308
- function useWebSDKConfig() {
309
- const client = useWebSDK();
310
- return client.getConfig();
311
- }
312
-
313
- // src/utils/product-slug.ts
314
- var PRODUCT_SLUG_RENAMES = Object.freeze(
315
- [Object.freeze({ legacy: "botlit", current: "botmanor" })]
316
- );
317
- Object.freeze(
318
- Object.fromEntries(PRODUCT_SLUG_RENAMES.map((r) => [r.legacy, r.current]))
319
- );
320
- var ProductContext = React.createContext({
321
- product: null,
322
- loading: true,
323
- error: null,
324
- hasProvider: false,
325
- refetch: () => {
326
- }
3
+ var chunk3X26GE4S_js = require('../chunk-3X26GE4S.js');
4
+ var chunk4U6YAII5_js = require('../chunk-4U6YAII5.js');
5
+ require('../chunk-3LFFY7AZ.js');
6
+ var chunkGEXC4LRP_js = require('../chunk-GEXC4LRP.js');
7
+ require('../chunk-7A6R7CGG.js');
8
+ require('../chunk-UXWT7G77.js');
9
+ require('../chunk-UQ2HALUD.js');
10
+ require('../chunk-C6OJIHNO.js');
11
+ require('../chunk-BYSZT35R.js');
12
+ require('../chunk-E3GUGJMY.js');
13
+ require('../chunk-JXLSTVBK.js');
14
+ require('../chunk-ZSTFX7FV.js');
15
+
16
+
17
+
18
+ Object.defineProperty(exports, "BlogListPage", {
19
+ enumerable: true,
20
+ get: function () { return chunk3X26GE4S_js.BlogListPage; }
21
+ });
22
+ Object.defineProperty(exports, "BlogPostPage", {
23
+ enumerable: true,
24
+ get: function () { return chunk3X26GE4S_js.BlogPostPage; }
25
+ });
26
+ Object.defineProperty(exports, "CaseStudiesListPage", {
27
+ enumerable: true,
28
+ get: function () { return chunk3X26GE4S_js.CaseStudiesListPage; }
29
+ });
30
+ Object.defineProperty(exports, "CaseStudyPage", {
31
+ enumerable: true,
32
+ get: function () { return chunk3X26GE4S_js.CaseStudyPage; }
33
+ });
34
+ Object.defineProperty(exports, "ChangelogPage", {
35
+ enumerable: true,
36
+ get: function () { return chunk3X26GE4S_js.ChangelogPage; }
37
+ });
38
+ Object.defineProperty(exports, "ContractsPage", {
39
+ enumerable: true,
40
+ get: function () { return chunk3X26GE4S_js.ContractsPage; }
41
+ });
42
+ Object.defineProperty(exports, "LEGAL_DOCUMENTS", {
43
+ enumerable: true,
44
+ get: function () { return chunk3X26GE4S_js.LEGAL_DOCUMENTS; }
45
+ });
46
+ Object.defineProperty(exports, "LEGAL_DOCUMENT_KEYS", {
47
+ enumerable: true,
48
+ get: function () { return chunk3X26GE4S_js.LEGAL_DOCUMENT_KEYS; }
49
+ });
50
+ Object.defineProperty(exports, "LegalPage", {
51
+ enumerable: true,
52
+ get: function () { return chunk3X26GE4S_js.LegalPage; }
53
+ });
54
+ Object.defineProperty(exports, "PressKitPage", {
55
+ enumerable: true,
56
+ get: function () { return chunk3X26GE4S_js.PressKitPage; }
57
+ });
58
+ Object.defineProperty(exports, "fetchLegalContent", {
59
+ enumerable: true,
60
+ get: function () { return chunk3X26GE4S_js.fetchLegalContent; }
61
+ });
62
+ Object.defineProperty(exports, "fetchLegalContentPage", {
63
+ enumerable: true,
64
+ get: function () { return chunk3X26GE4S_js.fetchLegalContentPage; }
65
+ });
66
+ Object.defineProperty(exports, "fetchPublicJobPosting", {
67
+ enumerable: true,
68
+ get: function () { return chunk3X26GE4S_js.fetchPublicJobPosting; }
69
+ });
70
+ Object.defineProperty(exports, "fetchPublicJobPostings", {
71
+ enumerable: true,
72
+ get: function () { return chunk3X26GE4S_js.fetchPublicJobPostings; }
73
+ });
74
+ Object.defineProperty(exports, "fetchPublicJobPostingsForSubmission", {
75
+ enumerable: true,
76
+ get: function () { return chunk3X26GE4S_js.fetchPublicJobPostingsForSubmission; }
77
+ });
78
+ Object.defineProperty(exports, "findLegalDocument", {
79
+ enumerable: true,
80
+ get: function () { return chunk3X26GE4S_js.findLegalDocument; }
81
+ });
82
+ Object.defineProperty(exports, "formatLegalDate", {
83
+ enumerable: true,
84
+ get: function () { return chunk3X26GE4S_js.formatLegalDate; }
85
+ });
86
+ Object.defineProperty(exports, "hasCustomerSignOff", {
87
+ enumerable: true,
88
+ get: function () { return chunk3X26GE4S_js.hasCustomerSignOff; }
89
+ });
90
+ Object.defineProperty(exports, "splitLeadingHeading", {
91
+ enumerable: true,
92
+ get: function () { return chunk3X26GE4S_js.splitLeadingHeading; }
93
+ });
94
+ Object.defineProperty(exports, "usePublicJobPosting", {
95
+ enumerable: true,
96
+ get: function () { return chunk3X26GE4S_js.usePublicJobPosting; }
97
+ });
98
+ Object.defineProperty(exports, "usePublicJobPostings", {
99
+ enumerable: true,
100
+ get: function () { return chunk3X26GE4S_js.usePublicJobPostings; }
101
+ });
102
+ Object.defineProperty(exports, "usePublicJobPostingsForSubmission", {
103
+ enumerable: true,
104
+ get: function () { return chunk3X26GE4S_js.usePublicJobPostingsForSubmission; }
105
+ });
106
+ Object.defineProperty(exports, "ContentRenderer", {
107
+ enumerable: true,
108
+ get: function () { return chunk4U6YAII5_js.ContentRenderer; }
109
+ });
110
+ Object.defineProperty(exports, "useContentPage", {
111
+ enumerable: true,
112
+ get: function () { return chunk4U6YAII5_js.useContentPage; }
113
+ });
114
+ Object.defineProperty(exports, "useContentPages", {
115
+ enumerable: true,
116
+ get: function () { return chunk4U6YAII5_js.useContentPages; }
117
+ });
118
+ Object.defineProperty(exports, "Markdown", {
119
+ enumerable: true,
120
+ get: function () { return chunkGEXC4LRP_js.Markdown; }
327
121
  });
328
- function useProduct() {
329
- return React.useContext(ProductContext);
330
- }
331
- var PUBLIC_CONTENT_PAGE_QUERY = `
332
- query PublicContentPage($slug: String!, $productId: String) {
333
- publicContentPage(slug: $slug, productId: $productId) {
334
- id
335
- title
336
- slug
337
- content
338
- contentFormat
339
- seoTitle
340
- seoDescription
341
- seoKeywords
342
- lastUpdated
343
- version
344
- excerpt
345
- thumbnail
346
- author
347
- publishedAt
348
- readingTime
349
- featured
350
- category
351
- }
352
- }
353
- `;
354
- var PUBLIC_RESOLVED_CONTENT_PAGE_QUERY = `
355
- query PublicResolvedContentPage($slug: String!, $productId: String!) {
356
- publicResolvedContentPage(slug: $slug, productId: $productId) {
357
- id
358
- title
359
- slug
360
- content
361
- contentFormat
362
- seoTitle
363
- seoDescription
364
- seoKeywords
365
- lastUpdated
366
- version
367
- excerpt
368
- thumbnail
369
- author
370
- publishedAt
371
- readingTime
372
- featured
373
- category
374
- }
375
- }
376
- `;
377
- var PUBLIC_CONTENT_PAGES_QUERY = `
378
- query PublicContentPages($productId: String!, $limit: Int, $offset: Int) {
379
- publicContentPages(productId: $productId, limit: $limit, offset: $offset) {
380
- items {
381
- id
382
- title
383
- slug
384
- lastUpdated
385
- tags
386
- seoDescription
387
- excerpt
388
- thumbnail
389
- author
390
- publishedAt
391
- readingTime
392
- featured
393
- category
394
- }
395
- total
396
- hasMore
397
- }
398
- }
399
- `;
400
- function resolveContentPageGate(resolvedProductId, hasProvider, productLoading) {
401
- if (resolvedProductId) return "fetch";
402
- return hasProvider && productLoading ? "wait" : "unavailable";
403
- }
404
- function useContentPage(slug, options) {
405
- const client = useWebSDK();
406
- const config = useWebSDKConfig();
407
- const {
408
- product,
409
- loading: productLoading,
410
- error: productError,
411
- hasProvider
412
- } = useProduct();
413
- const resolvedProductId = product?.id || config.productId;
414
- const gate = resolveContentPageGate(
415
- resolvedProductId,
416
- hasProvider,
417
- productLoading
418
- );
419
- const enabled = options?.enabled !== false;
420
- const [data, setData] = React.useState(null);
421
- const [loading, setLoading] = React.useState(enabled);
422
- const [error, setError] = React.useState(null);
423
- const fetchPage = React.useCallback(async () => {
424
- if (!enabled) {
425
- setLoading(false);
426
- return;
427
- }
428
- if (gate === "wait") return;
429
- if (gate === "unavailable") {
430
- setData(null);
431
- setError(
432
- productError ?? "Product unavailable \u2014 cannot load content page"
433
- );
434
- setLoading(false);
435
- return;
436
- }
437
- setLoading(true);
438
- setError(null);
439
- try {
440
- const query = options?.resolve ? PUBLIC_RESOLVED_CONTENT_PAGE_QUERY : PUBLIC_CONTENT_PAGE_QUERY;
441
- const queryName = options?.resolve ? "publicResolvedContentPage" : "publicContentPage";
442
- const result = await client.query(
443
- query,
444
- {
445
- slug,
446
- productId: resolvedProductId
447
- }
448
- );
449
- if (result.errors?.length) {
450
- setError(result.errors[0]?.message ?? "Unknown error");
451
- setData(null);
452
- } else {
453
- setData(result.data?.[queryName] ?? null);
454
- }
455
- } catch (err) {
456
- setError(err instanceof Error ? err.message : "Failed to fetch content");
457
- setData(null);
458
- } finally {
459
- setLoading(false);
460
- }
461
- }, [
462
- client,
463
- enabled,
464
- gate,
465
- productError,
466
- resolvedProductId,
467
- slug,
468
- options?.resolve
469
- ]);
470
- React.useEffect(() => {
471
- fetchPage();
472
- }, [fetchPage]);
473
- return { data, loading, error, refetch: fetchPage };
474
- }
475
- function useContentPages(options) {
476
- const client = useWebSDK();
477
- const config = useWebSDKConfig();
478
- const [data, setData] = React.useState(null);
479
- const [loading, setLoading] = React.useState(true);
480
- const [error, setError] = React.useState(null);
481
- const fetchPages = React.useCallback(async () => {
482
- setLoading(true);
483
- setError(null);
484
- try {
485
- const result = await client.query(PUBLIC_CONTENT_PAGES_QUERY, {
486
- productId: config.productId,
487
- limit: options?.limit ?? 50,
488
- offset: options?.offset ?? 0
489
- });
490
- if (result.errors?.length) {
491
- setError(result.errors[0]?.message ?? "Unknown error");
492
- setData(null);
493
- } else {
494
- setData(result.data?.publicContentPages ?? null);
495
- }
496
- } catch (err) {
497
- setError(
498
- err instanceof Error ? err.message : "Failed to fetch content pages"
499
- );
500
- setData(null);
501
- } finally {
502
- setLoading(false);
503
- }
504
- }, [client, config.productId, options?.limit, options?.offset]);
505
- React.useEffect(() => {
506
- fetchPages();
507
- }, [fetchPages]);
508
- return { data, loading, error, refetch: fetchPages };
509
- }
510
- function ContentRenderer({
511
- content,
512
- format = "markdown",
513
- className
514
- }) {
515
- if (format === "markdown") {
516
- return /* @__PURE__ */ jsxRuntime.jsx(Markdown, { className, children: content });
517
- }
518
- if (format === "html") {
519
- const sanitized = DOMPurify__default.default.sanitize(content);
520
- return /* @__PURE__ */ jsxRuntime.jsx(
521
- "div",
522
- {
523
- className,
524
- dangerouslySetInnerHTML: { __html: sanitized }
525
- }
526
- );
527
- }
528
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className, children: /* @__PURE__ */ jsxRuntime.jsx("pre", { style: { whiteSpace: "pre-wrap", fontFamily: "inherit" }, children: content }) });
529
- }
530
- var PROSE_CSS = `
531
- .boff-prose {
532
- max-width: none;
533
- }
534
-
535
- .boff-prose h1 {
536
- font-size: 2.25rem;
537
- font-weight: 800;
538
- line-height: 1.2;
539
- margin-top: 0;
540
- margin-bottom: 1rem;
541
- color: hsl(var(--foreground));
542
- }
543
-
544
- .boff-prose h2 {
545
- font-size: 1.75rem;
546
- font-weight: 700;
547
- line-height: 1.3;
548
- margin-top: 2rem;
549
- margin-bottom: 1rem;
550
- color: hsl(var(--foreground));
551
- }
552
-
553
- .boff-prose h3 {
554
- font-size: 1.5rem;
555
- font-weight: 600;
556
- line-height: 1.4;
557
- margin-top: 1.5rem;
558
- margin-bottom: 0.75rem;
559
- color: hsl(var(--foreground));
560
- }
561
-
562
- .boff-prose h4 {
563
- font-size: 1.25rem;
564
- font-weight: 600;
565
- line-height: 1.5;
566
- margin-top: 1.25rem;
567
- margin-bottom: 0.5rem;
568
- color: hsl(var(--foreground));
569
- }
570
-
571
- .boff-prose p {
572
- margin-bottom: 1rem;
573
- line-height: 1.75;
574
- color: hsl(var(--foreground));
575
- }
576
-
577
- .boff-prose ul,
578
- .boff-prose ol {
579
- margin-bottom: 1rem;
580
- padding-left: 1.5rem;
581
- }
582
-
583
- .boff-prose ul {
584
- list-style-type: disc;
585
- }
586
-
587
- .boff-prose ol {
588
- list-style-type: decimal;
589
- }
590
-
591
- .boff-prose li {
592
- margin-bottom: 0.25rem;
593
- line-height: 1.75;
594
- color: hsl(var(--foreground));
595
- }
596
-
597
- .boff-prose li > ul,
598
- .boff-prose li > ol {
599
- margin-top: 0.25rem;
600
- margin-bottom: 0.25rem;
601
- }
602
-
603
- .boff-prose a {
604
- color: hsl(var(--primary));
605
- text-decoration: underline;
606
- text-underline-offset: 2px;
607
- }
608
-
609
- .boff-prose a:hover {
610
- opacity: 0.8;
611
- }
612
-
613
- .boff-prose hr {
614
- border: none;
615
- height: 1px;
616
- background-color: hsl(var(--border));
617
- margin: 2rem 0;
618
- }
619
-
620
- .boff-prose blockquote {
621
- border-left: 4px solid hsl(var(--border));
622
- padding-left: 1rem;
623
- margin: 1.5rem 0;
624
- font-style: italic;
625
- color: hsl(var(--muted-foreground));
626
- }
627
-
628
- .boff-prose strong {
629
- font-weight: 700;
630
- color: hsl(var(--foreground));
631
- }
632
-
633
- .boff-prose code {
634
- background-color: hsl(var(--muted));
635
- padding: 0.125rem 0.25rem;
636
- border-radius: 0.25rem;
637
- font-size: 0.875em;
638
- font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
639
- }
640
-
641
- .boff-prose pre {
642
- background-color: hsl(var(--muted));
643
- padding: 1rem;
644
- border-radius: 0.5rem;
645
- overflow-x: auto;
646
- margin-bottom: 1rem;
647
- }
648
-
649
- .boff-prose pre code {
650
- background-color: transparent;
651
- padding: 0;
652
- }
653
-
654
- .boff-prose img {
655
- max-width: 100%;
656
- height: auto;
657
- }
658
-
659
- .boff-prose table {
660
- width: 100%;
661
- border-collapse: collapse;
662
- margin-bottom: 1rem;
663
- }
664
-
665
- .boff-prose th,
666
- .boff-prose td {
667
- border: 1px solid hsl(var(--border));
668
- padding: 0.5rem;
669
- text-align: left;
670
- }
671
-
672
- .boff-prose th {
673
- font-weight: 600;
674
- background-color: hsl(var(--muted));
675
- }
676
-
677
- /* Smaller variant used inside cards and compact panels */
678
- .boff-prose-sm {
679
- font-size: 0.875rem;
680
- }
681
-
682
- .boff-prose-sm h1 {
683
- font-size: 1.75rem;
684
- }
685
-
686
- .boff-prose-sm h2 {
687
- font-size: 1.5rem;
688
- }
689
-
690
- .boff-prose-sm h3 {
691
- font-size: 1.25rem;
692
- }
693
-
694
- .boff-prose-sm h4 {
695
- font-size: 1.125rem;
696
- }
697
-
698
- .boff-prose-sm p,
699
- .boff-prose-sm li {
700
- line-height: 1.625;
701
- }
702
- `;
703
- function ProseStyles() {
704
- return /* @__PURE__ */ jsxRuntime.jsx("style", { dangerouslySetInnerHTML: { __html: PROSE_CSS } });
705
- }
706
- function canonicalFromLocation() {
707
- if (typeof window === "undefined" || !window.location) return void 0;
708
- const { origin, pathname } = window.location;
709
- if (!origin || origin === "null") return void 0;
710
- const path = pathname === "/" ? "/" : pathname.replace(/\/+$/, "");
711
- return `${origin}${path}`;
712
- }
713
- function resolveImageUrl(image, base) {
714
- if (!image || /^https?:\/\//i.test(image)) return image;
715
- if (!base) return image;
716
- try {
717
- return new URL(image, base).toString();
718
- } catch {
719
- return image;
720
- }
721
- }
722
- function serializeJsonLd(data) {
723
- return JSON.stringify(data).replace(/</g, "\\u003c").replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029");
724
- }
725
- function PageHead({
726
- title,
727
- description,
728
- productName,
729
- keywords,
730
- image,
731
- url,
732
- favicon,
733
- robots = "index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1",
734
- locale = "en_US",
735
- twitterHandle,
736
- themeColor = "#000000",
737
- author,
738
- structuredData,
739
- additionalMeta
740
- }) {
741
- const fullTitle = productName && !title.includes(productName) ? `${title} | ${productName}` : title;
742
- const resolvedUrl = url ?? canonicalFromLocation();
743
- const fullImageUrl = resolveImageUrl(image, resolvedUrl);
744
- const structuredDataItems = structuredData ? Array.isArray(structuredData) ? structuredData : [structuredData] : [];
745
- return /* @__PURE__ */ jsxRuntime.jsxs(reactHelmetAsync.Helmet, { children: [
746
- /* @__PURE__ */ jsxRuntime.jsx("title", { children: fullTitle }),
747
- /* @__PURE__ */ jsxRuntime.jsx("meta", { name: "title", content: fullTitle }),
748
- description ? /* @__PURE__ */ jsxRuntime.jsx("meta", { name: "description", content: description }) : null,
749
- keywords ? /* @__PURE__ */ jsxRuntime.jsx("meta", { name: "keywords", content: keywords }) : null,
750
- author ? /* @__PURE__ */ jsxRuntime.jsx("meta", { name: "author", content: author }) : null,
751
- /* @__PURE__ */ jsxRuntime.jsx("meta", { name: "robots", content: robots }),
752
- resolvedUrl ? /* @__PURE__ */ jsxRuntime.jsx("link", { rel: "canonical", href: resolvedUrl }) : null,
753
- favicon ? /* @__PURE__ */ jsxRuntime.jsx("link", { rel: "icon", href: favicon }) : null,
754
- /* @__PURE__ */ jsxRuntime.jsx("meta", { property: "og:type", content: "website" }),
755
- /* @__PURE__ */ jsxRuntime.jsx("meta", { property: "og:title", content: fullTitle }),
756
- description ? /* @__PURE__ */ jsxRuntime.jsx("meta", { property: "og:description", content: description }) : null,
757
- fullImageUrl ? /* @__PURE__ */ jsxRuntime.jsx("meta", { property: "og:image", content: fullImageUrl }) : null,
758
- resolvedUrl ? /* @__PURE__ */ jsxRuntime.jsx("meta", { property: "og:url", content: resolvedUrl }) : null,
759
- /* @__PURE__ */ jsxRuntime.jsx("meta", { property: "og:locale", content: locale }),
760
- productName ? /* @__PURE__ */ jsxRuntime.jsx("meta", { property: "og:site_name", content: productName }) : null,
761
- /* @__PURE__ */ jsxRuntime.jsx("meta", { name: "twitter:card", content: "summary_large_image" }),
762
- /* @__PURE__ */ jsxRuntime.jsx("meta", { name: "twitter:title", content: fullTitle }),
763
- description ? /* @__PURE__ */ jsxRuntime.jsx("meta", { name: "twitter:description", content: description }) : null,
764
- fullImageUrl ? /* @__PURE__ */ jsxRuntime.jsx("meta", { name: "twitter:image", content: fullImageUrl }) : null,
765
- twitterHandle ? /* @__PURE__ */ jsxRuntime.jsx("meta", { name: "twitter:creator", content: twitterHandle }) : null,
766
- /* @__PURE__ */ jsxRuntime.jsx("meta", { name: "theme-color", content: themeColor }),
767
- structuredDataItems.map((data, index) => /* @__PURE__ */ jsxRuntime.jsx("script", { type: "application/ld+json", children: serializeJsonLd(data) }, `ld-${index}`)),
768
- additionalMeta?.map((meta, index) => /* @__PURE__ */ jsxRuntime.jsx(
769
- "meta",
770
- {
771
- ...meta.name ? { name: meta.name } : {},
772
- ...meta.property ? { property: meta.property } : {},
773
- content: meta.content
774
- },
775
- `meta-${index}`
776
- ))
777
- ] });
778
- }
779
- var LEGAL_DOCUMENTS = {
780
- privacy: {
781
- key: "privacy",
782
- slug: "privacy-policy",
783
- title: "Privacy Policy",
784
- description: "How we collect, use, share and protect your personal information."
785
- },
786
- terms: {
787
- key: "terms",
788
- slug: "terms-of-service",
789
- title: "Terms of Service",
790
- description: "The terms that govern your use of the service."
791
- },
792
- cookies: {
793
- key: "cookies",
794
- slug: "cookie-policy",
795
- title: "Cookie Policy",
796
- description: "The cookies and similar technologies we use, and why."
797
- },
798
- refunds: {
799
- key: "refunds",
800
- slug: "refund-policy",
801
- title: "Refund Policy",
802
- description: "How cancellations and refunds work for paid plans."
803
- },
804
- aup: {
805
- key: "aup",
806
- slug: "acceptable-use-policy",
807
- title: "Acceptable Use Policy",
808
- description: "What you may and may not do with the service."
809
- },
810
- dpa: {
811
- key: "dpa",
812
- slug: "data-processing-addendum",
813
- title: "Data Processing Addendum",
814
- description: "Our obligations as a processor of personal data on your behalf."
815
- },
816
- "ai-terms": {
817
- key: "ai-terms",
818
- slug: "ai-product-terms",
819
- title: "AI Product Terms",
820
- description: "The terms that apply to AI features, including how your data is and is not used."
821
- },
822
- subprocessors: {
823
- key: "subprocessors",
824
- slug: "subprocessors",
825
- title: "Subprocessors",
826
- description: "The third parties that help us host, secure and support the service."
827
- }
828
- };
829
- var LEGAL_DOCUMENT_KEYS = Object.keys(
830
- LEGAL_DOCUMENTS
831
- );
832
- function findLegalDocument(keyOrSlug) {
833
- if (Object.prototype.hasOwnProperty.call(LEGAL_DOCUMENTS, keyOrSlug)) {
834
- return LEGAL_DOCUMENTS[keyOrSlug];
835
- }
836
- return Object.values(LEGAL_DOCUMENTS).find((doc) => doc.slug === keyOrSlug) ?? null;
837
- }
838
- var LEGAL_CONTENT_QUERY = `
839
- query PublicLegalContentPage($slug: String!, $productId: String!) {
840
- publicResolvedContentPage(slug: $slug, productId: $productId) {
841
- title
842
- slug
843
- content
844
- contentFormat
845
- seoTitle
846
- seoDescription
847
- seoKeywords
848
- lastUpdated
849
- }
850
- }
851
- `;
852
- async function fetchLegalContentPage(client, slug, productId) {
853
- const result = await client.query(LEGAL_CONTENT_QUERY, { slug, productId }, { cache: false });
854
- if (result.errors?.length) {
855
- throw new Error(result.errors[0]?.message ?? "Unknown error");
856
- }
857
- return result.data?.publicResolvedContentPage ?? null;
858
- }
859
- async function fetchLegalContent(client, options) {
860
- const docs = (options.documents ?? LEGAL_DOCUMENT_KEYS).map(
861
- (key) => LEGAL_DOCUMENTS[key]
862
- );
863
- const entries = await Promise.all(
864
- docs.map(
865
- async (doc) => [
866
- doc.slug,
867
- await fetchLegalContentPage(client, doc.slug, options.productId)
868
- ]
869
- )
870
- );
871
- return Object.fromEntries(entries);
872
- }
873
- function splitLeadingHeading(content, format) {
874
- if (format.toLowerCase() !== "markdown")
875
- return { heading: null, body: content };
876
- const match = /^\s*#[ \t]+([^\n]+?)[ \t#]*(?:\n|$)/.exec(content);
877
- if (!match?.[1]) return { heading: null, body: content };
878
- return {
879
- heading: match[1].trim(),
880
- body: content.slice(match[0].length).replace(/^\s+/, "")
881
- };
882
- }
883
- function formatLegalDate(iso) {
884
- if (!iso) return null;
885
- const date = new Date(iso);
886
- if (Number.isNaN(date.getTime())) return null;
887
- return date.toLocaleDateString("en-US", {
888
- year: "numeric",
889
- month: "long",
890
- day: "numeric",
891
- timeZone: "UTC"
892
- });
893
- }
894
- function LegalPage(props) {
895
- const {
896
- document: document2,
897
- slug: slugProp,
898
- initialContent,
899
- fallbackTitle,
900
- fallbackDescription,
901
- productName,
902
- canonicalUrl,
903
- contactEmail,
904
- className = ""
905
- } = props;
906
- const registered = document2 ? LEGAL_DOCUMENTS[document2] : slugProp ? findLegalDocument(slugProp) : null;
907
- const slug = registered?.slug ?? slugProp ?? "";
908
- const prefetched = initialContent !== void 0;
909
- const { data: fetched, loading } = useContentPage(slug, {
910
- resolve: true,
911
- enabled: !prefetched && slug !== ""
912
- });
913
- const page = prefetched ? initialContent ?? null : fetched;
914
- const isLoading = !prefetched && slug !== "" && loading;
915
- const defaultTitle = fallbackTitle ?? registered?.title ?? "Legal";
916
- const defaultDescription = fallbackDescription ?? registered?.description ?? "";
917
- const { heading, body } = page ? splitLeadingHeading(page.content, page.contentFormat) : { heading: null, body: "" };
918
- const title = page?.title || heading || defaultTitle;
919
- const updated = formatLegalDate(page?.lastUpdated);
920
- const unavailable = !isLoading && !page;
921
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `w-full ${className}`, children: [
922
- /* @__PURE__ */ jsxRuntime.jsx(ProseStyles, {}),
923
- /* @__PURE__ */ jsxRuntime.jsx(
924
- PageHead,
925
- {
926
- title: page?.seoTitle || title,
927
- description: page?.seoDescription?.trim() || defaultDescription,
928
- keywords: page?.seoKeywords ?? void 0,
929
- productName,
930
- url: canonicalUrl,
931
- robots: unavailable ? "noindex, follow" : void 0
932
- }
933
- ),
934
- /* @__PURE__ */ jsxRuntime.jsxs(
935
- "article",
936
- {
937
- className: "max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-16 sm:py-24",
938
- "aria-busy": isLoading,
939
- children: [
940
- /* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-3xl sm:text-4xl font-bold tracking-tight text-foreground mb-3", children: title }),
941
- updated && /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-sm text-muted-foreground mb-10", children: [
942
- "Last updated:",
943
- " ",
944
- /* @__PURE__ */ jsxRuntime.jsx("time", { dateTime: page?.lastUpdated ?? void 0, children: updated })
945
- ] }),
946
- isLoading ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-3", "data-testid": "legal-page-loading", children: [
947
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-4 w-3/4 rounded bg-muted animate-pulse" }),
948
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-4 w-full rounded bg-muted animate-pulse" }),
949
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-4 w-5/6 rounded bg-muted animate-pulse" })
950
- ] }) : page ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "boff-prose max-w-none", children: /* @__PURE__ */ jsxRuntime.jsx(ContentRenderer, { content: body, format: page.contentFormat }) }) : /* @__PURE__ */ jsxRuntime.jsxs(
951
- "div",
952
- {
953
- role: "status",
954
- className: "rounded-xl border border-border bg-muted/30 p-6",
955
- children: [
956
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-base text-foreground", children: "This document is not available right now." }),
957
- contactEmail && /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "mt-2 text-sm text-muted-foreground", children: [
958
- "For a copy, contact",
959
- " ",
960
- /* @__PURE__ */ jsxRuntime.jsx(
961
- "a",
962
- {
963
- className: "text-primary underline",
964
- href: `mailto:${contactEmail}`,
965
- children: contactEmail
966
- }
967
- ),
968
- "."
969
- ] })
970
- ]
971
- }
972
- )
973
- ]
974
- }
975
- )
976
- ] });
977
- }
978
- var PUBLIC_JOB_POSTING_FIELDS = (
979
- /* GraphQL */
980
- `
981
- id
982
- slug
983
- title
984
- department
985
- location
986
- employmentType
987
- openings
988
- description
989
- overview
990
- responsibilities
991
- learnings
992
- qualifications
993
- rounds {
994
- roundNumber
995
- name
996
- roundType
997
- isRequired
998
- description
999
- }
1000
- `
1001
- );
1002
- var PUBLIC_JOB_POSTINGS_QUERY = (
1003
- /* GraphQL */
1004
- `
1005
- query PublicJobPostings($productId: ID, $limit: Int, $offset: Int) {
1006
- publicJobPostings(productId: $productId, limit: $limit, offset: $offset) {
1007
- ${PUBLIC_JOB_POSTING_FIELDS}
1008
- }
1009
- }
1010
- `
1011
- );
1012
- var PUBLIC_JOB_POSTING_QUERY = (
1013
- /* GraphQL */
1014
- `
1015
- query PublicJobPosting($slug: String!, $productId: ID) {
1016
- publicJobPosting(slug: $slug, productId: $productId) {
1017
- ${PUBLIC_JOB_POSTING_FIELDS}
1018
- }
1019
- }
1020
- `
1021
- );
1022
- var PUBLIC_JOB_POSTINGS_FOR_SUBMISSION_QUERY = (
1023
- /* GraphQL */
1024
- `
1025
- query PublicJobPostingsForSubmission(
1026
- $productId: ID
1027
- $limit: Int
1028
- $offset: Int
1029
- ) {
1030
- publicJobPostingsForSubmission(
1031
- productId: $productId
1032
- limit: $limit
1033
- offset: $offset
1034
- ) {
1035
- id
1036
- title
1037
- }
1038
- }
1039
- `
1040
- );
1041
- async function fetchPublicJobPostings(client, options) {
1042
- const result = await client.query(PUBLIC_JOB_POSTINGS_QUERY, {
1043
- productId: options?.productId,
1044
- limit: options?.limit ?? 50,
1045
- offset: options?.offset ?? 0
1046
- });
1047
- if (result.errors?.length) {
1048
- throw new Error(result.errors[0]?.message ?? "Unknown error");
1049
- }
1050
- return result.data?.publicJobPostings ?? [];
1051
- }
1052
- async function fetchPublicJobPosting(client, slug, options) {
1053
- const result = await client.query(PUBLIC_JOB_POSTING_QUERY, {
1054
- slug,
1055
- productId: options?.productId
1056
- });
1057
- if (result.errors?.length) {
1058
- throw new Error(result.errors[0]?.message ?? "Unknown error");
1059
- }
1060
- return result.data?.publicJobPosting ?? null;
1061
- }
1062
- async function fetchPublicJobPostingsForSubmission(client, options) {
1063
- const result = await client.query(PUBLIC_JOB_POSTINGS_FOR_SUBMISSION_QUERY, {
1064
- productId: options?.productId,
1065
- limit: options?.limit ?? 50,
1066
- offset: options?.offset ?? 0
1067
- });
1068
- if (result.errors?.length) {
1069
- throw new Error(result.errors[0]?.message ?? "Unknown error");
1070
- }
1071
- return result.data?.publicJobPostingsForSubmission ?? [];
1072
- }
1073
- function usePublicJobPostings(options) {
1074
- const client = useWebSDK();
1075
- const config = useWebSDKConfig();
1076
- const { product } = useProduct();
1077
- const resolvedProductId = options?.productId ?? product?.id ?? config.productId;
1078
- const [postings, setPostings] = React.useState([]);
1079
- const [loading, setLoading] = React.useState(true);
1080
- const [error, setError] = React.useState(null);
1081
- const fetchPostings = React.useCallback(async () => {
1082
- setLoading(true);
1083
- setError(null);
1084
- try {
1085
- const items = await fetchPublicJobPostings(client, {
1086
- productId: resolvedProductId,
1087
- limit: options?.limit,
1088
- offset: options?.offset
1089
- });
1090
- setPostings(items);
1091
- } catch (err) {
1092
- setError(
1093
- err instanceof Error ? err.message : "Failed to fetch job postings"
1094
- );
1095
- setPostings([]);
1096
- } finally {
1097
- setLoading(false);
1098
- }
1099
- }, [client, resolvedProductId, options?.limit, options?.offset]);
1100
- React.useEffect(() => {
1101
- fetchPostings();
1102
- }, [fetchPostings]);
1103
- return { postings, loading, error, refetch: fetchPostings };
1104
- }
1105
- function usePublicJobPostingsForSubmission(options) {
1106
- const client = useWebSDK();
1107
- const config = useWebSDKConfig();
1108
- const { product } = useProduct();
1109
- const resolvedProductId = options?.productId ?? product?.id ?? config.productId;
1110
- const [postings, setPostings] = React.useState([]);
1111
- const [loading, setLoading] = React.useState(true);
1112
- const [error, setError] = React.useState(null);
1113
- const fetchPostings = React.useCallback(async () => {
1114
- setLoading(true);
1115
- setError(null);
1116
- try {
1117
- const items = await fetchPublicJobPostingsForSubmission(client, {
1118
- productId: resolvedProductId,
1119
- limit: options?.limit,
1120
- offset: options?.offset
1121
- });
1122
- setPostings(items);
1123
- } catch (err) {
1124
- setError(
1125
- err instanceof Error ? err.message : "Failed to fetch job postings"
1126
- );
1127
- setPostings([]);
1128
- } finally {
1129
- setLoading(false);
1130
- }
1131
- }, [client, resolvedProductId, options?.limit, options?.offset]);
1132
- React.useEffect(() => {
1133
- fetchPostings();
1134
- }, [fetchPostings]);
1135
- return { postings, loading, error, refetch: fetchPostings };
1136
- }
1137
- function usePublicJobPosting(slug, options) {
1138
- const client = useWebSDK();
1139
- const config = useWebSDKConfig();
1140
- const { product } = useProduct();
1141
- const resolvedProductId = options?.productId ?? product?.id ?? config.productId;
1142
- const [posting, setPosting] = React.useState(null);
1143
- const [loading, setLoading] = React.useState(true);
1144
- const [error, setError] = React.useState(null);
1145
- const fetchPosting = React.useCallback(async () => {
1146
- if (!slug) {
1147
- setPosting(null);
1148
- setLoading(false);
1149
- return;
1150
- }
1151
- setLoading(true);
1152
- setError(null);
1153
- try {
1154
- const item = await fetchPublicJobPosting(client, slug, {
1155
- productId: resolvedProductId
1156
- });
1157
- setPosting(item);
1158
- } catch (err) {
1159
- setError(
1160
- err instanceof Error ? err.message : "Failed to fetch job posting"
1161
- );
1162
- setPosting(null);
1163
- } finally {
1164
- setLoading(false);
1165
- }
1166
- }, [client, resolvedProductId, slug]);
1167
- React.useEffect(() => {
1168
- fetchPosting();
1169
- }, [fetchPosting]);
1170
- return { posting, loading, error, refetch: fetchPosting };
1171
- }
1172
- var BLOG_POSTS_QUERY = `
1173
- query BlogPosts($productId: String!, $limit: Int, $offset: Int) {
1174
- publicContentPages(productId: $productId, limit: $limit, offset: $offset) {
1175
- items {
1176
- id
1177
- title
1178
- slug
1179
- lastUpdated
1180
- tags
1181
- seoDescription
1182
- excerpt
1183
- thumbnail
1184
- author
1185
- publishedAt
1186
- readingTime
1187
- featured
1188
- category
1189
- }
1190
- total
1191
- hasMore
1192
- }
1193
- }
1194
- `;
1195
- var BLOG_POST_QUERY = `
1196
- query BlogPost($slug: String!, $productId: String) {
1197
- publicContentPage(slug: $slug, productId: $productId) {
1198
- id
1199
- title
1200
- slug
1201
- content
1202
- contentFormat
1203
- seoTitle
1204
- seoDescription
1205
- seoKeywords
1206
- lastUpdated
1207
- tags
1208
- excerpt
1209
- thumbnail
1210
- author
1211
- publishedAt
1212
- readingTime
1213
- featured
1214
- category
1215
- }
1216
- }
1217
- `;
1218
- function BlogCard({
1219
- post,
1220
- basePath = "/blog"
1221
- }) {
1222
- const date = post.publishedAt || post.lastUpdated;
1223
- const formattedDate = date ? new Date(date).toLocaleDateString("en-US", {
1224
- year: "numeric",
1225
- month: "long",
1226
- day: "numeric"
1227
- }) : "";
1228
- const [imgError, setImgError] = React.useState(false);
1229
- return /* @__PURE__ */ jsxRuntime.jsxs(
1230
- "a",
1231
- {
1232
- href: `${basePath}/${post.slug}`,
1233
- className: "group block bg-card border rounded-xl overflow-hidden hover:shadow-lg hover:border-primary/30 transition-all duration-300",
1234
- children: [
1235
- post.thumbnail && !imgError ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "aspect-[16/9] overflow-hidden", children: /* @__PURE__ */ jsxRuntime.jsx(
1236
- "img",
1237
- {
1238
- src: post.thumbnail,
1239
- alt: post.title,
1240
- className: "w-full h-full object-cover group-hover:scale-105 transition-transform duration-300",
1241
- loading: "lazy",
1242
- onError: () => setImgError(true)
1243
- }
1244
- ) }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "aspect-[16/9] bg-muted/50 flex items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(
1245
- "svg",
1246
- {
1247
- className: "w-12 h-12 text-muted-foreground/20",
1248
- fill: "none",
1249
- viewBox: "0 0 24 24",
1250
- stroke: "currentColor",
1251
- children: /* @__PURE__ */ jsxRuntime.jsx(
1252
- "path",
1253
- {
1254
- strokeLinecap: "round",
1255
- strokeLinejoin: "round",
1256
- strokeWidth: 1.5,
1257
- 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"
1258
- }
1259
- )
1260
- }
1261
- ) }),
1262
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-5 sm:p-6", children: [
1263
- 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 }),
1264
- /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-lg font-semibold text-foreground mb-2 group-hover:text-primary transition-colors line-clamp-2", children: post.title }),
1265
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground mb-4 line-clamp-3", children: post.excerpt || post.seoDescription || "" }),
1266
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3 text-xs text-muted-foreground", children: [
1267
- post.author && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium", children: post.author }),
1268
- post.author && formattedDate && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground/40", children: "\xB7" }),
1269
- formattedDate && /* @__PURE__ */ jsxRuntime.jsx("span", { children: formattedDate }),
1270
- post.readingTime && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1271
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground/40", children: "\xB7" }),
1272
- /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
1273
- post.readingTime,
1274
- " min read"
1275
- ] })
1276
- ] })
1277
- ] })
1278
- ] })
1279
- ]
1280
- }
1281
- );
1282
- }
1283
- function BlogListPage(props) {
1284
- const {
1285
- productName,
1286
- heroTitle = "Blog",
1287
- heroSubtitle = "Latest updates, engineering insights, and product news",
1288
- blogPostBasePath = "/blog",
1289
- className = "",
1290
- seoTitle,
1291
- seoDescription,
1292
- seoKeywords
1293
- } = props;
1294
- const client = useWebSDK();
1295
- const config = useWebSDKConfig();
1296
- const { product } = useProduct();
1297
- const resolvedProductId = product?.id || config.productId;
1298
- const [posts, setPosts] = React.useState([]);
1299
- const [total, setTotal] = React.useState(0);
1300
- const [loading, setLoading] = React.useState(true);
1301
- const [error, setError] = React.useState(null);
1302
- const [offset, setOffset] = React.useState(0);
1303
- const limit = 12;
1304
- const fetchPosts = React.useCallback(async () => {
1305
- if (!resolvedProductId) return;
1306
- setLoading(true);
1307
- try {
1308
- const result = await client.query(BLOG_POSTS_QUERY, {
1309
- productId: resolvedProductId,
1310
- limit,
1311
- offset
1312
- });
1313
- if (result.errors?.length) {
1314
- setError(result.errors[0]?.message ?? "Failed to load posts");
1315
- return;
1316
- }
1317
- const blogPosts = (result.data?.publicContentPages?.items || []).filter(
1318
- (p) => p.tags?.includes("blog")
1319
- );
1320
- setPosts(blogPosts);
1321
- setTotal(result.data?.publicContentPages?.total || 0);
1322
- setError(null);
1323
- } catch {
1324
- setError("Failed to load blog posts");
1325
- } finally {
1326
- setLoading(false);
1327
- }
1328
- }, [client, resolvedProductId, offset]);
1329
- React.useEffect(() => {
1330
- fetchPosts();
1331
- }, [fetchPosts]);
1332
- const featuredPosts = posts.filter((p) => p.featured);
1333
- const regularPosts = posts.filter((p) => !p.featured);
1334
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `w-full ${className}`, children: [
1335
- seoTitle && /* @__PURE__ */ jsxRuntime.jsx(
1336
- PageHead,
1337
- {
1338
- title: seoTitle,
1339
- description: seoDescription || heroSubtitle,
1340
- productName,
1341
- keywords: seoKeywords,
1342
- url: typeof window !== "undefined" ? window.location.origin + window.location.pathname : void 0
1343
- }
1344
- ),
1345
- /* @__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: [
1346
- /* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-3xl sm:text-4xl md:text-5xl font-bold tracking-tight text-foreground", children: heroTitle }),
1347
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-4 text-lg text-muted-foreground", children: heroSubtitle })
1348
- ] }) }) }),
1349
- /* @__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(
1350
- "div",
1351
- {
1352
- className: "bg-card border rounded-xl overflow-hidden animate-pulse",
1353
- children: [
1354
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "aspect-[16/9] bg-muted" }),
1355
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-6 space-y-3", children: [
1356
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-4 bg-muted rounded w-1/4" }),
1357
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-6 bg-muted rounded w-3/4" }),
1358
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-4 bg-muted rounded" }),
1359
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-4 bg-muted rounded w-1/2" })
1360
- ] })
1361
- ]
1362
- },
1363
- i
1364
- )) }) : 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: [
1365
- 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(
1366
- BlogCard,
1367
- {
1368
- post,
1369
- basePath: blogPostBasePath
1370
- },
1371
- post.id
1372
- )) }),
1373
- /* @__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(
1374
- BlogCard,
1375
- {
1376
- post,
1377
- basePath: blogPostBasePath
1378
- },
1379
- post.id
1380
- )) }),
1381
- total > limit && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-center gap-4 mt-12", children: [
1382
- /* @__PURE__ */ jsxRuntime.jsx(
1383
- "button",
1384
- {
1385
- onClick: () => setOffset(Math.max(0, offset - limit)),
1386
- disabled: offset === 0,
1387
- className: "px-5 py-2.5 text-sm font-medium border rounded-lg disabled:opacity-40 hover:bg-muted transition-colors",
1388
- children: "Previous"
1389
- }
1390
- ),
1391
- /* @__PURE__ */ jsxRuntime.jsx(
1392
- "button",
1393
- {
1394
- onClick: () => setOffset(offset + limit),
1395
- disabled: offset + limit >= total,
1396
- className: "px-5 py-2.5 text-sm font-medium border rounded-lg disabled:opacity-40 hover:bg-muted transition-colors",
1397
- children: "Next"
1398
- }
1399
- )
1400
- ] })
1401
- ] }) }) })
1402
- ] });
1403
- }
1404
- function BlogPostPage(props) {
1405
- const {
1406
- productName,
1407
- blogListUrl = "/blog",
1408
- backLabel = "Back to Blog",
1409
- className = ""
1410
- } = props;
1411
- const client = useWebSDK();
1412
- const config = useWebSDKConfig();
1413
- const { product } = useProduct();
1414
- const resolvedProductId = product?.id || config.productId;
1415
- const [post, setPost] = React.useState(null);
1416
- const [loading, setLoading] = React.useState(true);
1417
- const [error, setError] = React.useState(null);
1418
- const slug = typeof window !== "undefined" ? window.location.pathname.split("/").pop() || "" : "";
1419
- React.useEffect(() => {
1420
- if (!slug || !resolvedProductId) return;
1421
- async function fetchPost() {
1422
- setLoading(true);
1423
- try {
1424
- const result = await client.query(BLOG_POST_QUERY, {
1425
- slug,
1426
- productId: resolvedProductId
1427
- });
1428
- if (result.errors?.length) {
1429
- setError(result.errors[0]?.message ?? "Failed to load post");
1430
- return;
1431
- }
1432
- setPost(result.data?.publicContentPage ?? null);
1433
- setError(null);
1434
- } catch {
1435
- setError("Failed to load blog post");
1436
- } finally {
1437
- setLoading(false);
1438
- }
1439
- }
1440
- fetchPost();
1441
- }, [client, slug, resolvedProductId]);
1442
- if (loading) {
1443
- 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: [
1444
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-8 bg-muted rounded w-2/3" }),
1445
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-4 bg-muted rounded w-1/3" }),
1446
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "aspect-[16/9] bg-muted rounded-xl" }),
1447
- /* @__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)) })
1448
- ] }) }) });
1449
- }
1450
- if (error || !post) {
1451
- 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: [
1452
- /* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-2xl font-bold mb-4", children: "Post not found" }),
1453
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-muted-foreground mb-6", children: error || "This blog post doesn't exist or has been removed." }),
1454
- /* @__PURE__ */ jsxRuntime.jsxs(
1455
- "a",
1456
- {
1457
- href: blogListUrl,
1458
- className: "text-primary hover:underline font-medium",
1459
- children: [
1460
- "\u2190 ",
1461
- backLabel
1462
- ]
1463
- }
1464
- )
1465
- ] }) });
1466
- }
1467
- const date = post.publishedAt || post.lastUpdated;
1468
- const formattedDate = date ? new Date(date).toLocaleDateString("en-US", {
1469
- year: "numeric",
1470
- month: "long",
1471
- day: "numeric"
1472
- }) : "";
1473
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `w-full ${className}`, children: [
1474
- /* @__PURE__ */ jsxRuntime.jsx(ProseStyles, {}),
1475
- /* @__PURE__ */ jsxRuntime.jsx(
1476
- PageHead,
1477
- {
1478
- title: post.seoTitle || post.title,
1479
- description: post.seoDescription || post.excerpt || "",
1480
- productName,
1481
- keywords: post.seoKeywords || "",
1482
- image: post.thumbnail || void 0,
1483
- url: typeof window !== "undefined" ? window.location.origin + window.location.pathname : void 0
1484
- }
1485
- ),
1486
- /* @__PURE__ */ jsxRuntime.jsxs("article", { className: "max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-16 sm:py-20", children: [
1487
- /* @__PURE__ */ jsxRuntime.jsxs(
1488
- "a",
1489
- {
1490
- href: blogListUrl,
1491
- className: "inline-flex items-center gap-1 text-sm text-muted-foreground hover:text-foreground mb-8 transition-colors",
1492
- children: [
1493
- "\u2190 ",
1494
- backLabel
1495
- ]
1496
- }
1497
- ),
1498
- 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 }),
1499
- /* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-3xl sm:text-4xl md:text-5xl font-bold tracking-tight text-foreground mb-4", children: post.title }),
1500
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3 text-sm text-muted-foreground mb-8", children: [
1501
- post.author && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium text-foreground", children: post.author }),
1502
- post.author && formattedDate && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground/40", children: "\xB7" }),
1503
- formattedDate && /* @__PURE__ */ jsxRuntime.jsx("span", { children: formattedDate }),
1504
- post.readingTime && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1505
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground/40", children: "\xB7" }),
1506
- /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
1507
- post.readingTime,
1508
- " min read"
1509
- ] })
1510
- ] })
1511
- ] }),
1512
- post.thumbnail && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "aspect-[16/9] rounded-xl overflow-hidden mb-10", children: /* @__PURE__ */ jsxRuntime.jsx(
1513
- "img",
1514
- {
1515
- src: post.thumbnail,
1516
- alt: post.title,
1517
- className: "w-full h-full object-cover"
1518
- }
1519
- ) }),
1520
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "boff-prose max-w-none", children: /* @__PURE__ */ jsxRuntime.jsx(ContentRenderer, { content: post.content, format: post.contentFormat }) })
1521
- ] })
1522
- ] });
1523
- }
1524
- var CASE_STUDY_TAG = "case-study";
1525
- function hasCustomerSignOff(page) {
1526
- const signedAt = page.customerSignOff?.signedAt;
1527
- return typeof signedAt === "string" && !Number.isNaN(new Date(signedAt).getTime());
1528
- }
1529
- var ANIMATION_STYLES = `
1530
- @keyframes csFadeUp { from { opacity: 0; transform: translateY(16px); } to { opacity: 1; transform: translateY(0); } }
1531
- @keyframes csFloat { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-14px); } }
1532
- .cs-fade-up { animation: csFadeUp 0.5s cubic-bezier(0.16, 1, 0.3, 1) both; }
1533
- .cs-float { animation: csFloat 7s ease-in-out infinite; }
1534
- @media (prefers-reduced-motion: reduce) {
1535
- .cs-fade-up, .cs-float { animation: none !important; }
1536
- }
1537
- `;
1538
- function AnimationStyles() {
1539
- return /* @__PURE__ */ jsxRuntime.jsx("style", { dangerouslySetInnerHTML: { __html: ANIMATION_STYLES } });
1540
- }
1541
- var CASE_STUDIES_QUERY = `
1542
- query CaseStudies($productId: String!, $tag: String, $limit: Int, $offset: Int) {
1543
- publicContentPages(productId: $productId, tag: $tag, limit: $limit, offset: $offset) {
1544
- items {
1545
- id
1546
- title
1547
- slug
1548
- lastUpdated
1549
- tags
1550
- seoDescription
1551
- excerpt
1552
- thumbnail
1553
- author
1554
- publishedAt
1555
- readingTime
1556
- featured
1557
- category
1558
- customerSignOff {
1559
- signedAt
1560
- }
1561
- }
1562
- total
1563
- hasMore
1564
- }
1565
- }
1566
- `;
1567
- var CASE_STUDY_QUERY = `
1568
- query CaseStudy($slug: String!, $productId: String) {
1569
- publicContentPage(slug: $slug, productId: $productId) {
1570
- id
1571
- title
1572
- slug
1573
- content
1574
- contentFormat
1575
- seoTitle
1576
- seoDescription
1577
- seoKeywords
1578
- lastUpdated
1579
- tags
1580
- excerpt
1581
- thumbnail
1582
- author
1583
- publishedAt
1584
- readingTime
1585
- featured
1586
- category
1587
- customerSignOff {
1588
- signedAt
1589
- }
1590
- }
1591
- }
1592
- `;
1593
- function formatDate(value) {
1594
- if (!value) return "";
1595
- const d = new Date(value);
1596
- if (Number.isNaN(d.getTime())) return "";
1597
- return d.toLocaleDateString("en-US", {
1598
- year: "numeric",
1599
- month: "long",
1600
- day: "numeric"
1601
- });
1602
- }
1603
- function Thumbnail({
1604
- src,
1605
- alt,
1606
- className = ""
1607
- }) {
1608
- const [imgError, setImgError] = React.useState(false);
1609
- if (src && !imgError) {
1610
- return /* @__PURE__ */ jsxRuntime.jsx(
1611
- "img",
1612
- {
1613
- src,
1614
- alt,
1615
- className,
1616
- loading: "lazy",
1617
- onError: () => setImgError(true)
1618
- }
1619
- );
1620
- }
1621
- return /* @__PURE__ */ jsxRuntime.jsx(
1622
- "div",
1623
- {
1624
- className: `flex items-center justify-center bg-gradient-to-br from-muted/60 to-muted ${className}`,
1625
- "aria-hidden": "true",
1626
- children: /* @__PURE__ */ jsxRuntime.jsx(
1627
- "svg",
1628
- {
1629
- className: "w-12 h-12 text-muted-foreground/25",
1630
- fill: "none",
1631
- viewBox: "0 0 24 24",
1632
- stroke: "currentColor",
1633
- children: /* @__PURE__ */ jsxRuntime.jsx(
1634
- "path",
1635
- {
1636
- strokeLinecap: "round",
1637
- strokeLinejoin: "round",
1638
- strokeWidth: 1.5,
1639
- 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"
1640
- }
1641
- )
1642
- }
1643
- )
1644
- }
1645
- );
1646
- }
1647
- function MetaRow({
1648
- post,
1649
- className = ""
1650
- }) {
1651
- const formattedDate = formatDate(post.publishedAt || post.lastUpdated);
1652
- return /* @__PURE__ */ jsxRuntime.jsxs(
1653
- "div",
1654
- {
1655
- className: `flex flex-wrap items-center gap-x-3 gap-y-1 text-xs text-muted-foreground ${className}`,
1656
- children: [
1657
- post.author && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium", children: post.author }),
1658
- post.author && formattedDate && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground/40", children: "\xB7" }),
1659
- formattedDate && /* @__PURE__ */ jsxRuntime.jsx("span", { children: formattedDate }),
1660
- post.readingTime ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1661
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground/40", children: "\xB7" }),
1662
- /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
1663
- post.readingTime,
1664
- " min read"
1665
- ] })
1666
- ] }) : null
1667
- ]
1668
- }
1669
- );
1670
- }
1671
- function FeaturedCard({
1672
- post,
1673
- basePath
1674
- }) {
1675
- return /* @__PURE__ */ jsxRuntime.jsxs(
1676
- "a",
1677
- {
1678
- href: `${basePath}/${post.slug}`,
1679
- className: "group relative grid md:grid-cols-2 gap-0 overflow-hidden rounded-2xl border bg-card shadow-sm transition-all duration-300 hover:-translate-y-1 hover:shadow-xl hover:border-primary/40",
1680
- children: [
1681
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative aspect-[16/10] md:aspect-auto md:h-full overflow-hidden", children: [
1682
- /* @__PURE__ */ jsxRuntime.jsx(
1683
- Thumbnail,
1684
- {
1685
- src: post.thumbnail,
1686
- alt: post.title,
1687
- className: "w-full h-full min-h-[220px] object-cover transition-transform duration-500 group-hover:scale-[1.04]"
1688
- }
1689
- ),
1690
- /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "absolute top-4 left-4 inline-flex items-center gap-1 rounded-full bg-primary px-2.5 py-1 text-xs font-semibold text-primary-foreground shadow-sm", children: [
1691
- /* @__PURE__ */ jsxRuntime.jsx(
1692
- "svg",
1693
- {
1694
- className: "h-3 w-3",
1695
- viewBox: "0 0 20 20",
1696
- fill: "currentColor",
1697
- "aria-hidden": "true",
1698
- children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M9.049 2.927c.3-.921 1.603-.921 1.902 0l1.286 3.957a1 1 0 00.95.69h4.162c.969 0 1.371 1.24.588 1.81l-3.368 2.447a1 1 0 00-.364 1.118l1.287 3.957c.3.921-.755 1.688-1.54 1.118l-3.367-2.447a1 1 0 00-1.176 0l-3.367 2.447c-.784.57-1.838-.197-1.539-1.118l1.286-3.957a1 1 0 00-.363-1.118L2.98 9.384c-.783-.57-.38-1.81.588-1.81h4.162a1 1 0 00.95-.69l1.286-3.957z" })
1699
- }
1700
- ),
1701
- "Featured"
1702
- ] })
1703
- ] }),
1704
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col justify-center p-6 sm:p-8", children: [
1705
- post.category && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "mb-3 inline-block w-fit rounded-full bg-primary/10 px-2.5 py-1 text-xs font-medium text-primary", children: post.category }),
1706
- /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-xl sm:text-2xl font-bold text-foreground transition-colors group-hover:text-primary line-clamp-3", children: post.title }),
1707
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-3 text-sm sm:text-base text-muted-foreground line-clamp-3", children: post.excerpt || post.seoDescription || "" }),
1708
- /* @__PURE__ */ jsxRuntime.jsx(MetaRow, { post, className: "mt-5" }),
1709
- /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "mt-5 inline-flex items-center gap-1 text-sm font-semibold text-primary", children: [
1710
- "Read the story",
1711
- /* @__PURE__ */ jsxRuntime.jsx(
1712
- "svg",
1713
- {
1714
- className: "h-4 w-4 transition-transform duration-300 group-hover:translate-x-1",
1715
- viewBox: "0 0 20 20",
1716
- fill: "currentColor",
1717
- "aria-hidden": "true",
1718
- children: /* @__PURE__ */ jsxRuntime.jsx(
1719
- "path",
1720
- {
1721
- fillRule: "evenodd",
1722
- d: "M7.293 4.293a1 1 0 011.414 0l5 5a1 1 0 010 1.414l-5 5a1 1 0 01-1.414-1.414L11.586 10 7.293 5.707a1 1 0 010-1.414z",
1723
- clipRule: "evenodd"
1724
- }
1725
- )
1726
- }
1727
- )
1728
- ] })
1729
- ] })
1730
- ]
1731
- }
1732
- );
1733
- }
1734
- function CaseStudyCard({
1735
- post,
1736
- basePath = "/case-studies"
1737
- }) {
1738
- return /* @__PURE__ */ jsxRuntime.jsxs(
1739
- "a",
1740
- {
1741
- href: `${basePath}/${post.slug}`,
1742
- className: "group flex flex-col overflow-hidden rounded-xl border bg-card transition-all duration-300 hover:-translate-y-1 hover:shadow-lg hover:border-primary/40",
1743
- children: [
1744
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "aspect-[16/9] overflow-hidden", children: /* @__PURE__ */ jsxRuntime.jsx(
1745
- Thumbnail,
1746
- {
1747
- src: post.thumbnail,
1748
- alt: post.title,
1749
- className: "w-full h-full object-cover transition-transform duration-500 group-hover:scale-105"
1750
- }
1751
- ) }),
1752
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-1 flex-col p-5 sm:p-6", children: [
1753
- post.category && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "mb-3 inline-block w-fit rounded-full bg-primary/10 px-2.5 py-1 text-xs font-medium text-primary", children: post.category }),
1754
- /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "mb-2 text-lg font-semibold text-foreground transition-colors group-hover:text-primary line-clamp-2", children: post.title }),
1755
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mb-4 text-sm text-muted-foreground line-clamp-3", children: post.excerpt || post.seoDescription || "" }),
1756
- /* @__PURE__ */ jsxRuntime.jsx(MetaRow, { post, className: "mt-auto" })
1757
- ] })
1758
- ]
1759
- }
1760
- );
1761
- }
1762
- function EmptyState({
1763
- productLabel,
1764
- title,
1765
- subtitle,
1766
- contactHref,
1767
- contactLabel,
1768
- exploreHref,
1769
- exploreLabel
1770
- }) {
1771
- const highlights = [
1772
- {
1773
- title: "Real outcomes, real teams",
1774
- body: `We're partnering with our earliest adopters right now. As they ship results with ${productLabel}, their stories will land here \u2014 measured and unembellished.`,
1775
- icon: /* @__PURE__ */ jsxRuntime.jsx(
1776
- "path",
1777
- {
1778
- strokeLinecap: "round",
1779
- strokeLinejoin: "round",
1780
- strokeWidth: 1.5,
1781
- d: "M3 13.125C3 12.504 3.504 12 4.125 12h2.25c.621 0 1.125.504 1.125 1.125v6.75C7.5 20.496 6.996 21 6.375 21h-2.25A1.125 1.125 0 013 19.875v-6.75zM9.75 8.625c0-.621.504-1.125 1.125-1.125h2.25c.621 0 1.125.504 1.125 1.125v11.25c0 .621-.504 1.125-1.125 1.125h-2.25a1.125 1.125 0 01-1.125-1.125V8.625zM16.5 4.125c0-.621.504-1.125 1.125-1.125h2.25C20.496 3 21 3.504 21 4.125v15.75c0 .621-.504 1.125-1.125 1.125h-2.25a1.125 1.125 0 01-1.125-1.125V4.125z"
1782
- }
1783
- )
1784
- },
1785
- {
1786
- title: "See it on your use case",
1787
- body: `Want to know how ${productLabel} would work for your team? We'll walk you through exactly what to expect \u2014 no pitch, just specifics.`,
1788
- icon: /* @__PURE__ */ jsxRuntime.jsx(
1789
- "path",
1790
- {
1791
- strokeLinecap: "round",
1792
- strokeLinejoin: "round",
1793
- strokeWidth: 1.5,
1794
- d: "M2.036 12.322a1.012 1.012 0 010-.639C3.423 7.51 7.36 4.5 12 4.5c4.638 0 8.573 3.007 9.963 7.178.07.207.07.431 0 .639C20.577 16.49 16.64 19.5 12 19.5c-4.638 0-8.573-3.007-9.963-7.178z M15 12a3 3 0 11-6 0 3 3 0 016 0z"
1795
- }
1796
- )
1797
- },
1798
- {
1799
- title: "Be the first story",
1800
- body: `Building something with ${productLabel} worth talking about? Tell us \u2014 the best early stories become the case studies featured on this page.`,
1801
- icon: /* @__PURE__ */ jsxRuntime.jsx(
1802
- "path",
1803
- {
1804
- strokeLinecap: "round",
1805
- strokeLinejoin: "round",
1806
- strokeWidth: 1.5,
1807
- d: "M11.48 3.499a.562.562 0 011.04 0l2.125 5.111a.563.563 0 00.475.345l5.518.442c.499.04.701.663.321.988l-4.204 3.602a.563.563 0 00-.182.557l1.285 5.385a.562.562 0 01-.84.61l-4.725-2.885a.562.562 0 00-.586 0L6.982 20.54a.562.562 0 01-.84-.61l1.285-5.386a.562.562 0 00-.182-.557l-4.204-3.602a.562.562 0 01.321-.988l5.518-.442a.563.563 0 00.475-.345L11.48 3.5z"
1808
- }
1809
- )
1810
- }
1811
- ];
1812
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "cs-fade-up mx-auto max-w-4xl", children: [
1813
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative overflow-hidden rounded-3xl border bg-card px-6 py-14 text-center sm:px-12 sm:py-16", children: [
1814
- /* @__PURE__ */ jsxRuntime.jsx(
1815
- "div",
1816
- {
1817
- className: "pointer-events-none absolute -top-24 -right-16 h-64 w-64 rounded-full bg-primary/10 blur-3xl",
1818
- "aria-hidden": "true"
1819
- }
1820
- ),
1821
- /* @__PURE__ */ jsxRuntime.jsx(
1822
- "div",
1823
- {
1824
- className: "pointer-events-none absolute -bottom-24 -left-16 h-64 w-64 rounded-full bg-primary/5 blur-3xl",
1825
- "aria-hidden": "true"
1826
- }
1827
- ),
1828
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", children: [
1829
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "cs-float mx-auto flex h-20 w-20 items-center justify-center rounded-2xl bg-primary/10 text-primary ring-1 ring-primary/20", children: /* @__PURE__ */ jsxRuntime.jsx(
1830
- "svg",
1831
- {
1832
- className: "h-10 w-10",
1833
- fill: "none",
1834
- viewBox: "0 0 24 24",
1835
- stroke: "currentColor",
1836
- "aria-hidden": "true",
1837
- children: /* @__PURE__ */ jsxRuntime.jsx(
1838
- "path",
1839
- {
1840
- strokeLinecap: "round",
1841
- strokeLinejoin: "round",
1842
- strokeWidth: 1.5,
1843
- d: "M19.5 14.25v-2.625a3.375 3.375 0 00-3.375-3.375h-1.5A1.125 1.125 0 0113.5 7.125v-1.5a3.375 3.375 0 00-3.375-3.375H8.25m2.25 0H5.625c-.621 0-1.125.504-1.125 1.125v17.25c0 .621.504 1.125 1.125 1.125h12.75c.621 0 1.125-.504 1.125-1.125V11.25a9 9 0 00-9-9z"
1844
- }
1845
- )
1846
- }
1847
- ) }),
1848
- /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "mt-8 text-2xl font-bold tracking-tight text-foreground sm:text-3xl", children: title }),
1849
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mx-auto mt-4 max-w-2xl text-base leading-relaxed text-muted-foreground sm:text-lg", children: subtitle }),
1850
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-8 flex flex-col items-center justify-center gap-3 sm:flex-row", children: [
1851
- /* @__PURE__ */ jsxRuntime.jsxs(
1852
- "a",
1853
- {
1854
- href: contactHref,
1855
- className: "inline-flex w-full items-center justify-center gap-2 rounded-lg bg-primary px-6 py-3 text-sm font-semibold text-primary-foreground shadow-sm transition-all hover:opacity-90 hover:shadow-md sm:w-auto",
1856
- children: [
1857
- contactLabel,
1858
- /* @__PURE__ */ jsxRuntime.jsx(
1859
- "svg",
1860
- {
1861
- className: "h-4 w-4",
1862
- viewBox: "0 0 20 20",
1863
- fill: "currentColor",
1864
- "aria-hidden": "true",
1865
- children: /* @__PURE__ */ jsxRuntime.jsx(
1866
- "path",
1867
- {
1868
- fillRule: "evenodd",
1869
- d: "M7.293 4.293a1 1 0 011.414 0l5 5a1 1 0 010 1.414l-5 5a1 1 0 01-1.414-1.414L11.586 10 7.293 5.707a1 1 0 010-1.414z",
1870
- clipRule: "evenodd"
1871
- }
1872
- )
1873
- }
1874
- )
1875
- ]
1876
- }
1877
- ),
1878
- /* @__PURE__ */ jsxRuntime.jsx(
1879
- "a",
1880
- {
1881
- href: exploreHref,
1882
- className: "inline-flex w-full items-center justify-center gap-2 rounded-lg border border-border bg-background px-6 py-3 text-sm font-semibold text-foreground transition-colors hover:bg-muted sm:w-auto",
1883
- children: exploreLabel
1884
- }
1885
- )
1886
- ] })
1887
- ] })
1888
- ] }),
1889
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-8 grid gap-4 sm:grid-cols-3", children: highlights.map((h) => /* @__PURE__ */ jsxRuntime.jsxs(
1890
- "div",
1891
- {
1892
- className: "rounded-2xl border bg-card/60 p-5 text-left transition-colors hover:border-primary/30",
1893
- children: [
1894
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-10 w-10 items-center justify-center rounded-lg bg-primary/10 text-primary", children: /* @__PURE__ */ jsxRuntime.jsx(
1895
- "svg",
1896
- {
1897
- className: "h-5 w-5",
1898
- fill: "none",
1899
- viewBox: "0 0 24 24",
1900
- stroke: "currentColor",
1901
- "aria-hidden": "true",
1902
- children: h.icon
1903
- }
1904
- ) }),
1905
- /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "mt-4 text-sm font-semibold text-foreground", children: h.title }),
1906
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-1.5 text-sm leading-relaxed text-muted-foreground", children: h.body })
1907
- ]
1908
- },
1909
- h.title
1910
- )) })
1911
- ] });
1912
- }
1913
- function CaseStudiesListPage(props) {
1914
- const {
1915
- productName,
1916
- eyebrow = "Customer Stories",
1917
- heroTitle = "Case Studies",
1918
- heroSubtitle = "Real-world stories of teams building with our platform.",
1919
- caseStudyBasePath = "/case-studies",
1920
- className = "",
1921
- seoTitle,
1922
- seoDescription,
1923
- seoKeywords,
1924
- contactHref = "/contact",
1925
- contactLabel = "Get in touch",
1926
- exploreHref = "/features",
1927
- exploreLabel = "Explore the product",
1928
- emptyTitle,
1929
- emptySubtitle
1930
- } = props;
1931
- const client = useWebSDK();
1932
- const config = useWebSDKConfig();
1933
- const { product } = useProduct();
1934
- const resolvedProductId = product?.id || config.productId;
1935
- const productLabel = product?.name || productName || "our platform";
1936
- const [posts, setPosts] = React.useState([]);
1937
- const [total, setTotal] = React.useState(0);
1938
- const [loading, setLoading] = React.useState(true);
1939
- const [error, setError] = React.useState(null);
1940
- const [offset, setOffset] = React.useState(0);
1941
- const [activeCategory, setActiveCategory] = React.useState("All");
1942
- const limit = 12;
1943
- const fetchPosts = React.useCallback(async () => {
1944
- if (!resolvedProductId) return;
1945
- setLoading(true);
1946
- try {
1947
- const result = await client.query(CASE_STUDIES_QUERY, {
1948
- productId: resolvedProductId,
1949
- // Filter server-side: an unfiltered page of `limit` rows is mostly blog/legal pages, so
1950
- // real case studies past it were never listed and the page fell back to the empty state.
1951
- tag: CASE_STUDY_TAG,
1952
- limit,
1953
- offset
1954
- });
1955
- if (result.errors?.length) {
1956
- setError(result.errors[0]?.message ?? "Failed to load case studies");
1957
- setPosts([]);
1958
- return;
1959
- }
1960
- const items = result.data?.publicContentPages?.items || [];
1961
- const caseStudies = items.filter(
1962
- (p) => p.tags?.includes(CASE_STUDY_TAG) && hasCustomerSignOff(p)
1963
- );
1964
- setPosts(caseStudies);
1965
- const dropped = items.length - caseStudies.length;
1966
- setTotal(
1967
- Math.max(0, (result.data?.publicContentPages?.total || 0) - dropped)
1968
- );
1969
- setError(null);
1970
- } catch {
1971
- setError("load-failed");
1972
- setPosts([]);
1973
- } finally {
1974
- setLoading(false);
1975
- }
1976
- }, [client, resolvedProductId, offset]);
1977
- React.useEffect(() => {
1978
- fetchPosts();
1979
- }, [fetchPosts]);
1980
- const categories = React.useMemo(() => {
1981
- const set = /* @__PURE__ */ new Set();
1982
- posts.forEach((p) => {
1983
- if (p.category) set.add(p.category);
1984
- });
1985
- return ["All", ...Array.from(set)];
1986
- }, [posts]);
1987
- const visiblePosts = React.useMemo(() => {
1988
- if (activeCategory === "All") return posts;
1989
- return posts.filter((p) => p.category === activeCategory);
1990
- }, [posts, activeCategory]);
1991
- const featuredPosts = visiblePosts.filter((p) => p.featured);
1992
- const regularPosts = visiblePosts.filter((p) => !p.featured);
1993
- const hasContent = !loading && !error && posts.length > 0;
1994
- const showEmpty = !loading && (error !== null || posts.length === 0);
1995
- const resolvedEmptyTitle = emptyTitle ?? "Case studies are on the way";
1996
- const resolvedEmptySubtitle = emptySubtitle ?? `${productLabel} is early in its journey, and we'd rather show you real, measurable results than filler. We're working closely with our first teams now \u2014 their stories will be published here soon. In the meantime, we'd love to show you what's possible.`;
1997
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `w-full ${className}`, "data-cs": "case-studies-v2", children: [
1998
- /* @__PURE__ */ jsxRuntime.jsx(ProseStyles, {}),
1999
- /* @__PURE__ */ jsxRuntime.jsx(AnimationStyles, {}),
2000
- seoTitle && /* @__PURE__ */ jsxRuntime.jsx(
2001
- PageHead,
2002
- {
2003
- title: seoTitle,
2004
- description: seoDescription || heroSubtitle,
2005
- productName,
2006
- keywords: seoKeywords
2007
- }
2008
- ),
2009
- /* @__PURE__ */ jsxRuntime.jsxs("section", { className: "relative overflow-hidden", children: [
2010
- /* @__PURE__ */ jsxRuntime.jsx(
2011
- "div",
2012
- {
2013
- className: "pointer-events-none absolute inset-x-0 -top-32 mx-auto h-72 max-w-3xl rounded-full bg-primary/10 blur-3xl",
2014
- "aria-hidden": "true"
2015
- }
2016
- ),
2017
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative mx-auto max-w-7xl px-4 py-16 sm:px-6 sm:py-20 md:py-24 lg:px-8", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mx-auto max-w-3xl text-center", children: [
2018
- /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "inline-flex items-center gap-2 rounded-full border border-primary/20 bg-primary/10 px-3 py-1 text-xs font-semibold uppercase tracking-wide text-primary", children: [
2019
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "h-1.5 w-1.5 rounded-full bg-primary" }),
2020
- eyebrow
2021
- ] }),
2022
- /* @__PURE__ */ jsxRuntime.jsx("h1", { className: "mt-5 text-3xl font-bold tracking-tight text-foreground sm:text-4xl md:text-5xl", children: heroTitle }),
2023
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-4 text-lg text-muted-foreground", children: heroSubtitle })
2024
- ] }) })
2025
- ] }),
2026
- /* @__PURE__ */ jsxRuntime.jsx("section", { className: "pb-20 sm:pb-24", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mx-auto max-w-7xl px-4 sm:px-6 lg:px-8", children: loading ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid gap-6 sm:gap-8 md:grid-cols-2 lg:grid-cols-3", children: [1, 2, 3, 4, 5, 6].map((i) => /* @__PURE__ */ jsxRuntime.jsxs(
2027
- "div",
2028
- {
2029
- className: "overflow-hidden rounded-xl border bg-card animate-pulse",
2030
- children: [
2031
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "aspect-[16/9] bg-muted" }),
2032
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-3 p-6", children: [
2033
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-4 w-1/4 rounded bg-muted" }),
2034
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-6 w-3/4 rounded bg-muted" }),
2035
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-4 rounded bg-muted" }),
2036
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-4 w-1/2 rounded bg-muted" })
2037
- ] })
2038
- ]
2039
- },
2040
- i
2041
- )) }) : showEmpty ? /* @__PURE__ */ jsxRuntime.jsx(
2042
- EmptyState,
2043
- {
2044
- productLabel,
2045
- title: resolvedEmptyTitle,
2046
- subtitle: resolvedEmptySubtitle,
2047
- contactHref,
2048
- contactLabel,
2049
- exploreHref,
2050
- exploreLabel
2051
- }
2052
- ) : hasContent ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2053
- categories.length > 2 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mb-10 flex flex-wrap justify-center gap-2", children: categories.map((cat) => /* @__PURE__ */ jsxRuntime.jsx(
2054
- "button",
2055
- {
2056
- type: "button",
2057
- onClick: () => setActiveCategory(cat),
2058
- className: `rounded-full border px-4 py-1.5 text-sm font-medium transition-colors ${activeCategory === cat ? "border-primary bg-primary text-primary-foreground" : "border-border bg-background text-muted-foreground hover:border-primary/40 hover:text-foreground"}`,
2059
- children: cat
2060
- },
2061
- cat
2062
- )) }),
2063
- featuredPosts.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mb-10 grid gap-6 sm:gap-8 lg:grid-cols-2", children: featuredPosts.map((post) => /* @__PURE__ */ jsxRuntime.jsx(
2064
- FeaturedCard,
2065
- {
2066
- post,
2067
- basePath: caseStudyBasePath
2068
- },
2069
- post.id
2070
- )) }),
2071
- regularPosts.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid gap-6 sm:gap-8 md:grid-cols-2 lg:grid-cols-3", children: regularPosts.map((post) => /* @__PURE__ */ jsxRuntime.jsx(
2072
- CaseStudyCard,
2073
- {
2074
- post,
2075
- basePath: caseStudyBasePath
2076
- },
2077
- post.id
2078
- )) }),
2079
- activeCategory === "All" && total > limit && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-14 flex justify-center gap-4", children: [
2080
- /* @__PURE__ */ jsxRuntime.jsx(
2081
- "button",
2082
- {
2083
- onClick: () => setOffset(Math.max(0, offset - limit)),
2084
- disabled: offset === 0,
2085
- className: "rounded-lg border px-5 py-2.5 text-sm font-medium transition-colors hover:bg-muted disabled:opacity-40",
2086
- children: "Previous"
2087
- }
2088
- ),
2089
- /* @__PURE__ */ jsxRuntime.jsx(
2090
- "button",
2091
- {
2092
- onClick: () => setOffset(offset + limit),
2093
- disabled: offset + limit >= total,
2094
- className: "rounded-lg border px-5 py-2.5 text-sm font-medium transition-colors hover:bg-muted disabled:opacity-40",
2095
- children: "Next"
2096
- }
2097
- )
2098
- ] }),
2099
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-16 overflow-hidden rounded-2xl border bg-gradient-to-br from-primary/10 via-card to-card p-8 text-center sm:p-10", children: [
2100
- /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-xl font-bold text-foreground sm:text-2xl", children: "Want results like these?" }),
2101
- /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "mx-auto mt-2 max-w-xl text-sm text-muted-foreground sm:text-base", children: [
2102
- "Tell us about your goals and we\u2019ll show you exactly how",
2103
- " ",
2104
- productLabel,
2105
- " can help."
2106
- ] }),
2107
- /* @__PURE__ */ jsxRuntime.jsx(
2108
- "a",
2109
- {
2110
- href: contactHref,
2111
- className: "mt-6 inline-flex items-center justify-center gap-2 rounded-lg bg-primary px-6 py-3 text-sm font-semibold text-primary-foreground shadow-sm transition-all hover:opacity-90 hover:shadow-md",
2112
- children: contactLabel
2113
- }
2114
- )
2115
- ] })
2116
- ] }) : null }) })
2117
- ] });
2118
- }
2119
- function CaseStudyPage(props) {
2120
- const {
2121
- productName,
2122
- caseStudiesListUrl = "/case-studies",
2123
- backLabel = "Back to Case Studies",
2124
- contactHref = "/contact",
2125
- contactLabel = "Get in touch",
2126
- className = ""
2127
- } = props;
2128
- const client = useWebSDK();
2129
- const config = useWebSDKConfig();
2130
- const { product } = useProduct();
2131
- const resolvedProductId = product?.id || config.productId;
2132
- const productLabel = product?.name || productName || "our team";
2133
- const [post, setPost] = React.useState(null);
2134
- const [loading, setLoading] = React.useState(true);
2135
- const [error, setError] = React.useState(null);
2136
- const slug = typeof window !== "undefined" ? window.location.pathname.split("/").pop() || "" : "";
2137
- React.useEffect(() => {
2138
- if (!slug || !resolvedProductId) return;
2139
- async function fetchPost() {
2140
- setLoading(true);
2141
- try {
2142
- const result = await client.query(CASE_STUDY_QUERY, {
2143
- slug,
2144
- productId: resolvedProductId
2145
- });
2146
- if (result.errors?.length) {
2147
- setError(result.errors[0]?.message ?? "Failed to load case study");
2148
- return;
2149
- }
2150
- const page = result.data?.publicContentPage ?? null;
2151
- setPost(page && hasCustomerSignOff(page) ? page : null);
2152
- setError(null);
2153
- } catch {
2154
- setError("Failed to load case study");
2155
- } finally {
2156
- setLoading(false);
2157
- }
2158
- }
2159
- fetchPost();
2160
- }, [client, slug, resolvedProductId]);
2161
- if (loading) {
2162
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: `w-full ${className}`, children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mx-auto max-w-4xl px-4 py-24 sm:px-6 lg:px-8", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "animate-pulse space-y-6", children: [
2163
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-8 w-2/3 rounded bg-muted" }),
2164
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-4 w-1/3 rounded bg-muted" }),
2165
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "aspect-[16/9] rounded-xl bg-muted" }),
2166
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-3", children: [1, 2, 3, 4, 5].map((i) => /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-4 rounded bg-muted" }, i)) })
2167
- ] }) }) });
2168
- }
2169
- if (error || !post) {
2170
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: `w-full ${className}`, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mx-auto max-w-2xl px-4 py-24 text-center sm:px-6 lg:px-8", children: [
2171
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mx-auto flex h-16 w-16 items-center justify-center rounded-2xl bg-primary/10 text-primary", children: /* @__PURE__ */ jsxRuntime.jsx(
2172
- "svg",
2173
- {
2174
- className: "h-8 w-8",
2175
- fill: "none",
2176
- viewBox: "0 0 24 24",
2177
- stroke: "currentColor",
2178
- "aria-hidden": "true",
2179
- children: /* @__PURE__ */ jsxRuntime.jsx(
2180
- "path",
2181
- {
2182
- strokeLinecap: "round",
2183
- strokeLinejoin: "round",
2184
- strokeWidth: 1.5,
2185
- d: "M9.879 7.519c1.171-1.025 3.071-1.025 4.242 0 1.172 1.025 1.172 2.687 0 3.712-.203.179-.43.326-.67.442-.745.361-1.45.999-1.45 1.827v.75M21 12a9 9 0 11-18 0 9 9 0 0118 0zm-9 5.25h.008v.008H12v-.008z"
2186
- }
2187
- )
2188
- }
2189
- ) }),
2190
- /* @__PURE__ */ jsxRuntime.jsx("h1", { className: "mt-6 text-2xl font-bold text-foreground", children: "Case study not found" }),
2191
- /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "mx-auto mt-3 max-w-md text-muted-foreground", children: [
2192
- "This story isn\u2019t available yet \u2014 but there\u2019s plenty more to explore, and we\u2019re always happy to talk through what ",
2193
- productLabel,
2194
- " can do."
2195
- ] }),
2196
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-8 flex flex-col items-center justify-center gap-3 sm:flex-row", children: [
2197
- /* @__PURE__ */ jsxRuntime.jsxs(
2198
- "a",
2199
- {
2200
- href: caseStudiesListUrl,
2201
- className: "inline-flex items-center justify-center rounded-lg border px-5 py-2.5 text-sm font-semibold text-foreground transition-colors hover:bg-muted",
2202
- children: [
2203
- "\u2190 ",
2204
- backLabel
2205
- ]
2206
- }
2207
- ),
2208
- /* @__PURE__ */ jsxRuntime.jsx(
2209
- "a",
2210
- {
2211
- href: contactHref,
2212
- className: "inline-flex items-center justify-center rounded-lg bg-primary px-5 py-2.5 text-sm font-semibold text-primary-foreground transition-all hover:opacity-90",
2213
- children: contactLabel
2214
- }
2215
- )
2216
- ] })
2217
- ] }) });
2218
- }
2219
- const formattedDate = formatDate(post.publishedAt || post.lastUpdated);
2220
- const approvedDate = formatDate(post.customerSignOff?.signedAt);
2221
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `w-full ${className}`, children: [
2222
- /* @__PURE__ */ jsxRuntime.jsx(ProseStyles, {}),
2223
- /* @__PURE__ */ jsxRuntime.jsx(
2224
- PageHead,
2225
- {
2226
- title: post.seoTitle || post.title,
2227
- description: post.seoDescription || post.excerpt || "",
2228
- productName,
2229
- keywords: post.seoKeywords || ""
2230
- }
2231
- ),
2232
- /* @__PURE__ */ jsxRuntime.jsxs("article", { className: "mx-auto max-w-4xl px-4 py-16 sm:px-6 sm:py-20 lg:px-8", children: [
2233
- /* @__PURE__ */ jsxRuntime.jsxs(
2234
- "a",
2235
- {
2236
- href: caseStudiesListUrl,
2237
- className: "mb-8 inline-flex items-center gap-1 text-sm text-muted-foreground transition-colors hover:text-foreground",
2238
- children: [
2239
- "\u2190 ",
2240
- backLabel
2241
- ]
2242
- }
2243
- ),
2244
- post.category && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "mb-4 inline-block rounded-full bg-primary/10 px-2.5 py-1 text-xs font-medium text-primary", children: post.category }),
2245
- /* @__PURE__ */ jsxRuntime.jsx("h1", { className: "mb-4 text-3xl font-bold tracking-tight text-foreground sm:text-4xl md:text-5xl", children: post.title }),
2246
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mb-8 flex flex-wrap items-center gap-3 text-sm text-muted-foreground", children: [
2247
- post.author && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium text-foreground", children: post.author }),
2248
- post.author && formattedDate && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground/40", children: "\xB7" }),
2249
- formattedDate && /* @__PURE__ */ jsxRuntime.jsx("span", { children: formattedDate }),
2250
- post.readingTime ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2251
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground/40", children: "\xB7" }),
2252
- /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
2253
- post.readingTime,
2254
- " min read"
2255
- ] })
2256
- ] }) : null
2257
- ] }),
2258
- approvedDate && /* @__PURE__ */ jsxRuntime.jsxs(
2259
- "p",
2260
- {
2261
- className: "-mt-4 mb-8 text-xs text-muted-foreground",
2262
- "data-cs": "customer-signoff",
2263
- children: [
2264
- "Approved for publication by the customer on ",
2265
- approvedDate
2266
- ]
2267
- }
2268
- ),
2269
- post.thumbnail && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mb-10 aspect-[16/9] overflow-hidden rounded-xl", children: /* @__PURE__ */ jsxRuntime.jsx(
2270
- "img",
2271
- {
2272
- src: post.thumbnail,
2273
- alt: post.title,
2274
- className: "h-full w-full object-cover"
2275
- }
2276
- ) }),
2277
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "boff-prose max-w-none", children: /* @__PURE__ */ jsxRuntime.jsx(ContentRenderer, { content: post.content, format: post.contentFormat }) }),
2278
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-14 rounded-2xl border bg-gradient-to-br from-primary/10 via-card to-card p-8 text-center", children: [
2279
- /* @__PURE__ */ jsxRuntime.jsxs("h2", { className: "text-xl font-bold text-foreground", children: [
2280
- "Curious what ",
2281
- productLabel,
2282
- " could do for you?"
2283
- ] }),
2284
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mx-auto mt-2 max-w-lg text-sm text-muted-foreground", children: "We\u2019re happy to walk you through it \u2014 no pressure, just a conversation about your goals." }),
2285
- /* @__PURE__ */ jsxRuntime.jsx(
2286
- "a",
2287
- {
2288
- href: contactHref,
2289
- className: "mt-6 inline-flex items-center justify-center rounded-lg bg-primary px-6 py-3 text-sm font-semibold text-primary-foreground shadow-sm transition-all hover:opacity-90 hover:shadow-md",
2290
- children: contactLabel
2291
- }
2292
- )
2293
- ] })
2294
- ] })
2295
- ] });
2296
- }
2297
- function cn(...inputs) {
2298
- return tailwindMerge.twMerge(clsx.clsx(inputs));
2299
- }
2300
- var buttonVariants = classVarianceAuthority.cva(
2301
- "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
2302
- {
2303
- variants: {
2304
- variant: {
2305
- default: "bg-primary text-primary-foreground shadow hover:bg-primary/90",
2306
- destructive: "bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90",
2307
- outline: "border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground",
2308
- secondary: "bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80",
2309
- ghost: "hover:bg-accent hover:text-accent-foreground",
2310
- link: "text-primary underline-offset-4 hover:underline"
2311
- },
2312
- size: {
2313
- default: "h-9 px-4 py-2",
2314
- sm: "h-8 rounded-md px-3 text-xs",
2315
- lg: "h-10 rounded-md px-8",
2316
- icon: "h-9 w-9"
2317
- }
2318
- },
2319
- defaultVariants: {
2320
- variant: "default",
2321
- size: "default"
2322
- }
2323
- }
2324
- );
2325
- var Button = React__namespace.forwardRef(
2326
- ({ className, variant, size, asChild = false, ...props }, ref) => {
2327
- const Comp = asChild ? reactSlot.Slot : "button";
2328
- return /* @__PURE__ */ jsxRuntime.jsx(
2329
- Comp,
2330
- {
2331
- className: cn(buttonVariants({ variant, size, className })),
2332
- ref,
2333
- ...props
2334
- }
2335
- );
2336
- }
2337
- );
2338
- Button.displayName = "Button";
2339
- var Input = React__namespace.forwardRef(
2340
- ({ className, type, ...props }, ref) => {
2341
- return /* @__PURE__ */ jsxRuntime.jsx(
2342
- "input",
2343
- {
2344
- type,
2345
- className: cn(
2346
- "flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-base shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
2347
- className
2348
- ),
2349
- ref,
2350
- ...props
2351
- }
2352
- );
2353
- }
2354
- );
2355
- Input.displayName = "Input";
2356
- var CHANGELOG_TAG = "changelog";
2357
- var CHANGELOG_LIST_QUERY = `
2358
- query ChangelogList($productId: String!, $limit: Int, $offset: Int) {
2359
- publicContentPages(productId: $productId, limit: $limit, offset: $offset) {
2360
- items {
2361
- id
2362
- title
2363
- slug
2364
- lastUpdated
2365
- tags
2366
- excerpt
2367
- seoDescription
2368
- thumbnail
2369
- author
2370
- publishedAt
2371
- readingTime
2372
- featured
2373
- category
2374
- }
2375
- total
2376
- hasMore
2377
- }
2378
- }
2379
- `;
2380
- var CHANGELOG_ENTRY_QUERY = `
2381
- query ChangelogEntry($slug: String!, $productId: String) {
2382
- publicContentPage(slug: $slug, productId: $productId) {
2383
- content
2384
- contentFormat
2385
- }
2386
- }
2387
- `;
2388
- function formatDate2(value) {
2389
- if (!value) return "";
2390
- return new Date(value).toLocaleDateString("en-US", {
2391
- year: "numeric",
2392
- month: "long",
2393
- day: "numeric"
2394
- });
2395
- }
2396
- function sortByDateDesc(a, b) {
2397
- const da = new Date(a.publishedAt || a.lastUpdated || 0).getTime();
2398
- const db = new Date(b.publishedAt || b.lastUpdated || 0).getTime();
2399
- return db - da;
2400
- }
2401
- function normalize(value) {
2402
- return value.toLowerCase().trim();
2403
- }
2404
- function paginate(items, page, pageSize) {
2405
- const start = (page - 1) * pageSize;
2406
- return items.slice(start, start + pageSize);
2407
- }
2408
- function parseEntryDate(date) {
2409
- return new Date(date).getTime();
2410
- }
2411
- function useCmsChangelog() {
2412
- const client = useWebSDK();
2413
- const config = useWebSDKConfig();
2414
- const { product } = useProduct();
2415
- const resolvedProductId = product?.id || config.productId;
2416
- const [entries, setEntries] = React.useState([]);
2417
- const [loading, setLoading] = React.useState(true);
2418
- const [error, setError] = React.useState(null);
2419
- const fetchEntries = React.useCallback(async () => {
2420
- if (!resolvedProductId) {
2421
- setLoading(false);
2422
- return;
2423
- }
2424
- setLoading(true);
2425
- try {
2426
- const listResult = await client.query(CHANGELOG_LIST_QUERY, {
2427
- productId: resolvedProductId,
2428
- limit: 50,
2429
- offset: 0
2430
- });
2431
- if (listResult.errors?.length) {
2432
- setError(listResult.errors[0]?.message ?? "Failed to load changelog");
2433
- return;
2434
- }
2435
- const changelogItems = (listResult.data?.publicContentPages?.items || []).filter((p) => p.tags?.includes(CHANGELOG_TAG)).sort(sortByDateDesc);
2436
- const resolved = await Promise.all(
2437
- changelogItems.map(async (item) => {
2438
- try {
2439
- const detail = await client.query(CHANGELOG_ENTRY_QUERY, {
2440
- slug: item.slug,
2441
- productId: resolvedProductId
2442
- });
2443
- const page = detail.data?.publicContentPage;
2444
- return {
2445
- ...item,
2446
- content: page?.content,
2447
- contentFormat: page?.contentFormat
2448
- };
2449
- } catch {
2450
- return { ...item };
2451
- }
2452
- })
2453
- );
2454
- setEntries(resolved);
2455
- setError(null);
2456
- } catch {
2457
- setError("Failed to load changelog");
2458
- } finally {
2459
- setLoading(false);
2460
- }
2461
- }, [client, resolvedProductId]);
2462
- React.useEffect(() => {
2463
- fetchEntries();
2464
- }, [fetchEntries]);
2465
- return { entries, loading, error };
2466
- }
2467
- function CmsTimeline(props) {
2468
- const { entries, loading, error } = props;
2469
- if (loading) {
2470
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "max-w-3xl mx-auto space-y-12", children: [1, 2, 3].map((i) => /* @__PURE__ */ jsxRuntime.jsx(
2471
- "div",
2472
- {
2473
- className: "rounded-2xl border border-border/40 bg-card overflow-hidden animate-pulse",
2474
- children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-6 sm:p-8 space-y-4", children: [
2475
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-3 bg-muted rounded w-1/3" }),
2476
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-6 bg-muted rounded w-2/3" }),
2477
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-4 bg-muted rounded" }),
2478
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-4 bg-muted rounded w-5/6" })
2479
- ] })
2480
- },
2481
- i
2482
- )) });
2483
- }
2484
- if (error) {
2485
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-center py-12", children: /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-muted-foreground", children: error }) });
2486
- }
2487
- if (entries.length === 0) {
2488
- 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!" }) });
2489
- }
2490
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "max-w-3xl mx-auto space-y-12", children: entries.map((entry) => /* @__PURE__ */ jsxRuntime.jsxs(
2491
- "article",
2492
- {
2493
- id: entry.slug,
2494
- className: "rounded-2xl border border-border/40 bg-card overflow-hidden scroll-mt-24",
2495
- children: [
2496
- entry.thumbnail && /* @__PURE__ */ jsxRuntime.jsx(
2497
- "img",
2498
- {
2499
- src: entry.thumbnail,
2500
- alt: "",
2501
- width: 1024,
2502
- height: 576,
2503
- loading: "lazy",
2504
- decoding: "async",
2505
- className: "w-full aspect-video object-cover border-b border-border/40"
2506
- }
2507
- ),
2508
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-6 sm:p-8", children: [
2509
- /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-xs text-muted-foreground mb-2", children: [
2510
- entry.publishedAt && /* @__PURE__ */ jsxRuntime.jsx("time", { dateTime: entry.publishedAt, children: formatDate2(entry.publishedAt || entry.lastUpdated) }),
2511
- entry.author && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2512
- " \xB7 ",
2513
- entry.author
2514
- ] })
2515
- ] }),
2516
- /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-xl sm:text-2xl font-bold text-foreground mb-4", children: entry.title }),
2517
- entry.content ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "boff-prose boff-prose-sm max-w-none", children: /* @__PURE__ */ jsxRuntime.jsx(
2518
- ContentRenderer,
2519
- {
2520
- content: entry.content,
2521
- format: entry.contentFormat
2522
- }
2523
- ) }) : entry.excerpt && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground leading-relaxed", children: entry.excerpt })
2524
- ] })
2525
- ]
2526
- },
2527
- entry.id
2528
- )) });
2529
- }
2530
- function EntryContent({ content }) {
2531
- if (typeof content === "string") {
2532
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "boff-prose boff-prose-sm max-w-none", children: /* @__PURE__ */ jsxRuntime.jsx(ContentRenderer, { content, format: "markdown" }) });
2533
- }
2534
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "boff-prose boff-prose-sm max-w-none", children: content });
2535
- }
2536
- function EntriesTimeline(props) {
2537
- const { entries, pageSize, productName } = props;
2538
- const [query, setQuery] = React.useState("");
2539
- const [selectedCategory, setSelectedCategory] = React.useState(null);
2540
- const [page, setPage] = React.useState(1);
2541
- const categories = React.useMemo(
2542
- () => Array.from(new Set(entries.map((e) => e.category))).sort(),
2543
- [entries]
2544
- );
2545
- const filtered = React.useMemo(() => {
2546
- const normalized = normalize(query);
2547
- return entries.filter((entry) => {
2548
- if (selectedCategory && entry.category !== selectedCategory) {
2549
- return false;
2550
- }
2551
- if (!normalized) return true;
2552
- const haystack = [
2553
- entry.title,
2554
- entry.version,
2555
- entry.category,
2556
- ...entry.tags ?? [],
2557
- typeof entry.content === "string" ? entry.content : ""
2558
- ].join(" ").toLowerCase();
2559
- return haystack.includes(normalized);
2560
- }).sort((a, b) => parseEntryDate(b.date) - parseEntryDate(a.date));
2561
- }, [entries, query, selectedCategory]);
2562
- const totalPages = Math.max(1, Math.ceil(filtered.length / pageSize));
2563
- const safePage = Math.min(page, totalPages);
2564
- const pageItems = paginate(filtered, safePage, pageSize);
2565
- React.useEffect(() => {
2566
- setPage(1);
2567
- }, [query, selectedCategory]);
2568
- if (entries.length === 0) {
2569
- 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!" }) });
2570
- }
2571
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "max-w-3xl mx-auto", children: [
2572
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mb-8 space-y-4", children: [
2573
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", children: [
2574
- /* @__PURE__ */ jsxRuntime.jsx(
2575
- lucideReact.Search,
2576
- {
2577
- className: "absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground",
2578
- "aria-hidden": "true"
2579
- }
2580
- ),
2581
- /* @__PURE__ */ jsxRuntime.jsx(
2582
- Input,
2583
- {
2584
- type: "search",
2585
- placeholder: "Search releases, features, fixes\u2026",
2586
- value: query,
2587
- onChange: (e) => setQuery(e.target.value),
2588
- className: "pl-9",
2589
- "aria-label": "Search changelog"
2590
- }
2591
- )
2592
- ] }),
2593
- categories.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap gap-2", children: [
2594
- /* @__PURE__ */ jsxRuntime.jsx(
2595
- "button",
2596
- {
2597
- type: "button",
2598
- onClick: () => setSelectedCategory(null),
2599
- className: cn(
2600
- "px-3 py-1 rounded-full text-xs font-medium border transition-colors",
2601
- selectedCategory === null ? "bg-primary text-primary-foreground border-primary" : "bg-background text-muted-foreground border-border/60 hover:border-primary/40"
2602
- ),
2603
- children: "All"
2604
- }
2605
- ),
2606
- categories.map((category) => /* @__PURE__ */ jsxRuntime.jsx(
2607
- "button",
2608
- {
2609
- type: "button",
2610
- onClick: () => setSelectedCategory(category),
2611
- className: cn(
2612
- "px-3 py-1 rounded-full text-xs font-medium border transition-colors",
2613
- selectedCategory === category ? "bg-primary text-primary-foreground border-primary" : "bg-background text-muted-foreground border-border/60 hover:border-primary/40"
2614
- ),
2615
- children: category
2616
- },
2617
- category
2618
- ))
2619
- ] })
2620
- ] }),
2621
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", children: [
2622
- /* @__PURE__ */ jsxRuntime.jsx(
2623
- "div",
2624
- {
2625
- className: "absolute left-[1.125rem] top-3 bottom-3 w-px bg-border/60 hidden sm:block",
2626
- "aria-hidden": "true"
2627
- }
2628
- ),
2629
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-8", children: pageItems.map((entry) => /* @__PURE__ */ jsxRuntime.jsxs(
2630
- "article",
2631
- {
2632
- className: "relative sm:pl-12",
2633
- children: [
2634
- /* @__PURE__ */ jsxRuntime.jsx(
2635
- "div",
2636
- {
2637
- 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",
2638
- "aria-hidden": "true",
2639
- children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "h-2 w-2 rounded-full bg-primary-foreground" })
2640
- }
2641
- ),
2642
- /* @__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: [
2643
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap items-center gap-2 mb-3", children: [
2644
- /* @__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 }),
2645
- /* @__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: [
2646
- "v",
2647
- entry.version
2648
- ] }),
2649
- /* @__PURE__ */ jsxRuntime.jsx(
2650
- "time",
2651
- {
2652
- className: "text-xs text-muted-foreground",
2653
- dateTime: entry.date,
2654
- children: formatDate2(entry.date)
2655
- }
2656
- )
2657
- ] }),
2658
- 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(
2659
- "span",
2660
- {
2661
- className: "text-[10px] uppercase tracking-wide text-muted-foreground bg-background border border-border/40 px-1.5 py-0.5 rounded",
2662
- children: tag
2663
- },
2664
- tag
2665
- )) }),
2666
- /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-xl sm:text-2xl font-bold text-foreground mb-4", children: entry.title }),
2667
- /* @__PURE__ */ jsxRuntime.jsx(EntryContent, { content: entry.content })
2668
- ] }) })
2669
- ]
2670
- },
2671
- `${entry.version}-${entry.date}`
2672
- )) })
2673
- ] }),
2674
- 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." }) }),
2675
- totalPages > 1 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-10 flex items-center justify-between", children: [
2676
- /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-sm text-muted-foreground", children: [
2677
- "Page ",
2678
- safePage,
2679
- " of ",
2680
- totalPages,
2681
- " \xB7 ",
2682
- filtered.length,
2683
- " entries"
2684
- ] }),
2685
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
2686
- /* @__PURE__ */ jsxRuntime.jsx(
2687
- Button,
2688
- {
2689
- variant: "outline",
2690
- size: "sm",
2691
- onClick: () => setPage((p) => Math.max(1, p - 1)),
2692
- disabled: safePage <= 1,
2693
- "aria-label": "Previous page",
2694
- children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronLeft, { className: "h-4 w-4" })
2695
- }
2696
- ),
2697
- /* @__PURE__ */ jsxRuntime.jsx(
2698
- Button,
2699
- {
2700
- variant: "outline",
2701
- size: "sm",
2702
- onClick: () => setPage((p) => Math.min(totalPages, p + 1)),
2703
- disabled: safePage >= totalPages,
2704
- "aria-label": "Next page",
2705
- children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronRight, { className: "h-4 w-4" })
2706
- }
2707
- )
2708
- ] })
2709
- ] }),
2710
- productName && pageItems.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("script", { type: "application/ld+json", children: JSON.stringify({
2711
- "@context": "https://schema.org",
2712
- "@type": "ItemList",
2713
- name: `${productName} Changelog`,
2714
- itemListElement: pageItems.map((entry, index) => ({
2715
- "@type": "ListItem",
2716
- position: (safePage - 1) * pageSize + index + 1,
2717
- name: entry.title,
2718
- description: typeof entry.content === "string" ? entry.content.slice(0, 200) : void 0,
2719
- datePublished: entry.date
2720
- }))
2721
- }) })
2722
- ] });
2723
- }
2724
- function ChangelogPage(props) {
2725
- const {
2726
- productName,
2727
- heroEyebrow = "What's new",
2728
- heroTitle = "Changelog",
2729
- heroSubtitle = "Platform updates, improvements, and announcements \u2014 newest first.",
2730
- ctaTitle,
2731
- ctaSubtitle,
2732
- ctaHref,
2733
- ctaLabel,
2734
- className = "",
2735
- seoTitle,
2736
- seoDescription,
2737
- seoKeywords,
2738
- entries,
2739
- pageSize = 10
2740
- } = props;
2741
- const { product } = useProduct();
2742
- const resolvedProductName = productName || product?.name;
2743
- const cms = useCmsChangelog();
2744
- const showCta = Boolean(ctaHref && ctaLabel);
2745
- const isPropDriven = entries != null;
2746
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `w-full ${className}`, children: [
2747
- /* @__PURE__ */ jsxRuntime.jsx(ProseStyles, {}),
2748
- /* @__PURE__ */ jsxRuntime.jsx(
2749
- PageHead,
2750
- {
2751
- title: seoTitle || heroTitle,
2752
- description: seoDescription || heroSubtitle,
2753
- productName: resolvedProductName,
2754
- keywords: seoKeywords
2755
- }
2756
- ),
2757
- /* @__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: [
2758
- /* @__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: [
2759
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.History, { className: "h-3.5 w-3.5", "aria-hidden": "true" }),
2760
- heroEyebrow
2761
- ] }),
2762
- /* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-4xl sm:text-5xl font-bold text-foreground mb-4", children: heroTitle }),
2763
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-lg text-muted-foreground", children: heroSubtitle })
2764
- ] }) }),
2765
- /* @__PURE__ */ jsxRuntime.jsx("section", { className: "px-4 sm:px-6 lg:px-8 pb-20", children: isPropDriven ? /* @__PURE__ */ jsxRuntime.jsx(
2766
- EntriesTimeline,
2767
- {
2768
- entries,
2769
- pageSize,
2770
- productName: resolvedProductName
2771
- }
2772
- ) : /* @__PURE__ */ jsxRuntime.jsx(
2773
- CmsTimeline,
2774
- {
2775
- entries: cms.entries,
2776
- loading: cms.loading,
2777
- error: cms.error
2778
- }
2779
- ) }),
2780
- 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: [
2781
- ctaTitle && /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-2xl font-bold text-foreground mb-3", children: ctaTitle }),
2782
- ctaSubtitle && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-muted-foreground mb-7", children: ctaSubtitle }),
2783
- /* @__PURE__ */ jsxRuntime.jsx(
2784
- "a",
2785
- {
2786
- href: ctaHref,
2787
- 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",
2788
- children: ctaLabel
2789
- }
2790
- )
2791
- ] }) })
2792
- ] });
2793
- }
2794
-
2795
- // src/content/utils.ts
2796
- function formatBytes(bytes) {
2797
- if (bytes == null) return "";
2798
- if (bytes < 1024) return `${bytes} B`;
2799
- if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(0)} KB`;
2800
- return `${(bytes / 1024 / 1024).toFixed(1)} MB`;
2801
- }
2802
- function fileExt(file) {
2803
- const name = file.fileName || file.fileUrl.split("/").pop()?.split("?")[0] || "";
2804
- const ext = name.split(".").pop();
2805
- return ext && ext !== name ? ext.toUpperCase() : "FILE";
2806
- }
2807
- async function downloadFile(e, file) {
2808
- e.preventDefault();
2809
- const openInTab = () => window.open(file.fileUrl, "_blank", "noopener,noreferrer");
2810
- const name = file.fileName || file.fileUrl.split("/").pop()?.split("?")[0] || file.title || "download";
2811
- try {
2812
- const res = await fetch(file.fileUrl, { mode: "cors" });
2813
- if (!res.ok) {
2814
- openInTab();
2815
- return;
2816
- }
2817
- const blob = await res.blob();
2818
- const objUrl = URL.createObjectURL(blob);
2819
- const a = document.createElement("a");
2820
- a.href = objUrl;
2821
- a.download = name;
2822
- document.body.appendChild(a);
2823
- a.click();
2824
- a.remove();
2825
- URL.revokeObjectURL(objUrl);
2826
- } catch {
2827
- openInTab();
2828
- }
2829
- }
2830
- var PRESS_KIT_QUERY = `
2831
- query PublicPressKit($productId: String!, $limit: Int, $offset: Int) {
2832
- publicPressKitAssets(productId: $productId, limit: $limit, offset: $offset) {
2833
- items {
2834
- id
2835
- category
2836
- title
2837
- slug
2838
- description
2839
- fileUrl
2840
- fileName
2841
- fileSize
2842
- mimeType
2843
- thumbnailUrl
2844
- version
2845
- tags
2846
- sortOrder
2847
- updatedAt
2848
- }
2849
- total
2850
- hasMore
2851
- }
2852
- }
2853
- `;
2854
- var PRESS_RELEASES_QUERY = `
2855
- query PublicPressReleases($productId: String!, $limit: Int, $offset: Int) {
2856
- publicContentPages(productId: $productId, limit: $limit, offset: $offset) {
2857
- items {
2858
- id
2859
- title
2860
- slug
2861
- lastUpdated
2862
- tags
2863
- seoDescription
2864
- excerpt
2865
- thumbnail
2866
- author
2867
- publishedAt
2868
- readingTime
2869
- featured
2870
- category
2871
- }
2872
- total
2873
- hasMore
2874
- }
2875
- }
2876
- `;
2877
- var CATEGORY_ORDER = [
2878
- "LOGO",
2879
- "BANNER",
2880
- "SCREENSHOT",
2881
- "BRAND_GUIDE",
2882
- "BROCHURE",
2883
- "DOCUMENT",
2884
- "PRESS_RELEASE"
2885
- ];
2886
- var CATEGORY_LABELS = {
2887
- LOGO: "Logos",
2888
- BANNER: "Banners",
2889
- SCREENSHOT: "Screenshots",
2890
- BRAND_GUIDE: "Brand Guides",
2891
- BROCHURE: "Brochures",
2892
- DOCUMENT: "Documents",
2893
- PRESS_RELEASE: "Press Releases"
2894
- };
2895
- function isImageAsset(a) {
2896
- return (a.mimeType || "").startsWith("image/");
2897
- }
2898
- function PressKitLightbox({
2899
- asset,
2900
- onClose
2901
- }) {
2902
- React.useEffect(() => {
2903
- if (!asset) return;
2904
- const onKey = (e) => {
2905
- if (e.key === "Escape") onClose();
2906
- };
2907
- document.addEventListener("keydown", onKey);
2908
- const prevOverflow = document.body.style.overflow;
2909
- document.body.style.overflow = "hidden";
2910
- return () => {
2911
- document.removeEventListener("keydown", onKey);
2912
- document.body.style.overflow = prevOverflow;
2913
- };
2914
- }, [asset, onClose]);
2915
- if (!asset) return null;
2916
- const ext = (asset.mimeType || "file").split("/").pop()?.toUpperCase() || "FILE";
2917
- const size = formatBytes(asset.fileSize);
2918
- return /* @__PURE__ */ jsxRuntime.jsx(
2919
- "div",
2920
- {
2921
- role: "dialog",
2922
- "aria-modal": "true",
2923
- "aria-label": `${asset.title} preview`,
2924
- onClick: onClose,
2925
- className: "fixed inset-0 z-50 flex items-center justify-center bg-black/80 p-4 sm:p-8",
2926
- children: /* @__PURE__ */ jsxRuntime.jsxs(
2927
- "div",
2928
- {
2929
- onClick: (e) => e.stopPropagation(),
2930
- className: "relative flex max-h-full w-full max-w-4xl flex-col overflow-hidden rounded-xl border bg-card shadow-2xl",
2931
- children: [
2932
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start justify-between gap-4 border-b p-4", children: [
2933
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0", children: [
2934
- /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "truncate text-sm font-semibold text-foreground", children: asset.title }),
2935
- /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-xs text-muted-foreground", children: [
2936
- ext,
2937
- size ? ` \xB7 ${size}` : ""
2938
- ] })
2939
- ] }),
2940
- /* @__PURE__ */ jsxRuntime.jsx(
2941
- "button",
2942
- {
2943
- type: "button",
2944
- onClick: onClose,
2945
- "aria-label": "Close preview",
2946
- className: "shrink-0 rounded-md p-1.5 text-muted-foreground transition-colors hover:bg-muted hover:text-foreground",
2947
- children: /* @__PURE__ */ jsxRuntime.jsx(
2948
- "svg",
2949
- {
2950
- className: "h-5 w-5",
2951
- fill: "none",
2952
- viewBox: "0 0 24 24",
2953
- stroke: "currentColor",
2954
- strokeWidth: 2,
2955
- children: /* @__PURE__ */ jsxRuntime.jsx(
2956
- "path",
2957
- {
2958
- strokeLinecap: "round",
2959
- strokeLinejoin: "round",
2960
- d: "M6 18L18 6M6 6l12 12"
2961
- }
2962
- )
2963
- }
2964
- )
2965
- }
2966
- )
2967
- ] }),
2968
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-1 items-center justify-center overflow-auto bg-muted/20 p-4", children: /* @__PURE__ */ jsxRuntime.jsx(
2969
- "img",
2970
- {
2971
- src: asset.fileUrl,
2972
- alt: asset.title,
2973
- className: "max-h-[70vh] w-auto max-w-full object-contain"
2974
- }
2975
- ) }),
2976
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex justify-end border-t p-4", children: /* @__PURE__ */ jsxRuntime.jsxs(
2977
- "a",
2978
- {
2979
- href: asset.fileUrl,
2980
- target: "_blank",
2981
- rel: "noopener noreferrer",
2982
- download: true,
2983
- onClick: (e) => {
2984
- void downloadFile(e, asset);
2985
- },
2986
- 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",
2987
- children: [
2988
- /* @__PURE__ */ jsxRuntime.jsx(
2989
- "svg",
2990
- {
2991
- className: "h-4 w-4",
2992
- fill: "none",
2993
- viewBox: "0 0 24 24",
2994
- stroke: "currentColor",
2995
- strokeWidth: 2,
2996
- children: /* @__PURE__ */ jsxRuntime.jsx(
2997
- "path",
2998
- {
2999
- strokeLinecap: "round",
3000
- strokeLinejoin: "round",
3001
- d: "M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4"
3002
- }
3003
- )
3004
- }
3005
- ),
3006
- "Download"
3007
- ]
3008
- }
3009
- ) })
3010
- ]
3011
- }
3012
- )
3013
- }
3014
- );
3015
- }
3016
- function AssetCard({
3017
- asset,
3018
- onPreview
3019
- }) {
3020
- const date = asset.updatedAt ? new Date(asset.updatedAt).toLocaleDateString("en-US", {
3021
- year: "numeric",
3022
- month: "short",
3023
- day: "numeric"
3024
- }) : "";
3025
- const ext = (asset.mimeType || "file").split("/").pop()?.toUpperCase() || "FILE";
3026
- const size = formatBytes(asset.fileSize);
3027
- const [imgError, setImgError] = React.useState(false);
3028
- const thumb = asset.thumbnailUrl && !imgError ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "aspect-[16/9] overflow-hidden bg-muted/30", children: /* @__PURE__ */ jsxRuntime.jsx(
3029
- "img",
3030
- {
3031
- src: asset.thumbnailUrl,
3032
- alt: asset.title,
3033
- className: "w-full h-full object-contain group-hover:scale-105 transition-transform duration-300",
3034
- loading: "lazy",
3035
- onError: () => setImgError(true)
3036
- }
3037
- ) }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "aspect-[16/9] bg-muted/50 flex items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(
3038
- "svg",
3039
- {
3040
- className: "w-12 h-12 text-muted-foreground/20",
3041
- fill: "none",
3042
- viewBox: "0 0 24 24",
3043
- stroke: "currentColor",
3044
- children: /* @__PURE__ */ jsxRuntime.jsx(
3045
- "path",
3046
- {
3047
- strokeLinecap: "round",
3048
- strokeLinejoin: "round",
3049
- strokeWidth: 1.5,
3050
- 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"
3051
- }
3052
- )
3053
- }
3054
- ) });
3055
- 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: [
3056
- onPreview && isImageAsset(asset) ? /* @__PURE__ */ jsxRuntime.jsx(
3057
- "button",
3058
- {
3059
- type: "button",
3060
- onClick: () => onPreview(asset),
3061
- "aria-label": `Preview ${asset.title}`,
3062
- className: "block w-full text-left cursor-zoom-in",
3063
- children: thumb
3064
- }
3065
- ) : /* @__PURE__ */ jsxRuntime.jsx(
3066
- "a",
3067
- {
3068
- href: asset.fileUrl,
3069
- target: "_blank",
3070
- rel: "noopener noreferrer",
3071
- "aria-label": `Open ${asset.title}`,
3072
- className: "block",
3073
- children: thumb
3074
- }
3075
- ),
3076
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col flex-1 p-5", children: [
3077
- /* @__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 }),
3078
- /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-base font-semibold text-foreground mb-1 line-clamp-2", children: asset.title }),
3079
- asset.description && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground mb-3 line-clamp-2", children: asset.description }),
3080
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-auto flex items-center justify-between pt-3", children: [
3081
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 text-xs text-muted-foreground", children: [
3082
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium", children: ext }),
3083
- size && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
3084
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground/40", children: "\xB7" }),
3085
- /* @__PURE__ */ jsxRuntime.jsx("span", { children: size })
3086
- ] }),
3087
- date && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
3088
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground/40", children: "\xB7" }),
3089
- /* @__PURE__ */ jsxRuntime.jsx("span", { children: date })
3090
- ] })
3091
- ] }),
3092
- /* @__PURE__ */ jsxRuntime.jsxs(
3093
- "a",
3094
- {
3095
- href: asset.fileUrl,
3096
- target: "_blank",
3097
- rel: "noopener noreferrer",
3098
- download: true,
3099
- onClick: (e) => {
3100
- void downloadFile(e, asset);
3101
- },
3102
- className: "inline-flex items-center gap-1.5 text-sm font-medium text-primary hover:underline",
3103
- children: [
3104
- /* @__PURE__ */ jsxRuntime.jsx(
3105
- "svg",
3106
- {
3107
- className: "w-4 h-4",
3108
- fill: "none",
3109
- viewBox: "0 0 24 24",
3110
- stroke: "currentColor",
3111
- strokeWidth: 2,
3112
- children: /* @__PURE__ */ jsxRuntime.jsx(
3113
- "path",
3114
- {
3115
- strokeLinecap: "round",
3116
- strokeLinejoin: "round",
3117
- d: "M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4"
3118
- }
3119
- )
3120
- }
3121
- ),
3122
- "Download"
3123
- ]
3124
- }
3125
- )
3126
- ] })
3127
- ] })
3128
- ] });
3129
- }
3130
- function PressReleaseCard({
3131
- release,
3132
- basePath
3133
- }) {
3134
- const date = release.publishedAt || release.lastUpdated;
3135
- const formattedDate = date ? new Date(date).toLocaleDateString("en-US", {
3136
- year: "numeric",
3137
- month: "long",
3138
- day: "numeric"
3139
- }) : "";
3140
- const [imgError, setImgError] = React.useState(false);
3141
- return /* @__PURE__ */ jsxRuntime.jsxs(
3142
- "a",
3143
- {
3144
- href: `${basePath}/${release.slug}`,
3145
- className: "group flex flex-col bg-card border rounded-xl overflow-hidden hover:shadow-lg hover:border-primary/30 transition-all duration-300",
3146
- children: [
3147
- release.thumbnail && !imgError ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "aspect-[16/9] overflow-hidden bg-muted/30", children: /* @__PURE__ */ jsxRuntime.jsx(
3148
- "img",
3149
- {
3150
- src: release.thumbnail,
3151
- alt: release.title,
3152
- className: "w-full h-full object-cover group-hover:scale-105 transition-transform duration-300",
3153
- loading: "lazy",
3154
- onError: () => setImgError(true)
3155
- }
3156
- ) }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "aspect-[16/9] bg-muted/50 flex items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(
3157
- "svg",
3158
- {
3159
- className: "w-12 h-12 text-muted-foreground/20",
3160
- fill: "none",
3161
- viewBox: "0 0 24 24",
3162
- stroke: "currentColor",
3163
- children: /* @__PURE__ */ jsxRuntime.jsx(
3164
- "path",
3165
- {
3166
- strokeLinecap: "round",
3167
- strokeLinejoin: "round",
3168
- strokeWidth: 1.5,
3169
- 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"
3170
- }
3171
- )
3172
- }
3173
- ) }),
3174
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col flex-1 p-5 sm:p-6", children: [
3175
- 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 }),
3176
- /* @__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 }),
3177
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground mb-4 line-clamp-3", children: release.excerpt || release.seoDescription || "" }),
3178
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-auto flex items-center gap-3 text-xs text-muted-foreground", children: [
3179
- release.author && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium", children: release.author }),
3180
- release.author && formattedDate && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground/40", children: "\xB7" }),
3181
- formattedDate && /* @__PURE__ */ jsxRuntime.jsx("span", { children: formattedDate }),
3182
- release.readingTime ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
3183
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground/40", children: "\xB7" }),
3184
- /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
3185
- release.readingTime,
3186
- " min read"
3187
- ] })
3188
- ] }) : null
3189
- ] })
3190
- ] })
3191
- ]
3192
- }
3193
- );
3194
- }
3195
- function PressKitPage(props) {
3196
- const {
3197
- productName,
3198
- heroTitle = "Press & Media Kit",
3199
- heroSubtitle = "Official logos, brand assets, and media resources. Everything you need to write about us.",
3200
- mediaContactEmail,
3201
- brandKitUrl,
3202
- className = "",
3203
- seoTitle,
3204
- seoDescription,
3205
- seoKeywords,
3206
- showPressReleases = true,
3207
- pressReleaseBasePath = "/press",
3208
- pressReleasesTitle = "Press Releases"
3209
- } = props;
3210
- const client = useWebSDK();
3211
- const config = useWebSDKConfig();
3212
- const { product } = useProduct();
3213
- const resolvedProductId = product?.id || config.productId;
3214
- const contactEmail = mediaContactEmail || product?.contactEmail || void 0;
3215
- const [assets, setAssets] = React.useState([]);
3216
- const [loading, setLoading] = React.useState(true);
3217
- const [error, setError] = React.useState(null);
3218
- const [activeCategory, setActiveCategory] = React.useState("ALL");
3219
- const [releases, setReleases] = React.useState([]);
3220
- const [releasesLoading, setReleasesLoading] = React.useState(true);
3221
- const [preview, setPreview] = React.useState(null);
3222
- const fetchAssets = React.useCallback(async () => {
3223
- if (!resolvedProductId) {
3224
- setLoading(false);
3225
- return;
3226
- }
3227
- setLoading(true);
3228
- try {
3229
- const PAGE_SIZE = 100;
3230
- const MAX_PAGES = 10;
3231
- const all = [];
3232
- let offset = 0;
3233
- for (let pageNum = 0; pageNum < MAX_PAGES; pageNum++) {
3234
- const result = await client.query(PRESS_KIT_QUERY, {
3235
- productId: resolvedProductId,
3236
- limit: PAGE_SIZE,
3237
- offset
3238
- });
3239
- if (result.errors?.length) {
3240
- setError(result.errors[0]?.message ?? "Failed to load press kit");
3241
- return;
3242
- }
3243
- const page = result.data?.publicPressKitAssets;
3244
- const items = page?.items || [];
3245
- all.push(...items);
3246
- if (!page?.hasMore || items.length === 0) break;
3247
- offset += PAGE_SIZE;
3248
- }
3249
- setAssets(all);
3250
- setError(null);
3251
- } catch {
3252
- setError("Failed to load press kit");
3253
- } finally {
3254
- setLoading(false);
3255
- }
3256
- }, [client, resolvedProductId]);
3257
- React.useEffect(() => {
3258
- fetchAssets();
3259
- }, [fetchAssets]);
3260
- const fetchReleases = React.useCallback(async () => {
3261
- if (!resolvedProductId || !showPressReleases) {
3262
- setReleasesLoading(false);
3263
- return;
3264
- }
3265
- setReleasesLoading(true);
3266
- try {
3267
- const PAGE_SIZE = 100;
3268
- const MAX_PAGES = 10;
3269
- const all = [];
3270
- let offset = 0;
3271
- for (let pageNum = 0; pageNum < MAX_PAGES; pageNum++) {
3272
- const result = await client.query(PRESS_RELEASES_QUERY, {
3273
- productId: resolvedProductId,
3274
- limit: PAGE_SIZE,
3275
- offset
3276
- });
3277
- if (result.errors?.length) {
3278
- setReleases([]);
3279
- return;
3280
- }
3281
- const page = result.data?.publicContentPages;
3282
- const items = page?.items || [];
3283
- all.push(...items);
3284
- if (!page?.hasMore || items.length === 0) break;
3285
- offset += PAGE_SIZE;
3286
- }
3287
- const pressOnly = all.filter((p) => p.tags?.includes("press")).sort((a, b) => {
3288
- const da = new Date(a.publishedAt || a.lastUpdated || 0).getTime();
3289
- const db = new Date(b.publishedAt || b.lastUpdated || 0).getTime();
3290
- return db - da;
3291
- });
3292
- setReleases(pressOnly);
3293
- } catch {
3294
- setReleases([]);
3295
- } finally {
3296
- setReleasesLoading(false);
3297
- }
3298
- }, [client, resolvedProductId, showPressReleases]);
3299
- React.useEffect(() => {
3300
- fetchReleases();
3301
- }, [fetchReleases]);
3302
- const presentCategories = CATEGORY_ORDER.filter(
3303
- (c) => assets.some((a) => a.category === c)
3304
- );
3305
- const visibleAssets = activeCategory === "ALL" ? assets : assets.filter((a) => a.category === activeCategory);
3306
- const grouped = (activeCategory === "ALL" ? presentCategories : [activeCategory]).map((cat) => ({
3307
- cat,
3308
- items: visibleAssets.filter((a) => a.category === cat).sort((a, b) => a.sortOrder - b.sortOrder)
3309
- })).filter((g) => g.items.length > 0);
3310
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `w-full ${className}`, children: [
3311
- seoTitle && /* @__PURE__ */ jsxRuntime.jsx(
3312
- PageHead,
3313
- {
3314
- title: seoTitle,
3315
- description: seoDescription || heroSubtitle,
3316
- productName,
3317
- keywords: seoKeywords
3318
- }
3319
- ),
3320
- /* @__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: [
3321
- /* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-3xl sm:text-4xl md:text-5xl font-bold tracking-tight text-foreground", children: heroTitle }),
3322
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-4 text-lg text-muted-foreground", children: heroSubtitle }),
3323
- (brandKitUrl || contactEmail) && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-8 flex flex-wrap items-center justify-center gap-4", children: [
3324
- brandKitUrl && /* @__PURE__ */ jsxRuntime.jsx(
3325
- "a",
3326
- {
3327
- href: brandKitUrl,
3328
- target: "_blank",
3329
- rel: "noopener noreferrer",
3330
- 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",
3331
- children: "Download brand kit"
3332
- }
3333
- ),
3334
- contactEmail && /* @__PURE__ */ jsxRuntime.jsx(
3335
- "a",
3336
- {
3337
- href: `mailto:${contactEmail}`,
3338
- className: "inline-flex items-center gap-2 px-6 py-3 rounded-lg border font-medium text-foreground hover:bg-muted transition-colors",
3339
- children: "Media inquiries"
3340
- }
3341
- )
3342
- ] })
3343
- ] }) }) }),
3344
- 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: [
3345
- /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-xl font-semibold text-foreground mb-6", children: pressReleasesTitle }),
3346
- /* @__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(
3347
- "div",
3348
- {
3349
- className: "bg-card border rounded-xl overflow-hidden animate-pulse",
3350
- children: [
3351
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "aspect-[16/9] bg-muted" }),
3352
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-5 space-y-3", children: [
3353
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-4 bg-muted rounded w-1/4" }),
3354
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-5 bg-muted rounded w-3/4" }),
3355
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-4 bg-muted rounded w-1/2" })
3356
- ] })
3357
- ]
3358
- },
3359
- i
3360
- )) : releases.map((r) => /* @__PURE__ */ jsxRuntime.jsx(
3361
- PressReleaseCard,
3362
- {
3363
- release: r,
3364
- basePath: pressReleaseBasePath
3365
- },
3366
- r.id
3367
- )) })
3368
- ] }) }),
3369
- /* @__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(
3370
- "div",
3371
- {
3372
- className: "bg-card border rounded-xl overflow-hidden animate-pulse",
3373
- children: [
3374
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "aspect-[16/9] bg-muted" }),
3375
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-5 space-y-3", children: [
3376
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-4 bg-muted rounded w-1/4" }),
3377
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-5 bg-muted rounded w-3/4" }),
3378
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-4 bg-muted rounded w-1/2" })
3379
- ] })
3380
- ]
3381
- },
3382
- i
3383
- )) }) : 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: [
3384
- "Press materials are coming soon. For media inquiries",
3385
- contactEmail ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
3386
- ", contact ",
3387
- /* @__PURE__ */ jsxRuntime.jsx(
3388
- "a",
3389
- {
3390
- href: `mailto:${contactEmail}`,
3391
- className: "text-primary underline underline-offset-4 hover:no-underline",
3392
- children: contactEmail
3393
- }
3394
- )
3395
- ] }) : " please reach out to our team",
3396
- "."
3397
- ] }) }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
3398
- presentCategories.length > 1 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-wrap gap-2 mb-10 justify-center", children: ["ALL", ...presentCategories].map((c) => {
3399
- const active = activeCategory === c;
3400
- return /* @__PURE__ */ jsxRuntime.jsx(
3401
- "button",
3402
- {
3403
- type: "button",
3404
- onClick: () => setActiveCategory(c),
3405
- 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"}`,
3406
- children: c === "ALL" ? "All" : CATEGORY_LABELS[c] ?? c
3407
- },
3408
- c
3409
- );
3410
- }) }),
3411
- grouped.map((group) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mb-12", children: [
3412
- /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-xl font-semibold text-foreground mb-5", children: CATEGORY_LABELS[group.cat] ?? group.cat }),
3413
- /* @__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)) })
3414
- ] }, group.cat)),
3415
- contactEmail && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-8 border-t pt-10 text-center", children: [
3416
- /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-lg font-semibold text-foreground mb-2", children: "Media Contact" }),
3417
- /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-muted-foreground", children: [
3418
- "For interviews, partnerships, and press inquiries:",
3419
- " ",
3420
- /* @__PURE__ */ jsxRuntime.jsx(
3421
- "a",
3422
- {
3423
- href: `mailto:${contactEmail}`,
3424
- className: "text-primary underline underline-offset-4 hover:no-underline",
3425
- children: contactEmail
3426
- }
3427
- )
3428
- ] })
3429
- ] })
3430
- ] }) }) }),
3431
- /* @__PURE__ */ jsxRuntime.jsx(PressKitLightbox, { asset: preview, onClose: () => setPreview(null) })
3432
- ] });
3433
- }
3434
- var CONTRACTS_QUERY = `
3435
- query PublicContracts($productId: String!, $limit: Int, $offset: Int) {
3436
- publicPressKitAssets(productId: $productId, category: CONTRACT, limit: $limit, offset: $offset) {
3437
- items {
3438
- id
3439
- title
3440
- slug
3441
- description
3442
- fileUrl
3443
- fileName
3444
- fileSize
3445
- mimeType
3446
- version
3447
- tags
3448
- sortOrder
3449
- updatedAt
3450
- }
3451
- hasMore
3452
- }
3453
- }
3454
- `;
3455
- function ContractsPage(props) {
3456
- const {
3457
- productName,
3458
- heroTitle = "Contracts & Legal Agreements",
3459
- heroSubtitle = "Download our standard legal agreements and policies.",
3460
- className = "",
3461
- seoTitle,
3462
- seoDescription,
3463
- seoKeywords
3464
- } = props;
3465
- const client = useWebSDK();
3466
- const config = useWebSDKConfig();
3467
- const { product } = useProduct();
3468
- const resolvedProductId = product?.id || config.productId;
3469
- const displayName = productName ?? product?.name ?? config.productId;
3470
- const [contracts, setContracts] = React.useState([]);
3471
- const [loading, setLoading] = React.useState(true);
3472
- const [error, setError] = React.useState(null);
3473
- const fetchContracts = React.useCallback(async () => {
3474
- if (!resolvedProductId) {
3475
- setLoading(false);
3476
- return;
3477
- }
3478
- setLoading(true);
3479
- try {
3480
- const PAGE_SIZE = 100;
3481
- const MAX_PAGES = 10;
3482
- const all = [];
3483
- let offset = 0;
3484
- for (let pageNum = 0; pageNum < MAX_PAGES; pageNum++) {
3485
- const result = await client.query(CONTRACTS_QUERY, {
3486
- productId: resolvedProductId,
3487
- limit: PAGE_SIZE,
3488
- offset
3489
- });
3490
- if (result.errors?.length) {
3491
- setError(result.errors[0]?.message ?? "Failed to load contracts");
3492
- return;
3493
- }
3494
- const page = result.data?.publicPressKitAssets;
3495
- const items = page?.items || [];
3496
- all.push(...items);
3497
- if (!page?.hasMore || items.length === 0) break;
3498
- offset += PAGE_SIZE;
3499
- }
3500
- all.sort(
3501
- (a, b) => a.sortOrder - b.sortOrder || a.title.localeCompare(b.title)
3502
- );
3503
- setContracts(all);
3504
- setError(null);
3505
- } catch {
3506
- setError("Failed to load contracts");
3507
- } finally {
3508
- setLoading(false);
3509
- }
3510
- }, [client, resolvedProductId]);
3511
- React.useEffect(() => {
3512
- fetchContracts();
3513
- }, [fetchContracts]);
3514
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `min-h-screen bg-background ${className}`, children: [
3515
- /* @__PURE__ */ jsxRuntime.jsx(
3516
- PageHead,
3517
- {
3518
- title: seoTitle || `Contracts \u2014 ${displayName}`,
3519
- description: seoDescription || heroSubtitle,
3520
- productName,
3521
- keywords: seoKeywords
3522
- }
3523
- ),
3524
- /* @__PURE__ */ jsxRuntime.jsx("section", { className: "border-b border-border bg-muted/30", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "max-w-5xl mx-auto px-4 sm:px-6 lg:px-8 py-16 text-center", children: [
3525
- /* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-4xl md:text-5xl font-bold text-foreground mb-4", children: heroTitle }),
3526
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-lg text-muted-foreground max-w-2xl mx-auto", children: heroSubtitle })
3527
- ] }) }),
3528
- /* @__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) => {
3529
- const ext = fileExt(c);
3530
- const size = formatBytes(c.fileSize);
3531
- return /* @__PURE__ */ jsxRuntime.jsxs(
3532
- "li",
3533
- {
3534
- className: "flex items-center justify-between gap-4 p-5 hover:bg-muted/40 transition-colors",
3535
- children: [
3536
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0", children: [
3537
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 flex-wrap", children: [
3538
- /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-base font-semibold text-foreground truncate", children: c.title }),
3539
- 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 })
3540
- ] }),
3541
- c.description && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground mt-1 line-clamp-2", children: c.description }),
3542
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-1 flex items-center gap-2 text-xs text-muted-foreground", children: [
3543
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium", children: ext }),
3544
- size && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
3545
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground/40", children: "\xB7" }),
3546
- /* @__PURE__ */ jsxRuntime.jsx("span", { children: size })
3547
- ] })
3548
- ] })
3549
- ] }),
3550
- /* @__PURE__ */ jsxRuntime.jsxs(
3551
- "a",
3552
- {
3553
- href: c.fileUrl,
3554
- target: "_blank",
3555
- rel: "noopener noreferrer",
3556
- download: true,
3557
- "aria-label": `Download ${c.title}`,
3558
- onClick: (e) => {
3559
- void downloadFile(e, c);
3560
- },
3561
- 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",
3562
- children: [
3563
- /* @__PURE__ */ jsxRuntime.jsx(
3564
- "svg",
3565
- {
3566
- className: "w-4 h-4",
3567
- fill: "none",
3568
- viewBox: "0 0 24 24",
3569
- stroke: "currentColor",
3570
- "aria-hidden": "true",
3571
- children: /* @__PURE__ */ jsxRuntime.jsx(
3572
- "path",
3573
- {
3574
- strokeLinecap: "round",
3575
- strokeLinejoin: "round",
3576
- strokeWidth: 2,
3577
- d: "M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4"
3578
- }
3579
- )
3580
- }
3581
- ),
3582
- "Download"
3583
- ]
3584
- }
3585
- )
3586
- ]
3587
- },
3588
- c.id
3589
- );
3590
- }) }) })
3591
- ] });
3592
- }
3593
-
3594
- exports.BlogListPage = BlogListPage;
3595
- exports.BlogPostPage = BlogPostPage;
3596
- exports.CaseStudiesListPage = CaseStudiesListPage;
3597
- exports.CaseStudyPage = CaseStudyPage;
3598
- exports.ChangelogPage = ChangelogPage;
3599
- exports.ContentRenderer = ContentRenderer;
3600
- exports.ContractsPage = ContractsPage;
3601
- exports.LEGAL_DOCUMENTS = LEGAL_DOCUMENTS;
3602
- exports.LEGAL_DOCUMENT_KEYS = LEGAL_DOCUMENT_KEYS;
3603
- exports.LegalPage = LegalPage;
3604
- exports.Markdown = Markdown;
3605
- exports.PressKitPage = PressKitPage;
3606
- exports.fetchLegalContent = fetchLegalContent;
3607
- exports.fetchLegalContentPage = fetchLegalContentPage;
3608
- exports.fetchPublicJobPosting = fetchPublicJobPosting;
3609
- exports.fetchPublicJobPostings = fetchPublicJobPostings;
3610
- exports.fetchPublicJobPostingsForSubmission = fetchPublicJobPostingsForSubmission;
3611
- exports.findLegalDocument = findLegalDocument;
3612
- exports.formatLegalDate = formatLegalDate;
3613
- exports.hasCustomerSignOff = hasCustomerSignOff;
3614
- exports.splitLeadingHeading = splitLeadingHeading;
3615
- exports.useContentPage = useContentPage;
3616
- exports.useContentPages = useContentPages;
3617
- exports.usePublicJobPosting = usePublicJobPosting;
3618
- exports.usePublicJobPostings = usePublicJobPostings;
3619
- exports.usePublicJobPostingsForSubmission = usePublicJobPostingsForSubmission;
3620
122
  //# sourceMappingURL=index.js.map
3621
123
  //# sourceMappingURL=index.js.map