keystone-design-bootstrap 1.0.97 → 1.0.99
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/contexts/index.js +134 -1
- package/dist/contexts/index.js.map +1 -1
- package/dist/design_system/components/DynamicFormFields.js +130 -3
- package/dist/design_system/components/DynamicFormFields.js.map +1 -1
- package/dist/design_system/elements/index.js +130 -3
- package/dist/design_system/elements/index.js.map +1 -1
- package/dist/design_system/sections/index.js +150 -9
- package/dist/design_system/sections/index.js.map +1 -1
- package/dist/index.js +184 -21
- package/dist/index.js.map +1 -1
- package/dist/lib/server-api.js +49 -2
- package/dist/lib/server-api.js.map +1 -1
- package/dist/tracking/index.js +212 -185
- package/dist/tracking/index.js.map +1 -1
- package/package.json +1 -1
- package/src/contexts/ThemeContext.tsx +2 -1
- package/src/design_system/chat/useRealtimeReplyOrchestrator.ts +24 -5
- package/src/design_system/components/ChatWidget.tsx +10 -3
- package/src/design_system/elements/map/GoogleMap.tsx +9 -2
- package/src/design_system/sections/blog-post.tsx +13 -3
- package/src/design_system/sections/home-hero-component.tsx +4 -1
- package/src/lib/server-api.ts +6 -2
- package/src/lib/server-log.ts +39 -0
- package/src/next/routes/chat.ts +3 -2
- package/src/next/routes/form.ts +2 -1
- package/src/tracking/MetaPixel.tsx +10 -1
- package/src/tracking/firePixelEvent.ts +7 -2
- package/src/tracking/logging.ts +39 -6
package/dist/lib/server-api.js
CHANGED
|
@@ -1,3 +1,47 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
|
|
3
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
4
|
+
var __propIsEnum = Object.prototype.propertyIsEnumerable;
|
|
5
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
6
|
+
var __spreadValues = (a, b) => {
|
|
7
|
+
for (var prop in b || (b = {}))
|
|
8
|
+
if (__hasOwnProp.call(b, prop))
|
|
9
|
+
__defNormalProp(a, prop, b[prop]);
|
|
10
|
+
if (__getOwnPropSymbols)
|
|
11
|
+
for (var prop of __getOwnPropSymbols(b)) {
|
|
12
|
+
if (__propIsEnum.call(b, prop))
|
|
13
|
+
__defNormalProp(a, prop, b[prop]);
|
|
14
|
+
}
|
|
15
|
+
return a;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
// src/lib/server-log.ts
|
|
19
|
+
function normalizeError(error) {
|
|
20
|
+
if (error instanceof Error) {
|
|
21
|
+
return {
|
|
22
|
+
message: error.message,
|
|
23
|
+
stack: error.stack || "",
|
|
24
|
+
name: error.name
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
return { message: String(error) };
|
|
28
|
+
}
|
|
29
|
+
function writeServerLog(level, event, detail) {
|
|
30
|
+
const payload = {
|
|
31
|
+
level,
|
|
32
|
+
event,
|
|
33
|
+
detail: detail != null ? detail : {},
|
|
34
|
+
at: (/* @__PURE__ */ new Date()).toISOString()
|
|
35
|
+
};
|
|
36
|
+
const line = `[ks-server] ${JSON.stringify(payload)}
|
|
37
|
+
`;
|
|
38
|
+
const stream = level === "ERROR" ? process.stderr : process.stdout;
|
|
39
|
+
stream.write(line);
|
|
40
|
+
}
|
|
41
|
+
function serverError(event, error, detail) {
|
|
42
|
+
writeServerLog("ERROR", event, __spreadValues(__spreadValues({}, detail != null ? detail : {}), error !== void 0 ? { error: normalizeError(error) } : {}));
|
|
43
|
+
}
|
|
44
|
+
|
|
1
45
|
// src/lib/server-api.ts
|
|
2
46
|
var API_URL = process.env.API_URL || "http://localhost:3000/api/v1";
|
|
3
47
|
var API_KEY = process.env.API_KEY || "";
|
|
@@ -17,13 +61,16 @@ async function serverFetch(endpoint, options = {}) {
|
|
|
17
61
|
}
|
|
18
62
|
const response = await fetch(url, fetchOptions);
|
|
19
63
|
if (!response.ok) {
|
|
20
|
-
|
|
64
|
+
serverError("SERVER_API_NON_OK_RESPONSE", void 0, {
|
|
65
|
+
endpoint,
|
|
66
|
+
status: response.status
|
|
67
|
+
});
|
|
21
68
|
return null;
|
|
22
69
|
}
|
|
23
70
|
const json = await response.json();
|
|
24
71
|
return (_a = json.data) != null ? _a : json;
|
|
25
72
|
} catch (error) {
|
|
26
|
-
|
|
73
|
+
serverError("SERVER_API_FETCH_FAILED", error, { endpoint });
|
|
27
74
|
return null;
|
|
28
75
|
}
|
|
29
76
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/lib/server-api.ts"],"sourcesContent":["/**\n * Server-side API client for SSR\n * API key is stored securely on the server - never exposed to browser\n */\n\nimport type { CompanyInformation } from '../types/api/company-information';\nimport type { Service } from '../types/api/service';\nimport type { Package } from '../types/api/package';\nimport type { WebsitePhotos } from '../types/api/website-photos';\n\nconst API_URL = process.env.API_URL || 'http://localhost:3000/api/v1';\nconst API_KEY = process.env.API_KEY || '';\n\ninterface FetchOptions {\n cache?: RequestCache;\n revalidate?: number;\n}\n\nasync function serverFetch<T>(endpoint: string, options: FetchOptions = {}): Promise<T | null> {\n const url = `${API_URL}${endpoint}`;\n \n try {\n const fetchOptions: RequestInit & { next?: { revalidate?: number } } = {\n headers: {\n 'X-API-Key': API_KEY,\n 'Content-Type': 'application/json',\n },\n cache: options.cache,\n };\n \n if (options.revalidate) {\n fetchOptions.next = { revalidate: options.revalidate };\n }\n \n const response = await fetch(url, fetchOptions);\n\n if (!response.ok) {\n console.error(`[Server API] Error ${response.status} for ${endpoint}`);\n return null;\n }\n\n const json = await response.json();\n \n // Rails API returns { data: [...], meta: {...} }\n return json.data ?? json;\n } catch (error) {\n console.error(`[Server API] Failed to fetch ${endpoint}:`, error);\n return null;\n }\n}\n\n/**\n * Generic serverApi object for flexible endpoint access\n */\nexport const serverApi = {\n get: <T = unknown>(endpoint: string, options?: FetchOptions): Promise<T | null> => {\n return serverFetch<T>(endpoint, options || { revalidate: 60 });\n }\n};\n\n// Revalidate data every 60 seconds (ISR)\nconst defaultOptions: FetchOptions = { revalidate: 60 };\n\nexport async function getCompanyInformation(): Promise<CompanyInformation | null> {\n return serverFetch<CompanyInformation>('/public/company_information', defaultOptions);\n}\n\n/** Ads config (e.g. Meta Pixel). Returns { meta_pixel_id?: string } or {}. Only present when account has connected Meta Ads. */\nexport async function getAdsConfig(): Promise<{ meta_pixel_id?: string } | null> {\n const data = await serverFetch<{ meta_pixel_id?: string }>('/public/ads_config', defaultOptions);\n return data ?? null;\n}\n\n/** Extract Meta Pixel ID from ads config for use with <MetaPixel pixelId={...} />. Only returns a value when present and valid (numeric). */\nexport function getMetaPixelId(adsConfig: { meta_pixel_id?: string } | null | undefined): string | null {\n const id = adsConfig && typeof adsConfig === 'object' && 'meta_pixel_id' in adsConfig && adsConfig.meta_pixel_id;\n const str = id != null && id !== '' ? String(id).trim() : '';\n return str !== '' && str !== 'null' && /^\\d+$/.test(str) ? str : null;\n}\n\nexport type AnalyticsConfig = {\n /** PostHog project API key — platform-wide, from POSTHOG_API_KEY env var on the Rails server. */\n posthog_api_key?: string;\n /** Per-site GTM container ID (e.g. GTM-ABC123), provisioned by Keystone. */\n gtm_container_public_id?: string;\n /**\n * Environment identifier from KEYSTONE_ENV on the Rails server (e.g. \"production\", \"staging\",\n * \"development\"). Registered as a PostHog super property on every event so the sync job can\n * filter by environment and avoid cross-environment data contamination.\n */\n environment?: string;\n};\n\n/** Analytics config for customer sites. Returns platform-wide analytics keys (e.g. PostHog). */\nexport async function getAnalyticsConfig(): Promise<AnalyticsConfig | null> {\n const data = await serverFetch<AnalyticsConfig>('/public/analytics_config', defaultOptions);\n return data ?? null;\n}\n\n/** Extract PostHog API key from analytics config for use with <PostHogProvider apiKey={...} />. */\nexport function getPostHogApiKey(analyticsConfig: AnalyticsConfig | null | undefined): string | null {\n const key = analyticsConfig?.posthog_api_key;\n const str = key != null && key !== '' ? String(key).trim() : '';\n return str !== '' && str !== 'null' ? str : null;\n}\n\n/** Extract GTM container ID from analytics config for use with <GoogleTagManager containerId={...} />. */\nexport function getGtmContainerPublicId(analyticsConfig: AnalyticsConfig | null | undefined): string | null {\n const value = analyticsConfig?.gtm_container_public_id;\n const str = value != null && value !== '' ? String(value).trim() : '';\n return str !== '' && str !== 'null' && /^GTM-[A-Z0-9]+$/i.test(str) ? str : null;\n}\n\n/** Extract environment string from analytics config for use with <PostHogProvider environment={...} />. */\nexport function getKeystoneEnvironment(analyticsConfig: AnalyticsConfig | null | undefined): string {\n return analyticsConfig?.environment?.trim() || 'development';\n}\n\nexport async function getServices(): Promise<Service[] | null> {\n return serverFetch<Service[]>('/public/services', defaultOptions);\n}\n\nexport async function getService(slug: string): Promise<Service | null> {\n return serverFetch<Service>(`/public/services/by_slug/${slug}`, defaultOptions);\n}\n\nexport async function getLocations() {\n return serverFetch('/public/locations', defaultOptions);\n}\n\nexport async function getLocation(slug: string) {\n return serverFetch(`/public/locations/by_slug/${slug}`, defaultOptions);\n}\n\nexport async function getReviews() {\n return serverFetch('/public/reviews', defaultOptions);\n}\n\nexport async function getFAQs() {\n return serverFetch('/public/faq_questions', defaultOptions);\n}\n\nexport async function getBlogPosts() {\n return serverFetch('/public/blog_posts', defaultOptions);\n}\n\nexport async function getBlogPost(slug: string) {\n return serverFetch(`/public/blog_posts/by_slug/${slug}`, defaultOptions);\n}\n\nexport async function getTeamMembers() {\n return serverFetch('/public/team_members', defaultOptions);\n}\n\nexport async function getWebsitePhotos(): Promise<WebsitePhotos | null> {\n return serverFetch<WebsitePhotos>('/public/website_photos', defaultOptions);\n}\n\nexport async function getJobPostings() {\n return serverFetch('/public/job_postings', defaultOptions);\n}\n\nexport async function getJobPosting(slug: string) {\n return serverFetch(`/public/job_postings/by_slug/${slug}`, defaultOptions);\n}\n\nexport async function getSocialPosts() {\n return serverFetch('/public/social_posts', defaultOptions);\n}\n\n/** Packages (bundles of service items). */\nexport async function getPackages(): Promise<Package[] | null> {\n return serverFetch<Package[]>('/public/packages', defaultOptions);\n}\n\nexport async function getPackage(slug: string): Promise<Package | null> {\n return serverFetch<Package>(`/public/packages/by_slug/${encodeURIComponent(slug)}`, defaultOptions);\n}\n\n// Alias for testimonials (API uses \"reviews\")\nexport async function getTestimonials() {\n return getReviews();\n}\n\n/** Form definition for dynamic form rendering, fetched by form_type. */\nexport async function getForm(formType: string) {\n type FormDefinition = import('../types/api/form').FormDefinition;\n return serverFetch<FormDefinition>(`/public/forms/${encodeURIComponent(formType)}`, defaultOptions);\n}\n\n/** Form definition for dynamic form rendering, fetched by numeric ID. */\nexport async function getFormById(id: number | string) {\n type FormDefinition = import('../types/api/form').FormDefinition;\n return serverFetch<FormDefinition>(`/public/form_by_id/${encodeURIComponent(id)}`, defaultOptions);\n}\n\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/lib/server-log.ts","../../src/lib/server-api.ts"],"sourcesContent":["type ServerLogLevel = 'INFO' | 'WARN' | 'ERROR';\n\nfunction normalizeError(error: unknown): Record<string, string> {\n if (error instanceof Error) {\n return {\n message: error.message,\n stack: error.stack || '',\n name: error.name,\n };\n }\n return { message: String(error) };\n}\n\nfunction writeServerLog(level: ServerLogLevel, event: string, detail?: Record<string, unknown>): void {\n const payload = {\n level,\n event,\n detail: detail ?? {},\n at: new Date().toISOString(),\n };\n const line = `[ks-server] ${JSON.stringify(payload)}\\n`;\n const stream = level === 'ERROR' ? process.stderr : process.stdout;\n stream.write(line);\n}\n\nexport function serverLog(event: string, detail?: Record<string, unknown>): void {\n writeServerLog('INFO', event, detail);\n}\n\nexport function serverWarn(event: string, detail?: Record<string, unknown>): void {\n writeServerLog('WARN', event, detail);\n}\n\nexport function serverError(event: string, error?: unknown, detail?: Record<string, unknown>): void {\n writeServerLog('ERROR', event, {\n ...(detail ?? {}),\n ...(error !== undefined ? { error: normalizeError(error) } : {}),\n });\n}\n","/**\n * Server-side API client for SSR\n * API key is stored securely on the server - never exposed to browser\n */\n\nimport type { CompanyInformation } from '../types/api/company-information';\nimport type { Service } from '../types/api/service';\nimport type { Package } from '../types/api/package';\nimport type { WebsitePhotos } from '../types/api/website-photos';\nimport { serverError } from './server-log';\n\nconst API_URL = process.env.API_URL || 'http://localhost:3000/api/v1';\nconst API_KEY = process.env.API_KEY || '';\n\ninterface FetchOptions {\n cache?: RequestCache;\n revalidate?: number;\n}\n\nasync function serverFetch<T>(endpoint: string, options: FetchOptions = {}): Promise<T | null> {\n const url = `${API_URL}${endpoint}`;\n \n try {\n const fetchOptions: RequestInit & { next?: { revalidate?: number } } = {\n headers: {\n 'X-API-Key': API_KEY,\n 'Content-Type': 'application/json',\n },\n cache: options.cache,\n };\n \n if (options.revalidate) {\n fetchOptions.next = { revalidate: options.revalidate };\n }\n \n const response = await fetch(url, fetchOptions);\n\n if (!response.ok) {\n serverError('SERVER_API_NON_OK_RESPONSE', undefined, {\n endpoint,\n status: response.status,\n });\n return null;\n }\n\n const json = await response.json();\n \n // Rails API returns { data: [...], meta: {...} }\n return json.data ?? json;\n } catch (error) {\n serverError('SERVER_API_FETCH_FAILED', error, { endpoint });\n return null;\n }\n}\n\n/**\n * Generic serverApi object for flexible endpoint access\n */\nexport const serverApi = {\n get: <T = unknown>(endpoint: string, options?: FetchOptions): Promise<T | null> => {\n return serverFetch<T>(endpoint, options || { revalidate: 60 });\n }\n};\n\n// Revalidate data every 60 seconds (ISR)\nconst defaultOptions: FetchOptions = { revalidate: 60 };\n\nexport async function getCompanyInformation(): Promise<CompanyInformation | null> {\n return serverFetch<CompanyInformation>('/public/company_information', defaultOptions);\n}\n\n/** Ads config (e.g. Meta Pixel). Returns { meta_pixel_id?: string } or {}. Only present when account has connected Meta Ads. */\nexport async function getAdsConfig(): Promise<{ meta_pixel_id?: string } | null> {\n const data = await serverFetch<{ meta_pixel_id?: string }>('/public/ads_config', defaultOptions);\n return data ?? null;\n}\n\n/** Extract Meta Pixel ID from ads config for use with <MetaPixel pixelId={...} />. Only returns a value when present and valid (numeric). */\nexport function getMetaPixelId(adsConfig: { meta_pixel_id?: string } | null | undefined): string | null {\n const id = adsConfig && typeof adsConfig === 'object' && 'meta_pixel_id' in adsConfig && adsConfig.meta_pixel_id;\n const str = id != null && id !== '' ? String(id).trim() : '';\n return str !== '' && str !== 'null' && /^\\d+$/.test(str) ? str : null;\n}\n\nexport type AnalyticsConfig = {\n /** PostHog project API key — platform-wide, from POSTHOG_API_KEY env var on the Rails server. */\n posthog_api_key?: string;\n /** Per-site GTM container ID (e.g. GTM-ABC123), provisioned by Keystone. */\n gtm_container_public_id?: string;\n /**\n * Environment identifier from KEYSTONE_ENV on the Rails server (e.g. \"production\", \"staging\",\n * \"development\"). Registered as a PostHog super property on every event so the sync job can\n * filter by environment and avoid cross-environment data contamination.\n */\n environment?: string;\n};\n\n/** Analytics config for customer sites. Returns platform-wide analytics keys (e.g. PostHog). */\nexport async function getAnalyticsConfig(): Promise<AnalyticsConfig | null> {\n const data = await serverFetch<AnalyticsConfig>('/public/analytics_config', defaultOptions);\n return data ?? null;\n}\n\n/** Extract PostHog API key from analytics config for use with <PostHogProvider apiKey={...} />. */\nexport function getPostHogApiKey(analyticsConfig: AnalyticsConfig | null | undefined): string | null {\n const key = analyticsConfig?.posthog_api_key;\n const str = key != null && key !== '' ? String(key).trim() : '';\n return str !== '' && str !== 'null' ? str : null;\n}\n\n/** Extract GTM container ID from analytics config for use with <GoogleTagManager containerId={...} />. */\nexport function getGtmContainerPublicId(analyticsConfig: AnalyticsConfig | null | undefined): string | null {\n const value = analyticsConfig?.gtm_container_public_id;\n const str = value != null && value !== '' ? String(value).trim() : '';\n return str !== '' && str !== 'null' && /^GTM-[A-Z0-9]+$/i.test(str) ? str : null;\n}\n\n/** Extract environment string from analytics config for use with <PostHogProvider environment={...} />. */\nexport function getKeystoneEnvironment(analyticsConfig: AnalyticsConfig | null | undefined): string {\n return analyticsConfig?.environment?.trim() || 'development';\n}\n\nexport async function getServices(): Promise<Service[] | null> {\n return serverFetch<Service[]>('/public/services', defaultOptions);\n}\n\nexport async function getService(slug: string): Promise<Service | null> {\n return serverFetch<Service>(`/public/services/by_slug/${slug}`, defaultOptions);\n}\n\nexport async function getLocations() {\n return serverFetch('/public/locations', defaultOptions);\n}\n\nexport async function getLocation(slug: string) {\n return serverFetch(`/public/locations/by_slug/${slug}`, defaultOptions);\n}\n\nexport async function getReviews() {\n return serverFetch('/public/reviews', defaultOptions);\n}\n\nexport async function getFAQs() {\n return serverFetch('/public/faq_questions', defaultOptions);\n}\n\nexport async function getBlogPosts() {\n return serverFetch('/public/blog_posts', defaultOptions);\n}\n\nexport async function getBlogPost(slug: string) {\n return serverFetch(`/public/blog_posts/by_slug/${slug}`, defaultOptions);\n}\n\nexport async function getTeamMembers() {\n return serverFetch('/public/team_members', defaultOptions);\n}\n\nexport async function getWebsitePhotos(): Promise<WebsitePhotos | null> {\n return serverFetch<WebsitePhotos>('/public/website_photos', defaultOptions);\n}\n\nexport async function getJobPostings() {\n return serverFetch('/public/job_postings', defaultOptions);\n}\n\nexport async function getJobPosting(slug: string) {\n return serverFetch(`/public/job_postings/by_slug/${slug}`, defaultOptions);\n}\n\nexport async function getSocialPosts() {\n return serverFetch('/public/social_posts', defaultOptions);\n}\n\n/** Packages (bundles of service items). */\nexport async function getPackages(): Promise<Package[] | null> {\n return serverFetch<Package[]>('/public/packages', defaultOptions);\n}\n\nexport async function getPackage(slug: string): Promise<Package | null> {\n return serverFetch<Package>(`/public/packages/by_slug/${encodeURIComponent(slug)}`, defaultOptions);\n}\n\n// Alias for testimonials (API uses \"reviews\")\nexport async function getTestimonials() {\n return getReviews();\n}\n\n/** Form definition for dynamic form rendering, fetched by form_type. */\nexport async function getForm(formType: string) {\n type FormDefinition = import('../types/api/form').FormDefinition;\n return serverFetch<FormDefinition>(`/public/forms/${encodeURIComponent(formType)}`, defaultOptions);\n}\n\n/** Form definition for dynamic form rendering, fetched by numeric ID. */\nexport async function getFormById(id: number | string) {\n type FormDefinition = import('../types/api/form').FormDefinition;\n return serverFetch<FormDefinition>(`/public/form_by_id/${encodeURIComponent(id)}`, defaultOptions);\n}\n\n"],"mappings":"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","names":[]}
|
package/dist/tracking/index.js
CHANGED
|
@@ -19,181 +19,8 @@ var __spreadValues = (a, b) => {
|
|
|
19
19
|
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
|
|
20
20
|
|
|
21
21
|
// src/tracking/MetaPixel.tsx
|
|
22
|
-
import Script from "next/script";
|
|
23
|
-
var FBEVENTS_URL = "https://connect.facebook.net/en_US/fbevents.js";
|
|
24
|
-
var PIXEL_SCRIPT = (pixelId) => `
|
|
25
|
-
!function(f,b,e,v,n,t,s)
|
|
26
|
-
{if(f.fbq)return;n=f.fbq=function(){n.callMethod?
|
|
27
|
-
n.callMethod.apply(n,arguments):n.queue.push(arguments)};
|
|
28
|
-
if(!f._fbq)f._fbq=n;n.push=n;n.loaded=!0;n.version='2.0';
|
|
29
|
-
n.queue=[];t=b.createElement(e);t.async=!0;
|
|
30
|
-
t.src=v;s=b.getElementsByTagName(e)[0];
|
|
31
|
-
s.parentNode.insertBefore(t,s)}(window, document,'script','${FBEVENTS_URL}');
|
|
32
|
-
fbq('init', '${pixelId.replace(/'/g, "\\'")}');
|
|
33
|
-
fbq('track', 'PageView');
|
|
34
|
-
window.__ks_pixel_ids = window.__ks_pixel_ids || [];
|
|
35
|
-
if (window.__ks_pixel_ids.indexOf('${pixelId.replace(/'/g, "\\'")}') === -1) {
|
|
36
|
-
window.__ks_pixel_ids.push('${pixelId.replace(/'/g, "\\'")}');
|
|
37
|
-
}
|
|
38
|
-
`;
|
|
39
|
-
function MetaPixel({ pixelId }) {
|
|
40
|
-
const raw = typeof pixelId === "string" ? pixelId.trim() : "";
|
|
41
|
-
const id = raw && raw !== "null" && /^\d+$/.test(raw) ? raw : "";
|
|
42
|
-
if (!id) {
|
|
43
|
-
return null;
|
|
44
|
-
}
|
|
45
|
-
return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(
|
|
46
|
-
Script,
|
|
47
|
-
{
|
|
48
|
-
id: "meta-pixel",
|
|
49
|
-
strategy: "afterInteractive",
|
|
50
|
-
dangerouslySetInnerHTML: { __html: PIXEL_SCRIPT(id) }
|
|
51
|
-
}
|
|
52
|
-
), /* @__PURE__ */ React.createElement("noscript", null, /* @__PURE__ */ React.createElement(
|
|
53
|
-
"img",
|
|
54
|
-
{
|
|
55
|
-
height: 1,
|
|
56
|
-
width: 1,
|
|
57
|
-
style: { display: "none" },
|
|
58
|
-
src: `https://www.facebook.com/tr?id=${id}&ev=PageView&noscript=1`,
|
|
59
|
-
alt: ""
|
|
60
|
-
}
|
|
61
|
-
)));
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
// src/tracking/MetaPixelTracker.tsx
|
|
65
22
|
import { useEffect } from "react";
|
|
66
|
-
import
|
|
67
|
-
|
|
68
|
-
// src/tracking/firePixelEvent.ts
|
|
69
|
-
var STORAGE_KEY = "ks_pud";
|
|
70
|
-
async function sha256Hex(str) {
|
|
71
|
-
const buffer = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(str));
|
|
72
|
-
return Array.from(new Uint8Array(buffer)).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
73
|
-
}
|
|
74
|
-
function getFbq() {
|
|
75
|
-
if (typeof window === "undefined") return void 0;
|
|
76
|
-
return window.fbq;
|
|
77
|
-
}
|
|
78
|
-
function getRegisteredPixelIds() {
|
|
79
|
-
var _a;
|
|
80
|
-
return (_a = window.__ks_pixel_ids) != null ? _a : [];
|
|
81
|
-
}
|
|
82
|
-
function applyStoredUserData(fbq) {
|
|
83
|
-
try {
|
|
84
|
-
const raw = sessionStorage.getItem(STORAGE_KEY);
|
|
85
|
-
if (!raw) return;
|
|
86
|
-
const hashed = JSON.parse(raw);
|
|
87
|
-
if (Object.keys(hashed).length === 0) return;
|
|
88
|
-
getRegisteredPixelIds().forEach((id) => fbq("init", id, hashed));
|
|
89
|
-
} catch (e) {
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
async function setPixelUserData(userData) {
|
|
93
|
-
const hashed = {};
|
|
94
|
-
if (userData.email) {
|
|
95
|
-
hashed.em = await sha256Hex(userData.email.trim().toLowerCase());
|
|
96
|
-
}
|
|
97
|
-
if (userData.phone) {
|
|
98
|
-
const digits = userData.phone.replace(/\D/g, "");
|
|
99
|
-
if (digits) hashed.ph = await sha256Hex(digits);
|
|
100
|
-
}
|
|
101
|
-
if (Object.keys(hashed).length === 0) return;
|
|
102
|
-
try {
|
|
103
|
-
sessionStorage.setItem(STORAGE_KEY, JSON.stringify(hashed));
|
|
104
|
-
} catch (e) {
|
|
105
|
-
}
|
|
106
|
-
const fbq = getFbq();
|
|
107
|
-
if (fbq) {
|
|
108
|
-
getRegisteredPixelIds().forEach((id) => fbq("init", id, hashed));
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
function firePixelEvent(event, params, eventId) {
|
|
112
|
-
const fbq = getFbq();
|
|
113
|
-
if (!fbq) {
|
|
114
|
-
console.debug("[MetaPixel] skipped \u2014 fbq not loaded", { event });
|
|
115
|
-
return;
|
|
116
|
-
}
|
|
117
|
-
applyStoredUserData(fbq);
|
|
118
|
-
const normalized = {};
|
|
119
|
-
if (params == null ? void 0 : params.contentName) normalized.content_name = params.contentName;
|
|
120
|
-
if (params == null ? void 0 : params.contentCategory) normalized.content_category = params.contentCategory;
|
|
121
|
-
const customData = Object.keys(normalized).length > 0 ? normalized : void 0;
|
|
122
|
-
const eventData = eventId ? { eventID: eventId } : void 0;
|
|
123
|
-
console.debug("[MetaPixel]", event, normalized, eventId ? { eventID: eventId } : "");
|
|
124
|
-
fbq("track", event, customData, eventData);
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
// src/tracking/MetaPixelTracker.tsx
|
|
128
|
-
function slugToTitle(slug) {
|
|
129
|
-
return slug.split("-").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
|
|
130
|
-
}
|
|
131
|
-
var ROUTE_RULES = [
|
|
132
|
-
{
|
|
133
|
-
pattern: /^\/services\/(.+)$/,
|
|
134
|
-
getParams: ([, slug]) => ({ contentName: slugToTitle(slug), contentCategory: "Service" })
|
|
135
|
-
},
|
|
136
|
-
{
|
|
137
|
-
pattern: /^\/locations\/(.+)$/,
|
|
138
|
-
getParams: ([, slug]) => ({ contentName: slugToTitle(slug), contentCategory: "Location" })
|
|
139
|
-
},
|
|
140
|
-
{
|
|
141
|
-
pattern: /^\/services$/,
|
|
142
|
-
getParams: () => ({ contentName: "Services", contentCategory: "Services" })
|
|
143
|
-
},
|
|
144
|
-
{
|
|
145
|
-
pattern: /^\/locations$/,
|
|
146
|
-
getParams: () => ({ contentName: "Locations", contentCategory: "Locations" })
|
|
147
|
-
},
|
|
148
|
-
{
|
|
149
|
-
pattern: /^\/portal$/,
|
|
150
|
-
getParams: () => ({ contentName: "Member Portal", contentCategory: "Pricing" })
|
|
151
|
-
},
|
|
152
|
-
{
|
|
153
|
-
pattern: /^\/service-menu$/,
|
|
154
|
-
getParams: () => ({ contentName: "Service Menu", contentCategory: "Pricing" })
|
|
155
|
-
},
|
|
156
|
-
{
|
|
157
|
-
pattern: /^\/faq$/,
|
|
158
|
-
getParams: () => ({ contentName: "FAQ", contentCategory: "FAQ" })
|
|
159
|
-
},
|
|
160
|
-
{
|
|
161
|
-
pattern: /^\/contact$/,
|
|
162
|
-
getParams: () => ({ contentName: "Contact", contentCategory: "Contact" })
|
|
163
|
-
}
|
|
164
|
-
];
|
|
165
|
-
function MetaPixelTracker({ bookingUrl }) {
|
|
166
|
-
const pathname = usePathname();
|
|
167
|
-
useEffect(() => {
|
|
168
|
-
for (const rule of ROUTE_RULES) {
|
|
169
|
-
const match = pathname.match(rule.pattern);
|
|
170
|
-
if (match) {
|
|
171
|
-
const { contentName, contentCategory } = rule.getParams(match);
|
|
172
|
-
firePixelEvent("ViewContent", { contentName, contentCategory });
|
|
173
|
-
break;
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
}, [pathname]);
|
|
177
|
-
useEffect(() => {
|
|
178
|
-
if (!bookingUrl) return;
|
|
179
|
-
const handleClick = (e) => {
|
|
180
|
-
var _a;
|
|
181
|
-
const anchor = e.target.closest("a");
|
|
182
|
-
if ((_a = anchor == null ? void 0 : anchor.href) == null ? void 0 : _a.startsWith(bookingUrl)) {
|
|
183
|
-
firePixelEvent("InitiateCheckout");
|
|
184
|
-
}
|
|
185
|
-
};
|
|
186
|
-
document.addEventListener("click", handleClick);
|
|
187
|
-
return () => document.removeEventListener("click", handleClick);
|
|
188
|
-
}, [bookingUrl]);
|
|
189
|
-
return null;
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
// src/tracking/PostHogProvider.tsx
|
|
193
|
-
import posthog from "posthog-js";
|
|
194
|
-
import { PostHogProvider as PHProvider } from "posthog-js/react";
|
|
195
|
-
import { useEffect as useEffect2, Suspense } from "react";
|
|
196
|
-
import { usePathname as usePathname2, useSearchParams } from "next/navigation";
|
|
23
|
+
import Script from "next/script";
|
|
197
24
|
|
|
198
25
|
// src/tracking/logging.ts
|
|
199
26
|
import { logs } from "@opentelemetry/api-logs";
|
|
@@ -202,9 +29,12 @@ import { resourceFromAttributes } from "@opentelemetry/resources";
|
|
|
202
29
|
import { BatchLogRecordProcessor, LoggerProvider } from "@opentelemetry/sdk-logs";
|
|
203
30
|
var BUILD_CONSOLE_DISABLED = process.env.NEXT_PUBLIC_LOGGING_DISABLED === "1";
|
|
204
31
|
var BUILD_POSTHOG_DISABLED = process.env.NEXT_PUBLIC_POSTHOG_LOGGING_DISABLED === "1";
|
|
32
|
+
var MAX_PENDING_POSTHOG_LOGS = 500;
|
|
205
33
|
var otelProvider = null;
|
|
206
34
|
var otelLogger = null;
|
|
207
35
|
var otelInitFingerprint = "";
|
|
36
|
+
var pendingPostHogLogs = [];
|
|
37
|
+
var browserConsole = globalThis == null ? void 0 : globalThis["console"];
|
|
208
38
|
var CHANNEL_COLORS = {
|
|
209
39
|
// Shared diagnostics
|
|
210
40
|
"global-client": "#d2a8ff",
|
|
@@ -248,6 +78,20 @@ function flattenAttributes(input, prefix = "") {
|
|
|
248
78
|
function stripTrailingSlash(host) {
|
|
249
79
|
return host.replace(/\/+$/, "");
|
|
250
80
|
}
|
|
81
|
+
function enqueuePendingPostHogLog(entry) {
|
|
82
|
+
if (pendingPostHogLogs.length >= MAX_PENDING_POSTHOG_LOGS) {
|
|
83
|
+
pendingPostHogLogs.shift();
|
|
84
|
+
}
|
|
85
|
+
pendingPostHogLogs.push(entry);
|
|
86
|
+
}
|
|
87
|
+
function flushPendingPostHogLogs() {
|
|
88
|
+
if (!otelLogger || pendingPostHogLogs.length === 0) return;
|
|
89
|
+
while (pendingPostHogLogs.length > 0) {
|
|
90
|
+
const entry = pendingPostHogLogs.shift();
|
|
91
|
+
if (!entry) break;
|
|
92
|
+
emitToPostHog(entry.level, entry.channel, entry.event, entry.detail);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
251
95
|
function initializePostHogLogging(options) {
|
|
252
96
|
var _a, _b, _c, _d, _e, _f;
|
|
253
97
|
if (typeof window === "undefined") return;
|
|
@@ -284,6 +128,7 @@ function initializePostHogLogging(options) {
|
|
|
284
128
|
logs.setGlobalLoggerProvider(otelProvider);
|
|
285
129
|
otelLogger = logs.getLogger(serviceName);
|
|
286
130
|
otelInitFingerprint = fingerprint;
|
|
131
|
+
flushPendingPostHogLogs();
|
|
287
132
|
}
|
|
288
133
|
function isConsoleEnabled() {
|
|
289
134
|
if (BUILD_CONSOLE_DISABLED) return false;
|
|
@@ -299,7 +144,7 @@ function formatDetail(detail) {
|
|
|
299
144
|
return Object.entries(detail).map(([k, v]) => `${k}=${typeof v === "number" ? v.toFixed(4) : String(v)}`).join(" ");
|
|
300
145
|
}
|
|
301
146
|
function emitConsole(level, channel, event, detail) {
|
|
302
|
-
var _a;
|
|
147
|
+
var _a, _b, _c, _d;
|
|
303
148
|
const color = (_a = CHANNEL_COLORS[channel]) != null ? _a : "#aaa";
|
|
304
149
|
const detailStr = formatDetail(detail);
|
|
305
150
|
const message = `%c[${channel}] %c${event}%c ${detailStr}`;
|
|
@@ -307,17 +152,17 @@ function emitConsole(level, channel, event, detail) {
|
|
|
307
152
|
const eventStyle = level === "error" ? "color:#e94e4e; font-weight:bold" : level === "warn" ? "color:#f5a623; font-weight:bold" : "color:#fff; font-weight:bold";
|
|
308
153
|
const detailStyle = "color:#999; font-weight:normal";
|
|
309
154
|
if (level === "error") {
|
|
310
|
-
|
|
155
|
+
(_b = browserConsole == null ? void 0 : browserConsole.error) == null ? void 0 : _b.call(browserConsole, message, channelStyle, eventStyle, detailStyle);
|
|
311
156
|
return;
|
|
312
157
|
}
|
|
313
158
|
if (level === "warn") {
|
|
314
|
-
|
|
159
|
+
(_c = browserConsole == null ? void 0 : browserConsole.warn) == null ? void 0 : _c.call(browserConsole, message, channelStyle, eventStyle, detailStyle);
|
|
315
160
|
return;
|
|
316
161
|
}
|
|
317
|
-
|
|
162
|
+
(_d = browserConsole == null ? void 0 : browserConsole.log) == null ? void 0 : _d.call(browserConsole, message, channelStyle, eventStyle, detailStyle);
|
|
318
163
|
}
|
|
319
164
|
function emitToPostHog(level, channel, event, detail) {
|
|
320
|
-
if (!otelLogger) return;
|
|
165
|
+
if (!otelLogger) return false;
|
|
321
166
|
const severityText = level === "warn" ? "WARNING" : level === "error" ? "ERROR" : "INFO";
|
|
322
167
|
const attributes = flattenAttributes(__spreadValues({ channel }, detail));
|
|
323
168
|
otelLogger.emit({
|
|
@@ -325,6 +170,7 @@ function emitToPostHog(level, channel, event, detail) {
|
|
|
325
170
|
body: event,
|
|
326
171
|
attributes
|
|
327
172
|
});
|
|
173
|
+
return true;
|
|
328
174
|
}
|
|
329
175
|
function emit(level, channel, event, detail, options) {
|
|
330
176
|
var _a;
|
|
@@ -333,7 +179,10 @@ function emit(level, channel, event, detail, options) {
|
|
|
333
179
|
emitConsole(level, channel, event, detail);
|
|
334
180
|
}
|
|
335
181
|
if (shouldShip) {
|
|
336
|
-
emitToPostHog(level, channel, event, detail);
|
|
182
|
+
const shipped = emitToPostHog(level, channel, event, detail);
|
|
183
|
+
if (!shipped) {
|
|
184
|
+
enqueuePendingPostHogLog({ level, channel, event, detail });
|
|
185
|
+
}
|
|
337
186
|
}
|
|
338
187
|
}
|
|
339
188
|
function log(channel, event, detail = {}, options) {
|
|
@@ -346,7 +195,185 @@ function error(channel, event, detail = {}, options) {
|
|
|
346
195
|
emit("error", channel, event, detail, options);
|
|
347
196
|
}
|
|
348
197
|
|
|
198
|
+
// src/tracking/firePixelEvent.ts
|
|
199
|
+
var STORAGE_KEY = "ks_pud";
|
|
200
|
+
async function sha256Hex(str) {
|
|
201
|
+
const buffer = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(str));
|
|
202
|
+
return Array.from(new Uint8Array(buffer)).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
203
|
+
}
|
|
204
|
+
function getFbq() {
|
|
205
|
+
if (typeof window === "undefined") return void 0;
|
|
206
|
+
return window.fbq;
|
|
207
|
+
}
|
|
208
|
+
function getRegisteredPixelIds() {
|
|
209
|
+
var _a;
|
|
210
|
+
return (_a = window.__ks_pixel_ids) != null ? _a : [];
|
|
211
|
+
}
|
|
212
|
+
function applyStoredUserData(fbq) {
|
|
213
|
+
try {
|
|
214
|
+
const raw = sessionStorage.getItem(STORAGE_KEY);
|
|
215
|
+
if (!raw) return;
|
|
216
|
+
const hashed = JSON.parse(raw);
|
|
217
|
+
if (Object.keys(hashed).length === 0) return;
|
|
218
|
+
getRegisteredPixelIds().forEach((id) => fbq("init", id, hashed));
|
|
219
|
+
} catch (e) {
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
async function setPixelUserData(userData) {
|
|
223
|
+
const hashed = {};
|
|
224
|
+
if (userData.email) {
|
|
225
|
+
hashed.em = await sha256Hex(userData.email.trim().toLowerCase());
|
|
226
|
+
}
|
|
227
|
+
if (userData.phone) {
|
|
228
|
+
const digits = userData.phone.replace(/\D/g, "");
|
|
229
|
+
if (digits) hashed.ph = await sha256Hex(digits);
|
|
230
|
+
}
|
|
231
|
+
if (Object.keys(hashed).length === 0) return;
|
|
232
|
+
try {
|
|
233
|
+
sessionStorage.setItem(STORAGE_KEY, JSON.stringify(hashed));
|
|
234
|
+
} catch (e) {
|
|
235
|
+
}
|
|
236
|
+
const fbq = getFbq();
|
|
237
|
+
if (fbq) {
|
|
238
|
+
getRegisteredPixelIds().forEach((id) => fbq("init", id, hashed));
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
function firePixelEvent(event, params, eventId) {
|
|
242
|
+
const fbq = getFbq();
|
|
243
|
+
if (!fbq) {
|
|
244
|
+
warn("meta-pixel", "PIXEL_EVENT_SKIPPED_FBQ_NOT_LOADED", { event });
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
247
|
+
applyStoredUserData(fbq);
|
|
248
|
+
const normalized = {};
|
|
249
|
+
if (params == null ? void 0 : params.contentName) normalized.content_name = params.contentName;
|
|
250
|
+
if (params == null ? void 0 : params.contentCategory) normalized.content_category = params.contentCategory;
|
|
251
|
+
const customData = Object.keys(normalized).length > 0 ? normalized : void 0;
|
|
252
|
+
const eventData = eventId ? { eventID: eventId } : void 0;
|
|
253
|
+
log("meta-pixel", "PIXEL_EVENT_FIRED", __spreadValues(__spreadValues({
|
|
254
|
+
event
|
|
255
|
+
}, normalized), eventId ? { eventId } : {}), { shipToPostHog: true });
|
|
256
|
+
fbq("track", event, customData, eventData);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// src/tracking/MetaPixel.tsx
|
|
260
|
+
var FBEVENTS_URL = "https://connect.facebook.net/en_US/fbevents.js";
|
|
261
|
+
var PIXEL_SCRIPT = (pixelId) => `
|
|
262
|
+
!function(f,b,e,v,n,t,s)
|
|
263
|
+
{if(f.fbq)return;n=f.fbq=function(){n.callMethod?
|
|
264
|
+
n.callMethod.apply(n,arguments):n.queue.push(arguments)};
|
|
265
|
+
if(!f._fbq)f._fbq=n;n.push=n;n.loaded=!0;n.version='2.0';
|
|
266
|
+
n.queue=[];t=b.createElement(e);t.async=!0;
|
|
267
|
+
t.src=v;s=b.getElementsByTagName(e)[0];
|
|
268
|
+
s.parentNode.insertBefore(t,s)}(window, document,'script','${FBEVENTS_URL}');
|
|
269
|
+
fbq('init', '${pixelId.replace(/'/g, "\\'")}');
|
|
270
|
+
window.__ks_pixel_ids = window.__ks_pixel_ids || [];
|
|
271
|
+
if (window.__ks_pixel_ids.indexOf('${pixelId.replace(/'/g, "\\'")}') === -1) {
|
|
272
|
+
window.__ks_pixel_ids.push('${pixelId.replace(/'/g, "\\'")}');
|
|
273
|
+
}
|
|
274
|
+
`;
|
|
275
|
+
function MetaPixel({ pixelId }) {
|
|
276
|
+
const raw = typeof pixelId === "string" ? pixelId.trim() : "";
|
|
277
|
+
const id = raw && raw !== "null" && /^\d+$/.test(raw) ? raw : "";
|
|
278
|
+
useEffect(() => {
|
|
279
|
+
if (!id) return;
|
|
280
|
+
log("meta-pixel", "PIXEL_INIT", { pixelId: id }, { shipToPostHog: true });
|
|
281
|
+
firePixelEvent("PageView");
|
|
282
|
+
}, [id]);
|
|
283
|
+
if (!id) {
|
|
284
|
+
return null;
|
|
285
|
+
}
|
|
286
|
+
return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(
|
|
287
|
+
Script,
|
|
288
|
+
{
|
|
289
|
+
id: "meta-pixel",
|
|
290
|
+
strategy: "afterInteractive",
|
|
291
|
+
dangerouslySetInnerHTML: { __html: PIXEL_SCRIPT(id) }
|
|
292
|
+
}
|
|
293
|
+
), /* @__PURE__ */ React.createElement("noscript", null, /* @__PURE__ */ React.createElement(
|
|
294
|
+
"img",
|
|
295
|
+
{
|
|
296
|
+
height: 1,
|
|
297
|
+
width: 1,
|
|
298
|
+
style: { display: "none" },
|
|
299
|
+
src: `https://www.facebook.com/tr?id=${id}&ev=PageView&noscript=1`,
|
|
300
|
+
alt: ""
|
|
301
|
+
}
|
|
302
|
+
)));
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
// src/tracking/MetaPixelTracker.tsx
|
|
306
|
+
import { useEffect as useEffect2 } from "react";
|
|
307
|
+
import { usePathname } from "next/navigation";
|
|
308
|
+
function slugToTitle(slug) {
|
|
309
|
+
return slug.split("-").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
|
|
310
|
+
}
|
|
311
|
+
var ROUTE_RULES = [
|
|
312
|
+
{
|
|
313
|
+
pattern: /^\/services\/(.+)$/,
|
|
314
|
+
getParams: ([, slug]) => ({ contentName: slugToTitle(slug), contentCategory: "Service" })
|
|
315
|
+
},
|
|
316
|
+
{
|
|
317
|
+
pattern: /^\/locations\/(.+)$/,
|
|
318
|
+
getParams: ([, slug]) => ({ contentName: slugToTitle(slug), contentCategory: "Location" })
|
|
319
|
+
},
|
|
320
|
+
{
|
|
321
|
+
pattern: /^\/services$/,
|
|
322
|
+
getParams: () => ({ contentName: "Services", contentCategory: "Services" })
|
|
323
|
+
},
|
|
324
|
+
{
|
|
325
|
+
pattern: /^\/locations$/,
|
|
326
|
+
getParams: () => ({ contentName: "Locations", contentCategory: "Locations" })
|
|
327
|
+
},
|
|
328
|
+
{
|
|
329
|
+
pattern: /^\/portal$/,
|
|
330
|
+
getParams: () => ({ contentName: "Member Portal", contentCategory: "Pricing" })
|
|
331
|
+
},
|
|
332
|
+
{
|
|
333
|
+
pattern: /^\/service-menu$/,
|
|
334
|
+
getParams: () => ({ contentName: "Service Menu", contentCategory: "Pricing" })
|
|
335
|
+
},
|
|
336
|
+
{
|
|
337
|
+
pattern: /^\/faq$/,
|
|
338
|
+
getParams: () => ({ contentName: "FAQ", contentCategory: "FAQ" })
|
|
339
|
+
},
|
|
340
|
+
{
|
|
341
|
+
pattern: /^\/contact$/,
|
|
342
|
+
getParams: () => ({ contentName: "Contact", contentCategory: "Contact" })
|
|
343
|
+
}
|
|
344
|
+
];
|
|
345
|
+
function MetaPixelTracker({ bookingUrl }) {
|
|
346
|
+
const pathname = usePathname();
|
|
347
|
+
useEffect2(() => {
|
|
348
|
+
for (const rule of ROUTE_RULES) {
|
|
349
|
+
const match = pathname.match(rule.pattern);
|
|
350
|
+
if (match) {
|
|
351
|
+
const { contentName, contentCategory } = rule.getParams(match);
|
|
352
|
+
firePixelEvent("ViewContent", { contentName, contentCategory });
|
|
353
|
+
break;
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
}, [pathname]);
|
|
357
|
+
useEffect2(() => {
|
|
358
|
+
if (!bookingUrl) return;
|
|
359
|
+
const handleClick = (e) => {
|
|
360
|
+
var _a;
|
|
361
|
+
const anchor = e.target.closest("a");
|
|
362
|
+
if ((_a = anchor == null ? void 0 : anchor.href) == null ? void 0 : _a.startsWith(bookingUrl)) {
|
|
363
|
+
firePixelEvent("InitiateCheckout");
|
|
364
|
+
}
|
|
365
|
+
};
|
|
366
|
+
document.addEventListener("click", handleClick);
|
|
367
|
+
return () => document.removeEventListener("click", handleClick);
|
|
368
|
+
}, [bookingUrl]);
|
|
369
|
+
return null;
|
|
370
|
+
}
|
|
371
|
+
|
|
349
372
|
// src/tracking/PostHogProvider.tsx
|
|
373
|
+
import posthog from "posthog-js";
|
|
374
|
+
import { PostHogProvider as PHProvider } from "posthog-js/react";
|
|
375
|
+
import { useEffect as useEffect3, Suspense } from "react";
|
|
376
|
+
import { usePathname as usePathname2, useSearchParams } from "next/navigation";
|
|
350
377
|
var DEFAULT_HOST = "https://us.i.posthog.com";
|
|
351
378
|
function resolvePageInfo(pathname) {
|
|
352
379
|
var _a;
|
|
@@ -384,7 +411,7 @@ function resolvePageInfo(pathname) {
|
|
|
384
411
|
function PostHogPageviewTracker() {
|
|
385
412
|
const pathname = usePathname2();
|
|
386
413
|
const searchParams = useSearchParams();
|
|
387
|
-
|
|
414
|
+
useEffect3(() => {
|
|
388
415
|
if (!pathname) return;
|
|
389
416
|
const search = searchParams == null ? void 0 : searchParams.toString();
|
|
390
417
|
const url = window.location.origin + pathname + (search ? `?${search}` : "");
|
|
@@ -398,7 +425,7 @@ function PostHogPageviewTracker() {
|
|
|
398
425
|
return null;
|
|
399
426
|
}
|
|
400
427
|
function PostHogProvider({ apiKey, apiHost, accountId, accountName, environment, children }) {
|
|
401
|
-
|
|
428
|
+
useEffect3(() => {
|
|
402
429
|
posthog.init(apiKey, {
|
|
403
430
|
api_host: apiHost != null ? apiHost : DEFAULT_HOST,
|
|
404
431
|
person_profiles: "identified_only",
|
|
@@ -416,7 +443,7 @@ function PostHogProvider({ apiKey, apiHost, accountId, accountName, environment,
|
|
|
416
443
|
accountName
|
|
417
444
|
});
|
|
418
445
|
}, [apiKey, apiHost, accountId, accountName, environment]);
|
|
419
|
-
|
|
446
|
+
useEffect3(() => {
|
|
420
447
|
const onWindowError = (event) => {
|
|
421
448
|
error(
|
|
422
449
|
"global-client",
|
|
@@ -447,7 +474,7 @@ function PostHogProvider({ apiKey, apiHost, accountId, accountName, environment,
|
|
|
447
474
|
}
|
|
448
475
|
|
|
449
476
|
// src/tracking/KeystoneAnalyticsTracker.tsx
|
|
450
|
-
import { useEffect as
|
|
477
|
+
import { useEffect as useEffect4 } from "react";
|
|
451
478
|
import { usePathname as usePathname3 } from "next/navigation";
|
|
452
479
|
|
|
453
480
|
// src/tracking/captureEvent.ts
|
|
@@ -462,7 +489,7 @@ function captureCustomEvent(event, properties) {
|
|
|
462
489
|
// src/tracking/KeystoneAnalyticsTracker.tsx
|
|
463
490
|
function KeystoneAnalyticsTracker({ bookingUrl }) {
|
|
464
491
|
const pathname = usePathname3();
|
|
465
|
-
|
|
492
|
+
useEffect4(() => {
|
|
466
493
|
if (!bookingUrl) return;
|
|
467
494
|
const handleClick = (e) => {
|
|
468
495
|
var _a;
|