@burdenoff/website-sdk 0.0.0-dev
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +24 -0
- package/README.md +357 -0
- package/dist/analytics/index.d.mts +27 -0
- package/dist/analytics/index.d.ts +27 -0
- package/dist/analytics/index.js +36 -0
- package/dist/analytics/index.js.map +1 -0
- package/dist/analytics/index.mjs +34 -0
- package/dist/analytics/index.mjs.map +1 -0
- package/dist/client/index.d.mts +29 -0
- package/dist/client/index.d.ts +29 -0
- package/dist/client/index.js +261 -0
- package/dist/client/index.js.map +1 -0
- package/dist/client/index.mjs +255 -0
- package/dist/client/index.mjs.map +1 -0
- package/dist/components/contact.d.mts +144 -0
- package/dist/components/contact.d.ts +144 -0
- package/dist/components/contact.js +806 -0
- package/dist/components/contact.js.map +1 -0
- package/dist/components/contact.mjs +780 -0
- package/dist/components/contact.mjs.map +1 -0
- package/dist/components/electron-download.d.mts +32 -0
- package/dist/components/electron-download.d.ts +32 -0
- package/dist/components/electron-download.js +131 -0
- package/dist/components/electron-download.js.map +1 -0
- package/dist/components/electron-download.mjs +108 -0
- package/dist/components/electron-download.mjs.map +1 -0
- package/dist/components/launch-countdown.d.mts +15 -0
- package/dist/components/launch-countdown.d.ts +15 -0
- package/dist/components/launch-countdown.js +89 -0
- package/dist/components/launch-countdown.js.map +1 -0
- package/dist/components/launch-countdown.mjs +87 -0
- package/dist/components/launch-countdown.mjs.map +1 -0
- package/dist/components/newsletter.d.mts +155 -0
- package/dist/components/newsletter.d.ts +155 -0
- package/dist/components/newsletter.js +880 -0
- package/dist/components/newsletter.js.map +1 -0
- package/dist/components/newsletter.mjs +854 -0
- package/dist/components/newsletter.mjs.map +1 -0
- package/dist/components/partners.d.mts +88 -0
- package/dist/components/partners.d.ts +88 -0
- package/dist/components/partners.js +1169 -0
- package/dist/components/partners.js.map +1 -0
- package/dist/components/partners.mjs +1143 -0
- package/dist/components/partners.mjs.map +1 -0
- package/dist/components/pricing.d.mts +194 -0
- package/dist/components/pricing.d.ts +194 -0
- package/dist/components/pricing.js +813 -0
- package/dist/components/pricing.js.map +1 -0
- package/dist/components/pricing.mjs +791 -0
- package/dist/components/pricing.mjs.map +1 -0
- package/dist/components/problems-solved.d.mts +37 -0
- package/dist/components/problems-solved.d.ts +37 -0
- package/dist/components/problems-solved.js +157 -0
- package/dist/components/problems-solved.js.map +1 -0
- package/dist/components/problems-solved.mjs +135 -0
- package/dist/components/problems-solved.mjs.map +1 -0
- package/dist/components/resource-links.d.mts +28 -0
- package/dist/components/resource-links.d.ts +28 -0
- package/dist/components/resource-links.js +229 -0
- package/dist/components/resource-links.js.map +1 -0
- package/dist/components/resource-links.mjs +227 -0
- package/dist/components/resource-links.mjs.map +1 -0
- package/dist/components/social-links.d.mts +37 -0
- package/dist/components/social-links.d.ts +37 -0
- package/dist/components/social-links.js +268 -0
- package/dist/components/social-links.js.map +1 -0
- package/dist/components/social-links.mjs +266 -0
- package/dist/components/social-links.mjs.map +1 -0
- package/dist/components/unsubscribe.d.mts +14 -0
- package/dist/components/unsubscribe.d.ts +14 -0
- package/dist/components/unsubscribe.js +538 -0
- package/dist/components/unsubscribe.js.map +1 -0
- package/dist/components/unsubscribe.mjs +517 -0
- package/dist/components/unsubscribe.mjs.map +1 -0
- package/dist/components/waitlist.d.mts +70 -0
- package/dist/components/waitlist.d.ts +70 -0
- package/dist/components/waitlist.js +864 -0
- package/dist/components/waitlist.js.map +1 -0
- package/dist/components/waitlist.mjs +841 -0
- package/dist/components/waitlist.mjs.map +1 -0
- package/dist/components/website-switcher.d.mts +27 -0
- package/dist/components/website-switcher.d.ts +27 -0
- package/dist/components/website-switcher.js +168 -0
- package/dist/components/website-switcher.js.map +1 -0
- package/dist/components/website-switcher.mjs +146 -0
- package/dist/components/website-switcher.mjs.map +1 -0
- package/dist/content/index.d.mts +247 -0
- package/dist/content/index.d.ts +247 -0
- package/dist/content/index.js +2458 -0
- package/dist/content/index.js.map +1 -0
- package/dist/content/index.mjs +2428 -0
- package/dist/content/index.mjs.map +1 -0
- package/dist/index.d.mts +72 -0
- package/dist/index.d.ts +72 -0
- package/dist/index.js +6273 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +6204 -0
- package/dist/index.mjs.map +1 -0
- package/dist/product/index.d.mts +94 -0
- package/dist/product/index.d.ts +94 -0
- package/dist/product/index.js +282 -0
- package/dist/product/index.js.map +1 -0
- package/dist/product/index.mjs +278 -0
- package/dist/product/index.mjs.map +1 -0
- package/dist/provider-D7Rij9UM.d.mts +86 -0
- package/dist/provider-D7Rij9UM.d.ts +86 -0
- package/dist/seo/index.d.mts +64 -0
- package/dist/seo/index.d.ts +64 -0
- package/dist/seo/index.js +61 -0
- package/dist/seo/index.js.map +1 -0
- package/dist/seo/index.mjs +59 -0
- package/dist/seo/index.mjs.map +1 -0
- package/dist/utils/index.d.mts +21 -0
- package/dist/utils/index.d.ts +21 -0
- package/dist/utils/index.js +12 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/index.mjs +10 -0
- package/dist/utils/index.mjs.map +1 -0
- package/package.json +168 -0
|
@@ -0,0 +1,2428 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { createContext, useState, useCallback, useEffect, useContext, useMemo } from 'react';
|
|
3
|
+
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
4
|
+
import { Helmet } from 'react-helmet-async';
|
|
5
|
+
import { History, Search, ChevronLeft, ChevronRight } from 'lucide-react';
|
|
6
|
+
import { Slot } from '@radix-ui/react-slot';
|
|
7
|
+
import { cva } from 'class-variance-authority';
|
|
8
|
+
import { clsx } from 'clsx';
|
|
9
|
+
import { twMerge } from 'tailwind-merge';
|
|
10
|
+
|
|
11
|
+
var __defProp = Object.defineProperty;
|
|
12
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
13
|
+
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
14
|
+
|
|
15
|
+
// src/client/security.ts
|
|
16
|
+
var LOOPBACK_HOSTS = /* @__PURE__ */ new Set(["localhost", "127.0.0.1", "::1"]);
|
|
17
|
+
var RESERVED_HEADERS = /* @__PURE__ */ new Set([
|
|
18
|
+
"authorization",
|
|
19
|
+
"cookie",
|
|
20
|
+
"x-actor-id",
|
|
21
|
+
"x-actor-type",
|
|
22
|
+
"x-consent-granted-by",
|
|
23
|
+
"x-consent-scope",
|
|
24
|
+
"x-consent-granted-at",
|
|
25
|
+
"x-consent-expires-at"
|
|
26
|
+
]);
|
|
27
|
+
function validateGatewayUrl(value) {
|
|
28
|
+
let url;
|
|
29
|
+
try {
|
|
30
|
+
url = new URL(value);
|
|
31
|
+
} catch {
|
|
32
|
+
throw new Error("Gateway URL must be a valid absolute URL");
|
|
33
|
+
}
|
|
34
|
+
if (url.protocol === "https:") {
|
|
35
|
+
return url.toString();
|
|
36
|
+
}
|
|
37
|
+
if (url.protocol === "http:" && LOOPBACK_HOSTS.has(url.hostname.toLowerCase())) {
|
|
38
|
+
return url.toString();
|
|
39
|
+
}
|
|
40
|
+
throw new Error("Gateway URL must use HTTPS unless it targets localhost");
|
|
41
|
+
}
|
|
42
|
+
function sanitizeHeaders(headers) {
|
|
43
|
+
if (!headers) return void 0;
|
|
44
|
+
return Object.fromEntries(
|
|
45
|
+
Object.entries(headers).filter(
|
|
46
|
+
([key]) => !RESERVED_HEADERS.has(key.toLowerCase())
|
|
47
|
+
)
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// src/client/graphql-client.ts
|
|
52
|
+
var _WebSDKClient = class _WebSDKClient {
|
|
53
|
+
constructor(config) {
|
|
54
|
+
__publicField(this, "config");
|
|
55
|
+
__publicField(this, "queryCache", /* @__PURE__ */ new Map());
|
|
56
|
+
__publicField(this, "inflight", /* @__PURE__ */ new Map());
|
|
57
|
+
this.config = {
|
|
58
|
+
...config,
|
|
59
|
+
gatewayUrl: validateGatewayUrl(config.gatewayUrl),
|
|
60
|
+
headers: sanitizeHeaders(config.headers)
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
getConfig() {
|
|
64
|
+
return this.config;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Execute a GraphQL query (with deduplication and caching)
|
|
68
|
+
*/
|
|
69
|
+
async query(query, variables) {
|
|
70
|
+
const cacheKey = JSON.stringify({ query, variables });
|
|
71
|
+
const cached = this.queryCache.get(cacheKey);
|
|
72
|
+
if (cached) {
|
|
73
|
+
if (Date.now() - cached.ts < _WebSDKClient.CACHE_TTL) {
|
|
74
|
+
return cached.data;
|
|
75
|
+
}
|
|
76
|
+
this.queryCache.delete(cacheKey);
|
|
77
|
+
}
|
|
78
|
+
const existing = this.inflight.get(cacheKey);
|
|
79
|
+
if (existing) {
|
|
80
|
+
return existing;
|
|
81
|
+
}
|
|
82
|
+
const promise = this.request(query, variables).then((result) => {
|
|
83
|
+
if (result.data && !result.errors?.length) {
|
|
84
|
+
if (this.queryCache.size >= _WebSDKClient.MAX_CACHE_ENTRIES) {
|
|
85
|
+
const oldestEntry = this.queryCache.keys().next().value;
|
|
86
|
+
if (oldestEntry) {
|
|
87
|
+
this.queryCache.delete(oldestEntry);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
this.queryCache.set(cacheKey, { data: result, ts: Date.now() });
|
|
91
|
+
}
|
|
92
|
+
return result;
|
|
93
|
+
}).finally(() => {
|
|
94
|
+
this.inflight.delete(cacheKey);
|
|
95
|
+
});
|
|
96
|
+
this.inflight.set(cacheKey, promise);
|
|
97
|
+
return promise;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Execute a GraphQL mutation
|
|
101
|
+
*/
|
|
102
|
+
async mutate(mutation, variables) {
|
|
103
|
+
return this.request(mutation, variables);
|
|
104
|
+
}
|
|
105
|
+
async request(query, variables) {
|
|
106
|
+
try {
|
|
107
|
+
const response = await fetch(this.config.gatewayUrl, {
|
|
108
|
+
method: "POST",
|
|
109
|
+
headers: {
|
|
110
|
+
"Content-Type": "application/json",
|
|
111
|
+
"x-product-id": this.config.productId,
|
|
112
|
+
...this.config.headers
|
|
113
|
+
},
|
|
114
|
+
body: JSON.stringify({ query, variables })
|
|
115
|
+
});
|
|
116
|
+
if (!response.ok) {
|
|
117
|
+
console.error(
|
|
118
|
+
"[WebSDK] GraphQL request failed",
|
|
119
|
+
response.status,
|
|
120
|
+
response.statusText
|
|
121
|
+
);
|
|
122
|
+
return {
|
|
123
|
+
errors: [
|
|
124
|
+
{
|
|
125
|
+
message: `HTTP ${response.status}: ${response.statusText}`,
|
|
126
|
+
extensions: { code: `HTTP_${response.status}` }
|
|
127
|
+
}
|
|
128
|
+
]
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
return await response.json();
|
|
132
|
+
} catch (error) {
|
|
133
|
+
console.error(
|
|
134
|
+
"[WebSDK] Network error",
|
|
135
|
+
error instanceof Error ? error.message : "unknown error"
|
|
136
|
+
);
|
|
137
|
+
return {
|
|
138
|
+
errors: [
|
|
139
|
+
{
|
|
140
|
+
message: error instanceof Error ? error.message : "Network error",
|
|
141
|
+
extensions: { code: "NETWORK_ERROR" }
|
|
142
|
+
}
|
|
143
|
+
]
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
};
|
|
148
|
+
__publicField(_WebSDKClient, "CACHE_TTL", 5 * 60 * 1e3);
|
|
149
|
+
// 5 minutes
|
|
150
|
+
__publicField(_WebSDKClient, "MAX_CACHE_ENTRIES", 100);
|
|
151
|
+
var WebSDKContext = createContext(null);
|
|
152
|
+
function useWebSDK() {
|
|
153
|
+
const client = useContext(WebSDKContext);
|
|
154
|
+
if (!client) {
|
|
155
|
+
throw new Error("useWebSDK must be used within a WebSDKProvider");
|
|
156
|
+
}
|
|
157
|
+
return client;
|
|
158
|
+
}
|
|
159
|
+
function useWebSDKConfig() {
|
|
160
|
+
const client = useWebSDK();
|
|
161
|
+
return client.getConfig();
|
|
162
|
+
}
|
|
163
|
+
var ProductContext = createContext({
|
|
164
|
+
product: null,
|
|
165
|
+
loading: true,
|
|
166
|
+
error: null,
|
|
167
|
+
hasProvider: false,
|
|
168
|
+
refetch: () => {
|
|
169
|
+
}
|
|
170
|
+
});
|
|
171
|
+
function useProduct() {
|
|
172
|
+
return useContext(ProductContext);
|
|
173
|
+
}
|
|
174
|
+
var PUBLIC_CONTENT_PAGE_QUERY = `
|
|
175
|
+
query PublicContentPage($slug: String!, $productId: String) {
|
|
176
|
+
publicContentPage(slug: $slug, productId: $productId) {
|
|
177
|
+
id
|
|
178
|
+
title
|
|
179
|
+
slug
|
|
180
|
+
content
|
|
181
|
+
contentFormat
|
|
182
|
+
seoTitle
|
|
183
|
+
seoDescription
|
|
184
|
+
seoKeywords
|
|
185
|
+
lastUpdated
|
|
186
|
+
version
|
|
187
|
+
excerpt
|
|
188
|
+
thumbnail
|
|
189
|
+
author
|
|
190
|
+
publishedAt
|
|
191
|
+
readingTime
|
|
192
|
+
featured
|
|
193
|
+
category
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
`;
|
|
197
|
+
var PUBLIC_RESOLVED_CONTENT_PAGE_QUERY = `
|
|
198
|
+
query PublicResolvedContentPage($slug: String!, $productId: String!) {
|
|
199
|
+
publicResolvedContentPage(slug: $slug, productId: $productId) {
|
|
200
|
+
id
|
|
201
|
+
title
|
|
202
|
+
slug
|
|
203
|
+
content
|
|
204
|
+
contentFormat
|
|
205
|
+
seoTitle
|
|
206
|
+
seoDescription
|
|
207
|
+
seoKeywords
|
|
208
|
+
lastUpdated
|
|
209
|
+
version
|
|
210
|
+
excerpt
|
|
211
|
+
thumbnail
|
|
212
|
+
author
|
|
213
|
+
publishedAt
|
|
214
|
+
readingTime
|
|
215
|
+
featured
|
|
216
|
+
category
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
`;
|
|
220
|
+
var PUBLIC_CONTENT_PAGES_QUERY = `
|
|
221
|
+
query PublicContentPages($productId: String!, $limit: Int, $offset: Int) {
|
|
222
|
+
publicContentPages(productId: $productId, limit: $limit, offset: $offset) {
|
|
223
|
+
items {
|
|
224
|
+
id
|
|
225
|
+
title
|
|
226
|
+
slug
|
|
227
|
+
lastUpdated
|
|
228
|
+
tags
|
|
229
|
+
seoDescription
|
|
230
|
+
excerpt
|
|
231
|
+
thumbnail
|
|
232
|
+
author
|
|
233
|
+
publishedAt
|
|
234
|
+
readingTime
|
|
235
|
+
featured
|
|
236
|
+
category
|
|
237
|
+
}
|
|
238
|
+
total
|
|
239
|
+
hasMore
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
`;
|
|
243
|
+
function useContentPage(slug, options) {
|
|
244
|
+
const client = useWebSDK();
|
|
245
|
+
const config = useWebSDKConfig();
|
|
246
|
+
const { product } = useProduct();
|
|
247
|
+
const resolvedProductId = product?.id || config.productId;
|
|
248
|
+
const [data, setData] = useState(null);
|
|
249
|
+
const [loading, setLoading] = useState(true);
|
|
250
|
+
const [error, setError] = useState(null);
|
|
251
|
+
const fetchPage = useCallback(async () => {
|
|
252
|
+
if (!resolvedProductId) return;
|
|
253
|
+
setLoading(true);
|
|
254
|
+
setError(null);
|
|
255
|
+
try {
|
|
256
|
+
const query = options?.resolve ? PUBLIC_RESOLVED_CONTENT_PAGE_QUERY : PUBLIC_CONTENT_PAGE_QUERY;
|
|
257
|
+
const queryName = options?.resolve ? "publicResolvedContentPage" : "publicContentPage";
|
|
258
|
+
const result = await client.query(
|
|
259
|
+
query,
|
|
260
|
+
{
|
|
261
|
+
slug,
|
|
262
|
+
productId: resolvedProductId
|
|
263
|
+
}
|
|
264
|
+
);
|
|
265
|
+
if (result.errors?.length) {
|
|
266
|
+
setError(result.errors[0]?.message ?? "Unknown error");
|
|
267
|
+
setData(null);
|
|
268
|
+
} else {
|
|
269
|
+
setData(result.data?.[queryName] ?? null);
|
|
270
|
+
}
|
|
271
|
+
} catch (err) {
|
|
272
|
+
setError(err instanceof Error ? err.message : "Failed to fetch content");
|
|
273
|
+
setData(null);
|
|
274
|
+
} finally {
|
|
275
|
+
setLoading(false);
|
|
276
|
+
}
|
|
277
|
+
}, [client, resolvedProductId, slug, options?.resolve]);
|
|
278
|
+
useEffect(() => {
|
|
279
|
+
fetchPage();
|
|
280
|
+
}, [fetchPage]);
|
|
281
|
+
return { data, loading, error, refetch: fetchPage };
|
|
282
|
+
}
|
|
283
|
+
function useContentPages(options) {
|
|
284
|
+
const client = useWebSDK();
|
|
285
|
+
const config = useWebSDKConfig();
|
|
286
|
+
const [data, setData] = useState(null);
|
|
287
|
+
const [loading, setLoading] = useState(true);
|
|
288
|
+
const [error, setError] = useState(null);
|
|
289
|
+
const fetchPages = useCallback(async () => {
|
|
290
|
+
setLoading(true);
|
|
291
|
+
setError(null);
|
|
292
|
+
try {
|
|
293
|
+
const result = await client.query(PUBLIC_CONTENT_PAGES_QUERY, {
|
|
294
|
+
productId: config.productId,
|
|
295
|
+
limit: options?.limit ?? 50,
|
|
296
|
+
offset: options?.offset ?? 0
|
|
297
|
+
});
|
|
298
|
+
if (result.errors?.length) {
|
|
299
|
+
setError(result.errors[0]?.message ?? "Unknown error");
|
|
300
|
+
setData(null);
|
|
301
|
+
} else {
|
|
302
|
+
setData(result.data?.publicContentPages ?? null);
|
|
303
|
+
}
|
|
304
|
+
} catch (err) {
|
|
305
|
+
setError(
|
|
306
|
+
err instanceof Error ? err.message : "Failed to fetch content pages"
|
|
307
|
+
);
|
|
308
|
+
setData(null);
|
|
309
|
+
} finally {
|
|
310
|
+
setLoading(false);
|
|
311
|
+
}
|
|
312
|
+
}, [client, config.productId, options?.limit, options?.offset]);
|
|
313
|
+
useEffect(() => {
|
|
314
|
+
fetchPages();
|
|
315
|
+
}, [fetchPages]);
|
|
316
|
+
return { data, loading, error, refetch: fetchPages };
|
|
317
|
+
}
|
|
318
|
+
function renderMarkdown(md) {
|
|
319
|
+
let html = md;
|
|
320
|
+
html = html.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
321
|
+
html = html.replace(/```(\w*)\n([\s\S]*?)```/g, (_m, _lang, code) => {
|
|
322
|
+
return `<pre><code>${code.trim()}</code></pre>`;
|
|
323
|
+
});
|
|
324
|
+
html = html.replace(/`([^`]+)`/g, "<code>$1</code>");
|
|
325
|
+
html = html.replace(/^###### (.+)$/gm, "<h6>$1</h6>");
|
|
326
|
+
html = html.replace(/^##### (.+)$/gm, "<h5>$1</h5>");
|
|
327
|
+
html = html.replace(/^#### (.+)$/gm, "<h4>$1</h4>");
|
|
328
|
+
html = html.replace(/^### (.+)$/gm, "<h3>$1</h3>");
|
|
329
|
+
html = html.replace(/^## (.+)$/gm, "<h2>$1</h2>");
|
|
330
|
+
html = html.replace(/^# (.+)$/gm, "<h1>$1</h1>");
|
|
331
|
+
html = html.replace(/\*\*\*(.+?)\*\*\*/g, "<strong><em>$1</em></strong>");
|
|
332
|
+
html = html.replace(/\*\*(.+?)\*\*/g, "<strong>$1</strong>");
|
|
333
|
+
html = html.replace(/\*(.+?)\*/g, "<em>$1</em>");
|
|
334
|
+
html = html.replace(
|
|
335
|
+
/\[([^\]]+)\]\(([^)]+)\)/g,
|
|
336
|
+
'<a href="$2" target="_blank" rel="noopener noreferrer">$1</a>'
|
|
337
|
+
);
|
|
338
|
+
html = html.replace(/^> (.+)$/gm, "<blockquote>$1</blockquote>");
|
|
339
|
+
html = html.replace(/(^- .+$(\n^- .+$)*)/gm, (block) => {
|
|
340
|
+
const items = block.split("\n").map((line) => `<li>${line.replace(/^- /, "")}</li>`).join("");
|
|
341
|
+
return `<ul>${items}</ul>`;
|
|
342
|
+
});
|
|
343
|
+
html = html.replace(/(^\d+\. .+$(\n^\d+\. .+$)*)/gm, (block) => {
|
|
344
|
+
const items = block.split("\n").map((line) => `<li>${line.replace(/^\d+\. /, "")}</li>`).join("");
|
|
345
|
+
return `<ol>${items}</ol>`;
|
|
346
|
+
});
|
|
347
|
+
html = html.replace(/^---$/gm, "<hr />");
|
|
348
|
+
html = html.split(/\n\n+/).map((block) => {
|
|
349
|
+
const trimmed = block.trim();
|
|
350
|
+
if (!trimmed) return "";
|
|
351
|
+
if (/^<(h[1-6]|ul|ol|pre|blockquote|hr)/.test(trimmed)) return trimmed;
|
|
352
|
+
return `<p>${trimmed.replace(/\n/g, "<br />")}</p>`;
|
|
353
|
+
}).join("\n");
|
|
354
|
+
return html;
|
|
355
|
+
}
|
|
356
|
+
function ContentRenderer({
|
|
357
|
+
content,
|
|
358
|
+
format = "markdown",
|
|
359
|
+
className
|
|
360
|
+
}) {
|
|
361
|
+
if (format === "markdown") {
|
|
362
|
+
return /* @__PURE__ */ jsx(
|
|
363
|
+
"div",
|
|
364
|
+
{
|
|
365
|
+
className,
|
|
366
|
+
dangerouslySetInnerHTML: { __html: renderMarkdown(content) }
|
|
367
|
+
}
|
|
368
|
+
);
|
|
369
|
+
}
|
|
370
|
+
if (format === "html") {
|
|
371
|
+
return /* @__PURE__ */ jsx(
|
|
372
|
+
"div",
|
|
373
|
+
{
|
|
374
|
+
className,
|
|
375
|
+
dangerouslySetInnerHTML: { __html: content }
|
|
376
|
+
}
|
|
377
|
+
);
|
|
378
|
+
}
|
|
379
|
+
return /* @__PURE__ */ jsx("div", { className, children: /* @__PURE__ */ jsx("pre", { style: { whiteSpace: "pre-wrap", fontFamily: "inherit" }, children: content }) });
|
|
380
|
+
}
|
|
381
|
+
function PageHead({
|
|
382
|
+
title,
|
|
383
|
+
description,
|
|
384
|
+
productName,
|
|
385
|
+
keywords,
|
|
386
|
+
image,
|
|
387
|
+
url,
|
|
388
|
+
favicon,
|
|
389
|
+
robots = "index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1",
|
|
390
|
+
locale = "en_US",
|
|
391
|
+
twitterHandle,
|
|
392
|
+
themeColor = "#000000",
|
|
393
|
+
structuredData,
|
|
394
|
+
additionalMeta
|
|
395
|
+
}) {
|
|
396
|
+
const fullTitle = productName && !title.includes(productName) ? `${title} | ${productName}` : title;
|
|
397
|
+
const fullImageUrl = image && !image.startsWith("http") && url ? `${url.replace(/\/$/, "")}${image}` : image;
|
|
398
|
+
const structuredDataItems = structuredData ? Array.isArray(structuredData) ? structuredData : [structuredData] : [];
|
|
399
|
+
return /* @__PURE__ */ jsxs(Helmet, { children: [
|
|
400
|
+
/* @__PURE__ */ jsx("title", { children: fullTitle }),
|
|
401
|
+
/* @__PURE__ */ jsx("meta", { name: "title", content: fullTitle }),
|
|
402
|
+
description && /* @__PURE__ */ jsx("meta", { name: "description", content: description }),
|
|
403
|
+
keywords && /* @__PURE__ */ jsx("meta", { name: "keywords", content: keywords }),
|
|
404
|
+
/* @__PURE__ */ jsx("meta", { name: "author", content: "Burdenoff Consultancy Services Pvt Ltd" }),
|
|
405
|
+
/* @__PURE__ */ jsx("meta", { name: "robots", content: robots }),
|
|
406
|
+
url && /* @__PURE__ */ jsx("link", { rel: "canonical", href: url }),
|
|
407
|
+
favicon && /* @__PURE__ */ jsx("link", { rel: "icon", href: favicon }),
|
|
408
|
+
/* @__PURE__ */ jsx("meta", { property: "og:type", content: "website" }),
|
|
409
|
+
/* @__PURE__ */ jsx("meta", { property: "og:title", content: fullTitle }),
|
|
410
|
+
description && /* @__PURE__ */ jsx("meta", { property: "og:description", content: description }),
|
|
411
|
+
fullImageUrl && /* @__PURE__ */ jsx("meta", { property: "og:image", content: fullImageUrl }),
|
|
412
|
+
url && /* @__PURE__ */ jsx("meta", { property: "og:url", content: url }),
|
|
413
|
+
/* @__PURE__ */ jsx("meta", { property: "og:locale", content: locale }),
|
|
414
|
+
productName && /* @__PURE__ */ jsx("meta", { property: "og:site_name", content: productName }),
|
|
415
|
+
/* @__PURE__ */ jsx("meta", { name: "twitter:card", content: "summary_large_image" }),
|
|
416
|
+
/* @__PURE__ */ jsx("meta", { name: "twitter:title", content: fullTitle }),
|
|
417
|
+
description && /* @__PURE__ */ jsx("meta", { name: "twitter:description", content: description }),
|
|
418
|
+
fullImageUrl && /* @__PURE__ */ jsx("meta", { name: "twitter:image", content: fullImageUrl }),
|
|
419
|
+
twitterHandle && /* @__PURE__ */ jsx("meta", { name: "twitter:creator", content: twitterHandle }),
|
|
420
|
+
/* @__PURE__ */ jsx("meta", { name: "theme-color", content: themeColor }),
|
|
421
|
+
structuredDataItems.map((data, index) => /* @__PURE__ */ jsx("script", { type: "application/ld+json", children: JSON.stringify(data) }, `ld-${index}`)),
|
|
422
|
+
additionalMeta?.map((meta, index) => /* @__PURE__ */ jsx(
|
|
423
|
+
"meta",
|
|
424
|
+
{
|
|
425
|
+
...meta.name ? { name: meta.name } : {},
|
|
426
|
+
...meta.property ? { property: meta.property } : {},
|
|
427
|
+
content: meta.content
|
|
428
|
+
},
|
|
429
|
+
`meta-${index}`
|
|
430
|
+
))
|
|
431
|
+
] });
|
|
432
|
+
}
|
|
433
|
+
var BLOG_POSTS_QUERY = `
|
|
434
|
+
query BlogPosts($productId: String!, $limit: Int, $offset: Int) {
|
|
435
|
+
publicContentPages(productId: $productId, limit: $limit, offset: $offset) {
|
|
436
|
+
items {
|
|
437
|
+
id
|
|
438
|
+
title
|
|
439
|
+
slug
|
|
440
|
+
lastUpdated
|
|
441
|
+
tags
|
|
442
|
+
seoDescription
|
|
443
|
+
excerpt
|
|
444
|
+
thumbnail
|
|
445
|
+
author
|
|
446
|
+
publishedAt
|
|
447
|
+
readingTime
|
|
448
|
+
featured
|
|
449
|
+
category
|
|
450
|
+
}
|
|
451
|
+
total
|
|
452
|
+
hasMore
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
`;
|
|
456
|
+
var BLOG_POST_QUERY = `
|
|
457
|
+
query BlogPost($slug: String!, $productId: String) {
|
|
458
|
+
publicContentPage(slug: $slug, productId: $productId) {
|
|
459
|
+
id
|
|
460
|
+
title
|
|
461
|
+
slug
|
|
462
|
+
content
|
|
463
|
+
contentFormat
|
|
464
|
+
seoTitle
|
|
465
|
+
seoDescription
|
|
466
|
+
seoKeywords
|
|
467
|
+
lastUpdated
|
|
468
|
+
tags
|
|
469
|
+
excerpt
|
|
470
|
+
thumbnail
|
|
471
|
+
author
|
|
472
|
+
publishedAt
|
|
473
|
+
readingTime
|
|
474
|
+
featured
|
|
475
|
+
category
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
`;
|
|
479
|
+
function BlogCard({
|
|
480
|
+
post,
|
|
481
|
+
basePath = "/blog"
|
|
482
|
+
}) {
|
|
483
|
+
const date = post.publishedAt || post.lastUpdated;
|
|
484
|
+
const formattedDate = date ? new Date(date).toLocaleDateString("en-US", {
|
|
485
|
+
year: "numeric",
|
|
486
|
+
month: "long",
|
|
487
|
+
day: "numeric"
|
|
488
|
+
}) : "";
|
|
489
|
+
const [imgError, setImgError] = useState(false);
|
|
490
|
+
return /* @__PURE__ */ jsxs(
|
|
491
|
+
"a",
|
|
492
|
+
{
|
|
493
|
+
href: `${basePath}/${post.slug}`,
|
|
494
|
+
className: "group block bg-card border rounded-xl overflow-hidden hover:shadow-lg hover:border-primary/30 transition-all duration-300",
|
|
495
|
+
children: [
|
|
496
|
+
post.thumbnail && !imgError ? /* @__PURE__ */ jsx("div", { className: "aspect-[16/9] overflow-hidden", children: /* @__PURE__ */ jsx(
|
|
497
|
+
"img",
|
|
498
|
+
{
|
|
499
|
+
src: post.thumbnail,
|
|
500
|
+
alt: post.title,
|
|
501
|
+
className: "w-full h-full object-cover group-hover:scale-105 transition-transform duration-300",
|
|
502
|
+
loading: "lazy",
|
|
503
|
+
onError: () => setImgError(true)
|
|
504
|
+
}
|
|
505
|
+
) }) : /* @__PURE__ */ jsx("div", { className: "aspect-[16/9] bg-muted/50 flex items-center justify-center", children: /* @__PURE__ */ jsx(
|
|
506
|
+
"svg",
|
|
507
|
+
{
|
|
508
|
+
className: "w-12 h-12 text-muted-foreground/20",
|
|
509
|
+
fill: "none",
|
|
510
|
+
viewBox: "0 0 24 24",
|
|
511
|
+
stroke: "currentColor",
|
|
512
|
+
children: /* @__PURE__ */ jsx(
|
|
513
|
+
"path",
|
|
514
|
+
{
|
|
515
|
+
strokeLinecap: "round",
|
|
516
|
+
strokeLinejoin: "round",
|
|
517
|
+
strokeWidth: 1.5,
|
|
518
|
+
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"
|
|
519
|
+
}
|
|
520
|
+
)
|
|
521
|
+
}
|
|
522
|
+
) }),
|
|
523
|
+
/* @__PURE__ */ jsxs("div", { className: "p-5 sm:p-6", children: [
|
|
524
|
+
post.category && /* @__PURE__ */ 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 }),
|
|
525
|
+
/* @__PURE__ */ jsx("h3", { className: "text-lg font-semibold text-foreground mb-2 group-hover:text-primary transition-colors line-clamp-2", children: post.title }),
|
|
526
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground mb-4 line-clamp-3", children: post.excerpt || post.seoDescription || "" }),
|
|
527
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 text-xs text-muted-foreground", children: [
|
|
528
|
+
post.author && /* @__PURE__ */ jsx("span", { className: "font-medium", children: post.author }),
|
|
529
|
+
post.author && formattedDate && /* @__PURE__ */ jsx("span", { className: "text-muted-foreground/40", children: "\xB7" }),
|
|
530
|
+
formattedDate && /* @__PURE__ */ jsx("span", { children: formattedDate }),
|
|
531
|
+
post.readingTime && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
532
|
+
/* @__PURE__ */ jsx("span", { className: "text-muted-foreground/40", children: "\xB7" }),
|
|
533
|
+
/* @__PURE__ */ jsxs("span", { children: [
|
|
534
|
+
post.readingTime,
|
|
535
|
+
" min read"
|
|
536
|
+
] })
|
|
537
|
+
] })
|
|
538
|
+
] })
|
|
539
|
+
] })
|
|
540
|
+
]
|
|
541
|
+
}
|
|
542
|
+
);
|
|
543
|
+
}
|
|
544
|
+
function BlogListPage(props) {
|
|
545
|
+
const {
|
|
546
|
+
productName,
|
|
547
|
+
heroTitle = "Blog",
|
|
548
|
+
heroSubtitle = "Latest updates, engineering insights, and product news",
|
|
549
|
+
blogPostBasePath = "/blog",
|
|
550
|
+
className = "",
|
|
551
|
+
seoTitle,
|
|
552
|
+
seoDescription,
|
|
553
|
+
seoKeywords
|
|
554
|
+
} = props;
|
|
555
|
+
const client = useWebSDK();
|
|
556
|
+
const config = useWebSDKConfig();
|
|
557
|
+
const { product } = useProduct();
|
|
558
|
+
const resolvedProductId = product?.id || config.productId;
|
|
559
|
+
const [posts, setPosts] = useState([]);
|
|
560
|
+
const [total, setTotal] = useState(0);
|
|
561
|
+
const [loading, setLoading] = useState(true);
|
|
562
|
+
const [error, setError] = useState(null);
|
|
563
|
+
const [offset, setOffset] = useState(0);
|
|
564
|
+
const limit = 12;
|
|
565
|
+
const fetchPosts = useCallback(async () => {
|
|
566
|
+
if (!resolvedProductId) return;
|
|
567
|
+
setLoading(true);
|
|
568
|
+
try {
|
|
569
|
+
const result = await client.query(BLOG_POSTS_QUERY, {
|
|
570
|
+
productId: resolvedProductId,
|
|
571
|
+
limit,
|
|
572
|
+
offset
|
|
573
|
+
});
|
|
574
|
+
if (result.errors?.length) {
|
|
575
|
+
setError(result.errors[0]?.message ?? "Failed to load posts");
|
|
576
|
+
return;
|
|
577
|
+
}
|
|
578
|
+
const blogPosts = (result.data?.publicContentPages?.items || []).filter(
|
|
579
|
+
(p) => p.tags?.includes("blog")
|
|
580
|
+
);
|
|
581
|
+
setPosts(blogPosts);
|
|
582
|
+
setTotal(result.data?.publicContentPages?.total || 0);
|
|
583
|
+
setError(null);
|
|
584
|
+
} catch {
|
|
585
|
+
setError("Failed to load blog posts");
|
|
586
|
+
} finally {
|
|
587
|
+
setLoading(false);
|
|
588
|
+
}
|
|
589
|
+
}, [client, resolvedProductId, offset]);
|
|
590
|
+
useEffect(() => {
|
|
591
|
+
fetchPosts();
|
|
592
|
+
}, [fetchPosts]);
|
|
593
|
+
const featuredPosts = posts.filter((p) => p.featured);
|
|
594
|
+
const regularPosts = posts.filter((p) => !p.featured);
|
|
595
|
+
return /* @__PURE__ */ jsxs("div", { className: `w-full ${className}`, children: [
|
|
596
|
+
seoTitle && /* @__PURE__ */ jsx(
|
|
597
|
+
PageHead,
|
|
598
|
+
{
|
|
599
|
+
title: seoTitle,
|
|
600
|
+
description: seoDescription || heroSubtitle,
|
|
601
|
+
productName,
|
|
602
|
+
keywords: seoKeywords
|
|
603
|
+
}
|
|
604
|
+
),
|
|
605
|
+
/* @__PURE__ */ jsx("section", { className: "py-16 sm:py-20 md:py-24", children: /* @__PURE__ */ jsx("div", { className: "max-w-7xl mx-auto px-4 sm:px-6 lg:px-8", children: /* @__PURE__ */ jsxs("div", { className: "text-center max-w-3xl mx-auto", children: [
|
|
606
|
+
/* @__PURE__ */ jsx("h1", { className: "text-3xl sm:text-4xl md:text-5xl font-bold tracking-tight text-foreground", children: heroTitle }),
|
|
607
|
+
/* @__PURE__ */ jsx("p", { className: "mt-4 text-lg text-muted-foreground", children: heroSubtitle })
|
|
608
|
+
] }) }) }),
|
|
609
|
+
/* @__PURE__ */ jsx("section", { className: "pb-16 sm:pb-20", children: /* @__PURE__ */ jsx("div", { className: "max-w-7xl mx-auto px-4 sm:px-6 lg:px-8", children: loading ? /* @__PURE__ */ jsx("div", { className: "grid md:grid-cols-2 lg:grid-cols-3 gap-6 sm:gap-8", children: [1, 2, 3].map((i) => /* @__PURE__ */ jsxs(
|
|
610
|
+
"div",
|
|
611
|
+
{
|
|
612
|
+
className: "bg-card border rounded-xl overflow-hidden animate-pulse",
|
|
613
|
+
children: [
|
|
614
|
+
/* @__PURE__ */ jsx("div", { className: "aspect-[16/9] bg-muted" }),
|
|
615
|
+
/* @__PURE__ */ jsxs("div", { className: "p-6 space-y-3", children: [
|
|
616
|
+
/* @__PURE__ */ jsx("div", { className: "h-4 bg-muted rounded w-1/4" }),
|
|
617
|
+
/* @__PURE__ */ jsx("div", { className: "h-6 bg-muted rounded w-3/4" }),
|
|
618
|
+
/* @__PURE__ */ jsx("div", { className: "h-4 bg-muted rounded" }),
|
|
619
|
+
/* @__PURE__ */ jsx("div", { className: "h-4 bg-muted rounded w-1/2" })
|
|
620
|
+
] })
|
|
621
|
+
]
|
|
622
|
+
},
|
|
623
|
+
i
|
|
624
|
+
)) }) : error ? /* @__PURE__ */ jsx("div", { className: "text-center py-12", children: /* @__PURE__ */ jsx("p", { className: "text-muted-foreground", children: error }) }) : posts.length === 0 ? /* @__PURE__ */ jsx("div", { className: "text-center py-12", children: /* @__PURE__ */ jsx("p", { className: "text-muted-foreground", children: "No blog posts yet. Check back soon!" }) }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
625
|
+
featuredPosts.length > 0 && /* @__PURE__ */ jsx("div", { className: "grid md:grid-cols-2 gap-6 sm:gap-8 mb-8", children: featuredPosts.map((post) => /* @__PURE__ */ jsx(
|
|
626
|
+
BlogCard,
|
|
627
|
+
{
|
|
628
|
+
post,
|
|
629
|
+
basePath: blogPostBasePath
|
|
630
|
+
},
|
|
631
|
+
post.id
|
|
632
|
+
)) }),
|
|
633
|
+
/* @__PURE__ */ jsx("div", { className: "grid md:grid-cols-2 lg:grid-cols-3 gap-6 sm:gap-8", children: regularPosts.map((post) => /* @__PURE__ */ jsx(
|
|
634
|
+
BlogCard,
|
|
635
|
+
{
|
|
636
|
+
post,
|
|
637
|
+
basePath: blogPostBasePath
|
|
638
|
+
},
|
|
639
|
+
post.id
|
|
640
|
+
)) }),
|
|
641
|
+
total > limit && /* @__PURE__ */ jsxs("div", { className: "flex justify-center gap-4 mt-12", children: [
|
|
642
|
+
/* @__PURE__ */ jsx(
|
|
643
|
+
"button",
|
|
644
|
+
{
|
|
645
|
+
onClick: () => setOffset(Math.max(0, offset - limit)),
|
|
646
|
+
disabled: offset === 0,
|
|
647
|
+
className: "px-5 py-2.5 text-sm font-medium border rounded-lg disabled:opacity-40 hover:bg-muted transition-colors",
|
|
648
|
+
children: "Previous"
|
|
649
|
+
}
|
|
650
|
+
),
|
|
651
|
+
/* @__PURE__ */ jsx(
|
|
652
|
+
"button",
|
|
653
|
+
{
|
|
654
|
+
onClick: () => setOffset(offset + limit),
|
|
655
|
+
disabled: offset + limit >= total,
|
|
656
|
+
className: "px-5 py-2.5 text-sm font-medium border rounded-lg disabled:opacity-40 hover:bg-muted transition-colors",
|
|
657
|
+
children: "Next"
|
|
658
|
+
}
|
|
659
|
+
)
|
|
660
|
+
] })
|
|
661
|
+
] }) }) })
|
|
662
|
+
] });
|
|
663
|
+
}
|
|
664
|
+
function BlogPostPage(props) {
|
|
665
|
+
const {
|
|
666
|
+
productName,
|
|
667
|
+
blogListUrl = "/blog",
|
|
668
|
+
backLabel = "Back to Blog",
|
|
669
|
+
className = ""
|
|
670
|
+
} = props;
|
|
671
|
+
const client = useWebSDK();
|
|
672
|
+
const config = useWebSDKConfig();
|
|
673
|
+
const { product } = useProduct();
|
|
674
|
+
const resolvedProductId = product?.id || config.productId;
|
|
675
|
+
const [post, setPost] = useState(null);
|
|
676
|
+
const [loading, setLoading] = useState(true);
|
|
677
|
+
const [error, setError] = useState(null);
|
|
678
|
+
const slug = typeof window !== "undefined" ? window.location.pathname.split("/").pop() || "" : "";
|
|
679
|
+
useEffect(() => {
|
|
680
|
+
if (!slug || !resolvedProductId) return;
|
|
681
|
+
async function fetchPost() {
|
|
682
|
+
setLoading(true);
|
|
683
|
+
try {
|
|
684
|
+
const result = await client.query(BLOG_POST_QUERY, {
|
|
685
|
+
slug,
|
|
686
|
+
productId: resolvedProductId
|
|
687
|
+
});
|
|
688
|
+
if (result.errors?.length) {
|
|
689
|
+
setError(result.errors[0]?.message ?? "Failed to load post");
|
|
690
|
+
return;
|
|
691
|
+
}
|
|
692
|
+
setPost(result.data?.publicContentPage ?? null);
|
|
693
|
+
setError(null);
|
|
694
|
+
} catch {
|
|
695
|
+
setError("Failed to load blog post");
|
|
696
|
+
} finally {
|
|
697
|
+
setLoading(false);
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
fetchPost();
|
|
701
|
+
}, [client, slug, resolvedProductId]);
|
|
702
|
+
if (loading) {
|
|
703
|
+
return /* @__PURE__ */ jsx("div", { className: `w-full ${className}`, children: /* @__PURE__ */ jsx("div", { className: "max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-24", children: /* @__PURE__ */ jsxs("div", { className: "animate-pulse space-y-6", children: [
|
|
704
|
+
/* @__PURE__ */ jsx("div", { className: "h-8 bg-muted rounded w-2/3" }),
|
|
705
|
+
/* @__PURE__ */ jsx("div", { className: "h-4 bg-muted rounded w-1/3" }),
|
|
706
|
+
/* @__PURE__ */ jsx("div", { className: "aspect-[16/9] bg-muted rounded-xl" }),
|
|
707
|
+
/* @__PURE__ */ jsx("div", { className: "space-y-3", children: [1, 2, 3, 4, 5].map((i) => /* @__PURE__ */ jsx("div", { className: "h-4 bg-muted rounded" }, i)) })
|
|
708
|
+
] }) }) });
|
|
709
|
+
}
|
|
710
|
+
if (error || !post) {
|
|
711
|
+
return /* @__PURE__ */ jsx("div", { className: `w-full ${className}`, children: /* @__PURE__ */ jsxs("div", { className: "max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-24 text-center", children: [
|
|
712
|
+
/* @__PURE__ */ jsx("h1", { className: "text-2xl font-bold mb-4", children: "Post not found" }),
|
|
713
|
+
/* @__PURE__ */ jsx("p", { className: "text-muted-foreground mb-6", children: error || "This blog post doesn't exist or has been removed." }),
|
|
714
|
+
/* @__PURE__ */ jsxs(
|
|
715
|
+
"a",
|
|
716
|
+
{
|
|
717
|
+
href: blogListUrl,
|
|
718
|
+
className: "text-primary hover:underline font-medium",
|
|
719
|
+
children: [
|
|
720
|
+
"\u2190 ",
|
|
721
|
+
backLabel
|
|
722
|
+
]
|
|
723
|
+
}
|
|
724
|
+
)
|
|
725
|
+
] }) });
|
|
726
|
+
}
|
|
727
|
+
const date = post.publishedAt || post.lastUpdated;
|
|
728
|
+
const formattedDate = date ? new Date(date).toLocaleDateString("en-US", {
|
|
729
|
+
year: "numeric",
|
|
730
|
+
month: "long",
|
|
731
|
+
day: "numeric"
|
|
732
|
+
}) : "";
|
|
733
|
+
return /* @__PURE__ */ jsxs("div", { className: `w-full ${className}`, children: [
|
|
734
|
+
/* @__PURE__ */ jsx(
|
|
735
|
+
PageHead,
|
|
736
|
+
{
|
|
737
|
+
title: post.seoTitle || post.title,
|
|
738
|
+
description: post.seoDescription || post.excerpt || "",
|
|
739
|
+
productName,
|
|
740
|
+
keywords: post.seoKeywords || ""
|
|
741
|
+
}
|
|
742
|
+
),
|
|
743
|
+
/* @__PURE__ */ jsxs("article", { className: "max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-16 sm:py-20", children: [
|
|
744
|
+
/* @__PURE__ */ jsxs(
|
|
745
|
+
"a",
|
|
746
|
+
{
|
|
747
|
+
href: blogListUrl,
|
|
748
|
+
className: "inline-flex items-center gap-1 text-sm text-muted-foreground hover:text-foreground mb-8 transition-colors",
|
|
749
|
+
children: [
|
|
750
|
+
"\u2190 ",
|
|
751
|
+
backLabel
|
|
752
|
+
]
|
|
753
|
+
}
|
|
754
|
+
),
|
|
755
|
+
post.category && /* @__PURE__ */ 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 }),
|
|
756
|
+
/* @__PURE__ */ jsx("h1", { className: "text-3xl sm:text-4xl md:text-5xl font-bold tracking-tight text-foreground mb-4", children: post.title }),
|
|
757
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 text-sm text-muted-foreground mb-8", children: [
|
|
758
|
+
post.author && /* @__PURE__ */ jsx("span", { className: "font-medium text-foreground", children: post.author }),
|
|
759
|
+
post.author && formattedDate && /* @__PURE__ */ jsx("span", { className: "text-muted-foreground/40", children: "\xB7" }),
|
|
760
|
+
formattedDate && /* @__PURE__ */ jsx("span", { children: formattedDate }),
|
|
761
|
+
post.readingTime && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
762
|
+
/* @__PURE__ */ jsx("span", { className: "text-muted-foreground/40", children: "\xB7" }),
|
|
763
|
+
/* @__PURE__ */ jsxs("span", { children: [
|
|
764
|
+
post.readingTime,
|
|
765
|
+
" min read"
|
|
766
|
+
] })
|
|
767
|
+
] })
|
|
768
|
+
] }),
|
|
769
|
+
post.thumbnail && /* @__PURE__ */ jsx("div", { className: "aspect-[16/9] rounded-xl overflow-hidden mb-10", children: /* @__PURE__ */ jsx(
|
|
770
|
+
"img",
|
|
771
|
+
{
|
|
772
|
+
src: post.thumbnail,
|
|
773
|
+
alt: post.title,
|
|
774
|
+
className: "w-full h-full object-cover"
|
|
775
|
+
}
|
|
776
|
+
) }),
|
|
777
|
+
/* @__PURE__ */ jsx("div", { className: "prose prose-gray dark:prose-invert max-w-none", children: /* @__PURE__ */ jsx(ContentRenderer, { content: post.content, format: post.contentFormat }) })
|
|
778
|
+
] })
|
|
779
|
+
] });
|
|
780
|
+
}
|
|
781
|
+
var CASE_STUDY_TAG = "case-study";
|
|
782
|
+
var CASE_STUDIES_QUERY = `
|
|
783
|
+
query CaseStudies($productId: String!, $limit: Int, $offset: Int) {
|
|
784
|
+
publicContentPages(productId: $productId, limit: $limit, offset: $offset) {
|
|
785
|
+
items {
|
|
786
|
+
id
|
|
787
|
+
title
|
|
788
|
+
slug
|
|
789
|
+
lastUpdated
|
|
790
|
+
tags
|
|
791
|
+
seoDescription
|
|
792
|
+
excerpt
|
|
793
|
+
thumbnail
|
|
794
|
+
author
|
|
795
|
+
publishedAt
|
|
796
|
+
readingTime
|
|
797
|
+
featured
|
|
798
|
+
category
|
|
799
|
+
}
|
|
800
|
+
total
|
|
801
|
+
hasMore
|
|
802
|
+
}
|
|
803
|
+
}
|
|
804
|
+
`;
|
|
805
|
+
var CASE_STUDY_QUERY = `
|
|
806
|
+
query CaseStudy($slug: String!, $productId: String) {
|
|
807
|
+
publicContentPage(slug: $slug, productId: $productId) {
|
|
808
|
+
id
|
|
809
|
+
title
|
|
810
|
+
slug
|
|
811
|
+
content
|
|
812
|
+
contentFormat
|
|
813
|
+
seoTitle
|
|
814
|
+
seoDescription
|
|
815
|
+
seoKeywords
|
|
816
|
+
lastUpdated
|
|
817
|
+
tags
|
|
818
|
+
excerpt
|
|
819
|
+
thumbnail
|
|
820
|
+
author
|
|
821
|
+
publishedAt
|
|
822
|
+
readingTime
|
|
823
|
+
featured
|
|
824
|
+
category
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
`;
|
|
828
|
+
function CaseStudyCard({
|
|
829
|
+
post,
|
|
830
|
+
basePath = "/case-studies"
|
|
831
|
+
}) {
|
|
832
|
+
const date = post.publishedAt || post.lastUpdated;
|
|
833
|
+
const formattedDate = date ? new Date(date).toLocaleDateString("en-US", {
|
|
834
|
+
year: "numeric",
|
|
835
|
+
month: "long",
|
|
836
|
+
day: "numeric"
|
|
837
|
+
}) : "";
|
|
838
|
+
const [imgError, setImgError] = useState(false);
|
|
839
|
+
return /* @__PURE__ */ jsxs(
|
|
840
|
+
"a",
|
|
841
|
+
{
|
|
842
|
+
href: `${basePath}/${post.slug}`,
|
|
843
|
+
className: "group block bg-card border rounded-xl overflow-hidden hover:shadow-lg hover:border-primary/30 transition-all duration-300",
|
|
844
|
+
children: [
|
|
845
|
+
post.thumbnail && !imgError ? /* @__PURE__ */ jsx("div", { className: "aspect-[16/9] overflow-hidden", children: /* @__PURE__ */ jsx(
|
|
846
|
+
"img",
|
|
847
|
+
{
|
|
848
|
+
src: post.thumbnail,
|
|
849
|
+
alt: post.title,
|
|
850
|
+
className: "w-full h-full object-cover group-hover:scale-105 transition-transform duration-300",
|
|
851
|
+
loading: "lazy",
|
|
852
|
+
onError: () => setImgError(true)
|
|
853
|
+
}
|
|
854
|
+
) }) : /* @__PURE__ */ jsx("div", { className: "aspect-[16/9] bg-muted/50 flex items-center justify-center", children: /* @__PURE__ */ jsx(
|
|
855
|
+
"svg",
|
|
856
|
+
{
|
|
857
|
+
className: "w-12 h-12 text-muted-foreground/20",
|
|
858
|
+
fill: "none",
|
|
859
|
+
viewBox: "0 0 24 24",
|
|
860
|
+
stroke: "currentColor",
|
|
861
|
+
children: /* @__PURE__ */ jsx(
|
|
862
|
+
"path",
|
|
863
|
+
{
|
|
864
|
+
strokeLinecap: "round",
|
|
865
|
+
strokeLinejoin: "round",
|
|
866
|
+
strokeWidth: 1.5,
|
|
867
|
+
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"
|
|
868
|
+
}
|
|
869
|
+
)
|
|
870
|
+
}
|
|
871
|
+
) }),
|
|
872
|
+
/* @__PURE__ */ jsxs("div", { className: "p-5 sm:p-6", children: [
|
|
873
|
+
post.category && /* @__PURE__ */ 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 }),
|
|
874
|
+
/* @__PURE__ */ jsx("h3", { className: "text-lg font-semibold text-foreground mb-2 group-hover:text-primary transition-colors line-clamp-2", children: post.title }),
|
|
875
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground mb-4 line-clamp-3", children: post.excerpt || post.seoDescription || "" }),
|
|
876
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 text-xs text-muted-foreground", children: [
|
|
877
|
+
post.author && /* @__PURE__ */ jsx("span", { className: "font-medium", children: post.author }),
|
|
878
|
+
post.author && formattedDate && /* @__PURE__ */ jsx("span", { className: "text-muted-foreground/40", children: "\xB7" }),
|
|
879
|
+
formattedDate && /* @__PURE__ */ jsx("span", { children: formattedDate }),
|
|
880
|
+
post.readingTime && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
881
|
+
/* @__PURE__ */ jsx("span", { className: "text-muted-foreground/40", children: "\xB7" }),
|
|
882
|
+
/* @__PURE__ */ jsxs("span", { children: [
|
|
883
|
+
post.readingTime,
|
|
884
|
+
" min read"
|
|
885
|
+
] })
|
|
886
|
+
] })
|
|
887
|
+
] })
|
|
888
|
+
] })
|
|
889
|
+
]
|
|
890
|
+
}
|
|
891
|
+
);
|
|
892
|
+
}
|
|
893
|
+
function CaseStudiesListPage(props) {
|
|
894
|
+
const {
|
|
895
|
+
productName,
|
|
896
|
+
heroTitle = "Case Studies",
|
|
897
|
+
heroSubtitle = "Real-world stories of teams building with our platform",
|
|
898
|
+
caseStudyBasePath = "/case-studies",
|
|
899
|
+
className = "",
|
|
900
|
+
seoTitle,
|
|
901
|
+
seoDescription,
|
|
902
|
+
seoKeywords
|
|
903
|
+
} = props;
|
|
904
|
+
const client = useWebSDK();
|
|
905
|
+
const config = useWebSDKConfig();
|
|
906
|
+
const { product } = useProduct();
|
|
907
|
+
const resolvedProductId = product?.id || config.productId;
|
|
908
|
+
const [posts, setPosts] = useState([]);
|
|
909
|
+
const [total, setTotal] = useState(0);
|
|
910
|
+
const [loading, setLoading] = useState(true);
|
|
911
|
+
const [error, setError] = useState(null);
|
|
912
|
+
const [offset, setOffset] = useState(0);
|
|
913
|
+
const limit = 12;
|
|
914
|
+
const fetchPosts = useCallback(async () => {
|
|
915
|
+
if (!resolvedProductId) return;
|
|
916
|
+
setLoading(true);
|
|
917
|
+
try {
|
|
918
|
+
const result = await client.query(CASE_STUDIES_QUERY, {
|
|
919
|
+
productId: resolvedProductId,
|
|
920
|
+
limit,
|
|
921
|
+
offset
|
|
922
|
+
});
|
|
923
|
+
if (result.errors?.length) {
|
|
924
|
+
setError(result.errors[0]?.message ?? "Failed to load case studies");
|
|
925
|
+
return;
|
|
926
|
+
}
|
|
927
|
+
const caseStudies = (result.data?.publicContentPages?.items || []).filter(
|
|
928
|
+
(p) => p.tags?.includes(CASE_STUDY_TAG)
|
|
929
|
+
);
|
|
930
|
+
setPosts(caseStudies);
|
|
931
|
+
setTotal(result.data?.publicContentPages?.total || 0);
|
|
932
|
+
setError(null);
|
|
933
|
+
} catch {
|
|
934
|
+
setError("Failed to load case studies");
|
|
935
|
+
} finally {
|
|
936
|
+
setLoading(false);
|
|
937
|
+
}
|
|
938
|
+
}, [client, resolvedProductId, offset]);
|
|
939
|
+
useEffect(() => {
|
|
940
|
+
fetchPosts();
|
|
941
|
+
}, [fetchPosts]);
|
|
942
|
+
const featuredPosts = posts.filter((p) => p.featured);
|
|
943
|
+
const regularPosts = posts.filter((p) => !p.featured);
|
|
944
|
+
return /* @__PURE__ */ jsxs("div", { className: `w-full ${className}`, children: [
|
|
945
|
+
seoTitle && /* @__PURE__ */ jsx(
|
|
946
|
+
PageHead,
|
|
947
|
+
{
|
|
948
|
+
title: seoTitle,
|
|
949
|
+
description: seoDescription || heroSubtitle,
|
|
950
|
+
productName,
|
|
951
|
+
keywords: seoKeywords
|
|
952
|
+
}
|
|
953
|
+
),
|
|
954
|
+
/* @__PURE__ */ jsx("section", { className: "py-16 sm:py-20 md:py-24", children: /* @__PURE__ */ jsx("div", { className: "max-w-7xl mx-auto px-4 sm:px-6 lg:px-8", children: /* @__PURE__ */ jsxs("div", { className: "text-center max-w-3xl mx-auto", children: [
|
|
955
|
+
/* @__PURE__ */ jsx("h1", { className: "text-3xl sm:text-4xl md:text-5xl font-bold tracking-tight text-foreground", children: heroTitle }),
|
|
956
|
+
/* @__PURE__ */ jsx("p", { className: "mt-4 text-lg text-muted-foreground", children: heroSubtitle })
|
|
957
|
+
] }) }) }),
|
|
958
|
+
/* @__PURE__ */ jsx("section", { className: "pb-16 sm:pb-20", children: /* @__PURE__ */ jsx("div", { className: "max-w-7xl mx-auto px-4 sm:px-6 lg:px-8", children: loading ? /* @__PURE__ */ jsx("div", { className: "grid md:grid-cols-2 lg:grid-cols-3 gap-6 sm:gap-8", children: [1, 2, 3].map((i) => /* @__PURE__ */ jsxs(
|
|
959
|
+
"div",
|
|
960
|
+
{
|
|
961
|
+
className: "bg-card border rounded-xl overflow-hidden animate-pulse",
|
|
962
|
+
children: [
|
|
963
|
+
/* @__PURE__ */ jsx("div", { className: "aspect-[16/9] bg-muted" }),
|
|
964
|
+
/* @__PURE__ */ jsxs("div", { className: "p-6 space-y-3", children: [
|
|
965
|
+
/* @__PURE__ */ jsx("div", { className: "h-4 bg-muted rounded w-1/4" }),
|
|
966
|
+
/* @__PURE__ */ jsx("div", { className: "h-6 bg-muted rounded w-3/4" }),
|
|
967
|
+
/* @__PURE__ */ jsx("div", { className: "h-4 bg-muted rounded" }),
|
|
968
|
+
/* @__PURE__ */ jsx("div", { className: "h-4 bg-muted rounded w-1/2" })
|
|
969
|
+
] })
|
|
970
|
+
]
|
|
971
|
+
},
|
|
972
|
+
i
|
|
973
|
+
)) }) : error ? /* @__PURE__ */ jsx("div", { className: "text-center py-12", children: /* @__PURE__ */ jsx("p", { className: "text-muted-foreground", children: error }) }) : posts.length === 0 ? /* @__PURE__ */ jsx("div", { className: "text-center py-12", children: /* @__PURE__ */ jsx("p", { className: "text-muted-foreground", children: "No case studies yet. Check back soon!" }) }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
974
|
+
featuredPosts.length > 0 && /* @__PURE__ */ jsx("div", { className: "grid md:grid-cols-2 gap-6 sm:gap-8 mb-8", children: featuredPosts.map((post) => /* @__PURE__ */ jsx(
|
|
975
|
+
CaseStudyCard,
|
|
976
|
+
{
|
|
977
|
+
post,
|
|
978
|
+
basePath: caseStudyBasePath
|
|
979
|
+
},
|
|
980
|
+
post.id
|
|
981
|
+
)) }),
|
|
982
|
+
/* @__PURE__ */ jsx("div", { className: "grid md:grid-cols-2 lg:grid-cols-3 gap-6 sm:gap-8", children: regularPosts.map((post) => /* @__PURE__ */ jsx(
|
|
983
|
+
CaseStudyCard,
|
|
984
|
+
{
|
|
985
|
+
post,
|
|
986
|
+
basePath: caseStudyBasePath
|
|
987
|
+
},
|
|
988
|
+
post.id
|
|
989
|
+
)) }),
|
|
990
|
+
total > limit && /* @__PURE__ */ jsxs("div", { className: "flex justify-center gap-4 mt-12", children: [
|
|
991
|
+
/* @__PURE__ */ jsx(
|
|
992
|
+
"button",
|
|
993
|
+
{
|
|
994
|
+
onClick: () => setOffset(Math.max(0, offset - limit)),
|
|
995
|
+
disabled: offset === 0,
|
|
996
|
+
className: "px-5 py-2.5 text-sm font-medium border rounded-lg disabled:opacity-40 hover:bg-muted transition-colors",
|
|
997
|
+
children: "Previous"
|
|
998
|
+
}
|
|
999
|
+
),
|
|
1000
|
+
/* @__PURE__ */ jsx(
|
|
1001
|
+
"button",
|
|
1002
|
+
{
|
|
1003
|
+
onClick: () => setOffset(offset + limit),
|
|
1004
|
+
disabled: offset + limit >= total,
|
|
1005
|
+
className: "px-5 py-2.5 text-sm font-medium border rounded-lg disabled:opacity-40 hover:bg-muted transition-colors",
|
|
1006
|
+
children: "Next"
|
|
1007
|
+
}
|
|
1008
|
+
)
|
|
1009
|
+
] })
|
|
1010
|
+
] }) }) })
|
|
1011
|
+
] });
|
|
1012
|
+
}
|
|
1013
|
+
function CaseStudyPage(props) {
|
|
1014
|
+
const {
|
|
1015
|
+
productName,
|
|
1016
|
+
caseStudiesListUrl = "/case-studies",
|
|
1017
|
+
backLabel = "Back to Case Studies",
|
|
1018
|
+
className = ""
|
|
1019
|
+
} = props;
|
|
1020
|
+
const client = useWebSDK();
|
|
1021
|
+
const config = useWebSDKConfig();
|
|
1022
|
+
const { product } = useProduct();
|
|
1023
|
+
const resolvedProductId = product?.id || config.productId;
|
|
1024
|
+
const [post, setPost] = useState(null);
|
|
1025
|
+
const [loading, setLoading] = useState(true);
|
|
1026
|
+
const [error, setError] = useState(null);
|
|
1027
|
+
const slug = typeof window !== "undefined" ? window.location.pathname.split("/").pop() || "" : "";
|
|
1028
|
+
useEffect(() => {
|
|
1029
|
+
if (!slug || !resolvedProductId) return;
|
|
1030
|
+
async function fetchPost() {
|
|
1031
|
+
setLoading(true);
|
|
1032
|
+
try {
|
|
1033
|
+
const result = await client.query(CASE_STUDY_QUERY, {
|
|
1034
|
+
slug,
|
|
1035
|
+
productId: resolvedProductId
|
|
1036
|
+
});
|
|
1037
|
+
if (result.errors?.length) {
|
|
1038
|
+
setError(result.errors[0]?.message ?? "Failed to load case study");
|
|
1039
|
+
return;
|
|
1040
|
+
}
|
|
1041
|
+
setPost(result.data?.publicContentPage ?? null);
|
|
1042
|
+
setError(null);
|
|
1043
|
+
} catch {
|
|
1044
|
+
setError("Failed to load case study");
|
|
1045
|
+
} finally {
|
|
1046
|
+
setLoading(false);
|
|
1047
|
+
}
|
|
1048
|
+
}
|
|
1049
|
+
fetchPost();
|
|
1050
|
+
}, [client, slug, resolvedProductId]);
|
|
1051
|
+
if (loading) {
|
|
1052
|
+
return /* @__PURE__ */ jsx("div", { className: `w-full ${className}`, children: /* @__PURE__ */ jsx("div", { className: "max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-24", children: /* @__PURE__ */ jsxs("div", { className: "animate-pulse space-y-6", children: [
|
|
1053
|
+
/* @__PURE__ */ jsx("div", { className: "h-8 bg-muted rounded w-2/3" }),
|
|
1054
|
+
/* @__PURE__ */ jsx("div", { className: "h-4 bg-muted rounded w-1/3" }),
|
|
1055
|
+
/* @__PURE__ */ jsx("div", { className: "aspect-[16/9] bg-muted rounded-xl" }),
|
|
1056
|
+
/* @__PURE__ */ jsx("div", { className: "space-y-3", children: [1, 2, 3, 4, 5].map((i) => /* @__PURE__ */ jsx("div", { className: "h-4 bg-muted rounded" }, i)) })
|
|
1057
|
+
] }) }) });
|
|
1058
|
+
}
|
|
1059
|
+
if (error || !post) {
|
|
1060
|
+
return /* @__PURE__ */ jsx("div", { className: `w-full ${className}`, children: /* @__PURE__ */ jsxs("div", { className: "max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-24 text-center", children: [
|
|
1061
|
+
/* @__PURE__ */ jsx("h1", { className: "text-2xl font-bold mb-4", children: "Case study not found" }),
|
|
1062
|
+
/* @__PURE__ */ jsx("p", { className: "text-muted-foreground mb-6", children: error || "This case study doesn't exist or has been removed." }),
|
|
1063
|
+
/* @__PURE__ */ jsxs(
|
|
1064
|
+
"a",
|
|
1065
|
+
{
|
|
1066
|
+
href: caseStudiesListUrl,
|
|
1067
|
+
className: "text-primary hover:underline font-medium",
|
|
1068
|
+
children: [
|
|
1069
|
+
"\u2190 ",
|
|
1070
|
+
backLabel
|
|
1071
|
+
]
|
|
1072
|
+
}
|
|
1073
|
+
)
|
|
1074
|
+
] }) });
|
|
1075
|
+
}
|
|
1076
|
+
const date = post.publishedAt || post.lastUpdated;
|
|
1077
|
+
const formattedDate = date ? new Date(date).toLocaleDateString("en-US", {
|
|
1078
|
+
year: "numeric",
|
|
1079
|
+
month: "long",
|
|
1080
|
+
day: "numeric"
|
|
1081
|
+
}) : "";
|
|
1082
|
+
return /* @__PURE__ */ jsxs("div", { className: `w-full ${className}`, children: [
|
|
1083
|
+
/* @__PURE__ */ jsx(
|
|
1084
|
+
PageHead,
|
|
1085
|
+
{
|
|
1086
|
+
title: post.seoTitle || post.title,
|
|
1087
|
+
description: post.seoDescription || post.excerpt || "",
|
|
1088
|
+
productName,
|
|
1089
|
+
keywords: post.seoKeywords || ""
|
|
1090
|
+
}
|
|
1091
|
+
),
|
|
1092
|
+
/* @__PURE__ */ jsxs("article", { className: "max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-16 sm:py-20", children: [
|
|
1093
|
+
/* @__PURE__ */ jsxs(
|
|
1094
|
+
"a",
|
|
1095
|
+
{
|
|
1096
|
+
href: caseStudiesListUrl,
|
|
1097
|
+
className: "inline-flex items-center gap-1 text-sm text-muted-foreground hover:text-foreground mb-8 transition-colors",
|
|
1098
|
+
children: [
|
|
1099
|
+
"\u2190 ",
|
|
1100
|
+
backLabel
|
|
1101
|
+
]
|
|
1102
|
+
}
|
|
1103
|
+
),
|
|
1104
|
+
post.category && /* @__PURE__ */ 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 }),
|
|
1105
|
+
/* @__PURE__ */ jsx("h1", { className: "text-3xl sm:text-4xl md:text-5xl font-bold tracking-tight text-foreground mb-4", children: post.title }),
|
|
1106
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 text-sm text-muted-foreground mb-8", children: [
|
|
1107
|
+
post.author && /* @__PURE__ */ jsx("span", { className: "font-medium text-foreground", children: post.author }),
|
|
1108
|
+
post.author && formattedDate && /* @__PURE__ */ jsx("span", { className: "text-muted-foreground/40", children: "\xB7" }),
|
|
1109
|
+
formattedDate && /* @__PURE__ */ jsx("span", { children: formattedDate }),
|
|
1110
|
+
post.readingTime && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1111
|
+
/* @__PURE__ */ jsx("span", { className: "text-muted-foreground/40", children: "\xB7" }),
|
|
1112
|
+
/* @__PURE__ */ jsxs("span", { children: [
|
|
1113
|
+
post.readingTime,
|
|
1114
|
+
" min read"
|
|
1115
|
+
] })
|
|
1116
|
+
] })
|
|
1117
|
+
] }),
|
|
1118
|
+
post.thumbnail && /* @__PURE__ */ jsx("div", { className: "aspect-[16/9] rounded-xl overflow-hidden mb-10", children: /* @__PURE__ */ jsx(
|
|
1119
|
+
"img",
|
|
1120
|
+
{
|
|
1121
|
+
src: post.thumbnail,
|
|
1122
|
+
alt: post.title,
|
|
1123
|
+
className: "w-full h-full object-cover"
|
|
1124
|
+
}
|
|
1125
|
+
) }),
|
|
1126
|
+
/* @__PURE__ */ jsx("div", { className: "prose prose-gray dark:prose-invert max-w-none", children: /* @__PURE__ */ jsx(ContentRenderer, { content: post.content, format: post.contentFormat }) })
|
|
1127
|
+
] })
|
|
1128
|
+
] });
|
|
1129
|
+
}
|
|
1130
|
+
function cn(...inputs) {
|
|
1131
|
+
return twMerge(clsx(inputs));
|
|
1132
|
+
}
|
|
1133
|
+
var buttonVariants = cva(
|
|
1134
|
+
"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",
|
|
1135
|
+
{
|
|
1136
|
+
variants: {
|
|
1137
|
+
variant: {
|
|
1138
|
+
default: "bg-primary text-primary-foreground shadow hover:bg-primary/90",
|
|
1139
|
+
destructive: "bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90",
|
|
1140
|
+
outline: "border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground",
|
|
1141
|
+
secondary: "bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80",
|
|
1142
|
+
ghost: "hover:bg-accent hover:text-accent-foreground",
|
|
1143
|
+
link: "text-primary underline-offset-4 hover:underline"
|
|
1144
|
+
},
|
|
1145
|
+
size: {
|
|
1146
|
+
default: "h-9 px-4 py-2",
|
|
1147
|
+
sm: "h-8 rounded-md px-3 text-xs",
|
|
1148
|
+
lg: "h-10 rounded-md px-8",
|
|
1149
|
+
icon: "h-9 w-9"
|
|
1150
|
+
}
|
|
1151
|
+
},
|
|
1152
|
+
defaultVariants: {
|
|
1153
|
+
variant: "default",
|
|
1154
|
+
size: "default"
|
|
1155
|
+
}
|
|
1156
|
+
}
|
|
1157
|
+
);
|
|
1158
|
+
var Button = React.forwardRef(
|
|
1159
|
+
({ className, variant, size, asChild = false, ...props }, ref) => {
|
|
1160
|
+
const Comp = asChild ? Slot : "button";
|
|
1161
|
+
return /* @__PURE__ */ jsx(
|
|
1162
|
+
Comp,
|
|
1163
|
+
{
|
|
1164
|
+
className: cn(buttonVariants({ variant, size, className })),
|
|
1165
|
+
ref,
|
|
1166
|
+
...props
|
|
1167
|
+
}
|
|
1168
|
+
);
|
|
1169
|
+
}
|
|
1170
|
+
);
|
|
1171
|
+
Button.displayName = "Button";
|
|
1172
|
+
var Input = React.forwardRef(
|
|
1173
|
+
({ className, type, ...props }, ref) => {
|
|
1174
|
+
return /* @__PURE__ */ jsx(
|
|
1175
|
+
"input",
|
|
1176
|
+
{
|
|
1177
|
+
type,
|
|
1178
|
+
className: cn(
|
|
1179
|
+
"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",
|
|
1180
|
+
className
|
|
1181
|
+
),
|
|
1182
|
+
ref,
|
|
1183
|
+
...props
|
|
1184
|
+
}
|
|
1185
|
+
);
|
|
1186
|
+
}
|
|
1187
|
+
);
|
|
1188
|
+
Input.displayName = "Input";
|
|
1189
|
+
var CHANGELOG_TAG = "changelog";
|
|
1190
|
+
var CHANGELOG_LIST_QUERY = `
|
|
1191
|
+
query ChangelogList($productId: String!, $limit: Int, $offset: Int) {
|
|
1192
|
+
publicContentPages(productId: $productId, limit: $limit, offset: $offset) {
|
|
1193
|
+
items {
|
|
1194
|
+
id
|
|
1195
|
+
title
|
|
1196
|
+
slug
|
|
1197
|
+
lastUpdated
|
|
1198
|
+
tags
|
|
1199
|
+
excerpt
|
|
1200
|
+
seoDescription
|
|
1201
|
+
thumbnail
|
|
1202
|
+
author
|
|
1203
|
+
publishedAt
|
|
1204
|
+
readingTime
|
|
1205
|
+
featured
|
|
1206
|
+
category
|
|
1207
|
+
}
|
|
1208
|
+
total
|
|
1209
|
+
hasMore
|
|
1210
|
+
}
|
|
1211
|
+
}
|
|
1212
|
+
`;
|
|
1213
|
+
var CHANGELOG_ENTRY_QUERY = `
|
|
1214
|
+
query ChangelogEntry($slug: String!, $productId: String) {
|
|
1215
|
+
publicContentPage(slug: $slug, productId: $productId) {
|
|
1216
|
+
content
|
|
1217
|
+
contentFormat
|
|
1218
|
+
}
|
|
1219
|
+
}
|
|
1220
|
+
`;
|
|
1221
|
+
function formatDate(value) {
|
|
1222
|
+
if (!value) return "";
|
|
1223
|
+
return new Date(value).toLocaleDateString("en-US", {
|
|
1224
|
+
year: "numeric",
|
|
1225
|
+
month: "long",
|
|
1226
|
+
day: "numeric"
|
|
1227
|
+
});
|
|
1228
|
+
}
|
|
1229
|
+
function sortByDateDesc(a, b) {
|
|
1230
|
+
const da = new Date(a.publishedAt || a.lastUpdated || 0).getTime();
|
|
1231
|
+
const db = new Date(b.publishedAt || b.lastUpdated || 0).getTime();
|
|
1232
|
+
return db - da;
|
|
1233
|
+
}
|
|
1234
|
+
function normalize(value) {
|
|
1235
|
+
return value.toLowerCase().trim();
|
|
1236
|
+
}
|
|
1237
|
+
function paginate(items, page, pageSize) {
|
|
1238
|
+
const start = (page - 1) * pageSize;
|
|
1239
|
+
return items.slice(start, start + pageSize);
|
|
1240
|
+
}
|
|
1241
|
+
function parseEntryDate(date) {
|
|
1242
|
+
return new Date(date).getTime();
|
|
1243
|
+
}
|
|
1244
|
+
function useCmsChangelog() {
|
|
1245
|
+
const client = useWebSDK();
|
|
1246
|
+
const config = useWebSDKConfig();
|
|
1247
|
+
const { product } = useProduct();
|
|
1248
|
+
const resolvedProductId = product?.id || config.productId;
|
|
1249
|
+
const [entries, setEntries] = useState([]);
|
|
1250
|
+
const [loading, setLoading] = useState(true);
|
|
1251
|
+
const [error, setError] = useState(null);
|
|
1252
|
+
const fetchEntries = useCallback(async () => {
|
|
1253
|
+
if (!resolvedProductId) {
|
|
1254
|
+
setLoading(false);
|
|
1255
|
+
return;
|
|
1256
|
+
}
|
|
1257
|
+
setLoading(true);
|
|
1258
|
+
try {
|
|
1259
|
+
const listResult = await client.query(CHANGELOG_LIST_QUERY, {
|
|
1260
|
+
productId: resolvedProductId,
|
|
1261
|
+
limit: 50,
|
|
1262
|
+
offset: 0
|
|
1263
|
+
});
|
|
1264
|
+
if (listResult.errors?.length) {
|
|
1265
|
+
setError(listResult.errors[0]?.message ?? "Failed to load changelog");
|
|
1266
|
+
return;
|
|
1267
|
+
}
|
|
1268
|
+
const changelogItems = (listResult.data?.publicContentPages?.items || []).filter((p) => p.tags?.includes(CHANGELOG_TAG)).sort(sortByDateDesc);
|
|
1269
|
+
const resolved = await Promise.all(
|
|
1270
|
+
changelogItems.map(async (item) => {
|
|
1271
|
+
try {
|
|
1272
|
+
const detail = await client.query(CHANGELOG_ENTRY_QUERY, {
|
|
1273
|
+
slug: item.slug,
|
|
1274
|
+
productId: resolvedProductId
|
|
1275
|
+
});
|
|
1276
|
+
const page = detail.data?.publicContentPage;
|
|
1277
|
+
return {
|
|
1278
|
+
...item,
|
|
1279
|
+
content: page?.content,
|
|
1280
|
+
contentFormat: page?.contentFormat
|
|
1281
|
+
};
|
|
1282
|
+
} catch {
|
|
1283
|
+
return { ...item };
|
|
1284
|
+
}
|
|
1285
|
+
})
|
|
1286
|
+
);
|
|
1287
|
+
setEntries(resolved);
|
|
1288
|
+
setError(null);
|
|
1289
|
+
} catch {
|
|
1290
|
+
setError("Failed to load changelog");
|
|
1291
|
+
} finally {
|
|
1292
|
+
setLoading(false);
|
|
1293
|
+
}
|
|
1294
|
+
}, [client, resolvedProductId]);
|
|
1295
|
+
useEffect(() => {
|
|
1296
|
+
fetchEntries();
|
|
1297
|
+
}, [fetchEntries]);
|
|
1298
|
+
return { entries, loading, error };
|
|
1299
|
+
}
|
|
1300
|
+
function CmsTimeline(props) {
|
|
1301
|
+
const { entries, loading, error } = props;
|
|
1302
|
+
if (loading) {
|
|
1303
|
+
return /* @__PURE__ */ jsx("div", { className: "max-w-3xl mx-auto space-y-12", children: [1, 2, 3].map((i) => /* @__PURE__ */ jsx(
|
|
1304
|
+
"div",
|
|
1305
|
+
{
|
|
1306
|
+
className: "rounded-2xl border border-border/40 bg-card overflow-hidden animate-pulse",
|
|
1307
|
+
children: /* @__PURE__ */ jsxs("div", { className: "p-6 sm:p-8 space-y-4", children: [
|
|
1308
|
+
/* @__PURE__ */ jsx("div", { className: "h-3 bg-muted rounded w-1/3" }),
|
|
1309
|
+
/* @__PURE__ */ jsx("div", { className: "h-6 bg-muted rounded w-2/3" }),
|
|
1310
|
+
/* @__PURE__ */ jsx("div", { className: "h-4 bg-muted rounded" }),
|
|
1311
|
+
/* @__PURE__ */ jsx("div", { className: "h-4 bg-muted rounded w-5/6" })
|
|
1312
|
+
] })
|
|
1313
|
+
},
|
|
1314
|
+
i
|
|
1315
|
+
)) });
|
|
1316
|
+
}
|
|
1317
|
+
if (error) {
|
|
1318
|
+
return /* @__PURE__ */ jsx("div", { className: "text-center py-12", children: /* @__PURE__ */ jsx("p", { className: "text-muted-foreground", children: error }) });
|
|
1319
|
+
}
|
|
1320
|
+
if (entries.length === 0) {
|
|
1321
|
+
return /* @__PURE__ */ jsx("div", { className: "text-center py-12", children: /* @__PURE__ */ jsx("p", { className: "text-muted-foreground", children: "No changelog entries yet. Check back soon!" }) });
|
|
1322
|
+
}
|
|
1323
|
+
return /* @__PURE__ */ jsx("div", { className: "max-w-3xl mx-auto space-y-12", children: entries.map((entry) => /* @__PURE__ */ jsxs(
|
|
1324
|
+
"article",
|
|
1325
|
+
{
|
|
1326
|
+
id: entry.slug,
|
|
1327
|
+
className: "rounded-2xl border border-border/40 bg-card overflow-hidden scroll-mt-24",
|
|
1328
|
+
children: [
|
|
1329
|
+
entry.thumbnail && /* @__PURE__ */ jsx(
|
|
1330
|
+
"img",
|
|
1331
|
+
{
|
|
1332
|
+
src: entry.thumbnail,
|
|
1333
|
+
alt: "",
|
|
1334
|
+
width: 1024,
|
|
1335
|
+
height: 576,
|
|
1336
|
+
loading: "lazy",
|
|
1337
|
+
decoding: "async",
|
|
1338
|
+
className: "w-full aspect-video object-cover border-b border-border/40"
|
|
1339
|
+
}
|
|
1340
|
+
),
|
|
1341
|
+
/* @__PURE__ */ jsxs("div", { className: "p-6 sm:p-8", children: [
|
|
1342
|
+
/* @__PURE__ */ jsxs("p", { className: "text-xs text-muted-foreground mb-2", children: [
|
|
1343
|
+
entry.publishedAt && /* @__PURE__ */ jsx("time", { dateTime: entry.publishedAt, children: formatDate(entry.publishedAt || entry.lastUpdated) }),
|
|
1344
|
+
entry.author && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1345
|
+
" \xB7 ",
|
|
1346
|
+
entry.author
|
|
1347
|
+
] })
|
|
1348
|
+
] }),
|
|
1349
|
+
/* @__PURE__ */ jsx("h2", { className: "text-xl sm:text-2xl font-bold text-foreground mb-4", children: entry.title }),
|
|
1350
|
+
entry.content ? /* @__PURE__ */ jsx("div", { className: "prose prose-sm prose-gray dark:prose-invert max-w-none", children: /* @__PURE__ */ jsx(
|
|
1351
|
+
ContentRenderer,
|
|
1352
|
+
{
|
|
1353
|
+
content: entry.content,
|
|
1354
|
+
format: entry.contentFormat
|
|
1355
|
+
}
|
|
1356
|
+
) }) : entry.excerpt && /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground leading-relaxed", children: entry.excerpt })
|
|
1357
|
+
] })
|
|
1358
|
+
]
|
|
1359
|
+
},
|
|
1360
|
+
entry.id
|
|
1361
|
+
)) });
|
|
1362
|
+
}
|
|
1363
|
+
function EntryContent({ content }) {
|
|
1364
|
+
if (typeof content === "string") {
|
|
1365
|
+
return /* @__PURE__ */ jsx("div", { className: "prose prose-sm prose-gray dark:prose-invert max-w-none", children: /* @__PURE__ */ jsx(ContentRenderer, { content, format: "markdown" }) });
|
|
1366
|
+
}
|
|
1367
|
+
return /* @__PURE__ */ jsx("div", { className: "prose prose-sm max-w-none", children: content });
|
|
1368
|
+
}
|
|
1369
|
+
function EntriesTimeline(props) {
|
|
1370
|
+
const { entries, pageSize, productName } = props;
|
|
1371
|
+
const [query, setQuery] = useState("");
|
|
1372
|
+
const [selectedCategory, setSelectedCategory] = useState(null);
|
|
1373
|
+
const [page, setPage] = useState(1);
|
|
1374
|
+
const categories = useMemo(
|
|
1375
|
+
() => Array.from(new Set(entries.map((e) => e.category))).sort(),
|
|
1376
|
+
[entries]
|
|
1377
|
+
);
|
|
1378
|
+
const filtered = useMemo(() => {
|
|
1379
|
+
const normalized = normalize(query);
|
|
1380
|
+
return entries.filter((entry) => {
|
|
1381
|
+
if (selectedCategory && entry.category !== selectedCategory) {
|
|
1382
|
+
return false;
|
|
1383
|
+
}
|
|
1384
|
+
if (!normalized) return true;
|
|
1385
|
+
const haystack = [
|
|
1386
|
+
entry.title,
|
|
1387
|
+
entry.version,
|
|
1388
|
+
entry.category,
|
|
1389
|
+
...entry.tags ?? [],
|
|
1390
|
+
typeof entry.content === "string" ? entry.content : ""
|
|
1391
|
+
].join(" ").toLowerCase();
|
|
1392
|
+
return haystack.includes(normalized);
|
|
1393
|
+
}).sort((a, b) => parseEntryDate(b.date) - parseEntryDate(a.date));
|
|
1394
|
+
}, [entries, query, selectedCategory]);
|
|
1395
|
+
const totalPages = Math.max(1, Math.ceil(filtered.length / pageSize));
|
|
1396
|
+
const safePage = Math.min(page, totalPages);
|
|
1397
|
+
const pageItems = paginate(filtered, safePage, pageSize);
|
|
1398
|
+
useEffect(() => {
|
|
1399
|
+
setPage(1);
|
|
1400
|
+
}, [query, selectedCategory]);
|
|
1401
|
+
if (entries.length === 0) {
|
|
1402
|
+
return /* @__PURE__ */ jsx("div", { className: "text-center py-12", children: /* @__PURE__ */ jsx("p", { className: "text-muted-foreground", children: "No changelog entries yet. Check back soon!" }) });
|
|
1403
|
+
}
|
|
1404
|
+
return /* @__PURE__ */ jsxs("div", { className: "max-w-3xl mx-auto", children: [
|
|
1405
|
+
/* @__PURE__ */ jsxs("div", { className: "mb-8 space-y-4", children: [
|
|
1406
|
+
/* @__PURE__ */ jsxs("div", { className: "relative", children: [
|
|
1407
|
+
/* @__PURE__ */ jsx(
|
|
1408
|
+
Search,
|
|
1409
|
+
{
|
|
1410
|
+
className: "absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground",
|
|
1411
|
+
"aria-hidden": "true"
|
|
1412
|
+
}
|
|
1413
|
+
),
|
|
1414
|
+
/* @__PURE__ */ jsx(
|
|
1415
|
+
Input,
|
|
1416
|
+
{
|
|
1417
|
+
type: "search",
|
|
1418
|
+
placeholder: "Search releases, features, fixes\u2026",
|
|
1419
|
+
value: query,
|
|
1420
|
+
onChange: (e) => setQuery(e.target.value),
|
|
1421
|
+
className: "pl-9",
|
|
1422
|
+
"aria-label": "Search changelog"
|
|
1423
|
+
}
|
|
1424
|
+
)
|
|
1425
|
+
] }),
|
|
1426
|
+
categories.length > 0 && /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap gap-2", children: [
|
|
1427
|
+
/* @__PURE__ */ jsx(
|
|
1428
|
+
"button",
|
|
1429
|
+
{
|
|
1430
|
+
type: "button",
|
|
1431
|
+
onClick: () => setSelectedCategory(null),
|
|
1432
|
+
className: cn(
|
|
1433
|
+
"px-3 py-1 rounded-full text-xs font-medium border transition-colors",
|
|
1434
|
+
selectedCategory === null ? "bg-primary text-primary-foreground border-primary" : "bg-background text-muted-foreground border-border/60 hover:border-primary/40"
|
|
1435
|
+
),
|
|
1436
|
+
children: "All"
|
|
1437
|
+
}
|
|
1438
|
+
),
|
|
1439
|
+
categories.map((category) => /* @__PURE__ */ jsx(
|
|
1440
|
+
"button",
|
|
1441
|
+
{
|
|
1442
|
+
type: "button",
|
|
1443
|
+
onClick: () => setSelectedCategory(category),
|
|
1444
|
+
className: cn(
|
|
1445
|
+
"px-3 py-1 rounded-full text-xs font-medium border transition-colors",
|
|
1446
|
+
selectedCategory === category ? "bg-primary text-primary-foreground border-primary" : "bg-background text-muted-foreground border-border/60 hover:border-primary/40"
|
|
1447
|
+
),
|
|
1448
|
+
children: category
|
|
1449
|
+
},
|
|
1450
|
+
category
|
|
1451
|
+
))
|
|
1452
|
+
] })
|
|
1453
|
+
] }),
|
|
1454
|
+
/* @__PURE__ */ jsxs("div", { className: "relative", children: [
|
|
1455
|
+
/* @__PURE__ */ jsx(
|
|
1456
|
+
"div",
|
|
1457
|
+
{
|
|
1458
|
+
className: "absolute left-[1.125rem] top-3 bottom-3 w-px bg-border/60 hidden sm:block",
|
|
1459
|
+
"aria-hidden": "true"
|
|
1460
|
+
}
|
|
1461
|
+
),
|
|
1462
|
+
/* @__PURE__ */ jsx("div", { className: "space-y-8", children: pageItems.map((entry) => /* @__PURE__ */ jsxs(
|
|
1463
|
+
"article",
|
|
1464
|
+
{
|
|
1465
|
+
className: "relative sm:pl-12",
|
|
1466
|
+
children: [
|
|
1467
|
+
/* @__PURE__ */ jsx(
|
|
1468
|
+
"div",
|
|
1469
|
+
{
|
|
1470
|
+
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",
|
|
1471
|
+
"aria-hidden": "true",
|
|
1472
|
+
children: /* @__PURE__ */ jsx("span", { className: "h-2 w-2 rounded-full bg-primary-foreground" })
|
|
1473
|
+
}
|
|
1474
|
+
),
|
|
1475
|
+
/* @__PURE__ */ jsx("div", { className: "rounded-2xl border border-border/40 bg-card overflow-hidden", children: /* @__PURE__ */ jsxs("div", { className: "p-6 sm:p-8", children: [
|
|
1476
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-2 mb-3", children: [
|
|
1477
|
+
/* @__PURE__ */ 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 }),
|
|
1478
|
+
/* @__PURE__ */ 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: [
|
|
1479
|
+
"v",
|
|
1480
|
+
entry.version
|
|
1481
|
+
] }),
|
|
1482
|
+
/* @__PURE__ */ jsx(
|
|
1483
|
+
"time",
|
|
1484
|
+
{
|
|
1485
|
+
className: "text-xs text-muted-foreground",
|
|
1486
|
+
dateTime: entry.date,
|
|
1487
|
+
children: formatDate(entry.date)
|
|
1488
|
+
}
|
|
1489
|
+
)
|
|
1490
|
+
] }),
|
|
1491
|
+
entry.tags && entry.tags.length > 0 && /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-1.5 mb-3", children: entry.tags.map((tag) => /* @__PURE__ */ jsx(
|
|
1492
|
+
"span",
|
|
1493
|
+
{
|
|
1494
|
+
className: "text-[10px] uppercase tracking-wide text-muted-foreground bg-background border border-border/40 px-1.5 py-0.5 rounded",
|
|
1495
|
+
children: tag
|
|
1496
|
+
},
|
|
1497
|
+
tag
|
|
1498
|
+
)) }),
|
|
1499
|
+
/* @__PURE__ */ jsx("h2", { className: "text-xl sm:text-2xl font-bold text-foreground mb-4", children: entry.title }),
|
|
1500
|
+
/* @__PURE__ */ jsx(EntryContent, { content: entry.content })
|
|
1501
|
+
] }) })
|
|
1502
|
+
]
|
|
1503
|
+
},
|
|
1504
|
+
`${entry.version}-${entry.date}`
|
|
1505
|
+
)) })
|
|
1506
|
+
] }),
|
|
1507
|
+
pageItems.length === 0 && /* @__PURE__ */ jsx("div", { className: "text-center py-12", children: /* @__PURE__ */ jsx("p", { className: "text-muted-foreground", children: "No entries match your filters." }) }),
|
|
1508
|
+
totalPages > 1 && /* @__PURE__ */ jsxs("div", { className: "mt-10 flex items-center justify-between", children: [
|
|
1509
|
+
/* @__PURE__ */ jsxs("p", { className: "text-sm text-muted-foreground", children: [
|
|
1510
|
+
"Page ",
|
|
1511
|
+
safePage,
|
|
1512
|
+
" of ",
|
|
1513
|
+
totalPages,
|
|
1514
|
+
" \xB7 ",
|
|
1515
|
+
filtered.length,
|
|
1516
|
+
" entries"
|
|
1517
|
+
] }),
|
|
1518
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
1519
|
+
/* @__PURE__ */ jsx(
|
|
1520
|
+
Button,
|
|
1521
|
+
{
|
|
1522
|
+
variant: "outline",
|
|
1523
|
+
size: "sm",
|
|
1524
|
+
onClick: () => setPage((p) => Math.max(1, p - 1)),
|
|
1525
|
+
disabled: safePage <= 1,
|
|
1526
|
+
"aria-label": "Previous page",
|
|
1527
|
+
children: /* @__PURE__ */ jsx(ChevronLeft, { className: "h-4 w-4" })
|
|
1528
|
+
}
|
|
1529
|
+
),
|
|
1530
|
+
/* @__PURE__ */ jsx(
|
|
1531
|
+
Button,
|
|
1532
|
+
{
|
|
1533
|
+
variant: "outline",
|
|
1534
|
+
size: "sm",
|
|
1535
|
+
onClick: () => setPage((p) => Math.min(totalPages, p + 1)),
|
|
1536
|
+
disabled: safePage >= totalPages,
|
|
1537
|
+
"aria-label": "Next page",
|
|
1538
|
+
children: /* @__PURE__ */ jsx(ChevronRight, { className: "h-4 w-4" })
|
|
1539
|
+
}
|
|
1540
|
+
)
|
|
1541
|
+
] })
|
|
1542
|
+
] }),
|
|
1543
|
+
productName && pageItems.length > 0 && /* @__PURE__ */ jsx("script", { type: "application/ld+json", children: JSON.stringify({
|
|
1544
|
+
"@context": "https://schema.org",
|
|
1545
|
+
"@type": "ItemList",
|
|
1546
|
+
name: `${productName} Changelog`,
|
|
1547
|
+
itemListElement: pageItems.map((entry, index) => ({
|
|
1548
|
+
"@type": "ListItem",
|
|
1549
|
+
position: (safePage - 1) * pageSize + index + 1,
|
|
1550
|
+
name: entry.title,
|
|
1551
|
+
description: typeof entry.content === "string" ? entry.content.slice(0, 200) : void 0,
|
|
1552
|
+
datePublished: entry.date
|
|
1553
|
+
}))
|
|
1554
|
+
}) })
|
|
1555
|
+
] });
|
|
1556
|
+
}
|
|
1557
|
+
function ChangelogPage(props) {
|
|
1558
|
+
const {
|
|
1559
|
+
productName,
|
|
1560
|
+
heroEyebrow = "What's new",
|
|
1561
|
+
heroTitle = "Changelog",
|
|
1562
|
+
heroSubtitle = "Platform updates, improvements, and announcements \u2014 newest first.",
|
|
1563
|
+
ctaTitle,
|
|
1564
|
+
ctaSubtitle,
|
|
1565
|
+
ctaHref,
|
|
1566
|
+
ctaLabel,
|
|
1567
|
+
className = "",
|
|
1568
|
+
seoTitle,
|
|
1569
|
+
seoDescription,
|
|
1570
|
+
seoKeywords,
|
|
1571
|
+
entries,
|
|
1572
|
+
pageSize = 10
|
|
1573
|
+
} = props;
|
|
1574
|
+
const { product } = useProduct();
|
|
1575
|
+
const resolvedProductName = productName || product?.name;
|
|
1576
|
+
const cms = useCmsChangelog();
|
|
1577
|
+
const showCta = Boolean(ctaHref && ctaLabel);
|
|
1578
|
+
const isPropDriven = entries != null;
|
|
1579
|
+
return /* @__PURE__ */ jsxs("div", { className: `w-full ${className}`, children: [
|
|
1580
|
+
/* @__PURE__ */ jsx(
|
|
1581
|
+
PageHead,
|
|
1582
|
+
{
|
|
1583
|
+
title: seoTitle || heroTitle,
|
|
1584
|
+
description: seoDescription || heroSubtitle,
|
|
1585
|
+
productName: resolvedProductName,
|
|
1586
|
+
keywords: seoKeywords
|
|
1587
|
+
}
|
|
1588
|
+
),
|
|
1589
|
+
/* @__PURE__ */ jsx("section", { className: "py-16 md:py-20 px-4 sm:px-6 lg:px-8 text-center", children: /* @__PURE__ */ jsxs("div", { className: "max-w-3xl mx-auto", children: [
|
|
1590
|
+
/* @__PURE__ */ 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: [
|
|
1591
|
+
/* @__PURE__ */ jsx(History, { className: "h-3.5 w-3.5", "aria-hidden": "true" }),
|
|
1592
|
+
heroEyebrow
|
|
1593
|
+
] }),
|
|
1594
|
+
/* @__PURE__ */ jsx("h1", { className: "text-4xl sm:text-5xl font-bold text-foreground mb-4", children: heroTitle }),
|
|
1595
|
+
/* @__PURE__ */ jsx("p", { className: "text-lg text-muted-foreground", children: heroSubtitle })
|
|
1596
|
+
] }) }),
|
|
1597
|
+
/* @__PURE__ */ jsx("section", { className: "px-4 sm:px-6 lg:px-8 pb-20", children: isPropDriven ? /* @__PURE__ */ jsx(
|
|
1598
|
+
EntriesTimeline,
|
|
1599
|
+
{
|
|
1600
|
+
entries,
|
|
1601
|
+
pageSize,
|
|
1602
|
+
productName: resolvedProductName
|
|
1603
|
+
}
|
|
1604
|
+
) : /* @__PURE__ */ jsx(
|
|
1605
|
+
CmsTimeline,
|
|
1606
|
+
{
|
|
1607
|
+
entries: cms.entries,
|
|
1608
|
+
loading: cms.loading,
|
|
1609
|
+
error: cms.error
|
|
1610
|
+
}
|
|
1611
|
+
) }),
|
|
1612
|
+
showCta && /* @__PURE__ */ jsx("section", { className: "py-14 px-4 sm:px-6 lg:px-8 bg-primary/5 border-t border-border/40", children: /* @__PURE__ */ jsxs("div", { className: "max-w-2xl mx-auto text-center", children: [
|
|
1613
|
+
ctaTitle && /* @__PURE__ */ jsx("h2", { className: "text-2xl font-bold text-foreground mb-3", children: ctaTitle }),
|
|
1614
|
+
ctaSubtitle && /* @__PURE__ */ jsx("p", { className: "text-muted-foreground mb-7", children: ctaSubtitle }),
|
|
1615
|
+
/* @__PURE__ */ jsx(
|
|
1616
|
+
"a",
|
|
1617
|
+
{
|
|
1618
|
+
href: ctaHref,
|
|
1619
|
+
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",
|
|
1620
|
+
children: ctaLabel
|
|
1621
|
+
}
|
|
1622
|
+
)
|
|
1623
|
+
] }) })
|
|
1624
|
+
] });
|
|
1625
|
+
}
|
|
1626
|
+
|
|
1627
|
+
// src/content/utils.ts
|
|
1628
|
+
function formatBytes(bytes) {
|
|
1629
|
+
if (bytes == null) return "";
|
|
1630
|
+
if (bytes < 1024) return `${bytes} B`;
|
|
1631
|
+
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(0)} KB`;
|
|
1632
|
+
return `${(bytes / 1024 / 1024).toFixed(1)} MB`;
|
|
1633
|
+
}
|
|
1634
|
+
function fileExt(file) {
|
|
1635
|
+
const name = file.fileName || file.fileUrl.split("/").pop()?.split("?")[0] || "";
|
|
1636
|
+
const ext = name.split(".").pop();
|
|
1637
|
+
return ext && ext !== name ? ext.toUpperCase() : "FILE";
|
|
1638
|
+
}
|
|
1639
|
+
async function downloadFile(e, file) {
|
|
1640
|
+
e.preventDefault();
|
|
1641
|
+
const openInTab = () => window.open(file.fileUrl, "_blank", "noopener,noreferrer");
|
|
1642
|
+
const name = file.fileName || file.fileUrl.split("/").pop()?.split("?")[0] || file.title || "download";
|
|
1643
|
+
try {
|
|
1644
|
+
const res = await fetch(file.fileUrl, { mode: "cors" });
|
|
1645
|
+
if (!res.ok) {
|
|
1646
|
+
openInTab();
|
|
1647
|
+
return;
|
|
1648
|
+
}
|
|
1649
|
+
const blob = await res.blob();
|
|
1650
|
+
const objUrl = URL.createObjectURL(blob);
|
|
1651
|
+
const a = document.createElement("a");
|
|
1652
|
+
a.href = objUrl;
|
|
1653
|
+
a.download = name;
|
|
1654
|
+
document.body.appendChild(a);
|
|
1655
|
+
a.click();
|
|
1656
|
+
a.remove();
|
|
1657
|
+
URL.revokeObjectURL(objUrl);
|
|
1658
|
+
} catch {
|
|
1659
|
+
openInTab();
|
|
1660
|
+
}
|
|
1661
|
+
}
|
|
1662
|
+
var PRESS_KIT_QUERY = `
|
|
1663
|
+
query PublicPressKit($productId: String!, $limit: Int, $offset: Int) {
|
|
1664
|
+
publicPressKitAssets(productId: $productId, limit: $limit, offset: $offset) {
|
|
1665
|
+
items {
|
|
1666
|
+
id
|
|
1667
|
+
category
|
|
1668
|
+
title
|
|
1669
|
+
slug
|
|
1670
|
+
description
|
|
1671
|
+
fileUrl
|
|
1672
|
+
fileName
|
|
1673
|
+
fileSize
|
|
1674
|
+
mimeType
|
|
1675
|
+
thumbnailUrl
|
|
1676
|
+
version
|
|
1677
|
+
tags
|
|
1678
|
+
sortOrder
|
|
1679
|
+
updatedAt
|
|
1680
|
+
}
|
|
1681
|
+
total
|
|
1682
|
+
hasMore
|
|
1683
|
+
}
|
|
1684
|
+
}
|
|
1685
|
+
`;
|
|
1686
|
+
var PRESS_RELEASES_QUERY = `
|
|
1687
|
+
query PublicPressReleases($productId: String!, $limit: Int, $offset: Int) {
|
|
1688
|
+
publicContentPages(productId: $productId, limit: $limit, offset: $offset) {
|
|
1689
|
+
items {
|
|
1690
|
+
id
|
|
1691
|
+
title
|
|
1692
|
+
slug
|
|
1693
|
+
lastUpdated
|
|
1694
|
+
tags
|
|
1695
|
+
seoDescription
|
|
1696
|
+
excerpt
|
|
1697
|
+
thumbnail
|
|
1698
|
+
author
|
|
1699
|
+
publishedAt
|
|
1700
|
+
readingTime
|
|
1701
|
+
featured
|
|
1702
|
+
category
|
|
1703
|
+
}
|
|
1704
|
+
total
|
|
1705
|
+
hasMore
|
|
1706
|
+
}
|
|
1707
|
+
}
|
|
1708
|
+
`;
|
|
1709
|
+
var CATEGORY_ORDER = [
|
|
1710
|
+
"LOGO",
|
|
1711
|
+
"BANNER",
|
|
1712
|
+
"SCREENSHOT",
|
|
1713
|
+
"BRAND_GUIDE",
|
|
1714
|
+
"BROCHURE",
|
|
1715
|
+
"DOCUMENT",
|
|
1716
|
+
"PRESS_RELEASE"
|
|
1717
|
+
];
|
|
1718
|
+
var CATEGORY_LABELS = {
|
|
1719
|
+
LOGO: "Logos",
|
|
1720
|
+
BANNER: "Banners",
|
|
1721
|
+
SCREENSHOT: "Screenshots",
|
|
1722
|
+
BRAND_GUIDE: "Brand Guides",
|
|
1723
|
+
BROCHURE: "Brochures",
|
|
1724
|
+
DOCUMENT: "Documents",
|
|
1725
|
+
PRESS_RELEASE: "Press Releases"
|
|
1726
|
+
};
|
|
1727
|
+
function isImageAsset(a) {
|
|
1728
|
+
return (a.mimeType || "").startsWith("image/");
|
|
1729
|
+
}
|
|
1730
|
+
function PressKitLightbox({
|
|
1731
|
+
asset,
|
|
1732
|
+
onClose
|
|
1733
|
+
}) {
|
|
1734
|
+
useEffect(() => {
|
|
1735
|
+
if (!asset) return;
|
|
1736
|
+
const onKey = (e) => {
|
|
1737
|
+
if (e.key === "Escape") onClose();
|
|
1738
|
+
};
|
|
1739
|
+
document.addEventListener("keydown", onKey);
|
|
1740
|
+
const prevOverflow = document.body.style.overflow;
|
|
1741
|
+
document.body.style.overflow = "hidden";
|
|
1742
|
+
return () => {
|
|
1743
|
+
document.removeEventListener("keydown", onKey);
|
|
1744
|
+
document.body.style.overflow = prevOverflow;
|
|
1745
|
+
};
|
|
1746
|
+
}, [asset, onClose]);
|
|
1747
|
+
if (!asset) return null;
|
|
1748
|
+
const ext = (asset.mimeType || "file").split("/").pop()?.toUpperCase() || "FILE";
|
|
1749
|
+
const size = formatBytes(asset.fileSize);
|
|
1750
|
+
return /* @__PURE__ */ jsx(
|
|
1751
|
+
"div",
|
|
1752
|
+
{
|
|
1753
|
+
role: "dialog",
|
|
1754
|
+
"aria-modal": "true",
|
|
1755
|
+
"aria-label": `${asset.title} preview`,
|
|
1756
|
+
onClick: onClose,
|
|
1757
|
+
className: "fixed inset-0 z-50 flex items-center justify-center bg-black/80 p-4 sm:p-8",
|
|
1758
|
+
children: /* @__PURE__ */ jsxs(
|
|
1759
|
+
"div",
|
|
1760
|
+
{
|
|
1761
|
+
onClick: (e) => e.stopPropagation(),
|
|
1762
|
+
className: "relative flex max-h-full w-full max-w-4xl flex-col overflow-hidden rounded-xl border bg-card shadow-2xl",
|
|
1763
|
+
children: [
|
|
1764
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-4 border-b p-4", children: [
|
|
1765
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
|
|
1766
|
+
/* @__PURE__ */ jsx("h3", { className: "truncate text-sm font-semibold text-foreground", children: asset.title }),
|
|
1767
|
+
/* @__PURE__ */ jsxs("p", { className: "text-xs text-muted-foreground", children: [
|
|
1768
|
+
ext,
|
|
1769
|
+
size ? ` \xB7 ${size}` : ""
|
|
1770
|
+
] })
|
|
1771
|
+
] }),
|
|
1772
|
+
/* @__PURE__ */ jsx(
|
|
1773
|
+
"button",
|
|
1774
|
+
{
|
|
1775
|
+
type: "button",
|
|
1776
|
+
onClick: onClose,
|
|
1777
|
+
"aria-label": "Close preview",
|
|
1778
|
+
className: "shrink-0 rounded-md p-1.5 text-muted-foreground transition-colors hover:bg-muted hover:text-foreground",
|
|
1779
|
+
children: /* @__PURE__ */ jsx(
|
|
1780
|
+
"svg",
|
|
1781
|
+
{
|
|
1782
|
+
className: "h-5 w-5",
|
|
1783
|
+
fill: "none",
|
|
1784
|
+
viewBox: "0 0 24 24",
|
|
1785
|
+
stroke: "currentColor",
|
|
1786
|
+
strokeWidth: 2,
|
|
1787
|
+
children: /* @__PURE__ */ jsx(
|
|
1788
|
+
"path",
|
|
1789
|
+
{
|
|
1790
|
+
strokeLinecap: "round",
|
|
1791
|
+
strokeLinejoin: "round",
|
|
1792
|
+
d: "M6 18L18 6M6 6l12 12"
|
|
1793
|
+
}
|
|
1794
|
+
)
|
|
1795
|
+
}
|
|
1796
|
+
)
|
|
1797
|
+
}
|
|
1798
|
+
)
|
|
1799
|
+
] }),
|
|
1800
|
+
/* @__PURE__ */ jsx("div", { className: "flex flex-1 items-center justify-center overflow-auto bg-muted/20 p-4", children: /* @__PURE__ */ jsx(
|
|
1801
|
+
"img",
|
|
1802
|
+
{
|
|
1803
|
+
src: asset.fileUrl,
|
|
1804
|
+
alt: asset.title,
|
|
1805
|
+
className: "max-h-[70vh] w-auto max-w-full object-contain"
|
|
1806
|
+
}
|
|
1807
|
+
) }),
|
|
1808
|
+
/* @__PURE__ */ jsx("div", { className: "flex justify-end border-t p-4", children: /* @__PURE__ */ jsxs(
|
|
1809
|
+
"a",
|
|
1810
|
+
{
|
|
1811
|
+
href: asset.fileUrl,
|
|
1812
|
+
target: "_blank",
|
|
1813
|
+
rel: "noopener noreferrer",
|
|
1814
|
+
download: true,
|
|
1815
|
+
onClick: (e) => {
|
|
1816
|
+
void downloadFile(e, asset);
|
|
1817
|
+
},
|
|
1818
|
+
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",
|
|
1819
|
+
children: [
|
|
1820
|
+
/* @__PURE__ */ jsx(
|
|
1821
|
+
"svg",
|
|
1822
|
+
{
|
|
1823
|
+
className: "h-4 w-4",
|
|
1824
|
+
fill: "none",
|
|
1825
|
+
viewBox: "0 0 24 24",
|
|
1826
|
+
stroke: "currentColor",
|
|
1827
|
+
strokeWidth: 2,
|
|
1828
|
+
children: /* @__PURE__ */ jsx(
|
|
1829
|
+
"path",
|
|
1830
|
+
{
|
|
1831
|
+
strokeLinecap: "round",
|
|
1832
|
+
strokeLinejoin: "round",
|
|
1833
|
+
d: "M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4"
|
|
1834
|
+
}
|
|
1835
|
+
)
|
|
1836
|
+
}
|
|
1837
|
+
),
|
|
1838
|
+
"Download"
|
|
1839
|
+
]
|
|
1840
|
+
}
|
|
1841
|
+
) })
|
|
1842
|
+
]
|
|
1843
|
+
}
|
|
1844
|
+
)
|
|
1845
|
+
}
|
|
1846
|
+
);
|
|
1847
|
+
}
|
|
1848
|
+
function AssetCard({
|
|
1849
|
+
asset,
|
|
1850
|
+
onPreview
|
|
1851
|
+
}) {
|
|
1852
|
+
const date = asset.updatedAt ? new Date(asset.updatedAt).toLocaleDateString("en-US", {
|
|
1853
|
+
year: "numeric",
|
|
1854
|
+
month: "short",
|
|
1855
|
+
day: "numeric"
|
|
1856
|
+
}) : "";
|
|
1857
|
+
const ext = (asset.mimeType || "file").split("/").pop()?.toUpperCase() || "FILE";
|
|
1858
|
+
const size = formatBytes(asset.fileSize);
|
|
1859
|
+
const [imgError, setImgError] = useState(false);
|
|
1860
|
+
const thumb = asset.thumbnailUrl && !imgError ? /* @__PURE__ */ jsx("div", { className: "aspect-[16/9] overflow-hidden bg-muted/30", children: /* @__PURE__ */ jsx(
|
|
1861
|
+
"img",
|
|
1862
|
+
{
|
|
1863
|
+
src: asset.thumbnailUrl,
|
|
1864
|
+
alt: asset.title,
|
|
1865
|
+
className: "w-full h-full object-contain group-hover:scale-105 transition-transform duration-300",
|
|
1866
|
+
loading: "lazy",
|
|
1867
|
+
onError: () => setImgError(true)
|
|
1868
|
+
}
|
|
1869
|
+
) }) : /* @__PURE__ */ jsx("div", { className: "aspect-[16/9] bg-muted/50 flex items-center justify-center", children: /* @__PURE__ */ jsx(
|
|
1870
|
+
"svg",
|
|
1871
|
+
{
|
|
1872
|
+
className: "w-12 h-12 text-muted-foreground/20",
|
|
1873
|
+
fill: "none",
|
|
1874
|
+
viewBox: "0 0 24 24",
|
|
1875
|
+
stroke: "currentColor",
|
|
1876
|
+
children: /* @__PURE__ */ jsx(
|
|
1877
|
+
"path",
|
|
1878
|
+
{
|
|
1879
|
+
strokeLinecap: "round",
|
|
1880
|
+
strokeLinejoin: "round",
|
|
1881
|
+
strokeWidth: 1.5,
|
|
1882
|
+
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"
|
|
1883
|
+
}
|
|
1884
|
+
)
|
|
1885
|
+
}
|
|
1886
|
+
) });
|
|
1887
|
+
return /* @__PURE__ */ 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: [
|
|
1888
|
+
onPreview && isImageAsset(asset) ? /* @__PURE__ */ jsx(
|
|
1889
|
+
"button",
|
|
1890
|
+
{
|
|
1891
|
+
type: "button",
|
|
1892
|
+
onClick: () => onPreview(asset),
|
|
1893
|
+
"aria-label": `Preview ${asset.title}`,
|
|
1894
|
+
className: "block w-full text-left cursor-zoom-in",
|
|
1895
|
+
children: thumb
|
|
1896
|
+
}
|
|
1897
|
+
) : /* @__PURE__ */ jsx(
|
|
1898
|
+
"a",
|
|
1899
|
+
{
|
|
1900
|
+
href: asset.fileUrl,
|
|
1901
|
+
target: "_blank",
|
|
1902
|
+
rel: "noopener noreferrer",
|
|
1903
|
+
"aria-label": `Open ${asset.title}`,
|
|
1904
|
+
className: "block",
|
|
1905
|
+
children: thumb
|
|
1906
|
+
}
|
|
1907
|
+
),
|
|
1908
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-col flex-1 p-5", children: [
|
|
1909
|
+
/* @__PURE__ */ 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 }),
|
|
1910
|
+
/* @__PURE__ */ jsx("h3", { className: "text-base font-semibold text-foreground mb-1 line-clamp-2", children: asset.title }),
|
|
1911
|
+
asset.description && /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground mb-3 line-clamp-2", children: asset.description }),
|
|
1912
|
+
/* @__PURE__ */ jsxs("div", { className: "mt-auto flex items-center justify-between pt-3", children: [
|
|
1913
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-xs text-muted-foreground", children: [
|
|
1914
|
+
/* @__PURE__ */ jsx("span", { className: "font-medium", children: ext }),
|
|
1915
|
+
size && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1916
|
+
/* @__PURE__ */ jsx("span", { className: "text-muted-foreground/40", children: "\xB7" }),
|
|
1917
|
+
/* @__PURE__ */ jsx("span", { children: size })
|
|
1918
|
+
] }),
|
|
1919
|
+
date && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1920
|
+
/* @__PURE__ */ jsx("span", { className: "text-muted-foreground/40", children: "\xB7" }),
|
|
1921
|
+
/* @__PURE__ */ jsx("span", { children: date })
|
|
1922
|
+
] })
|
|
1923
|
+
] }),
|
|
1924
|
+
/* @__PURE__ */ jsxs(
|
|
1925
|
+
"a",
|
|
1926
|
+
{
|
|
1927
|
+
href: asset.fileUrl,
|
|
1928
|
+
target: "_blank",
|
|
1929
|
+
rel: "noopener noreferrer",
|
|
1930
|
+
download: true,
|
|
1931
|
+
onClick: (e) => {
|
|
1932
|
+
void downloadFile(e, asset);
|
|
1933
|
+
},
|
|
1934
|
+
className: "inline-flex items-center gap-1.5 text-sm font-medium text-primary hover:underline",
|
|
1935
|
+
children: [
|
|
1936
|
+
/* @__PURE__ */ jsx(
|
|
1937
|
+
"svg",
|
|
1938
|
+
{
|
|
1939
|
+
className: "w-4 h-4",
|
|
1940
|
+
fill: "none",
|
|
1941
|
+
viewBox: "0 0 24 24",
|
|
1942
|
+
stroke: "currentColor",
|
|
1943
|
+
strokeWidth: 2,
|
|
1944
|
+
children: /* @__PURE__ */ jsx(
|
|
1945
|
+
"path",
|
|
1946
|
+
{
|
|
1947
|
+
strokeLinecap: "round",
|
|
1948
|
+
strokeLinejoin: "round",
|
|
1949
|
+
d: "M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4"
|
|
1950
|
+
}
|
|
1951
|
+
)
|
|
1952
|
+
}
|
|
1953
|
+
),
|
|
1954
|
+
"Download"
|
|
1955
|
+
]
|
|
1956
|
+
}
|
|
1957
|
+
)
|
|
1958
|
+
] })
|
|
1959
|
+
] })
|
|
1960
|
+
] });
|
|
1961
|
+
}
|
|
1962
|
+
function PressReleaseCard({
|
|
1963
|
+
release,
|
|
1964
|
+
basePath
|
|
1965
|
+
}) {
|
|
1966
|
+
const date = release.publishedAt || release.lastUpdated;
|
|
1967
|
+
const formattedDate = date ? new Date(date).toLocaleDateString("en-US", {
|
|
1968
|
+
year: "numeric",
|
|
1969
|
+
month: "long",
|
|
1970
|
+
day: "numeric"
|
|
1971
|
+
}) : "";
|
|
1972
|
+
const [imgError, setImgError] = useState(false);
|
|
1973
|
+
return /* @__PURE__ */ jsxs(
|
|
1974
|
+
"a",
|
|
1975
|
+
{
|
|
1976
|
+
href: `${basePath}/${release.slug}`,
|
|
1977
|
+
className: "group flex flex-col bg-card border rounded-xl overflow-hidden hover:shadow-lg hover:border-primary/30 transition-all duration-300",
|
|
1978
|
+
children: [
|
|
1979
|
+
release.thumbnail && !imgError ? /* @__PURE__ */ jsx("div", { className: "aspect-[16/9] overflow-hidden bg-muted/30", children: /* @__PURE__ */ jsx(
|
|
1980
|
+
"img",
|
|
1981
|
+
{
|
|
1982
|
+
src: release.thumbnail,
|
|
1983
|
+
alt: release.title,
|
|
1984
|
+
className: "w-full h-full object-cover group-hover:scale-105 transition-transform duration-300",
|
|
1985
|
+
loading: "lazy",
|
|
1986
|
+
onError: () => setImgError(true)
|
|
1987
|
+
}
|
|
1988
|
+
) }) : /* @__PURE__ */ jsx("div", { className: "aspect-[16/9] bg-muted/50 flex items-center justify-center", children: /* @__PURE__ */ jsx(
|
|
1989
|
+
"svg",
|
|
1990
|
+
{
|
|
1991
|
+
className: "w-12 h-12 text-muted-foreground/20",
|
|
1992
|
+
fill: "none",
|
|
1993
|
+
viewBox: "0 0 24 24",
|
|
1994
|
+
stroke: "currentColor",
|
|
1995
|
+
children: /* @__PURE__ */ jsx(
|
|
1996
|
+
"path",
|
|
1997
|
+
{
|
|
1998
|
+
strokeLinecap: "round",
|
|
1999
|
+
strokeLinejoin: "round",
|
|
2000
|
+
strokeWidth: 1.5,
|
|
2001
|
+
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"
|
|
2002
|
+
}
|
|
2003
|
+
)
|
|
2004
|
+
}
|
|
2005
|
+
) }),
|
|
2006
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-col flex-1 p-5 sm:p-6", children: [
|
|
2007
|
+
release.category && /* @__PURE__ */ 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 }),
|
|
2008
|
+
/* @__PURE__ */ jsx("h3", { className: "text-lg font-semibold text-foreground mb-2 group-hover:text-primary transition-colors line-clamp-2", children: release.title }),
|
|
2009
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground mb-4 line-clamp-3", children: release.excerpt || release.seoDescription || "" }),
|
|
2010
|
+
/* @__PURE__ */ jsxs("div", { className: "mt-auto flex items-center gap-3 text-xs text-muted-foreground", children: [
|
|
2011
|
+
release.author && /* @__PURE__ */ jsx("span", { className: "font-medium", children: release.author }),
|
|
2012
|
+
release.author && formattedDate && /* @__PURE__ */ jsx("span", { className: "text-muted-foreground/40", children: "\xB7" }),
|
|
2013
|
+
formattedDate && /* @__PURE__ */ jsx("span", { children: formattedDate }),
|
|
2014
|
+
release.readingTime ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2015
|
+
/* @__PURE__ */ jsx("span", { className: "text-muted-foreground/40", children: "\xB7" }),
|
|
2016
|
+
/* @__PURE__ */ jsxs("span", { children: [
|
|
2017
|
+
release.readingTime,
|
|
2018
|
+
" min read"
|
|
2019
|
+
] })
|
|
2020
|
+
] }) : null
|
|
2021
|
+
] })
|
|
2022
|
+
] })
|
|
2023
|
+
]
|
|
2024
|
+
}
|
|
2025
|
+
);
|
|
2026
|
+
}
|
|
2027
|
+
function PressKitPage(props) {
|
|
2028
|
+
const {
|
|
2029
|
+
productName,
|
|
2030
|
+
heroTitle = "Press & Media Kit",
|
|
2031
|
+
heroSubtitle = "Official logos, brand assets, and media resources. Everything you need to write about us.",
|
|
2032
|
+
mediaContactEmail,
|
|
2033
|
+
brandKitUrl,
|
|
2034
|
+
className = "",
|
|
2035
|
+
seoTitle,
|
|
2036
|
+
seoDescription,
|
|
2037
|
+
seoKeywords,
|
|
2038
|
+
showPressReleases = true,
|
|
2039
|
+
pressReleaseBasePath = "/press",
|
|
2040
|
+
pressReleasesTitle = "Press Releases"
|
|
2041
|
+
} = props;
|
|
2042
|
+
const client = useWebSDK();
|
|
2043
|
+
const config = useWebSDKConfig();
|
|
2044
|
+
const { product } = useProduct();
|
|
2045
|
+
const resolvedProductId = product?.id || config.productId;
|
|
2046
|
+
const contactEmail = mediaContactEmail || product?.contactEmail || void 0;
|
|
2047
|
+
const [assets, setAssets] = useState([]);
|
|
2048
|
+
const [loading, setLoading] = useState(true);
|
|
2049
|
+
const [error, setError] = useState(null);
|
|
2050
|
+
const [activeCategory, setActiveCategory] = useState("ALL");
|
|
2051
|
+
const [releases, setReleases] = useState([]);
|
|
2052
|
+
const [releasesLoading, setReleasesLoading] = useState(true);
|
|
2053
|
+
const [preview, setPreview] = useState(null);
|
|
2054
|
+
const fetchAssets = useCallback(async () => {
|
|
2055
|
+
if (!resolvedProductId) {
|
|
2056
|
+
setLoading(false);
|
|
2057
|
+
return;
|
|
2058
|
+
}
|
|
2059
|
+
setLoading(true);
|
|
2060
|
+
try {
|
|
2061
|
+
const PAGE_SIZE = 100;
|
|
2062
|
+
const MAX_PAGES = 10;
|
|
2063
|
+
const all = [];
|
|
2064
|
+
let offset = 0;
|
|
2065
|
+
for (let pageNum = 0; pageNum < MAX_PAGES; pageNum++) {
|
|
2066
|
+
const result = await client.query(PRESS_KIT_QUERY, {
|
|
2067
|
+
productId: resolvedProductId,
|
|
2068
|
+
limit: PAGE_SIZE,
|
|
2069
|
+
offset
|
|
2070
|
+
});
|
|
2071
|
+
if (result.errors?.length) {
|
|
2072
|
+
setError(result.errors[0]?.message ?? "Failed to load press kit");
|
|
2073
|
+
return;
|
|
2074
|
+
}
|
|
2075
|
+
const page = result.data?.publicPressKitAssets;
|
|
2076
|
+
const items = page?.items || [];
|
|
2077
|
+
all.push(...items);
|
|
2078
|
+
if (!page?.hasMore || items.length === 0) break;
|
|
2079
|
+
offset += PAGE_SIZE;
|
|
2080
|
+
}
|
|
2081
|
+
setAssets(all);
|
|
2082
|
+
setError(null);
|
|
2083
|
+
} catch {
|
|
2084
|
+
setError("Failed to load press kit");
|
|
2085
|
+
} finally {
|
|
2086
|
+
setLoading(false);
|
|
2087
|
+
}
|
|
2088
|
+
}, [client, resolvedProductId]);
|
|
2089
|
+
useEffect(() => {
|
|
2090
|
+
fetchAssets();
|
|
2091
|
+
}, [fetchAssets]);
|
|
2092
|
+
const fetchReleases = useCallback(async () => {
|
|
2093
|
+
if (!resolvedProductId || !showPressReleases) {
|
|
2094
|
+
setReleasesLoading(false);
|
|
2095
|
+
return;
|
|
2096
|
+
}
|
|
2097
|
+
setReleasesLoading(true);
|
|
2098
|
+
try {
|
|
2099
|
+
const PAGE_SIZE = 100;
|
|
2100
|
+
const MAX_PAGES = 10;
|
|
2101
|
+
const all = [];
|
|
2102
|
+
let offset = 0;
|
|
2103
|
+
for (let pageNum = 0; pageNum < MAX_PAGES; pageNum++) {
|
|
2104
|
+
const result = await client.query(PRESS_RELEASES_QUERY, {
|
|
2105
|
+
productId: resolvedProductId,
|
|
2106
|
+
limit: PAGE_SIZE,
|
|
2107
|
+
offset
|
|
2108
|
+
});
|
|
2109
|
+
if (result.errors?.length) {
|
|
2110
|
+
setReleases([]);
|
|
2111
|
+
return;
|
|
2112
|
+
}
|
|
2113
|
+
const page = result.data?.publicContentPages;
|
|
2114
|
+
const items = page?.items || [];
|
|
2115
|
+
all.push(...items);
|
|
2116
|
+
if (!page?.hasMore || items.length === 0) break;
|
|
2117
|
+
offset += PAGE_SIZE;
|
|
2118
|
+
}
|
|
2119
|
+
const pressOnly = all.filter((p) => p.tags?.includes("press")).sort((a, b) => {
|
|
2120
|
+
const da = new Date(a.publishedAt || a.lastUpdated || 0).getTime();
|
|
2121
|
+
const db = new Date(b.publishedAt || b.lastUpdated || 0).getTime();
|
|
2122
|
+
return db - da;
|
|
2123
|
+
});
|
|
2124
|
+
setReleases(pressOnly);
|
|
2125
|
+
} catch {
|
|
2126
|
+
setReleases([]);
|
|
2127
|
+
} finally {
|
|
2128
|
+
setReleasesLoading(false);
|
|
2129
|
+
}
|
|
2130
|
+
}, [client, resolvedProductId, showPressReleases]);
|
|
2131
|
+
useEffect(() => {
|
|
2132
|
+
fetchReleases();
|
|
2133
|
+
}, [fetchReleases]);
|
|
2134
|
+
const presentCategories = CATEGORY_ORDER.filter(
|
|
2135
|
+
(c) => assets.some((a) => a.category === c)
|
|
2136
|
+
);
|
|
2137
|
+
const visibleAssets = activeCategory === "ALL" ? assets : assets.filter((a) => a.category === activeCategory);
|
|
2138
|
+
const grouped = (activeCategory === "ALL" ? presentCategories : [activeCategory]).map((cat) => ({
|
|
2139
|
+
cat,
|
|
2140
|
+
items: visibleAssets.filter((a) => a.category === cat).sort((a, b) => a.sortOrder - b.sortOrder)
|
|
2141
|
+
})).filter((g) => g.items.length > 0);
|
|
2142
|
+
return /* @__PURE__ */ jsxs("div", { className: `w-full ${className}`, children: [
|
|
2143
|
+
seoTitle && /* @__PURE__ */ jsx(
|
|
2144
|
+
PageHead,
|
|
2145
|
+
{
|
|
2146
|
+
title: seoTitle,
|
|
2147
|
+
description: seoDescription || heroSubtitle,
|
|
2148
|
+
productName,
|
|
2149
|
+
keywords: seoKeywords
|
|
2150
|
+
}
|
|
2151
|
+
),
|
|
2152
|
+
/* @__PURE__ */ jsx("section", { className: "py-16 sm:py-20 md:py-24", children: /* @__PURE__ */ jsx("div", { className: "max-w-7xl mx-auto px-4 sm:px-6 lg:px-8", children: /* @__PURE__ */ jsxs("div", { className: "text-center max-w-3xl mx-auto", children: [
|
|
2153
|
+
/* @__PURE__ */ jsx("h1", { className: "text-3xl sm:text-4xl md:text-5xl font-bold tracking-tight text-foreground", children: heroTitle }),
|
|
2154
|
+
/* @__PURE__ */ jsx("p", { className: "mt-4 text-lg text-muted-foreground", children: heroSubtitle }),
|
|
2155
|
+
(brandKitUrl || contactEmail) && /* @__PURE__ */ jsxs("div", { className: "mt-8 flex flex-wrap items-center justify-center gap-4", children: [
|
|
2156
|
+
brandKitUrl && /* @__PURE__ */ jsx(
|
|
2157
|
+
"a",
|
|
2158
|
+
{
|
|
2159
|
+
href: brandKitUrl,
|
|
2160
|
+
target: "_blank",
|
|
2161
|
+
rel: "noopener noreferrer",
|
|
2162
|
+
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",
|
|
2163
|
+
children: "Download brand kit"
|
|
2164
|
+
}
|
|
2165
|
+
),
|
|
2166
|
+
contactEmail && /* @__PURE__ */ jsx(
|
|
2167
|
+
"a",
|
|
2168
|
+
{
|
|
2169
|
+
href: `mailto:${contactEmail}`,
|
|
2170
|
+
className: "inline-flex items-center gap-2 px-6 py-3 rounded-lg border font-medium text-foreground hover:bg-muted transition-colors",
|
|
2171
|
+
children: "Media inquiries"
|
|
2172
|
+
}
|
|
2173
|
+
)
|
|
2174
|
+
] })
|
|
2175
|
+
] }) }) }),
|
|
2176
|
+
showPressReleases && (releasesLoading || releases.length > 0) && /* @__PURE__ */ jsx("section", { className: "pb-16 sm:pb-20", children: /* @__PURE__ */ jsxs("div", { className: "max-w-7xl mx-auto px-4 sm:px-6 lg:px-8", children: [
|
|
2177
|
+
/* @__PURE__ */ jsx("h2", { className: "text-xl font-semibold text-foreground mb-6", children: pressReleasesTitle }),
|
|
2178
|
+
/* @__PURE__ */ jsx("div", { className: "grid sm:grid-cols-2 lg:grid-cols-3 gap-6", children: releasesLoading ? [1, 2, 3].map((i) => /* @__PURE__ */ jsxs(
|
|
2179
|
+
"div",
|
|
2180
|
+
{
|
|
2181
|
+
className: "bg-card border rounded-xl overflow-hidden animate-pulse",
|
|
2182
|
+
children: [
|
|
2183
|
+
/* @__PURE__ */ jsx("div", { className: "aspect-[16/9] bg-muted" }),
|
|
2184
|
+
/* @__PURE__ */ jsxs("div", { className: "p-5 space-y-3", children: [
|
|
2185
|
+
/* @__PURE__ */ jsx("div", { className: "h-4 bg-muted rounded w-1/4" }),
|
|
2186
|
+
/* @__PURE__ */ jsx("div", { className: "h-5 bg-muted rounded w-3/4" }),
|
|
2187
|
+
/* @__PURE__ */ jsx("div", { className: "h-4 bg-muted rounded w-1/2" })
|
|
2188
|
+
] })
|
|
2189
|
+
]
|
|
2190
|
+
},
|
|
2191
|
+
i
|
|
2192
|
+
)) : releases.map((r) => /* @__PURE__ */ jsx(
|
|
2193
|
+
PressReleaseCard,
|
|
2194
|
+
{
|
|
2195
|
+
release: r,
|
|
2196
|
+
basePath: pressReleaseBasePath
|
|
2197
|
+
},
|
|
2198
|
+
r.id
|
|
2199
|
+
)) })
|
|
2200
|
+
] }) }),
|
|
2201
|
+
/* @__PURE__ */ jsx("section", { className: "pb-16 sm:pb-20", children: /* @__PURE__ */ jsx("div", { className: "max-w-7xl mx-auto px-4 sm:px-6 lg:px-8", children: loading ? /* @__PURE__ */ jsx("div", { className: "grid sm:grid-cols-2 lg:grid-cols-3 gap-6", children: [1, 2, 3, 4, 5, 6].map((i) => /* @__PURE__ */ jsxs(
|
|
2202
|
+
"div",
|
|
2203
|
+
{
|
|
2204
|
+
className: "bg-card border rounded-xl overflow-hidden animate-pulse",
|
|
2205
|
+
children: [
|
|
2206
|
+
/* @__PURE__ */ jsx("div", { className: "aspect-[16/9] bg-muted" }),
|
|
2207
|
+
/* @__PURE__ */ jsxs("div", { className: "p-5 space-y-3", children: [
|
|
2208
|
+
/* @__PURE__ */ jsx("div", { className: "h-4 bg-muted rounded w-1/4" }),
|
|
2209
|
+
/* @__PURE__ */ jsx("div", { className: "h-5 bg-muted rounded w-3/4" }),
|
|
2210
|
+
/* @__PURE__ */ jsx("div", { className: "h-4 bg-muted rounded w-1/2" })
|
|
2211
|
+
] })
|
|
2212
|
+
]
|
|
2213
|
+
},
|
|
2214
|
+
i
|
|
2215
|
+
)) }) : error ? /* @__PURE__ */ jsx("div", { className: "text-center py-12", children: /* @__PURE__ */ jsx("p", { className: "text-muted-foreground", children: error }) }) : assets.length === 0 ? /* @__PURE__ */ jsx("div", { className: "text-center py-12", children: /* @__PURE__ */ jsxs("p", { className: "text-muted-foreground", children: [
|
|
2216
|
+
"Press materials are coming soon. For media inquiries",
|
|
2217
|
+
contactEmail ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2218
|
+
", contact ",
|
|
2219
|
+
/* @__PURE__ */ jsx(
|
|
2220
|
+
"a",
|
|
2221
|
+
{
|
|
2222
|
+
href: `mailto:${contactEmail}`,
|
|
2223
|
+
className: "text-primary hover:underline",
|
|
2224
|
+
children: contactEmail
|
|
2225
|
+
}
|
|
2226
|
+
)
|
|
2227
|
+
] }) : " please reach out to our team",
|
|
2228
|
+
"."
|
|
2229
|
+
] }) }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2230
|
+
presentCategories.length > 1 && /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-2 mb-10 justify-center", children: ["ALL", ...presentCategories].map((c) => {
|
|
2231
|
+
const active = activeCategory === c;
|
|
2232
|
+
return /* @__PURE__ */ jsx(
|
|
2233
|
+
"button",
|
|
2234
|
+
{
|
|
2235
|
+
type: "button",
|
|
2236
|
+
onClick: () => setActiveCategory(c),
|
|
2237
|
+
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"}`,
|
|
2238
|
+
children: c === "ALL" ? "All" : CATEGORY_LABELS[c] ?? c
|
|
2239
|
+
},
|
|
2240
|
+
c
|
|
2241
|
+
);
|
|
2242
|
+
}) }),
|
|
2243
|
+
grouped.map((group) => /* @__PURE__ */ jsxs("div", { className: "mb-12", children: [
|
|
2244
|
+
/* @__PURE__ */ jsx("h2", { className: "text-xl font-semibold text-foreground mb-5", children: CATEGORY_LABELS[group.cat] ?? group.cat }),
|
|
2245
|
+
/* @__PURE__ */ jsx("div", { className: "grid sm:grid-cols-2 lg:grid-cols-3 gap-6", children: group.items.map((a) => /* @__PURE__ */ jsx(AssetCard, { asset: a, onPreview: setPreview }, a.id)) })
|
|
2246
|
+
] }, group.cat)),
|
|
2247
|
+
contactEmail && /* @__PURE__ */ jsxs("div", { className: "mt-8 border-t pt-10 text-center", children: [
|
|
2248
|
+
/* @__PURE__ */ jsx("h2", { className: "text-lg font-semibold text-foreground mb-2", children: "Media Contact" }),
|
|
2249
|
+
/* @__PURE__ */ jsxs("p", { className: "text-muted-foreground", children: [
|
|
2250
|
+
"For interviews, partnerships, and press inquiries:",
|
|
2251
|
+
" ",
|
|
2252
|
+
/* @__PURE__ */ jsx(
|
|
2253
|
+
"a",
|
|
2254
|
+
{
|
|
2255
|
+
href: `mailto:${contactEmail}`,
|
|
2256
|
+
className: "text-primary hover:underline",
|
|
2257
|
+
children: contactEmail
|
|
2258
|
+
}
|
|
2259
|
+
)
|
|
2260
|
+
] })
|
|
2261
|
+
] })
|
|
2262
|
+
] }) }) }),
|
|
2263
|
+
/* @__PURE__ */ jsx(PressKitLightbox, { asset: preview, onClose: () => setPreview(null) })
|
|
2264
|
+
] });
|
|
2265
|
+
}
|
|
2266
|
+
var CONTRACTS_QUERY = `
|
|
2267
|
+
query PublicContracts($productId: String!, $limit: Int, $offset: Int) {
|
|
2268
|
+
publicPressKitAssets(productId: $productId, category: CONTRACT, limit: $limit, offset: $offset) {
|
|
2269
|
+
items {
|
|
2270
|
+
id
|
|
2271
|
+
title
|
|
2272
|
+
slug
|
|
2273
|
+
description
|
|
2274
|
+
fileUrl
|
|
2275
|
+
fileName
|
|
2276
|
+
fileSize
|
|
2277
|
+
mimeType
|
|
2278
|
+
version
|
|
2279
|
+
tags
|
|
2280
|
+
sortOrder
|
|
2281
|
+
updatedAt
|
|
2282
|
+
}
|
|
2283
|
+
hasMore
|
|
2284
|
+
}
|
|
2285
|
+
}
|
|
2286
|
+
`;
|
|
2287
|
+
function ContractsPage(props) {
|
|
2288
|
+
const {
|
|
2289
|
+
productName,
|
|
2290
|
+
heroTitle = "Contracts & Legal Agreements",
|
|
2291
|
+
heroSubtitle = "Download our standard legal agreements and policies.",
|
|
2292
|
+
className = "",
|
|
2293
|
+
seoTitle,
|
|
2294
|
+
seoDescription,
|
|
2295
|
+
seoKeywords
|
|
2296
|
+
} = props;
|
|
2297
|
+
const client = useWebSDK();
|
|
2298
|
+
const config = useWebSDKConfig();
|
|
2299
|
+
const { product } = useProduct();
|
|
2300
|
+
const resolvedProductId = product?.id || config.productId;
|
|
2301
|
+
const displayName = productName ?? product?.name ?? config.productId;
|
|
2302
|
+
const [contracts, setContracts] = useState([]);
|
|
2303
|
+
const [loading, setLoading] = useState(true);
|
|
2304
|
+
const [error, setError] = useState(null);
|
|
2305
|
+
const fetchContracts = useCallback(async () => {
|
|
2306
|
+
if (!resolvedProductId) {
|
|
2307
|
+
setLoading(false);
|
|
2308
|
+
return;
|
|
2309
|
+
}
|
|
2310
|
+
setLoading(true);
|
|
2311
|
+
try {
|
|
2312
|
+
const PAGE_SIZE = 100;
|
|
2313
|
+
const MAX_PAGES = 10;
|
|
2314
|
+
const all = [];
|
|
2315
|
+
let offset = 0;
|
|
2316
|
+
for (let pageNum = 0; pageNum < MAX_PAGES; pageNum++) {
|
|
2317
|
+
const result = await client.query(CONTRACTS_QUERY, {
|
|
2318
|
+
productId: resolvedProductId,
|
|
2319
|
+
limit: PAGE_SIZE,
|
|
2320
|
+
offset
|
|
2321
|
+
});
|
|
2322
|
+
if (result.errors?.length) {
|
|
2323
|
+
setError(result.errors[0]?.message ?? "Failed to load contracts");
|
|
2324
|
+
return;
|
|
2325
|
+
}
|
|
2326
|
+
const page = result.data?.publicPressKitAssets;
|
|
2327
|
+
const items = page?.items || [];
|
|
2328
|
+
all.push(...items);
|
|
2329
|
+
if (!page?.hasMore || items.length === 0) break;
|
|
2330
|
+
offset += PAGE_SIZE;
|
|
2331
|
+
}
|
|
2332
|
+
all.sort(
|
|
2333
|
+
(a, b) => a.sortOrder - b.sortOrder || a.title.localeCompare(b.title)
|
|
2334
|
+
);
|
|
2335
|
+
setContracts(all);
|
|
2336
|
+
setError(null);
|
|
2337
|
+
} catch {
|
|
2338
|
+
setError("Failed to load contracts");
|
|
2339
|
+
} finally {
|
|
2340
|
+
setLoading(false);
|
|
2341
|
+
}
|
|
2342
|
+
}, [client, resolvedProductId]);
|
|
2343
|
+
useEffect(() => {
|
|
2344
|
+
fetchContracts();
|
|
2345
|
+
}, [fetchContracts]);
|
|
2346
|
+
return /* @__PURE__ */ jsxs("div", { className: `min-h-screen bg-background ${className}`, children: [
|
|
2347
|
+
/* @__PURE__ */ jsx(
|
|
2348
|
+
PageHead,
|
|
2349
|
+
{
|
|
2350
|
+
title: seoTitle || `Contracts \u2014 ${displayName}`,
|
|
2351
|
+
description: seoDescription || heroSubtitle,
|
|
2352
|
+
productName,
|
|
2353
|
+
keywords: seoKeywords
|
|
2354
|
+
}
|
|
2355
|
+
),
|
|
2356
|
+
/* @__PURE__ */ jsx("section", { className: "border-b border-border bg-muted/30", children: /* @__PURE__ */ jsxs("div", { className: "max-w-5xl mx-auto px-4 sm:px-6 lg:px-8 py-16 text-center", children: [
|
|
2357
|
+
/* @__PURE__ */ jsx("h1", { className: "text-4xl md:text-5xl font-bold text-foreground mb-4", children: heroTitle }),
|
|
2358
|
+
/* @__PURE__ */ jsx("p", { className: "text-lg text-muted-foreground max-w-2xl mx-auto", children: heroSubtitle })
|
|
2359
|
+
] }) }),
|
|
2360
|
+
/* @__PURE__ */ jsx("section", { className: "max-w-5xl mx-auto px-4 sm:px-6 lg:px-8 py-12", children: loading ? /* @__PURE__ */ jsx("div", { className: "flex justify-center py-20", children: /* @__PURE__ */ jsx("div", { className: "h-8 w-8 animate-spin rounded-full border-2 border-primary border-t-transparent" }) }) : error ? /* @__PURE__ */ jsx("div", { className: "text-center py-16", children: /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: error }) }) : contracts.length === 0 ? /* @__PURE__ */ jsx("div", { className: "text-center py-16", children: /* @__PURE__ */ jsx("p", { className: "text-base text-muted-foreground", children: "No contracts are available yet." }) }) : /* @__PURE__ */ jsx("ul", { className: "divide-y divide-border rounded-xl border border-border overflow-hidden bg-card", children: contracts.map((c) => {
|
|
2361
|
+
const ext = fileExt(c);
|
|
2362
|
+
const size = formatBytes(c.fileSize);
|
|
2363
|
+
return /* @__PURE__ */ jsxs(
|
|
2364
|
+
"li",
|
|
2365
|
+
{
|
|
2366
|
+
className: "flex items-center justify-between gap-4 p-5 hover:bg-muted/40 transition-colors",
|
|
2367
|
+
children: [
|
|
2368
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
|
|
2369
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 flex-wrap", children: [
|
|
2370
|
+
/* @__PURE__ */ jsx("h3", { className: "text-base font-semibold text-foreground truncate", children: c.title }),
|
|
2371
|
+
c.version && /* @__PURE__ */ jsx("span", { className: "text-xs font-medium text-primary bg-primary/10 px-2 py-0.5 rounded-full", children: c.version })
|
|
2372
|
+
] }),
|
|
2373
|
+
c.description && /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground mt-1 line-clamp-2", children: c.description }),
|
|
2374
|
+
/* @__PURE__ */ jsxs("div", { className: "mt-1 flex items-center gap-2 text-xs text-muted-foreground", children: [
|
|
2375
|
+
/* @__PURE__ */ jsx("span", { className: "font-medium", children: ext }),
|
|
2376
|
+
size && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2377
|
+
/* @__PURE__ */ jsx("span", { className: "text-muted-foreground/40", children: "\xB7" }),
|
|
2378
|
+
/* @__PURE__ */ jsx("span", { children: size })
|
|
2379
|
+
] })
|
|
2380
|
+
] })
|
|
2381
|
+
] }),
|
|
2382
|
+
/* @__PURE__ */ jsxs(
|
|
2383
|
+
"a",
|
|
2384
|
+
{
|
|
2385
|
+
href: c.fileUrl,
|
|
2386
|
+
target: "_blank",
|
|
2387
|
+
rel: "noopener noreferrer",
|
|
2388
|
+
download: true,
|
|
2389
|
+
"aria-label": `Download ${c.title}`,
|
|
2390
|
+
onClick: (e) => {
|
|
2391
|
+
void downloadFile(e, c);
|
|
2392
|
+
},
|
|
2393
|
+
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",
|
|
2394
|
+
children: [
|
|
2395
|
+
/* @__PURE__ */ jsx(
|
|
2396
|
+
"svg",
|
|
2397
|
+
{
|
|
2398
|
+
className: "w-4 h-4",
|
|
2399
|
+
fill: "none",
|
|
2400
|
+
viewBox: "0 0 24 24",
|
|
2401
|
+
stroke: "currentColor",
|
|
2402
|
+
"aria-hidden": "true",
|
|
2403
|
+
children: /* @__PURE__ */ jsx(
|
|
2404
|
+
"path",
|
|
2405
|
+
{
|
|
2406
|
+
strokeLinecap: "round",
|
|
2407
|
+
strokeLinejoin: "round",
|
|
2408
|
+
strokeWidth: 2,
|
|
2409
|
+
d: "M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4"
|
|
2410
|
+
}
|
|
2411
|
+
)
|
|
2412
|
+
}
|
|
2413
|
+
),
|
|
2414
|
+
"Download"
|
|
2415
|
+
]
|
|
2416
|
+
}
|
|
2417
|
+
)
|
|
2418
|
+
]
|
|
2419
|
+
},
|
|
2420
|
+
c.id
|
|
2421
|
+
);
|
|
2422
|
+
}) }) })
|
|
2423
|
+
] });
|
|
2424
|
+
}
|
|
2425
|
+
|
|
2426
|
+
export { BlogListPage, BlogPostPage, CaseStudiesListPage, CaseStudyPage, ChangelogPage, ContentRenderer, ContractsPage, PressKitPage, useContentPage, useContentPages };
|
|
2427
|
+
//# sourceMappingURL=index.mjs.map
|
|
2428
|
+
//# sourceMappingURL=index.mjs.map
|