@salesmind-ai/design-system 0.2.1 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +32 -2
- package/dist/AppearancePanel-UT57J69V.d.cts +51 -0
- package/dist/AppearancePanel-UT57J69V.d.ts +51 -0
- package/dist/AppearanceProvider-C36a8-eb.d.cts +45 -0
- package/dist/AppearanceProvider-C36a8-eb.d.ts +45 -0
- package/dist/Breadcrumb-RX-B_gDV.d.cts +44 -0
- package/dist/Breadcrumb-RX-B_gDV.d.ts +44 -0
- package/dist/ExportMenu-A2TLFiVv.d.cts +311 -0
- package/dist/ExportMenu-C8qck5AT.d.ts +311 -0
- package/dist/SectionShell-BfBw5q0Y.d.cts +18 -0
- package/dist/SectionShell-BfBw5q0Y.d.ts +18 -0
- package/dist/Select-BdZmK0Lt.d.cts +66 -0
- package/dist/Select-BdZmK0Lt.d.ts +66 -0
- package/dist/admin/index.cjs +2941 -0
- package/dist/admin/index.cjs.map +1 -0
- package/dist/admin/index.css +4145 -0
- package/dist/admin/index.css.map +1 -0
- package/dist/admin/index.d.cts +491 -0
- package/dist/admin/index.d.ts +491 -0
- package/dist/admin/index.js +2918 -0
- package/dist/admin/index.js.map +1 -0
- package/dist/{audit-CiyPkxk1.d.cts → audit-BS2fn7M4.d.ts} +2 -51
- package/dist/{audit-CiyPkxk1.d.ts → audit-DwCmg32J.d.cts} +2 -51
- package/dist/blog/index.cjs +1074 -0
- package/dist/blog/index.cjs.map +1 -0
- package/dist/blog/index.css +1422 -0
- package/dist/blog/index.css.map +1 -0
- package/dist/blog/index.d.cts +233 -0
- package/dist/blog/index.d.ts +233 -0
- package/dist/blog/index.js +1056 -0
- package/dist/blog/index.js.map +1 -0
- package/dist/chart-types-BGVVO-zl.d.cts +208 -0
- package/dist/chart-types-BGVVO-zl.d.ts +208 -0
- package/dist/charts/index.cjs +2698 -0
- package/dist/charts/index.cjs.map +1 -0
- package/dist/charts/index.css +1167 -0
- package/dist/charts/index.css.map +1 -0
- package/dist/charts/index.d.cts +453 -0
- package/dist/charts/index.d.ts +453 -0
- package/dist/charts/index.js +2682 -0
- package/dist/charts/index.js.map +1 -0
- package/dist/core/index.cjs +526 -395
- package/dist/core/index.cjs.map +1 -1
- package/dist/core/index.css +294 -0
- package/dist/core/index.css.map +1 -1
- package/dist/core/index.d.cts +7 -982
- package/dist/core/index.d.ts +7 -982
- package/dist/core/index.js +476 -351
- package/dist/core/index.js.map +1 -1
- package/dist/i18n/index.cjs +585 -0
- package/dist/i18n/index.cjs.map +1 -0
- package/dist/i18n/index.d.cts +855 -0
- package/dist/i18n/index.d.ts +855 -0
- package/dist/i18n/index.js +547 -0
- package/dist/i18n/index.js.map +1 -0
- package/dist/index.cjs +3 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.css +11 -7
- package/dist/index.css.map +1 -1
- package/dist/index.d.cts +22 -1290
- package/dist/index.d.ts +22 -1290
- package/dist/index.js +3 -2
- package/dist/index.js.map +1 -1
- package/dist/marketing/index.cjs +2144 -3023
- package/dist/marketing/index.cjs.map +1 -1
- package/dist/marketing/index.css +3729 -4824
- package/dist/marketing/index.css.map +1 -1
- package/dist/marketing/index.d.cts +1351 -4
- package/dist/marketing/index.d.ts +1351 -4
- package/dist/marketing/index.js +2190 -3054
- package/dist/marketing/index.js.map +1 -1
- package/dist/motion/index.cjs +1230 -0
- package/dist/motion/index.cjs.map +1 -0
- package/dist/motion/index.css +699 -0
- package/dist/motion/index.css.map +1 -0
- package/dist/motion/index.d.cts +68 -0
- package/dist/motion/index.d.ts +68 -0
- package/dist/motion/index.js +1218 -0
- package/dist/motion/index.js.map +1 -0
- package/dist/nav/index.cjs +1533 -0
- package/dist/nav/index.cjs.map +1 -0
- package/dist/nav/index.css +1984 -0
- package/dist/nav/index.css.map +1 -0
- package/dist/nav/index.d.cts +279 -0
- package/dist/nav/index.d.ts +279 -0
- package/dist/nav/index.js +1501 -0
- package/dist/nav/index.js.map +1 -0
- package/dist/report/index.cjs +26 -1649
- package/dist/report/index.cjs.map +1 -1
- package/dist/report/index.css +0 -963
- package/dist/report/index.css.map +1 -1
- package/dist/report/index.d.cts +4 -2
- package/dist/report/index.d.ts +4 -2
- package/dist/report/index.js +27 -1640
- package/dist/report/index.js.map +1 -1
- package/dist/sections/index.cjs +385 -0
- package/dist/sections/index.cjs.map +1 -0
- package/dist/sections/index.css +815 -0
- package/dist/sections/index.css.map +1 -0
- package/dist/sections/index.d.cts +69 -0
- package/dist/sections/index.d.ts +69 -0
- package/dist/sections/index.js +374 -0
- package/dist/sections/index.js.map +1 -0
- package/dist/social-proof/index.cjs +1255 -0
- package/dist/social-proof/index.cjs.map +1 -0
- package/dist/social-proof/index.css +1423 -0
- package/dist/social-proof/index.css.map +1 -0
- package/dist/social-proof/index.d.cts +258 -0
- package/dist/social-proof/index.d.ts +258 -0
- package/dist/social-proof/index.js +1238 -0
- package/dist/social-proof/index.js.map +1 -0
- package/dist/theme/index.cjs +573 -0
- package/dist/theme/index.cjs.map +1 -0
- package/dist/theme/index.css +464 -0
- package/dist/theme/index.css.map +1 -0
- package/dist/theme/index.d.cts +48 -0
- package/dist/theme/index.d.ts +48 -0
- package/dist/theme/index.js +558 -0
- package/dist/theme/index.js.map +1 -0
- package/dist/types-DAlgDGzw.d.cts +52 -0
- package/dist/types-DAlgDGzw.d.ts +52 -0
- package/dist/web/client/index.cjs +501 -0
- package/dist/web/client/index.cjs.map +1 -0
- package/dist/web/client/index.css +456 -0
- package/dist/web/client/index.css.map +1 -0
- package/dist/web/client/index.d.cts +172 -0
- package/dist/web/client/index.d.ts +172 -0
- package/dist/web/client/index.js +486 -0
- package/dist/web/client/index.js.map +1 -0
- package/dist/web/index.d.cts +6 -893
- package/dist/web/index.d.ts +6 -893
- package/dist/web/server/index.cjs +569 -0
- package/dist/web/server/index.cjs.map +1 -0
- package/dist/web/server/index.d.cts +725 -0
- package/dist/web/server/index.d.ts +725 -0
- package/dist/web/server/index.js +562 -0
- package/dist/web/server/index.js.map +1 -0
- package/package.json +81 -9
- package/dist/ExportMenu-hEe5MhLq.d.cts +0 -1027
- package/dist/ExportMenu-hEe5MhLq.d.ts +0 -1027
- package/dist/index-B64suAAc.d.cts +0 -1498
- package/dist/index-B64suAAc.d.ts +0 -1498
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import { ReactNode } from 'react';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Generic analytics script loader.
|
|
6
|
+
*
|
|
7
|
+
* Framework-agnostic: injects a `<script>` tag into `<head>` once,
|
|
8
|
+
* guards against double-loading, and respects cookie consent.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```ts
|
|
12
|
+
* import { createAnalyticsLoader } from '@salesmind-ai/design-system/web';
|
|
13
|
+
*
|
|
14
|
+
* const loadGA = createAnalyticsLoader({
|
|
15
|
+
* id: 'ga-script',
|
|
16
|
+
* src: (gaId) => `https://www.googletagmanager.com/gtag/js?id=${gaId}`,
|
|
17
|
+
* onLoad: (gaId) => {
|
|
18
|
+
* window.dataLayer = window.dataLayer || [];
|
|
19
|
+
* function gtag(...args: unknown[]) { window.dataLayer.push(args); }
|
|
20
|
+
* gtag('js', new Date());
|
|
21
|
+
* gtag('config', gaId);
|
|
22
|
+
* },
|
|
23
|
+
* });
|
|
24
|
+
*
|
|
25
|
+
* // Later, after consent:
|
|
26
|
+
* loadGA('G-XXXXXX');
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
interface AnalyticsLoaderConfig<TId extends string = string> {
|
|
30
|
+
/** Unique DOM id for the script element (prevents double-loading) */
|
|
31
|
+
id: string;
|
|
32
|
+
/** Build the script src URL from the tracking ID */
|
|
33
|
+
src: (trackingId: TId) => string;
|
|
34
|
+
/** Async attribute on the script tag (default: true) */
|
|
35
|
+
async?: boolean;
|
|
36
|
+
/** Called after the script is appended to the DOM */
|
|
37
|
+
onLoad?: (trackingId: TId) => void;
|
|
38
|
+
/** Custom inline script content instead of an external src */
|
|
39
|
+
inlineScript?: (trackingId: TId) => string;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Create a reusable analytics loader function.
|
|
43
|
+
* The returned function injects the script once and is safe to call multiple times.
|
|
44
|
+
*/
|
|
45
|
+
declare function createAnalyticsLoader<TId extends string = string>(config: AnalyticsLoaderConfig<TId>): (trackingId: TId) => void;
|
|
46
|
+
declare global {
|
|
47
|
+
interface Window {
|
|
48
|
+
dataLayer: unknown[];
|
|
49
|
+
clarity?: (...args: unknown[]) => void;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Load Google Analytics (gtag.js).
|
|
54
|
+
* Call with your GA measurement ID after cookie consent.
|
|
55
|
+
*/
|
|
56
|
+
declare const loadGoogleAnalytics: (trackingId: string) => void;
|
|
57
|
+
/**
|
|
58
|
+
* Load Microsoft Clarity.
|
|
59
|
+
* Call with your Clarity project ID after cookie consent.
|
|
60
|
+
*/
|
|
61
|
+
declare const loadClarity: (trackingId: string) => void;
|
|
62
|
+
|
|
63
|
+
/** Cookie consent state */
|
|
64
|
+
type ConsentStatus = 'granted' | 'denied' | null;
|
|
65
|
+
/** Event name dispatched on window when consent changes */
|
|
66
|
+
declare const COOKIE_CONSENT_EVENT = "cookie_consent_granted";
|
|
67
|
+
/** localStorage key for cookie consent */
|
|
68
|
+
declare const COOKIE_CONSENT_KEY = "cookie_consent";
|
|
69
|
+
/** Labels for i18n support */
|
|
70
|
+
interface CookieConsentLabels {
|
|
71
|
+
title?: string;
|
|
72
|
+
description?: string;
|
|
73
|
+
privacyLinkText?: string;
|
|
74
|
+
acceptLabel?: string;
|
|
75
|
+
declineLabel?: string;
|
|
76
|
+
}
|
|
77
|
+
interface CookieConsentProps {
|
|
78
|
+
/** Delay in ms before showing the banner (default: 1000) */
|
|
79
|
+
delay?: number;
|
|
80
|
+
/** URL to the privacy policy page (default: "/legal/privacy") */
|
|
81
|
+
privacyUrl?: string;
|
|
82
|
+
/** Called when the user accepts cookies */
|
|
83
|
+
onAccept?: () => void;
|
|
84
|
+
/** Called when the user declines cookies */
|
|
85
|
+
onDecline?: () => void;
|
|
86
|
+
/** Override default labels for i18n */
|
|
87
|
+
labels?: CookieConsentLabels;
|
|
88
|
+
/** Custom className for the container */
|
|
89
|
+
className?: string;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* CookieConsent — GDPR-compliant cookie consent banner.
|
|
93
|
+
*
|
|
94
|
+
* - Animated entrance/exit via CSS transitions (no framer-motion)
|
|
95
|
+
* - Respects prior consent stored in localStorage
|
|
96
|
+
* - Dispatches a `cookie_consent_granted` event on the window for analytics loaders
|
|
97
|
+
* - Uses DS Button component
|
|
98
|
+
*
|
|
99
|
+
* @example
|
|
100
|
+
* ```tsx
|
|
101
|
+
* import { CookieConsent } from '@salesmind-ai/design-system/web';
|
|
102
|
+
*
|
|
103
|
+
* <CookieConsent
|
|
104
|
+
* privacyUrl="/legal/privacy"
|
|
105
|
+
* onAccept={() => loadGoogleAnalytics('G-XXXX')}
|
|
106
|
+
* />
|
|
107
|
+
* ```
|
|
108
|
+
*/
|
|
109
|
+
declare function CookieConsent({ delay, privacyUrl, onAccept, onDecline, labels, className, }: CookieConsentProps): react_jsx_runtime.JSX.Element | null;
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* React hook that tracks cookie consent status.
|
|
113
|
+
*
|
|
114
|
+
* Returns `true` once the user has granted cookie consent,
|
|
115
|
+
* `false` if denied, `null` if not yet decided.
|
|
116
|
+
*
|
|
117
|
+
* Listens for the `cookie_consent_granted` window event dispatched
|
|
118
|
+
* by the CookieConsent component.
|
|
119
|
+
*
|
|
120
|
+
* @example
|
|
121
|
+
* ```tsx
|
|
122
|
+
* const hasConsent = useCookieConsent();
|
|
123
|
+
*
|
|
124
|
+
* useEffect(() => {
|
|
125
|
+
* if (hasConsent) loadGoogleAnalytics('G-XXXX');
|
|
126
|
+
* }, [hasConsent]);
|
|
127
|
+
* ```
|
|
128
|
+
*/
|
|
129
|
+
declare function useCookieConsent(): ConsentStatus;
|
|
130
|
+
|
|
131
|
+
/** Arbitrary properties bag attached to every analytics event. */
|
|
132
|
+
type AnalyticsEventProps = Record<string, string | number | boolean | undefined>;
|
|
133
|
+
/** Signature for the track function provided by AnalyticsProvider. */
|
|
134
|
+
type TrackFn = (event: string, props?: AnalyticsEventProps) => void;
|
|
135
|
+
/** Value exposed by the AnalyticsContext. */
|
|
136
|
+
interface AnalyticsContextValue {
|
|
137
|
+
track: TrackFn;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/** Props for the AnalyticsProvider component */
|
|
141
|
+
interface AnalyticsProviderProps {
|
|
142
|
+
/** Callback invoked on every track() call. Wire this to your analytics backend. */
|
|
143
|
+
onTrack: TrackFn;
|
|
144
|
+
/** Enable console logging in development (default: false) */
|
|
145
|
+
debug?: boolean;
|
|
146
|
+
children: ReactNode;
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Provides analytics event tracking to all descendant DS components.
|
|
150
|
+
*
|
|
151
|
+
* @example
|
|
152
|
+
* ```tsx
|
|
153
|
+
* <AnalyticsProvider
|
|
154
|
+
* onTrack={(event, props) => {
|
|
155
|
+
* window.gtag?.('event', event, props);
|
|
156
|
+
* }}
|
|
157
|
+
* >
|
|
158
|
+
* <App />
|
|
159
|
+
* </AnalyticsProvider>
|
|
160
|
+
* ```
|
|
161
|
+
*/
|
|
162
|
+
declare function AnalyticsProvider({ onTrack, debug, children }: AnalyticsProviderProps): react_jsx_runtime.JSX.Element;
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Hook that returns the analytics `track` function.
|
|
166
|
+
*
|
|
167
|
+
* Safe to call without a wrapping `<AnalyticsProvider>` — all calls
|
|
168
|
+
* become no-ops in that case (Storybook, tests, SSR).
|
|
169
|
+
*/
|
|
170
|
+
declare function useAnalytics(): AnalyticsContextValue;
|
|
171
|
+
|
|
172
|
+
export { type AnalyticsContextValue, type AnalyticsEventProps, type AnalyticsLoaderConfig, AnalyticsProvider, type AnalyticsProviderProps, COOKIE_CONSENT_EVENT, COOKIE_CONSENT_KEY, type ConsentStatus, CookieConsent, type CookieConsentLabels, type CookieConsentProps, type TrackFn, createAnalyticsLoader, loadClarity, loadGoogleAnalytics, useAnalytics, useCookieConsent };
|
|
@@ -0,0 +1,486 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import React, { createContext, forwardRef, useState, useEffect, useContext, useCallback, useMemo } from 'react';
|
|
3
|
+
import { Slot } from '@radix-ui/react-slot';
|
|
4
|
+
import clsx from 'clsx';
|
|
5
|
+
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
6
|
+
import { X } from 'lucide-react';
|
|
7
|
+
|
|
8
|
+
// src/web/analytics/create-analytics-loader.ts
|
|
9
|
+
function createAnalyticsLoader(config) {
|
|
10
|
+
return (trackingId) => {
|
|
11
|
+
if (typeof document === "undefined") return;
|
|
12
|
+
if (document.getElementById(config.id)) return;
|
|
13
|
+
const script = document.createElement("script");
|
|
14
|
+
script.id = config.id;
|
|
15
|
+
if (config.inlineScript) {
|
|
16
|
+
script.textContent = config.inlineScript(trackingId);
|
|
17
|
+
} else {
|
|
18
|
+
script.async = config.async ?? true;
|
|
19
|
+
script.src = config.src(trackingId);
|
|
20
|
+
}
|
|
21
|
+
document.head.appendChild(script);
|
|
22
|
+
config.onLoad?.(trackingId);
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
var loadGoogleAnalytics = createAnalyticsLoader({
|
|
26
|
+
id: "ga-script",
|
|
27
|
+
src: (gaId) => `https://www.googletagmanager.com/gtag/js?id=${gaId}`,
|
|
28
|
+
onLoad: (gaId) => {
|
|
29
|
+
window.dataLayer = window.dataLayer || [];
|
|
30
|
+
window.dataLayer.push(["js", /* @__PURE__ */ new Date()]);
|
|
31
|
+
window.dataLayer.push(["config", gaId]);
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
var loadClarity = createAnalyticsLoader({
|
|
35
|
+
id: "clarity-script",
|
|
36
|
+
src: (clarityId) => `https://www.clarity.ms/tag/${clarityId}`,
|
|
37
|
+
onLoad: () => {
|
|
38
|
+
if (!window.clarity) {
|
|
39
|
+
const queue = [];
|
|
40
|
+
window.clarity = (...args) => {
|
|
41
|
+
queue.push(args);
|
|
42
|
+
};
|
|
43
|
+
window.clarity.q = queue;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
var Button = React.forwardRef(
|
|
48
|
+
({ className, variant = "primary", size = "md", asChild = false, isLoading = false, children, disabled, ...props }, ref) => {
|
|
49
|
+
const buttonClass = clsx(
|
|
50
|
+
"ds-button",
|
|
51
|
+
`ds-button--${variant}`,
|
|
52
|
+
size === "icon" ? "ds-button--icon ds-button--md" : `ds-button--${size}`,
|
|
53
|
+
isLoading && "ds-button--loading",
|
|
54
|
+
className
|
|
55
|
+
);
|
|
56
|
+
if (asChild) {
|
|
57
|
+
return /* @__PURE__ */ jsx(
|
|
58
|
+
Slot,
|
|
59
|
+
{
|
|
60
|
+
ref,
|
|
61
|
+
"aria-disabled": isLoading || disabled || void 0,
|
|
62
|
+
className: buttonClass,
|
|
63
|
+
...props,
|
|
64
|
+
children
|
|
65
|
+
}
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
return /* @__PURE__ */ jsxs(
|
|
69
|
+
"button",
|
|
70
|
+
{
|
|
71
|
+
ref,
|
|
72
|
+
disabled: isLoading || disabled,
|
|
73
|
+
className: buttonClass,
|
|
74
|
+
...props,
|
|
75
|
+
children: [
|
|
76
|
+
isLoading && /* @__PURE__ */ jsx(
|
|
77
|
+
"svg",
|
|
78
|
+
{
|
|
79
|
+
className: "ds-button__spinner",
|
|
80
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
81
|
+
width: "1em",
|
|
82
|
+
height: "1em",
|
|
83
|
+
viewBox: "0 0 24 24",
|
|
84
|
+
fill: "none",
|
|
85
|
+
stroke: "currentColor",
|
|
86
|
+
strokeWidth: "2",
|
|
87
|
+
strokeLinecap: "round",
|
|
88
|
+
strokeLinejoin: "round",
|
|
89
|
+
children: /* @__PURE__ */ jsx("path", { d: "M21 12a9 9 0 1 1-6.219-8.56" })
|
|
90
|
+
}
|
|
91
|
+
),
|
|
92
|
+
/* @__PURE__ */ jsx("span", { className: clsx("ds-button__content", isLoading && "ds-button__content--hidden"), children })
|
|
93
|
+
]
|
|
94
|
+
}
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
);
|
|
98
|
+
Button.displayName = "Button";
|
|
99
|
+
var UtmContext = createContext(null);
|
|
100
|
+
|
|
101
|
+
// src/web/utm/useUtmDefaults.ts
|
|
102
|
+
function useUtmDefaults() {
|
|
103
|
+
return useContext(UtmContext);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// src/web/utm/builders.ts
|
|
107
|
+
var PLACEHOLDER_ORIGIN = "https://__placeholder__.internal";
|
|
108
|
+
function buildUtmUrl(baseUrl, params) {
|
|
109
|
+
const isRelative = baseUrl.startsWith("/");
|
|
110
|
+
let url;
|
|
111
|
+
try {
|
|
112
|
+
url = isRelative ? new URL(baseUrl, PLACEHOLDER_ORIGIN) : new URL(baseUrl);
|
|
113
|
+
} catch {
|
|
114
|
+
return baseUrl;
|
|
115
|
+
}
|
|
116
|
+
const existingParams = [];
|
|
117
|
+
url.searchParams.forEach((value, key) => {
|
|
118
|
+
existingParams.push([key, value]);
|
|
119
|
+
});
|
|
120
|
+
for (const [key] of existingParams) {
|
|
121
|
+
url.searchParams.delete(key);
|
|
122
|
+
}
|
|
123
|
+
for (const [key, value] of existingParams) {
|
|
124
|
+
if (!key.startsWith("utm_")) {
|
|
125
|
+
url.searchParams.set(key, value);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
url.searchParams.set("utm_source", params.source);
|
|
129
|
+
url.searchParams.set("utm_medium", params.medium);
|
|
130
|
+
url.searchParams.set("utm_campaign", params.campaign);
|
|
131
|
+
if (params.term !== void 0) {
|
|
132
|
+
url.searchParams.set("utm_term", params.term);
|
|
133
|
+
}
|
|
134
|
+
if (params.content !== void 0) {
|
|
135
|
+
url.searchParams.set("utm_content", params.content);
|
|
136
|
+
}
|
|
137
|
+
if (isRelative) {
|
|
138
|
+
return url.href.replace(PLACEHOLDER_ORIGIN, "");
|
|
139
|
+
}
|
|
140
|
+
return url.href;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// src/web/utm/classifiers.ts
|
|
144
|
+
var INTERNAL_PATTERNS = [
|
|
145
|
+
/^\/(?!\/)/,
|
|
146
|
+
// Relative paths
|
|
147
|
+
/^https?:\/\/(www\.)?sales-mind\.ai/i,
|
|
148
|
+
// Marketing site
|
|
149
|
+
/^https?:\/\/app\.sales-mind\.ai/i,
|
|
150
|
+
// Web app
|
|
151
|
+
/^https?:\/\/apps\.sales-mind\.ai/i,
|
|
152
|
+
// Web app (legacy)
|
|
153
|
+
/^https?:\/\/meet\.sales-mind\.ai/i,
|
|
154
|
+
// Booking
|
|
155
|
+
/^https?:\/\/docs\.sales-mind\.ai/i
|
|
156
|
+
// Docs
|
|
157
|
+
];
|
|
158
|
+
var SYSTEM_PATTERNS = [
|
|
159
|
+
/^https?:\/\/.*\/api\//i,
|
|
160
|
+
// API endpoints
|
|
161
|
+
/^https?:\/\/.*\/webhook/i,
|
|
162
|
+
// Webhooks
|
|
163
|
+
/^https?:\/\/.*\/oauth/i,
|
|
164
|
+
// OAuth callbacks
|
|
165
|
+
/^https?:\/\/.*\/callback/i,
|
|
166
|
+
// Callbacks
|
|
167
|
+
/^https?:\/\/.*\.supabase\.(co|com)/i,
|
|
168
|
+
// Supabase
|
|
169
|
+
/^https?:\/\/.*\.firebaseapp\.com/i,
|
|
170
|
+
// Firebase
|
|
171
|
+
/^https?:\/\/.*\.cloudfunctions\.net/i
|
|
172
|
+
// Cloud Functions
|
|
173
|
+
];
|
|
174
|
+
var ASSET_PATTERNS = [
|
|
175
|
+
/\.(css|js|mjs|map|woff2?|ttf|eot|svg|png|jpe?g|gif|webp|avif|ico|pdf)(\?.*)?$/i
|
|
176
|
+
];
|
|
177
|
+
var CONVERSION_PATTERNS = [
|
|
178
|
+
/^https?:\/\/(www\.)?calendly\.com/i,
|
|
179
|
+
/^https?:\/\/(checkout\.)?stripe\.com/i,
|
|
180
|
+
/^https?:\/\/buy\.stripe\.com/i,
|
|
181
|
+
/^https?:\/\/chromewebstore\.google\.com/i,
|
|
182
|
+
/^https?:\/\/meet\.sales-mind\.ai/i
|
|
183
|
+
];
|
|
184
|
+
var PROTOCOL_EXEMPT = [
|
|
185
|
+
/^mailto:/i,
|
|
186
|
+
/^tel:/i,
|
|
187
|
+
/^#/,
|
|
188
|
+
/^javascript:/i
|
|
189
|
+
];
|
|
190
|
+
function classifyUrl(url) {
|
|
191
|
+
if (PROTOCOL_EXEMPT.some((p) => p.test(url))) return "protocol";
|
|
192
|
+
if (ASSET_PATTERNS.some((p) => p.test(url))) return "asset";
|
|
193
|
+
if (SYSTEM_PATTERNS.some((p) => p.test(url))) return "system";
|
|
194
|
+
if (CONVERSION_PATTERNS.some((p) => p.test(url))) return "conversion";
|
|
195
|
+
if (INTERNAL_PATTERNS.some((p) => p.test(url))) return "internal";
|
|
196
|
+
return "external";
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// src/components/OutboundLink/outbound-link-utils.ts
|
|
200
|
+
var LEGACY_UTM_SOURCE = "salesmind";
|
|
201
|
+
var EXEMPT_PATTERNS = [
|
|
202
|
+
/^https?:\/\/(www\.)?(stripe\.com|checkout\.stripe\.com|paypal\.com)/i,
|
|
203
|
+
/^https?:\/\/(www\.)?github\.com\/login\/oauth/i
|
|
204
|
+
];
|
|
205
|
+
var isExemptUrl = (urlStr) => {
|
|
206
|
+
if (urlStr.startsWith("mailto:") || urlStr.startsWith("tel:")) return true;
|
|
207
|
+
const classification = classifyUrl(urlStr);
|
|
208
|
+
if (classification === "system" || classification === "protocol" || classification === "asset") {
|
|
209
|
+
return true;
|
|
210
|
+
}
|
|
211
|
+
return EXEMPT_PATTERNS.some((pattern) => pattern.test(urlStr));
|
|
212
|
+
};
|
|
213
|
+
var appendGovernedUTMs = (href, params, preserveExisting = true) => {
|
|
214
|
+
try {
|
|
215
|
+
const url = new URL(href);
|
|
216
|
+
if (preserveExisting) {
|
|
217
|
+
const hasAll = url.searchParams.has("utm_source") && url.searchParams.has("utm_medium") && url.searchParams.has("utm_campaign");
|
|
218
|
+
if (hasAll) return href;
|
|
219
|
+
}
|
|
220
|
+
return buildUtmUrl(href, params);
|
|
221
|
+
} catch {
|
|
222
|
+
return href;
|
|
223
|
+
}
|
|
224
|
+
};
|
|
225
|
+
var appendUTMs = (href, context, pageSlug, options) => {
|
|
226
|
+
try {
|
|
227
|
+
const url = new URL(href);
|
|
228
|
+
const { mediumOverride = "outbound_link", campaignOverride, preserveExisting = true } = options;
|
|
229
|
+
const utms = {
|
|
230
|
+
utm_source: LEGACY_UTM_SOURCE,
|
|
231
|
+
utm_medium: mediumOverride,
|
|
232
|
+
utm_campaign: campaignOverride || pageSlug,
|
|
233
|
+
utm_content: context
|
|
234
|
+
};
|
|
235
|
+
Object.entries(utms).forEach(([key, value]) => {
|
|
236
|
+
if (value) {
|
|
237
|
+
if (preserveExisting && url.searchParams.has(key)) {
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
240
|
+
url.searchParams.set(key, value);
|
|
241
|
+
}
|
|
242
|
+
});
|
|
243
|
+
return url.toString();
|
|
244
|
+
} catch {
|
|
245
|
+
return href;
|
|
246
|
+
}
|
|
247
|
+
};
|
|
248
|
+
var OutboundLink = forwardRef(
|
|
249
|
+
({
|
|
250
|
+
href,
|
|
251
|
+
context,
|
|
252
|
+
campaignOverride,
|
|
253
|
+
mediumOverride = "outbound_link",
|
|
254
|
+
preserveExistingUTM = true,
|
|
255
|
+
openInNewTab = true,
|
|
256
|
+
disableTracking = false,
|
|
257
|
+
utmParams,
|
|
258
|
+
onClick,
|
|
259
|
+
children,
|
|
260
|
+
...props
|
|
261
|
+
}, ref) => {
|
|
262
|
+
const contextParams = useUtmDefaults();
|
|
263
|
+
const resolvedUtmParams = utmParams ?? contextParams;
|
|
264
|
+
let hostname = "";
|
|
265
|
+
try {
|
|
266
|
+
const url = new URL(href);
|
|
267
|
+
hostname = url.hostname;
|
|
268
|
+
} catch {
|
|
269
|
+
}
|
|
270
|
+
const [finalHref, setFinalHref] = useState(href);
|
|
271
|
+
useEffect(() => {
|
|
272
|
+
let isExternal = false;
|
|
273
|
+
let currentMedium = mediumOverride;
|
|
274
|
+
try {
|
|
275
|
+
const url = new URL(href);
|
|
276
|
+
const currentHost = window.location.hostname;
|
|
277
|
+
isExternal = url.hostname !== currentHost;
|
|
278
|
+
if (isExternal && currentHost.includes("sales-mind.ai") && url.hostname.includes("sales-mind.ai")) {
|
|
279
|
+
if (currentMedium === "outbound_link") {
|
|
280
|
+
currentMedium = "cross_subdomain";
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
} catch {
|
|
284
|
+
isExternal = false;
|
|
285
|
+
}
|
|
286
|
+
const isExempt = isExemptUrl(href) || disableTracking;
|
|
287
|
+
if (isExternal && !isExempt) {
|
|
288
|
+
if (resolvedUtmParams) {
|
|
289
|
+
setFinalHref(appendGovernedUTMs(href, resolvedUtmParams, preserveExistingUTM));
|
|
290
|
+
} else {
|
|
291
|
+
const pageSlug = window.location.pathname.replace(/^\/|\/$/g, "") || "home";
|
|
292
|
+
setFinalHref(appendUTMs(href, context, pageSlug, {
|
|
293
|
+
mediumOverride: currentMedium,
|
|
294
|
+
campaignOverride,
|
|
295
|
+
preserveExisting: preserveExistingUTM
|
|
296
|
+
}));
|
|
297
|
+
}
|
|
298
|
+
} else {
|
|
299
|
+
setFinalHref(href);
|
|
300
|
+
}
|
|
301
|
+
}, [href, context, mediumOverride, campaignOverride, preserveExistingUTM, disableTracking, resolvedUtmParams]);
|
|
302
|
+
const handleClick = (e) => {
|
|
303
|
+
if (typeof window === "undefined" || disableTracking) {
|
|
304
|
+
onClick?.(e);
|
|
305
|
+
return;
|
|
306
|
+
}
|
|
307
|
+
let clickExternal = false;
|
|
308
|
+
let clickCrossSubdomain = false;
|
|
309
|
+
let clickMedium = mediumOverride;
|
|
310
|
+
try {
|
|
311
|
+
const url = new URL(href);
|
|
312
|
+
const currentHost = window.location.hostname;
|
|
313
|
+
clickExternal = url.hostname !== currentHost;
|
|
314
|
+
if (clickExternal && currentHost.includes("sales-mind.ai") && url.hostname.includes("sales-mind.ai")) {
|
|
315
|
+
clickCrossSubdomain = true;
|
|
316
|
+
if (clickMedium === "outbound_link") {
|
|
317
|
+
clickMedium = "cross_subdomain";
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
} catch {
|
|
321
|
+
}
|
|
322
|
+
if (clickExternal) {
|
|
323
|
+
const detail = {
|
|
324
|
+
destination_domain: hostname,
|
|
325
|
+
destination_url: finalHref,
|
|
326
|
+
utm_medium_type: clickMedium,
|
|
327
|
+
page_slug: window.location.pathname,
|
|
328
|
+
component_location: context,
|
|
329
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
330
|
+
is_cross_subdomain: clickCrossSubdomain
|
|
331
|
+
};
|
|
332
|
+
const event = new CustomEvent("outbound_click", { detail });
|
|
333
|
+
window.dispatchEvent(event);
|
|
334
|
+
}
|
|
335
|
+
onClick?.(e);
|
|
336
|
+
};
|
|
337
|
+
const relParts = [];
|
|
338
|
+
let shouldOpenNewTab = openInNewTab;
|
|
339
|
+
try {
|
|
340
|
+
const url = new URL(href);
|
|
341
|
+
if (typeof window !== "undefined") {
|
|
342
|
+
const currentHost = window.location.hostname;
|
|
343
|
+
if (url.hostname !== currentHost && currentHost.includes("sales-mind.ai") && url.hostname.includes("sales-mind.ai")) {
|
|
344
|
+
shouldOpenNewTab = false;
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
} catch {
|
|
348
|
+
}
|
|
349
|
+
if (shouldOpenNewTab) relParts.push("noopener", "noreferrer");
|
|
350
|
+
if (mediumOverride === "citation") relParts.push("nofollow");
|
|
351
|
+
const rel = relParts.length > 0 ? relParts.join(" ") : void 0;
|
|
352
|
+
return (
|
|
353
|
+
// eslint-disable-next-line no-restricted-syntax
|
|
354
|
+
/* @__PURE__ */ jsx(
|
|
355
|
+
"a",
|
|
356
|
+
{
|
|
357
|
+
ref,
|
|
358
|
+
href: finalHref,
|
|
359
|
+
target: shouldOpenNewTab ? "_blank" : void 0,
|
|
360
|
+
rel,
|
|
361
|
+
onClick: handleClick,
|
|
362
|
+
...props,
|
|
363
|
+
children
|
|
364
|
+
}
|
|
365
|
+
)
|
|
366
|
+
);
|
|
367
|
+
}
|
|
368
|
+
);
|
|
369
|
+
OutboundLink.displayName = "OutboundLink";
|
|
370
|
+
var COOKIE_CONSENT_EVENT = "cookie_consent_granted";
|
|
371
|
+
var COOKIE_CONSENT_KEY = "cookie_consent";
|
|
372
|
+
function CookieConsent({
|
|
373
|
+
delay = 1e3,
|
|
374
|
+
privacyUrl = "/legal/privacy",
|
|
375
|
+
onAccept,
|
|
376
|
+
onDecline,
|
|
377
|
+
labels,
|
|
378
|
+
className
|
|
379
|
+
}) {
|
|
380
|
+
const [state, setState] = useState("idle");
|
|
381
|
+
useEffect(() => {
|
|
382
|
+
const consent = localStorage.getItem(COOKIE_CONSENT_KEY);
|
|
383
|
+
if (!consent) {
|
|
384
|
+
const timer = setTimeout(() => setState("open"), delay);
|
|
385
|
+
return () => clearTimeout(timer);
|
|
386
|
+
} else {
|
|
387
|
+
setState("closed");
|
|
388
|
+
}
|
|
389
|
+
}, [delay]);
|
|
390
|
+
const handleDismiss = useCallback(() => {
|
|
391
|
+
setState("closing");
|
|
392
|
+
}, []);
|
|
393
|
+
const handleAccept = useCallback(() => {
|
|
394
|
+
localStorage.setItem(COOKIE_CONSENT_KEY, "granted");
|
|
395
|
+
handleDismiss();
|
|
396
|
+
if (typeof window !== "undefined") {
|
|
397
|
+
window.dispatchEvent(new Event(COOKIE_CONSENT_EVENT));
|
|
398
|
+
}
|
|
399
|
+
onAccept?.();
|
|
400
|
+
}, [onAccept, handleDismiss]);
|
|
401
|
+
const handleDecline = useCallback(() => {
|
|
402
|
+
localStorage.setItem(COOKIE_CONSENT_KEY, "denied");
|
|
403
|
+
handleDismiss();
|
|
404
|
+
onDecline?.();
|
|
405
|
+
}, [onDecline, handleDismiss]);
|
|
406
|
+
const handleTransitionEnd = useCallback(() => {
|
|
407
|
+
if (state === "closing") {
|
|
408
|
+
setState("closed");
|
|
409
|
+
}
|
|
410
|
+
}, [state]);
|
|
411
|
+
if (state === "idle" || state === "closed") return null;
|
|
412
|
+
return /* @__PURE__ */ jsx(
|
|
413
|
+
"div",
|
|
414
|
+
{
|
|
415
|
+
className: `ds-cookie-consent ${className ?? ""}`,
|
|
416
|
+
"data-state": state === "open" ? "open" : "closed",
|
|
417
|
+
onTransitionEnd: handleTransitionEnd,
|
|
418
|
+
role: "dialog",
|
|
419
|
+
"aria-label": labels?.title ?? "Cookie consent",
|
|
420
|
+
children: /* @__PURE__ */ jsxs("div", { className: "ds-cookie-consent__inner", children: [
|
|
421
|
+
/* @__PURE__ */ jsxs("div", { className: "ds-cookie-consent__content", children: [
|
|
422
|
+
/* @__PURE__ */ jsx("h3", { className: "ds-cookie-consent__title", children: labels?.title ?? "We use cookies" }),
|
|
423
|
+
/* @__PURE__ */ jsxs("p", { className: "ds-cookie-consent__description", children: [
|
|
424
|
+
labels?.description ?? "We use tracking cookies to understand how you use the product and help us improve it.",
|
|
425
|
+
/* @__PURE__ */ jsx(OutboundLink, { href: privacyUrl, context: "cookie-consent-privacy", className: "ds-cookie-consent__link", children: labels?.privacyLinkText ?? "Privacy Policy" })
|
|
426
|
+
] })
|
|
427
|
+
] }),
|
|
428
|
+
/* @__PURE__ */ jsxs("div", { className: "ds-cookie-consent__actions", children: [
|
|
429
|
+
/* @__PURE__ */ jsx(Button, { variant: "outline", size: "sm", onClick: handleDecline, children: labels?.declineLabel ?? "Decline" }),
|
|
430
|
+
/* @__PURE__ */ jsx(Button, { variant: "primary", size: "sm", onClick: handleAccept, children: labels?.acceptLabel ?? "Accept" })
|
|
431
|
+
] }),
|
|
432
|
+
/* @__PURE__ */ jsx(
|
|
433
|
+
"button",
|
|
434
|
+
{
|
|
435
|
+
onClick: handleDecline,
|
|
436
|
+
className: "ds-cookie-consent__close",
|
|
437
|
+
"aria-label": "Close",
|
|
438
|
+
children: /* @__PURE__ */ jsx(X, { size: 16 })
|
|
439
|
+
}
|
|
440
|
+
)
|
|
441
|
+
] })
|
|
442
|
+
}
|
|
443
|
+
);
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
// src/web/analytics/use-cookie-consent.ts
|
|
447
|
+
function useCookieConsent() {
|
|
448
|
+
const [status, setStatus] = useState(() => {
|
|
449
|
+
if (typeof window === "undefined") return null;
|
|
450
|
+
const stored = localStorage.getItem(COOKIE_CONSENT_KEY);
|
|
451
|
+
if (stored === "granted") return "granted";
|
|
452
|
+
if (stored === "denied") return "denied";
|
|
453
|
+
return null;
|
|
454
|
+
});
|
|
455
|
+
useEffect(() => {
|
|
456
|
+
const handleConsent = () => setStatus("granted");
|
|
457
|
+
window.addEventListener(COOKIE_CONSENT_EVENT, handleConsent);
|
|
458
|
+
return () => window.removeEventListener(COOKIE_CONSENT_EVENT, handleConsent);
|
|
459
|
+
}, []);
|
|
460
|
+
return status;
|
|
461
|
+
}
|
|
462
|
+
var AnalyticsContext = createContext(null);
|
|
463
|
+
function AnalyticsProvider({ onTrack, debug = false, children }) {
|
|
464
|
+
const track = useCallback(
|
|
465
|
+
(event, props) => {
|
|
466
|
+
if (debug && typeof console !== "undefined") {
|
|
467
|
+
console.log("[DS Analytics]", event, props);
|
|
468
|
+
}
|
|
469
|
+
onTrack(event, props);
|
|
470
|
+
},
|
|
471
|
+
[onTrack, debug]
|
|
472
|
+
);
|
|
473
|
+
const value = useMemo(() => ({ track }), [track]);
|
|
474
|
+
return /* @__PURE__ */ jsx(AnalyticsContext.Provider, { value, children });
|
|
475
|
+
}
|
|
476
|
+
var NOOP_VALUE = {
|
|
477
|
+
track: () => {
|
|
478
|
+
}
|
|
479
|
+
};
|
|
480
|
+
function useAnalytics() {
|
|
481
|
+
return useContext(AnalyticsContext) ?? NOOP_VALUE;
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
export { AnalyticsProvider, COOKIE_CONSENT_EVENT, COOKIE_CONSENT_KEY, CookieConsent, createAnalyticsLoader, loadClarity, loadGoogleAnalytics, useAnalytics, useCookieConsent };
|
|
485
|
+
//# sourceMappingURL=out.js.map
|
|
486
|
+
//# sourceMappingURL=index.js.map
|