@healthya-team/pharmacy-analytics 1.0.0 → 1.0.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/dist/client/index.d.mts +5 -4
- package/dist/client/index.d.ts +5 -4
- package/dist/client/index.js +1 -0
- package/dist/client/index.mjs +2 -0
- package/dist/index.d.mts +217 -3
- package/dist/index.d.ts +217 -3
- package/dist/index.js +0 -9373
- package/dist/index.mjs +0 -9359
- package/dist/server/index.d.mts +7 -5
- package/dist/server/index.d.ts +7 -5
- package/dist/server/index.js +30 -1
- package/dist/server/index.mjs +29 -1
- package/dist/studio/index.d.mts +29 -27
- package/dist/studio/index.d.ts +29 -27
- package/dist/studio/index.js +393 -231
- package/dist/studio/index.mjs +340 -178
- package/package.json +3 -3
package/dist/studio/index.mjs
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { BarChart3 as BarChart34 } from "lucide-react";
|
|
3
3
|
|
|
4
4
|
// src/studio/components/UnifiedDashboard.tsx
|
|
5
|
-
import { useState as
|
|
5
|
+
import { useState as useState9, useEffect as useEffect7, useCallback as useCallback5, useRef as useRef4 } from "react";
|
|
6
6
|
import {
|
|
7
7
|
LayoutDashboard,
|
|
8
8
|
BarChart3 as BarChart33,
|
|
@@ -303,6 +303,43 @@ var fDur = (s) => {
|
|
|
303
303
|
};
|
|
304
304
|
var fPct = (v) => `${(v * 100).toFixed(1)}%`;
|
|
305
305
|
var fEngPct = (v) => `${(v * 100).toFixed(0)}%`;
|
|
306
|
+
var MONTH_NAMES = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
|
|
307
|
+
var evenTickInterval = (count, maxTicks = 8) => Math.max(0, Math.ceil(count / maxTicks) - 1);
|
|
308
|
+
var formatDateTick = (v) => {
|
|
309
|
+
if (!v || typeof v !== "string") return "";
|
|
310
|
+
if (v.length === 10 && v[4] === "-" && v[7] === "-") {
|
|
311
|
+
const m = parseInt(v.slice(5, 7), 10) - 1;
|
|
312
|
+
const d = parseInt(v.slice(8, 10), 10);
|
|
313
|
+
return `${d} ${MONTH_NAMES[m] || v.slice(5, 7)}`;
|
|
314
|
+
}
|
|
315
|
+
if (v.length === 8 && !v.includes("-") && !v.includes("/")) {
|
|
316
|
+
const m = parseInt(v.slice(4, 6), 10) - 1;
|
|
317
|
+
const d = parseInt(v.slice(6, 8), 10);
|
|
318
|
+
return `${d} ${MONTH_NAMES[m] || v.slice(4, 6)}`;
|
|
319
|
+
}
|
|
320
|
+
if (v.length === 5 && v[2] === "/") {
|
|
321
|
+
const m = parseInt(v.slice(0, 2), 10) - 1;
|
|
322
|
+
const d = parseInt(v.slice(3, 5), 10);
|
|
323
|
+
return `${d} ${MONTH_NAMES[m] || v.slice(0, 2)}`;
|
|
324
|
+
}
|
|
325
|
+
return v;
|
|
326
|
+
};
|
|
327
|
+
var formatTooltipDate = (v) => {
|
|
328
|
+
if (typeof v !== "string") return typeof v === "number" ? String(v) : "";
|
|
329
|
+
if (v.length === 10 && v[4] === "-" && v[7] === "-") {
|
|
330
|
+
const y = v.slice(0, 4);
|
|
331
|
+
const m = parseInt(v.slice(5, 7), 10) - 1;
|
|
332
|
+
const d = parseInt(v.slice(8, 10), 10);
|
|
333
|
+
return `${d} ${MONTH_NAMES[m] || v.slice(5, 7)} ${y}`;
|
|
334
|
+
}
|
|
335
|
+
if (v.length === 8 && !v.includes("-") && !v.includes("/")) {
|
|
336
|
+
const y = v.slice(0, 4);
|
|
337
|
+
const m = parseInt(v.slice(4, 6), 10) - 1;
|
|
338
|
+
const d = parseInt(v.slice(6, 8), 10);
|
|
339
|
+
return `${d} ${MONTH_NAMES[m] || v.slice(4, 6)} ${y}`;
|
|
340
|
+
}
|
|
341
|
+
return v;
|
|
342
|
+
};
|
|
306
343
|
var GLOBAL_CSS = `
|
|
307
344
|
@keyframes ga-spin { to { transform: rotate(360deg); } }
|
|
308
345
|
@keyframes ga-pulse { 0%,100%{box-shadow:0 0 0 3px rgba(62,207,142,.3)} 50%{box-shadow:0 0 0 7px rgba(62,207,142,.08)} }
|
|
@@ -315,13 +352,14 @@ var GLOBAL_CSS = `
|
|
|
315
352
|
.ga-tooltip-wrap { position:relative; display:inline-flex; align-items:center; }
|
|
316
353
|
.ga-tooltip-wrap .ga-tip {
|
|
317
354
|
visibility:hidden; opacity:0; pointer-events:none;
|
|
318
|
-
position:absolute; bottom:calc(100% + 6px);
|
|
355
|
+
position:absolute; bottom:calc(100% + 6px); right:-6px;
|
|
319
356
|
background:#1e293b; color:#f1f5f9; font-size:12px; line-height:1.5;
|
|
320
357
|
padding:8px 12px; border-radius:7px; white-space:normal; width:220px;
|
|
358
|
+
max-width:calc(100vw - 32px);
|
|
321
359
|
box-shadow:0 4px 16px rgba(0,0,0,.18); z-index:9999; transition:opacity .15s;
|
|
322
360
|
}
|
|
323
361
|
.ga-tooltip-wrap .ga-tip::after {
|
|
324
|
-
content:''; position:absolute; top:100%;
|
|
362
|
+
content:''; position:absolute; top:100%; right:10px;
|
|
325
363
|
border:5px solid transparent; border-top-color:#1e293b;
|
|
326
364
|
}
|
|
327
365
|
.ga-tooltip-wrap:hover .ga-tip { visibility:visible; opacity:1; }
|
|
@@ -339,13 +377,84 @@ var GLOBAL_CSS = `
|
|
|
339
377
|
`;
|
|
340
378
|
|
|
341
379
|
// src/studio/components/shared-ui.tsx
|
|
380
|
+
import { useState, useRef } from "react";
|
|
381
|
+
import { createPortal } from "react-dom";
|
|
342
382
|
import { PieChart, Pie, Cell, ResponsiveContainer, Tooltip } from "recharts";
|
|
343
383
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
344
384
|
function InfoIcon({ text }) {
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
385
|
+
const triggerRef = useRef(null);
|
|
386
|
+
const [tip, setTip] = useState(null);
|
|
387
|
+
const show = () => {
|
|
388
|
+
const el = triggerRef.current;
|
|
389
|
+
if (!el || typeof window === "undefined") return;
|
|
390
|
+
const rect = el.getBoundingClientRect();
|
|
391
|
+
const placeBelow = rect.top < 100;
|
|
392
|
+
const tipWidth = 230;
|
|
393
|
+
const pad = 12;
|
|
394
|
+
const centerX = rect.left + rect.width / 2;
|
|
395
|
+
const clampedLeft = Math.max(
|
|
396
|
+
tipWidth / 2 + pad,
|
|
397
|
+
Math.min(centerX, window.innerWidth - tipWidth / 2 - pad)
|
|
398
|
+
);
|
|
399
|
+
setTip({
|
|
400
|
+
top: placeBelow ? rect.bottom + 8 : rect.top - 8,
|
|
401
|
+
left: clampedLeft,
|
|
402
|
+
placement: placeBelow ? "bottom" : "top"
|
|
403
|
+
});
|
|
404
|
+
};
|
|
405
|
+
const hide = () => setTip(null);
|
|
406
|
+
return /* @__PURE__ */ jsxs(
|
|
407
|
+
"span",
|
|
408
|
+
{
|
|
409
|
+
className: "ga-tooltip-wrap",
|
|
410
|
+
onMouseEnter: show,
|
|
411
|
+
onMouseLeave: hide,
|
|
412
|
+
style: { position: "relative", display: "inline-flex", alignItems: "center" },
|
|
413
|
+
children: [
|
|
414
|
+
/* @__PURE__ */ jsx(
|
|
415
|
+
"button",
|
|
416
|
+
{
|
|
417
|
+
ref: triggerRef,
|
|
418
|
+
className: "ga-info-btn",
|
|
419
|
+
tabIndex: -1,
|
|
420
|
+
type: "button",
|
|
421
|
+
onFocus: show,
|
|
422
|
+
onBlur: hide,
|
|
423
|
+
children: "i"
|
|
424
|
+
}
|
|
425
|
+
),
|
|
426
|
+
tip && typeof document !== "undefined" && createPortal(
|
|
427
|
+
/* @__PURE__ */ jsx(
|
|
428
|
+
"div",
|
|
429
|
+
{
|
|
430
|
+
style: {
|
|
431
|
+
position: "fixed",
|
|
432
|
+
top: tip.top,
|
|
433
|
+
left: tip.left,
|
|
434
|
+
transform: tip.placement === "top" ? "translate(-50%, -100%)" : "translate(-50%, 0)",
|
|
435
|
+
width: 230,
|
|
436
|
+
background: "#0f172a",
|
|
437
|
+
color: "#f8fafc",
|
|
438
|
+
fontSize: 12,
|
|
439
|
+
lineHeight: 1.45,
|
|
440
|
+
padding: "8px 12px",
|
|
441
|
+
borderRadius: 8,
|
|
442
|
+
boxShadow: "0 10px 25px -5px rgba(0,0,0,0.3), 0 8px 10px -6px rgba(0,0,0,0.2)",
|
|
443
|
+
border: "1px solid #334155",
|
|
444
|
+
pointerEvents: "none",
|
|
445
|
+
zIndex: 99999,
|
|
446
|
+
wordWrap: "break-word",
|
|
447
|
+
whiteSpace: "normal",
|
|
448
|
+
fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif'
|
|
449
|
+
},
|
|
450
|
+
children: text
|
|
451
|
+
}
|
|
452
|
+
),
|
|
453
|
+
document.body
|
|
454
|
+
)
|
|
455
|
+
]
|
|
456
|
+
}
|
|
457
|
+
);
|
|
349
458
|
}
|
|
350
459
|
function Skel({
|
|
351
460
|
w,
|
|
@@ -792,7 +901,11 @@ function TrafficTab({ data, loading }) {
|
|
|
792
901
|
/* @__PURE__ */ jsx3(
|
|
793
902
|
XAxis,
|
|
794
903
|
{
|
|
795
|
-
dataKey: "
|
|
904
|
+
dataKey: "date",
|
|
905
|
+
tickFormatter: formatDateTick,
|
|
906
|
+
minTickGap: 28,
|
|
907
|
+
interval: evenTickInterval(data.timeSeries?.length ?? 0),
|
|
908
|
+
padding: { left: 2, right: 15 },
|
|
796
909
|
fontSize: 11,
|
|
797
910
|
tick: { fill: "#94a3b8" },
|
|
798
911
|
axisLine: false,
|
|
@@ -812,6 +925,7 @@ function TrafficTab({ data, loading }) {
|
|
|
812
925
|
/* @__PURE__ */ jsx3(
|
|
813
926
|
Tooltip2,
|
|
814
927
|
{
|
|
928
|
+
labelFormatter: formatTooltipDate,
|
|
815
929
|
contentStyle: {
|
|
816
930
|
background: "#fff",
|
|
817
931
|
border: "1px solid #e2e8f0",
|
|
@@ -1108,7 +1222,7 @@ function GeographyTab({ data, loading }) {
|
|
|
1108
1222
|
}
|
|
1109
1223
|
|
|
1110
1224
|
// src/studio/tabs/events-tab.tsx
|
|
1111
|
-
import { useState, useEffect, useMemo as useMemo2 } from "react";
|
|
1225
|
+
import { useState as useState2, useEffect, useMemo as useMemo2 } from "react";
|
|
1112
1226
|
|
|
1113
1227
|
// src/studio/sanity-client-shim.ts
|
|
1114
1228
|
var projectId = typeof process !== "undefined" ? process.env.SANITY_STUDIO_PROJECT_ID || process.env.NEXT_PUBLIC_SANITY_PROJECT_ID || "" : "";
|
|
@@ -1141,15 +1255,15 @@ function EventsTab({
|
|
|
1141
1255
|
apiUrl,
|
|
1142
1256
|
dateRange
|
|
1143
1257
|
}) {
|
|
1144
|
-
const [selectedEvent, setSelectedEvent] =
|
|
1145
|
-
const [paramRows, setParamRows] =
|
|
1146
|
-
const [paramsLoading, setParamsLoading] =
|
|
1147
|
-
const [paramsError, setParamsError] =
|
|
1148
|
-
const [conditionValues, setConditionValues] =
|
|
1149
|
-
const [selectedCondition, setSelectedCondition] =
|
|
1150
|
-
const [filteredEvents, setFilteredEvents] =
|
|
1151
|
-
const [filterLoading, setFilterLoading] =
|
|
1152
|
-
const [pharmacyNameMap, setPharmacyNameMap] =
|
|
1258
|
+
const [selectedEvent, setSelectedEvent] = useState2(null);
|
|
1259
|
+
const [paramRows, setParamRows] = useState2([]);
|
|
1260
|
+
const [paramsLoading, setParamsLoading] = useState2(false);
|
|
1261
|
+
const [paramsError, setParamsError] = useState2(null);
|
|
1262
|
+
const [conditionValues, setConditionValues] = useState2([]);
|
|
1263
|
+
const [selectedCondition, setSelectedCondition] = useState2(null);
|
|
1264
|
+
const [filteredEvents, setFilteredEvents] = useState2(null);
|
|
1265
|
+
const [filterLoading, setFilterLoading] = useState2(false);
|
|
1266
|
+
const [pharmacyNameMap, setPharmacyNameMap] = useState2({});
|
|
1153
1267
|
useEffect(() => {
|
|
1154
1268
|
sanityClient.fetch(`*[_type == "pharmacies"]{corporateId, name}`).then((pharmacies) => {
|
|
1155
1269
|
const map = {};
|
|
@@ -1393,7 +1507,7 @@ function AcquisitionTab({ data, loading }) {
|
|
|
1393
1507
|
}
|
|
1394
1508
|
|
|
1395
1509
|
// src/studio/tabs/conditions-products-tab.tsx
|
|
1396
|
-
import { useEffect as useEffect2, useMemo as useMemo3, useState as
|
|
1510
|
+
import { useEffect as useEffect2, useMemo as useMemo3, useState as useState3 } from "react";
|
|
1397
1511
|
import {
|
|
1398
1512
|
Bar as Bar2,
|
|
1399
1513
|
BarChart as BarChart2,
|
|
@@ -1514,18 +1628,30 @@ async function fetchPmProductCart(apiUrl, dateRange, conditionId, product, signa
|
|
|
1514
1628
|
}
|
|
1515
1629
|
return res.json();
|
|
1516
1630
|
}
|
|
1631
|
+
async function parseJsonResponse(res, label, url) {
|
|
1632
|
+
const text = await res.text();
|
|
1633
|
+
if (!res.ok) {
|
|
1634
|
+
let msg = `${label} API error: ${res.status} ${res.statusText}`;
|
|
1635
|
+
try {
|
|
1636
|
+
const err = JSON.parse(text);
|
|
1637
|
+
if (err?.error) msg = err.error;
|
|
1638
|
+
} catch {
|
|
1639
|
+
}
|
|
1640
|
+
throw new Error(msg);
|
|
1641
|
+
}
|
|
1642
|
+
if (/^\s*<(!doctype|html)/i.test(text)) {
|
|
1643
|
+
throw new Error(
|
|
1644
|
+
`${label} API (${url}) returned HTML instead of JSON. Check the Mixpanel API URL (mixpanelApiUrl) points at the /api/mixpanel route.`
|
|
1645
|
+
);
|
|
1646
|
+
}
|
|
1647
|
+
return JSON.parse(text);
|
|
1648
|
+
}
|
|
1517
1649
|
async function fetchTopEntities(apiUrl, dateRange, opts, signal) {
|
|
1518
1650
|
let url = `${apiUrl}?mode=top-entities&dateRange=${dateRange}`;
|
|
1519
1651
|
if (opts?.condition) url += `&condition=${encodeURIComponent(opts.condition)}`;
|
|
1520
1652
|
if (opts?.product) url += `&product=${encodeURIComponent(opts.product)}`;
|
|
1521
1653
|
const res = await fetch(url, { cache: "no-store", signal });
|
|
1522
|
-
|
|
1523
|
-
const err = await res.json().catch(() => null);
|
|
1524
|
-
throw new Error(
|
|
1525
|
-
err?.error || `Top entities API error: ${res.status} ${res.statusText}`
|
|
1526
|
-
);
|
|
1527
|
-
}
|
|
1528
|
-
const json = await res.json();
|
|
1654
|
+
const json = await parseJsonResponse(res, "Top entities", url);
|
|
1529
1655
|
return {
|
|
1530
1656
|
conditions: json.conditions ?? [],
|
|
1531
1657
|
products: json.products ?? [],
|
|
@@ -1536,13 +1662,7 @@ async function fetchTopEntities(apiUrl, dateRange, opts, signal) {
|
|
|
1536
1662
|
async function fetchTopSearches(apiUrl, dateRange, type, signal) {
|
|
1537
1663
|
const url = `${apiUrl}?mode=top-searches&dateRange=${dateRange}&type=${type}`;
|
|
1538
1664
|
const res = await fetch(url, { cache: "no-store", signal });
|
|
1539
|
-
|
|
1540
|
-
const err = await res.json().catch(() => null);
|
|
1541
|
-
throw new Error(
|
|
1542
|
-
err?.error || `Top searches API error: ${res.status} ${res.statusText}`
|
|
1543
|
-
);
|
|
1544
|
-
}
|
|
1545
|
-
const json = await res.json();
|
|
1665
|
+
const json = await parseJsonResponse(res, "Top searches", url);
|
|
1546
1666
|
return { topSearches: json.topSearches ?? [] };
|
|
1547
1667
|
}
|
|
1548
1668
|
async function clearMixpanelCache(apiUrl) {
|
|
@@ -1698,22 +1818,23 @@ function EmptyState({ label }) {
|
|
|
1698
1818
|
}
|
|
1699
1819
|
);
|
|
1700
1820
|
}
|
|
1701
|
-
function ConditionsProductsTab({ dateRange }) {
|
|
1702
|
-
const
|
|
1703
|
-
const [
|
|
1704
|
-
const [
|
|
1705
|
-
const [
|
|
1706
|
-
const [
|
|
1707
|
-
const [
|
|
1708
|
-
const [
|
|
1709
|
-
const [
|
|
1710
|
-
const [
|
|
1711
|
-
const [
|
|
1712
|
-
const [
|
|
1821
|
+
function ConditionsProductsTab({ apiUrl, dateRange }) {
|
|
1822
|
+
const targetUrl = apiUrl || MIXPANEL_API_URL;
|
|
1823
|
+
const [subTab, setSubTab] = useState3("conditions");
|
|
1824
|
+
const [data, setData] = useState3(null);
|
|
1825
|
+
const [loading, setLoading] = useState3(true);
|
|
1826
|
+
const [error, setError] = useState3(null);
|
|
1827
|
+
const [fetchTick] = useState3(0);
|
|
1828
|
+
const [topConditionSearches, setTopConditionSearches] = useState3(null);
|
|
1829
|
+
const [topConditionSearchesLoading, setTopConditionSearchesLoading] = useState3(true);
|
|
1830
|
+
const [conditionSearchVisible, setConditionSearchVisible] = useState3(20);
|
|
1831
|
+
const [topProductSearches, setTopProductSearches] = useState3(null);
|
|
1832
|
+
const [topProductSearchesLoading, setTopProductSearchesLoading] = useState3(true);
|
|
1833
|
+
const [productSearchVisible, setProductSearchVisible] = useState3(20);
|
|
1713
1834
|
useEffect2(() => {
|
|
1714
1835
|
const controller = new AbortController();
|
|
1715
1836
|
setTopConditionSearchesLoading(true);
|
|
1716
|
-
fetchTopSearches(
|
|
1837
|
+
fetchTopSearches(targetUrl, dateRange, "condition", controller.signal).then((res) => {
|
|
1717
1838
|
if (!controller.signal.aborted) setTopConditionSearches(res);
|
|
1718
1839
|
}).catch((err) => {
|
|
1719
1840
|
if (err instanceof Error && err.name === "AbortError") return;
|
|
@@ -1721,11 +1842,11 @@ function ConditionsProductsTab({ dateRange }) {
|
|
|
1721
1842
|
if (!controller.signal.aborted) setTopConditionSearchesLoading(false);
|
|
1722
1843
|
});
|
|
1723
1844
|
return () => controller.abort();
|
|
1724
|
-
}, [dateRange, fetchTick]);
|
|
1845
|
+
}, [targetUrl, dateRange, fetchTick]);
|
|
1725
1846
|
useEffect2(() => {
|
|
1726
1847
|
const controller = new AbortController();
|
|
1727
1848
|
setTopProductSearchesLoading(true);
|
|
1728
|
-
fetchTopSearches(
|
|
1849
|
+
fetchTopSearches(targetUrl, dateRange, "product", controller.signal).then((res) => {
|
|
1729
1850
|
if (!controller.signal.aborted) setTopProductSearches(res);
|
|
1730
1851
|
}).catch((err) => {
|
|
1731
1852
|
if (err instanceof Error && err.name === "AbortError") return;
|
|
@@ -1733,12 +1854,12 @@ function ConditionsProductsTab({ dateRange }) {
|
|
|
1733
1854
|
if (!controller.signal.aborted) setTopProductSearchesLoading(false);
|
|
1734
1855
|
});
|
|
1735
1856
|
return () => controller.abort();
|
|
1736
|
-
}, [dateRange, fetchTick]);
|
|
1857
|
+
}, [targetUrl, dateRange, fetchTick]);
|
|
1737
1858
|
useEffect2(() => {
|
|
1738
1859
|
const controller = new AbortController();
|
|
1739
1860
|
setLoading(true);
|
|
1740
1861
|
setError(null);
|
|
1741
|
-
fetchTopEntities(
|
|
1862
|
+
fetchTopEntities(targetUrl, dateRange, void 0, controller.signal).then((res) => {
|
|
1742
1863
|
if (!controller.signal.aborted) setData(res);
|
|
1743
1864
|
}).catch((err) => {
|
|
1744
1865
|
if (err instanceof Error && err.name === "AbortError") return;
|
|
@@ -1747,7 +1868,7 @@ function ConditionsProductsTab({ dateRange }) {
|
|
|
1747
1868
|
if (!controller.signal.aborted) setLoading(false);
|
|
1748
1869
|
});
|
|
1749
1870
|
return () => controller.abort();
|
|
1750
|
-
}, [dateRange, fetchTick]);
|
|
1871
|
+
}, [targetUrl, dateRange, fetchTick]);
|
|
1751
1872
|
const conditionBarData = useMemo3(
|
|
1752
1873
|
() => [...data?.conditions ?? []].sort((a, b) => b.condition_viewed - a.condition_viewed).filter((c) => c.condition_viewed > 0).slice(0, 12).map((c) => ({ fullName: c.name, name: shortLabel(c.name), Views: c.condition_viewed })),
|
|
1753
1874
|
[data]
|
|
@@ -1793,6 +1914,7 @@ function ConditionsProductsTab({ dateRange }) {
|
|
|
1793
1914
|
border: "1px solid #e2e8f0",
|
|
1794
1915
|
boxShadow: "0 4px 16px rgba(0,0,0,.08)"
|
|
1795
1916
|
};
|
|
1917
|
+
const barTooltipLabelStyle = { color: "#0f172a", fontWeight: 600 };
|
|
1796
1918
|
return /* @__PURE__ */ jsxs9("div", { style: { animation: "ga-fadein .25s ease", display: "flex", flexDirection: "column", gap: 20 }, children: [
|
|
1797
1919
|
/* @__PURE__ */ jsx9(
|
|
1798
1920
|
"div",
|
|
@@ -1904,6 +2026,7 @@ function ConditionsProductsTab({ dateRange }) {
|
|
|
1904
2026
|
formatter: (v) => [fNum(Number(v ?? 0)), "Views"],
|
|
1905
2027
|
labelFormatter: (label, payload) => payload?.[0]?.payload?.fullName ?? label,
|
|
1906
2028
|
contentStyle: barTooltipStyle,
|
|
2029
|
+
labelStyle: barTooltipLabelStyle,
|
|
1907
2030
|
cursor: { fill: "rgba(99,102,241,0.06)" }
|
|
1908
2031
|
}
|
|
1909
2032
|
),
|
|
@@ -2160,6 +2283,7 @@ function ConditionsProductsTab({ dateRange }) {
|
|
|
2160
2283
|
formatter: (v) => [fNum(Number(v ?? 0)), "Views"],
|
|
2161
2284
|
labelFormatter: (label, payload) => payload?.[0]?.payload?.fullName ?? label,
|
|
2162
2285
|
contentStyle: barTooltipStyle,
|
|
2286
|
+
labelStyle: barTooltipLabelStyle,
|
|
2163
2287
|
cursor: { fill: "rgba(99,102,241,0.06)" }
|
|
2164
2288
|
}
|
|
2165
2289
|
),
|
|
@@ -2213,10 +2337,10 @@ function ConditionsProductsTab({ dateRange }) {
|
|
|
2213
2337
|
}
|
|
2214
2338
|
|
|
2215
2339
|
// src/studio/funnel/funnel-events-tab.tsx
|
|
2216
|
-
import { useState as
|
|
2340
|
+
import { useState as useState5, useEffect as useEffect4, useMemo as useMemo7, useCallback as useCallback2 } from "react";
|
|
2217
2341
|
|
|
2218
2342
|
// src/studio/funnel/custom-select.tsx
|
|
2219
|
-
import { useState as
|
|
2343
|
+
import { useState as useState4, useEffect as useEffect3, useRef as useRef2, useMemo as useMemo4, useCallback } from "react";
|
|
2220
2344
|
import { jsx as jsx10, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
2221
2345
|
function CustomSelect({
|
|
2222
2346
|
label,
|
|
@@ -2227,10 +2351,10 @@ function CustomSelect({
|
|
|
2227
2351
|
loading,
|
|
2228
2352
|
onChange
|
|
2229
2353
|
}) {
|
|
2230
|
-
const [open, setOpen] =
|
|
2231
|
-
const [search, setSearch] =
|
|
2232
|
-
const ref =
|
|
2233
|
-
const searchRef =
|
|
2354
|
+
const [open, setOpen] = useState4(false);
|
|
2355
|
+
const [search, setSearch] = useState4("");
|
|
2356
|
+
const ref = useRef2(null);
|
|
2357
|
+
const searchRef = useRef2(null);
|
|
2234
2358
|
const selected = useMemo4(() => options.find((o) => o.value === value), [options, value]);
|
|
2235
2359
|
const filtered = useMemo4(
|
|
2236
2360
|
() => search.trim() ? options.filter((o) => o.label.toLowerCase().includes(search.toLowerCase())) : options,
|
|
@@ -2473,9 +2597,9 @@ function CustomSelect({
|
|
|
2473
2597
|
}
|
|
2474
2598
|
|
|
2475
2599
|
// src/studio/funnel/patient-modal.tsx
|
|
2476
|
-
import
|
|
2600
|
+
import React6, { useMemo as useMemo5 } from "react";
|
|
2477
2601
|
import { jsx as jsx11, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
2478
|
-
var PatientModal =
|
|
2602
|
+
var PatientModal = React6.memo(function PatientModal2({
|
|
2479
2603
|
open,
|
|
2480
2604
|
onClose,
|
|
2481
2605
|
loading,
|
|
@@ -3601,28 +3725,29 @@ function FunnelCard({
|
|
|
3601
3725
|
|
|
3602
3726
|
// src/studio/funnel/funnel-events-tab.tsx
|
|
3603
3727
|
import { jsx as jsx14, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
3604
|
-
function FunnelEventsTab({ dateRange }) {
|
|
3605
|
-
const
|
|
3606
|
-
const [
|
|
3607
|
-
const [
|
|
3608
|
-
const [
|
|
3609
|
-
const [
|
|
3610
|
-
const [
|
|
3611
|
-
const [
|
|
3612
|
-
const [
|
|
3613
|
-
const [
|
|
3614
|
-
const [
|
|
3615
|
-
const [
|
|
3616
|
-
const [
|
|
3617
|
-
const [
|
|
3618
|
-
const [
|
|
3619
|
-
const [
|
|
3620
|
-
const [
|
|
3728
|
+
function FunnelEventsTab({ apiUrl, dateRange }) {
|
|
3729
|
+
const targetUrl = apiUrl || MIXPANEL_API_URL;
|
|
3730
|
+
const [loading, setLoading] = useState5(false);
|
|
3731
|
+
const [error, setError] = useState5(null);
|
|
3732
|
+
const [refreshKey, setRefreshKey] = useState5(0);
|
|
3733
|
+
const [clearing, setClearing] = useState5(false);
|
|
3734
|
+
const [conditionOptions, setConditionOptions] = useState5([]);
|
|
3735
|
+
const [selectedCondition, setSelectedCondition] = useState5("");
|
|
3736
|
+
const [conditionsLoading, setConditionsLoading] = useState5(true);
|
|
3737
|
+
const [rows, setRows] = useState5([]);
|
|
3738
|
+
const [qfsFlows, setQfsFlows] = useState5([]);
|
|
3739
|
+
const [paramBreakdowns, setParamBreakdowns] = useState5({});
|
|
3740
|
+
const [qfsStepBreakdowns, setQfsStepBreakdowns] = useState5({});
|
|
3741
|
+
const [conditionServiceMap, setConditionServiceMap] = useState5({});
|
|
3742
|
+
const [patientModalOpen, setPatientModalOpen] = useState5(false);
|
|
3743
|
+
const [patientRecords, setPatientRecords] = useState5([]);
|
|
3744
|
+
const [patientsLoading, setPatientsLoading] = useState5(false);
|
|
3745
|
+
const [patientsError, setPatientsError] = useState5(null);
|
|
3621
3746
|
const doFetchPatients = useCallback2(() => {
|
|
3622
3747
|
if (!selectedCondition) return;
|
|
3623
3748
|
setPatientsLoading(true);
|
|
3624
3749
|
setPatientsError(null);
|
|
3625
|
-
fetchSignupPatients(
|
|
3750
|
+
fetchSignupPatients(targetUrl, dateRange, selectedCondition).then((records) => {
|
|
3626
3751
|
setPatientRecords(records);
|
|
3627
3752
|
setPatientsLoading(false);
|
|
3628
3753
|
}).catch((err) => {
|
|
@@ -3630,7 +3755,7 @@ function FunnelEventsTab({ dateRange }) {
|
|
|
3630
3755
|
setPatientsError(err instanceof Error ? err.message : String(err));
|
|
3631
3756
|
setPatientsLoading(false);
|
|
3632
3757
|
});
|
|
3633
|
-
}, [dateRange, selectedCondition]);
|
|
3758
|
+
}, [targetUrl, dateRange, selectedCondition]);
|
|
3634
3759
|
const handleSignupCountClick = useCallback2(() => {
|
|
3635
3760
|
if (!selectedCondition) return;
|
|
3636
3761
|
setPatientModalOpen(true);
|
|
@@ -3688,7 +3813,7 @@ function FunnelEventsTab({ dateRange }) {
|
|
|
3688
3813
|
const controller = new AbortController();
|
|
3689
3814
|
setConditionsLoading(true);
|
|
3690
3815
|
setSelectedCondition("");
|
|
3691
|
-
fetchFunnelConditions(
|
|
3816
|
+
fetchFunnelConditions(targetUrl, dateRange, void 0, true, controller.signal).then((values) => {
|
|
3692
3817
|
setConditionOptions(values);
|
|
3693
3818
|
setConditionsLoading(false);
|
|
3694
3819
|
}).catch((err) => {
|
|
@@ -3707,7 +3832,7 @@ function FunnelEventsTab({ dateRange }) {
|
|
|
3707
3832
|
setConditionServiceMap(map);
|
|
3708
3833
|
}).catch((err) => console.error("[FunnelEventsTab] fetchConditionServices error:", err));
|
|
3709
3834
|
return () => controller.abort();
|
|
3710
|
-
}, [dateRange, refreshKey]);
|
|
3835
|
+
}, [targetUrl, dateRange, refreshKey]);
|
|
3711
3836
|
useEffect4(() => {
|
|
3712
3837
|
if (!selectedCondition) {
|
|
3713
3838
|
setRows([]);
|
|
@@ -3720,7 +3845,7 @@ function FunnelEventsTab({ dateRange }) {
|
|
|
3720
3845
|
const controller = new AbortController();
|
|
3721
3846
|
setLoading(true);
|
|
3722
3847
|
setError(null);
|
|
3723
|
-
fetchFunnelEvents(
|
|
3848
|
+
fetchFunnelEvents(targetUrl, dateRange, selectedCondition, void 0, controller.signal).then((data) => {
|
|
3724
3849
|
const { eventCounts, qfsStepsByFlow, paramBreakdowns: bd, qfsStepBreakdowns: qfsStepBd } = data;
|
|
3725
3850
|
setParamBreakdowns(bd);
|
|
3726
3851
|
setQfsStepBreakdowns(qfsStepBd ?? {});
|
|
@@ -3743,7 +3868,7 @@ function FunnelEventsTab({ dateRange }) {
|
|
|
3743
3868
|
setLoading(false);
|
|
3744
3869
|
});
|
|
3745
3870
|
return () => controller.abort();
|
|
3746
|
-
}, [dateRange, selectedCondition, refreshKey]);
|
|
3871
|
+
}, [targetUrl, dateRange, selectedCondition, refreshKey]);
|
|
3747
3872
|
const qfsTotal = useMemo7(
|
|
3748
3873
|
() => qfsFlows.reduce((sum, f) => sum + (f.steps[0]?.count ?? 0), 0),
|
|
3749
3874
|
[qfsFlows]
|
|
@@ -3773,14 +3898,14 @@ function FunnelEventsTab({ dateRange }) {
|
|
|
3773
3898
|
const handleFetchLatest = useCallback2(async () => {
|
|
3774
3899
|
setClearing(true);
|
|
3775
3900
|
try {
|
|
3776
|
-
await clearMixpanelCache(
|
|
3901
|
+
await clearMixpanelCache(targetUrl);
|
|
3777
3902
|
} catch (err) {
|
|
3778
3903
|
console.error("[FunnelEventsTab] clear-cache error:", err);
|
|
3779
3904
|
} finally {
|
|
3780
3905
|
setClearing(false);
|
|
3781
3906
|
setRefreshKey((k) => k + 1);
|
|
3782
3907
|
}
|
|
3783
|
-
}, []);
|
|
3908
|
+
}, [targetUrl]);
|
|
3784
3909
|
const handleClosePatientModal = useCallback2(() => setPatientModalOpen(false), []);
|
|
3785
3910
|
return /* @__PURE__ */ jsxs14("div", { style: { padding: "28px 0 48px", background: "#f8f9fb", minHeight: 500 }, children: [
|
|
3786
3911
|
/* @__PURE__ */ jsxs14(
|
|
@@ -3945,7 +4070,7 @@ function FunnelEventsTab({ dateRange }) {
|
|
|
3945
4070
|
}
|
|
3946
4071
|
|
|
3947
4072
|
// src/studio/funnel/pm-funnel-tab.tsx
|
|
3948
|
-
import { useState as
|
|
4073
|
+
import { useState as useState6, useEffect as useEffect5, useCallback as useCallback3 } from "react";
|
|
3949
4074
|
import { Fragment as Fragment3, jsx as jsx15, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
3950
4075
|
var HIDDEN_PARAMS = /* @__PURE__ */ new Set([
|
|
3951
4076
|
"product_name",
|
|
@@ -4549,51 +4674,52 @@ function FlowTab({ label, icon, desc, active, onClick }) {
|
|
|
4549
4674
|
}
|
|
4550
4675
|
);
|
|
4551
4676
|
}
|
|
4552
|
-
function PmFunnelTab({ dateRange }) {
|
|
4553
|
-
const
|
|
4554
|
-
const [
|
|
4555
|
-
const [
|
|
4556
|
-
const [
|
|
4557
|
-
const [
|
|
4558
|
-
const [
|
|
4559
|
-
const [
|
|
4560
|
-
const [
|
|
4561
|
-
const [
|
|
4562
|
-
const [
|
|
4563
|
-
const [
|
|
4564
|
-
const [
|
|
4565
|
-
const [
|
|
4566
|
-
const [
|
|
4567
|
-
const [
|
|
4568
|
-
const [
|
|
4569
|
-
const [
|
|
4570
|
-
const [
|
|
4571
|
-
const [
|
|
4572
|
-
const [
|
|
4573
|
-
const [
|
|
4574
|
-
const [
|
|
4575
|
-
const [
|
|
4576
|
-
const [
|
|
4577
|
-
const [
|
|
4578
|
-
const [
|
|
4677
|
+
function PmFunnelTab({ apiUrl, dateRange }) {
|
|
4678
|
+
const targetUrl = apiUrl || MIXPANEL_API_URL;
|
|
4679
|
+
const [flowSource, setFlowSource] = useState6("product_detail");
|
|
4680
|
+
const [refreshKey, setRefreshKey] = useState6(0);
|
|
4681
|
+
const [clearing, setClearing] = useState6(false);
|
|
4682
|
+
const [pdFilterOptions, setPdFilterOptions] = useState6([]);
|
|
4683
|
+
const [pdSelectedFilter, setPdSelectedFilter] = useState6("");
|
|
4684
|
+
const [pdOptionsLoading, setPdOptionsLoading] = useState6(true);
|
|
4685
|
+
const [pdEventsData, setPdEventsData] = useState6(null);
|
|
4686
|
+
const [pdLoading, setPdLoading] = useState6(false);
|
|
4687
|
+
const [pdError, setPdError] = useState6(null);
|
|
4688
|
+
const [cdConditionOptions, setCdConditionOptions] = useState6([]);
|
|
4689
|
+
const [cdSelectedConditionId, setCdSelectedConditionId] = useState6("");
|
|
4690
|
+
const [cdOptionsLoading, setCdOptionsLoading] = useState6(true);
|
|
4691
|
+
const [cdConditionData, setCdConditionData] = useState6(null);
|
|
4692
|
+
const [cdConditionLoading, setCdConditionLoading] = useState6(false);
|
|
4693
|
+
const [cdConditionError, setCdConditionError] = useState6(null);
|
|
4694
|
+
const [cdSelectedProduct, setCdSelectedProduct] = useState6(null);
|
|
4695
|
+
const [cdCartData, setCdCartData] = useState6(null);
|
|
4696
|
+
const [cdCartLoading, setCdCartLoading] = useState6(false);
|
|
4697
|
+
const [cartAuthModalOpen, setCartAuthModalOpen] = useState6(false);
|
|
4698
|
+
const [cartAuthPatients, setCartAuthPatients] = useState6([]);
|
|
4699
|
+
const [cartAuthPatientsLoading, setCartAuthPatientsLoading] = useState6(false);
|
|
4700
|
+
const [cartAuthPatientsError, setCartAuthPatientsError] = useState6(null);
|
|
4701
|
+
const [authModalOpen, setAuthModalOpen] = useState6(false);
|
|
4702
|
+
const [authPatients, setAuthPatients] = useState6([]);
|
|
4703
|
+
const [authPatientsLoading, setAuthPatientsLoading] = useState6(false);
|
|
4704
|
+
const [authPatientsError, setAuthPatientsError] = useState6(null);
|
|
4579
4705
|
const handleFetchLatest = useCallback3(async () => {
|
|
4580
4706
|
setClearing(true);
|
|
4581
4707
|
try {
|
|
4582
|
-
await clearMixpanelCache(
|
|
4708
|
+
await clearMixpanelCache(targetUrl);
|
|
4583
4709
|
} catch (err) {
|
|
4584
4710
|
console.error("PM funnel clear-cache error:", err);
|
|
4585
4711
|
} finally {
|
|
4586
4712
|
setClearing(false);
|
|
4587
4713
|
setRefreshKey((k) => k + 1);
|
|
4588
4714
|
}
|
|
4589
|
-
}, []);
|
|
4715
|
+
}, [targetUrl]);
|
|
4590
4716
|
useEffect5(() => {
|
|
4591
4717
|
if (flowSource !== "product_detail") return;
|
|
4592
4718
|
const controller = new AbortController();
|
|
4593
4719
|
setPdOptionsLoading(true);
|
|
4594
4720
|
setPdSelectedFilter("");
|
|
4595
4721
|
setPdEventsData(null);
|
|
4596
|
-
fetchPmFunnelProducts(
|
|
4722
|
+
fetchPmFunnelProducts(targetUrl, dateRange, "product_detail", controller.signal).then((values) => {
|
|
4597
4723
|
if (!controller.signal.aborted) {
|
|
4598
4724
|
setPdFilterOptions(values);
|
|
4599
4725
|
setPdOptionsLoading(false);
|
|
@@ -4604,7 +4730,7 @@ function PmFunnelTab({ dateRange }) {
|
|
|
4604
4730
|
if (!controller.signal.aborted) setPdOptionsLoading(false);
|
|
4605
4731
|
});
|
|
4606
4732
|
return () => controller.abort();
|
|
4607
|
-
}, [dateRange, flowSource, refreshKey]);
|
|
4733
|
+
}, [targetUrl, dateRange, flowSource, refreshKey]);
|
|
4608
4734
|
useEffect5(() => {
|
|
4609
4735
|
if (flowSource !== "product_detail" || !pdSelectedFilter) {
|
|
4610
4736
|
setPdEventsData(null);
|
|
@@ -4614,7 +4740,7 @@ function PmFunnelTab({ dateRange }) {
|
|
|
4614
4740
|
const controller = new AbortController();
|
|
4615
4741
|
setPdLoading(true);
|
|
4616
4742
|
setPdError(null);
|
|
4617
|
-
fetchPmFunnelEvents(
|
|
4743
|
+
fetchPmFunnelEvents(targetUrl, dateRange, pdSelectedFilter, "product_detail", controller.signal).then((data) => {
|
|
4618
4744
|
if (!controller.signal.aborted) {
|
|
4619
4745
|
setPdEventsData(data);
|
|
4620
4746
|
setPdLoading(false);
|
|
@@ -4627,7 +4753,7 @@ function PmFunnelTab({ dateRange }) {
|
|
|
4627
4753
|
}
|
|
4628
4754
|
});
|
|
4629
4755
|
return () => controller.abort();
|
|
4630
|
-
}, [pdSelectedFilter, dateRange, flowSource, refreshKey]);
|
|
4756
|
+
}, [targetUrl, pdSelectedFilter, dateRange, flowSource, refreshKey]);
|
|
4631
4757
|
useEffect5(() => {
|
|
4632
4758
|
if (flowSource !== "condition_detail") return;
|
|
4633
4759
|
const controller = new AbortController();
|
|
@@ -4637,7 +4763,7 @@ function PmFunnelTab({ dateRange }) {
|
|
|
4637
4763
|
setCdSelectedProduct(null);
|
|
4638
4764
|
setCdCartData(null);
|
|
4639
4765
|
Promise.all([
|
|
4640
|
-
fetchPmConditionOptions(
|
|
4766
|
+
fetchPmConditionOptions(targetUrl, dateRange, controller.signal),
|
|
4641
4767
|
sanityClient.fetch(
|
|
4642
4768
|
`*[_type == "singleCondition" && defined(conditionId)]{conditionId, title}`
|
|
4643
4769
|
)
|
|
@@ -4659,7 +4785,7 @@ function PmFunnelTab({ dateRange }) {
|
|
|
4659
4785
|
if (!controller.signal.aborted) setCdOptionsLoading(false);
|
|
4660
4786
|
});
|
|
4661
4787
|
return () => controller.abort();
|
|
4662
|
-
}, [dateRange, flowSource, refreshKey]);
|
|
4788
|
+
}, [targetUrl, dateRange, flowSource, refreshKey]);
|
|
4663
4789
|
useEffect5(() => {
|
|
4664
4790
|
if (flowSource !== "condition_detail" || !cdSelectedConditionId) {
|
|
4665
4791
|
setCdConditionData(null);
|
|
@@ -4673,7 +4799,7 @@ function PmFunnelTab({ dateRange }) {
|
|
|
4673
4799
|
setCdConditionError(null);
|
|
4674
4800
|
setCdSelectedProduct(null);
|
|
4675
4801
|
setCdCartData(null);
|
|
4676
|
-
fetchPmConditionEvents(
|
|
4802
|
+
fetchPmConditionEvents(targetUrl, dateRange, Number(cdSelectedConditionId), controller.signal).then((data) => {
|
|
4677
4803
|
if (!controller.signal.aborted) {
|
|
4678
4804
|
setCdConditionData(data);
|
|
4679
4805
|
setCdConditionLoading(false);
|
|
@@ -4686,76 +4812,76 @@ function PmFunnelTab({ dateRange }) {
|
|
|
4686
4812
|
}
|
|
4687
4813
|
});
|
|
4688
4814
|
return () => controller.abort();
|
|
4689
|
-
}, [cdSelectedConditionId, dateRange, flowSource, refreshKey]);
|
|
4815
|
+
}, [targetUrl, cdSelectedConditionId, dateRange, flowSource, refreshKey]);
|
|
4690
4816
|
const handleProductSelect = useCallback3((productName) => {
|
|
4691
4817
|
if (productName === cdSelectedProduct) return;
|
|
4692
4818
|
setCdSelectedProduct(productName);
|
|
4693
4819
|
setCdCartData(null);
|
|
4694
4820
|
setCdCartLoading(true);
|
|
4695
|
-
fetchPmProductCart(
|
|
4821
|
+
fetchPmProductCart(targetUrl, dateRange, Number(cdSelectedConditionId), productName).then((data) => {
|
|
4696
4822
|
setCdCartData(data);
|
|
4697
4823
|
setCdCartLoading(false);
|
|
4698
4824
|
}).catch((err) => {
|
|
4699
4825
|
console.error("PM funnel cart error:", err);
|
|
4700
4826
|
setCdCartLoading(false);
|
|
4701
4827
|
});
|
|
4702
|
-
}, [cdSelectedConditionId, cdSelectedProduct, dateRange]);
|
|
4828
|
+
}, [targetUrl, cdSelectedConditionId, cdSelectedProduct, dateRange]);
|
|
4703
4829
|
const handlePdCartAuthClick = useCallback3(() => {
|
|
4704
4830
|
if (!pdSelectedFilter) return;
|
|
4705
4831
|
setCartAuthModalOpen(true);
|
|
4706
4832
|
setCartAuthPatientsLoading(true);
|
|
4707
4833
|
setCartAuthPatientsError(null);
|
|
4708
4834
|
setCartAuthPatients([]);
|
|
4709
|
-
fetchCartAuthPatients(
|
|
4835
|
+
fetchCartAuthPatients(targetUrl, dateRange, { flowSource: "product_detail", product: pdSelectedFilter }).then((records) => {
|
|
4710
4836
|
setCartAuthPatients(records);
|
|
4711
4837
|
setCartAuthPatientsLoading(false);
|
|
4712
4838
|
}).catch((err) => {
|
|
4713
4839
|
setCartAuthPatientsError(err instanceof Error ? err.message : String(err));
|
|
4714
4840
|
setCartAuthPatientsLoading(false);
|
|
4715
4841
|
});
|
|
4716
|
-
}, [pdSelectedFilter, dateRange]);
|
|
4842
|
+
}, [targetUrl, pdSelectedFilter, dateRange]);
|
|
4717
4843
|
const handleCdCartAuthClick = useCallback3(() => {
|
|
4718
4844
|
if (!cdSelectedProduct || !cdSelectedConditionId) return;
|
|
4719
4845
|
setCartAuthModalOpen(true);
|
|
4720
4846
|
setCartAuthPatientsLoading(true);
|
|
4721
4847
|
setCartAuthPatientsError(null);
|
|
4722
4848
|
setCartAuthPatients([]);
|
|
4723
|
-
fetchCartAuthPatients(
|
|
4849
|
+
fetchCartAuthPatients(targetUrl, dateRange, { flowSource: "condition_detail", product: cdSelectedProduct, conditionId: Number(cdSelectedConditionId) }).then((records) => {
|
|
4724
4850
|
setCartAuthPatients(records);
|
|
4725
4851
|
setCartAuthPatientsLoading(false);
|
|
4726
4852
|
}).catch((err) => {
|
|
4727
4853
|
setCartAuthPatientsError(err instanceof Error ? err.message : String(err));
|
|
4728
4854
|
setCartAuthPatientsLoading(false);
|
|
4729
4855
|
});
|
|
4730
|
-
}, [cdSelectedConditionId, cdSelectedProduct, dateRange]);
|
|
4856
|
+
}, [targetUrl, cdSelectedConditionId, cdSelectedProduct, dateRange]);
|
|
4731
4857
|
const handlePdAuthClick = useCallback3(() => {
|
|
4732
4858
|
if (!pdSelectedFilter) return;
|
|
4733
4859
|
setAuthModalOpen(true);
|
|
4734
4860
|
setAuthPatientsLoading(true);
|
|
4735
4861
|
setAuthPatientsError(null);
|
|
4736
4862
|
setAuthPatients([]);
|
|
4737
|
-
fetchAuthPatients(
|
|
4863
|
+
fetchAuthPatients(targetUrl, dateRange, { flowSource: "product_detail", product: pdSelectedFilter }).then((records) => {
|
|
4738
4864
|
setAuthPatients(records);
|
|
4739
4865
|
setAuthPatientsLoading(false);
|
|
4740
4866
|
}).catch((err) => {
|
|
4741
4867
|
setAuthPatientsError(err instanceof Error ? err.message : String(err));
|
|
4742
4868
|
setAuthPatientsLoading(false);
|
|
4743
4869
|
});
|
|
4744
|
-
}, [pdSelectedFilter, dateRange]);
|
|
4870
|
+
}, [targetUrl, pdSelectedFilter, dateRange]);
|
|
4745
4871
|
const handleCdAuthClick = useCallback3(() => {
|
|
4746
4872
|
if (!cdSelectedConditionId) return;
|
|
4747
4873
|
setAuthModalOpen(true);
|
|
4748
4874
|
setAuthPatientsLoading(true);
|
|
4749
4875
|
setAuthPatientsError(null);
|
|
4750
4876
|
setAuthPatients([]);
|
|
4751
|
-
fetchAuthPatients(
|
|
4877
|
+
fetchAuthPatients(targetUrl, dateRange, { flowSource: "condition_detail", conditionId: Number(cdSelectedConditionId) }).then((records) => {
|
|
4752
4878
|
setAuthPatients(records);
|
|
4753
4879
|
setAuthPatientsLoading(false);
|
|
4754
4880
|
}).catch((err) => {
|
|
4755
4881
|
setAuthPatientsError(err instanceof Error ? err.message : String(err));
|
|
4756
4882
|
setAuthPatientsLoading(false);
|
|
4757
4883
|
});
|
|
4758
|
-
}, [cdSelectedConditionId, dateRange]);
|
|
4884
|
+
}, [targetUrl, cdSelectedConditionId, dateRange]);
|
|
4759
4885
|
const pdFunnelEvents = [...PM_PRODUCT_DETAIL_EVENTS];
|
|
4760
4886
|
const pdAuthMethods = pdEventsData?.paramBreakdowns?.auth_completed?.method ?? {};
|
|
4761
4887
|
const pdGuestCount = (pdAuthMethods["guest"] ?? 0) + (pdAuthMethods["Guest"] ?? 0);
|
|
@@ -5065,7 +5191,7 @@ function PmFunnelTab({ dateRange }) {
|
|
|
5065
5191
|
}
|
|
5066
5192
|
|
|
5067
5193
|
// src/studio/search-console/search-console-dashboard.tsx
|
|
5068
|
-
import { useCallback as useCallback4, useEffect as useEffect6, useState as
|
|
5194
|
+
import { useCallback as useCallback4, useEffect as useEffect6, useState as useState8 } from "react";
|
|
5069
5195
|
import {
|
|
5070
5196
|
Area as Area2,
|
|
5071
5197
|
AreaChart as AreaChart2,
|
|
@@ -5088,8 +5214,8 @@ import {
|
|
|
5088
5214
|
} from "lucide-react";
|
|
5089
5215
|
|
|
5090
5216
|
// src/studio/search-console/dashboard-ui.tsx
|
|
5091
|
-
import { useRef as
|
|
5092
|
-
import { createPortal } from "react-dom";
|
|
5217
|
+
import { useRef as useRef3, useState as useState7 } from "react";
|
|
5218
|
+
import { createPortal as createPortal2 } from "react-dom";
|
|
5093
5219
|
import { AlertTriangle, Info, Loader2, RefreshCw } from "lucide-react";
|
|
5094
5220
|
import { Cell as Cell4, Pie as Pie3, PieChart as PieChart3, ResponsiveContainer as ResponsiveContainer4, Tooltip as Tooltip4 } from "recharts";
|
|
5095
5221
|
import { Fragment as Fragment4, jsx as jsx16, jsxs as jsxs16 } from "react/jsx-runtime";
|
|
@@ -5975,16 +6101,20 @@ function ErrorState({
|
|
|
5975
6101
|
] });
|
|
5976
6102
|
}
|
|
5977
6103
|
function InfoIcon2({ text }) {
|
|
5978
|
-
const triggerRef =
|
|
5979
|
-
const [tip, setTip] =
|
|
6104
|
+
const triggerRef = useRef3(null);
|
|
6105
|
+
const [tip, setTip] = useState7(null);
|
|
5980
6106
|
const show = () => {
|
|
5981
6107
|
const el = triggerRef.current;
|
|
5982
6108
|
if (!el) return;
|
|
5983
6109
|
const rect = el.getBoundingClientRect();
|
|
5984
6110
|
const placeBelow = rect.top < 120;
|
|
6111
|
+
const tipWidth = 230;
|
|
6112
|
+
const pad = 12;
|
|
6113
|
+
const centerX = rect.left + rect.width / 2;
|
|
6114
|
+
const clampedLeft = typeof window !== "undefined" ? Math.max(tipWidth / 2 + pad, Math.min(centerX, window.innerWidth - tipWidth / 2 - pad)) : centerX;
|
|
5985
6115
|
setTip({
|
|
5986
6116
|
top: placeBelow ? rect.bottom + 8 : rect.top - 8,
|
|
5987
|
-
left:
|
|
6117
|
+
left: clampedLeft,
|
|
5988
6118
|
placement: placeBelow ? "bottom" : "top"
|
|
5989
6119
|
});
|
|
5990
6120
|
};
|
|
@@ -6002,7 +6132,7 @@ function InfoIcon2({ text }) {
|
|
|
6002
6132
|
children: /* @__PURE__ */ jsx16(Info, { size: 12 })
|
|
6003
6133
|
}
|
|
6004
6134
|
),
|
|
6005
|
-
tip && typeof document !== "undefined" &&
|
|
6135
|
+
tip && typeof document !== "undefined" && createPortal2(
|
|
6006
6136
|
/* @__PURE__ */ jsx16(
|
|
6007
6137
|
"span",
|
|
6008
6138
|
{
|
|
@@ -6274,9 +6404,26 @@ function ScOverviewTab({ data, loading }) {
|
|
|
6274
6404
|
/* @__PURE__ */ jsx17("stop", { offset: "95%", stopColor: color, stopOpacity: 0 })
|
|
6275
6405
|
] }, id)) }),
|
|
6276
6406
|
/* @__PURE__ */ jsx17(CartesianGrid3, { strokeDasharray: "4 6", stroke: CHART_GRID }),
|
|
6277
|
-
/* @__PURE__ */ jsx17(
|
|
6407
|
+
/* @__PURE__ */ jsx17(
|
|
6408
|
+
XAxis3,
|
|
6409
|
+
{
|
|
6410
|
+
dataKey: "date",
|
|
6411
|
+
tickFormatter: formatDateTick,
|
|
6412
|
+
minTickGap: 28,
|
|
6413
|
+
interval: evenTickInterval(clicksSeries?.length ?? 0),
|
|
6414
|
+
padding: { left: 2, right: 15 },
|
|
6415
|
+
...chartAxis
|
|
6416
|
+
}
|
|
6417
|
+
),
|
|
6278
6418
|
/* @__PURE__ */ jsx17(YAxis3, { ...chartAxis, width: 38 }),
|
|
6279
|
-
/* @__PURE__ */ jsx17(
|
|
6419
|
+
/* @__PURE__ */ jsx17(
|
|
6420
|
+
Tooltip5,
|
|
6421
|
+
{
|
|
6422
|
+
labelFormatter: formatTooltipDate,
|
|
6423
|
+
contentStyle: chartTooltipStyle,
|
|
6424
|
+
cursor: { stroke: "#cbd5e1", strokeDasharray: "4 4" }
|
|
6425
|
+
}
|
|
6426
|
+
),
|
|
6280
6427
|
/* @__PURE__ */ jsx17(Legend3, { wrapperStyle: { fontSize: 12, fontWeight: 700, paddingTop: 10 } }),
|
|
6281
6428
|
/* @__PURE__ */ jsx17(Area2, { type: "monotone", dataKey: "clicks", name: "Clicks", stroke: "#0f9f6e", fill: "url(#scClicks)", strokeWidth: 2.4, dot: false }),
|
|
6282
6429
|
/* @__PURE__ */ jsx17(Area2, { type: "monotone", dataKey: "impressions", name: "Impressions", stroke: "#6366f1", fill: "url(#scImpressions)", strokeWidth: 2.4, dot: false })
|
|
@@ -6467,9 +6614,9 @@ function ScSitemapsTab({
|
|
|
6467
6614
|
onRefreshData,
|
|
6468
6615
|
loading
|
|
6469
6616
|
}) {
|
|
6470
|
-
const [submitting, setSubmitting] =
|
|
6471
|
-
const [submitMsg, setSubmitMsg] =
|
|
6472
|
-
const [newSitemapUrl, setNewSitemapUrl] =
|
|
6617
|
+
const [submitting, setSubmitting] = useState8(null);
|
|
6618
|
+
const [submitMsg, setSubmitMsg] = useState8(null);
|
|
6619
|
+
const [newSitemapUrl, setNewSitemapUrl] = useState8("");
|
|
6473
6620
|
const totalSubmitted = data.sitemaps.reduce((sum, sitemap) => sum + sitemap.contents.reduce((contentSum, content) => contentSum + content.submitted, 0), 0);
|
|
6474
6621
|
const totalIndexed = data.sitemaps.reduce((sum, sitemap) => sum + sitemap.contents.reduce((contentSum, content) => contentSum + content.indexed, 0), 0);
|
|
6475
6622
|
const indexRate = totalSubmitted > 0 ? totalIndexed / totalSubmitted : 0;
|
|
@@ -6604,11 +6751,11 @@ function SearchConsoleDashboard({
|
|
|
6604
6751
|
activeTab: controlledTab,
|
|
6605
6752
|
onTabChange
|
|
6606
6753
|
}) {
|
|
6607
|
-
const [data, setData] =
|
|
6608
|
-
const [loading, setLoading] =
|
|
6609
|
-
const [error, setError] =
|
|
6610
|
-
const [dateRange, setDateRange] =
|
|
6611
|
-
const [uncontrolledTab, setUncontrolledTab] =
|
|
6754
|
+
const [data, setData] = useState8(null);
|
|
6755
|
+
const [loading, setLoading] = useState8(true);
|
|
6756
|
+
const [error, setError] = useState8(null);
|
|
6757
|
+
const [dateRange, setDateRange] = useState8("28");
|
|
6758
|
+
const [uncontrolledTab, setUncontrolledTab] = useState8(initialTab);
|
|
6612
6759
|
const activeTab = controlledTab !== void 0 ? controlledTab : uncontrolledTab;
|
|
6613
6760
|
const handleTabChange = useCallback4(
|
|
6614
6761
|
(nextTab) => {
|
|
@@ -7194,7 +7341,10 @@ function CombinedOverviewTab({
|
|
|
7194
7341
|
XAxis4,
|
|
7195
7342
|
{
|
|
7196
7343
|
dataKey: "date",
|
|
7197
|
-
tickFormatter:
|
|
7344
|
+
tickFormatter: formatDateTick,
|
|
7345
|
+
minTickGap: 28,
|
|
7346
|
+
interval: evenTickInterval(scTimeSeries?.length ?? 0),
|
|
7347
|
+
padding: { left: 2, right: 15 },
|
|
7198
7348
|
tick: { fontSize: 10, fill: "#94a3b8" },
|
|
7199
7349
|
axisLine: false,
|
|
7200
7350
|
tickLine: false
|
|
@@ -7204,6 +7354,7 @@ function CombinedOverviewTab({
|
|
|
7204
7354
|
/* @__PURE__ */ jsx18(
|
|
7205
7355
|
Tooltip6,
|
|
7206
7356
|
{
|
|
7357
|
+
labelFormatter: formatTooltipDate,
|
|
7207
7358
|
contentStyle: {
|
|
7208
7359
|
background: "#1e293b",
|
|
7209
7360
|
border: "none",
|
|
@@ -7243,7 +7394,10 @@ function CombinedOverviewTab({
|
|
|
7243
7394
|
XAxis4,
|
|
7244
7395
|
{
|
|
7245
7396
|
dataKey: "date",
|
|
7246
|
-
tickFormatter:
|
|
7397
|
+
tickFormatter: formatDateTick,
|
|
7398
|
+
minTickGap: 28,
|
|
7399
|
+
interval: evenTickInterval(gaTimeSeries?.length ?? 0),
|
|
7400
|
+
padding: { left: 2, right: 15 },
|
|
7247
7401
|
tick: { fontSize: 10, fill: "#94a3b8" },
|
|
7248
7402
|
axisLine: false,
|
|
7249
7403
|
tickLine: false
|
|
@@ -7253,6 +7407,7 @@ function CombinedOverviewTab({
|
|
|
7253
7407
|
/* @__PURE__ */ jsx18(
|
|
7254
7408
|
Tooltip6,
|
|
7255
7409
|
{
|
|
7410
|
+
labelFormatter: formatTooltipDate,
|
|
7256
7411
|
contentStyle: {
|
|
7257
7412
|
background: "#1e293b",
|
|
7258
7413
|
border: "none",
|
|
@@ -7394,23 +7549,25 @@ function mapToScRange(range) {
|
|
|
7394
7549
|
function UnifiedDashboard({
|
|
7395
7550
|
apiUrl = "/api/analytics",
|
|
7396
7551
|
scApiUrl,
|
|
7552
|
+
mixpanelApiUrl,
|
|
7397
7553
|
brandName = "Pharmacy Analytics"
|
|
7398
7554
|
}) {
|
|
7399
|
-
const [activeMainSection, setActiveMainSection] =
|
|
7400
|
-
const [activeTab, setActiveTab] =
|
|
7401
|
-
const [scActiveTab, setScActiveTab] =
|
|
7402
|
-
const [dateRange, setDateRange] =
|
|
7403
|
-
const [gaOpen, setGaOpen] =
|
|
7404
|
-
const [customOpen, setCustomOpen] =
|
|
7405
|
-
const [scOpen, setScOpen] =
|
|
7406
|
-
const
|
|
7407
|
-
const [
|
|
7408
|
-
const [
|
|
7409
|
-
const [
|
|
7410
|
-
const [
|
|
7411
|
-
const [
|
|
7412
|
-
const
|
|
7413
|
-
const
|
|
7555
|
+
const [activeMainSection, setActiveMainSection] = useState9("all");
|
|
7556
|
+
const [activeTab, setActiveTab] = useState9("all-overview");
|
|
7557
|
+
const [scActiveTab, setScActiveTab] = useState9("overview");
|
|
7558
|
+
const [dateRange, setDateRange] = useState9("30");
|
|
7559
|
+
const [gaOpen, setGaOpen] = useState9(true);
|
|
7560
|
+
const [customOpen, setCustomOpen] = useState9(true);
|
|
7561
|
+
const [scOpen, setScOpen] = useState9(true);
|
|
7562
|
+
const isAll = activeMainSection === "all";
|
|
7563
|
+
const [gaData, setGaData] = useState9(null);
|
|
7564
|
+
const [scData, setScData] = useState9(null);
|
|
7565
|
+
const [loadingGa, setLoadingGa] = useState9(true);
|
|
7566
|
+
const [loadingSc, setLoadingSc] = useState9(true);
|
|
7567
|
+
const [gaError, setGaError] = useState9(null);
|
|
7568
|
+
const [scError, setScError] = useState9(null);
|
|
7569
|
+
const gaAbortRef = useRef4(null);
|
|
7570
|
+
const scAbortRef = useRef4(null);
|
|
7414
7571
|
const effectiveScUrl = scApiUrl || apiUrl;
|
|
7415
7572
|
const loadGaData = useCallback5(async () => {
|
|
7416
7573
|
gaAbortRef.current?.abort();
|
|
@@ -7808,6 +7965,7 @@ function UnifiedDashboard({
|
|
|
7808
7965
|
/* @__PURE__ */ jsxs19(
|
|
7809
7966
|
"button",
|
|
7810
7967
|
{
|
|
7968
|
+
disabled: !isAll,
|
|
7811
7969
|
onClick: () => setGaOpen((o) => !o),
|
|
7812
7970
|
style: {
|
|
7813
7971
|
display: "flex",
|
|
@@ -7818,7 +7976,7 @@ function UnifiedDashboard({
|
|
|
7818
7976
|
borderRadius: 6,
|
|
7819
7977
|
background: "transparent",
|
|
7820
7978
|
border: "none",
|
|
7821
|
-
cursor: "pointer",
|
|
7979
|
+
cursor: isAll ? "pointer" : "default",
|
|
7822
7980
|
marginBottom: 2
|
|
7823
7981
|
},
|
|
7824
7982
|
children: [
|
|
@@ -7838,11 +7996,11 @@ function UnifiedDashboard({
|
|
|
7838
7996
|
children: "Google Analytics"
|
|
7839
7997
|
}
|
|
7840
7998
|
),
|
|
7841
|
-
/* @__PURE__ */ jsx19("span", { style: { fontSize: 11, color: "#94a3b8" }, children: gaOpen ? "\u25BE" : "\u25B8" })
|
|
7999
|
+
/* @__PURE__ */ jsx19("span", { style: { fontSize: 11, color: "#94a3b8" }, children: isAll ? gaOpen ? "\u25BE" : "\u25B8" : null })
|
|
7842
8000
|
]
|
|
7843
8001
|
}
|
|
7844
8002
|
),
|
|
7845
|
-
gaOpen && GA_SUB_TABS.map((tab) => {
|
|
8003
|
+
(gaOpen || !isAll) && GA_SUB_TABS.map((tab) => {
|
|
7846
8004
|
const active = activeTab === tab.id;
|
|
7847
8005
|
return /* @__PURE__ */ jsxs19(
|
|
7848
8006
|
"button",
|
|
@@ -7877,6 +8035,7 @@ function UnifiedDashboard({
|
|
|
7877
8035
|
/* @__PURE__ */ jsxs19(
|
|
7878
8036
|
"button",
|
|
7879
8037
|
{
|
|
8038
|
+
disabled: !isAll,
|
|
7880
8039
|
onClick: () => setCustomOpen((o) => !o),
|
|
7881
8040
|
style: {
|
|
7882
8041
|
display: "flex",
|
|
@@ -7887,7 +8046,7 @@ function UnifiedDashboard({
|
|
|
7887
8046
|
borderRadius: 6,
|
|
7888
8047
|
background: "transparent",
|
|
7889
8048
|
border: "none",
|
|
7890
|
-
cursor: "pointer",
|
|
8049
|
+
cursor: isAll ? "pointer" : "default",
|
|
7891
8050
|
marginBottom: 2
|
|
7892
8051
|
},
|
|
7893
8052
|
children: [
|
|
@@ -7907,11 +8066,11 @@ function UnifiedDashboard({
|
|
|
7907
8066
|
children: "Custom Analytics"
|
|
7908
8067
|
}
|
|
7909
8068
|
),
|
|
7910
|
-
/* @__PURE__ */ jsx19("span", { style: { fontSize: 11, color: "#94a3b8" }, children: customOpen ? "\u25BE" : "\u25B8" })
|
|
8069
|
+
/* @__PURE__ */ jsx19("span", { style: { fontSize: 11, color: "#94a3b8" }, children: isAll ? customOpen ? "\u25BE" : "\u25B8" : null })
|
|
7911
8070
|
]
|
|
7912
8071
|
}
|
|
7913
8072
|
),
|
|
7914
|
-
customOpen && CUSTOM_SUB_TABS.map((tab) => {
|
|
8073
|
+
(customOpen || !isAll) && CUSTOM_SUB_TABS.map((tab) => {
|
|
7915
8074
|
const active = activeTab === tab.id;
|
|
7916
8075
|
return /* @__PURE__ */ jsxs19(
|
|
7917
8076
|
"button",
|
|
@@ -7946,6 +8105,7 @@ function UnifiedDashboard({
|
|
|
7946
8105
|
/* @__PURE__ */ jsxs19(
|
|
7947
8106
|
"button",
|
|
7948
8107
|
{
|
|
8108
|
+
disabled: !isAll,
|
|
7949
8109
|
onClick: () => setScOpen((o) => !o),
|
|
7950
8110
|
style: {
|
|
7951
8111
|
display: "flex",
|
|
@@ -7956,7 +8116,7 @@ function UnifiedDashboard({
|
|
|
7956
8116
|
borderRadius: 6,
|
|
7957
8117
|
background: "transparent",
|
|
7958
8118
|
border: "none",
|
|
7959
|
-
cursor: "pointer",
|
|
8119
|
+
cursor: isAll ? "pointer" : "default",
|
|
7960
8120
|
marginBottom: 2
|
|
7961
8121
|
},
|
|
7962
8122
|
children: [
|
|
@@ -7976,11 +8136,11 @@ function UnifiedDashboard({
|
|
|
7976
8136
|
children: "Search Console"
|
|
7977
8137
|
}
|
|
7978
8138
|
),
|
|
7979
|
-
/* @__PURE__ */ jsx19("span", { style: { fontSize: 11, color: "#94a3b8" }, children: scOpen ? "\u25BE" : "\u25B8" })
|
|
8139
|
+
/* @__PURE__ */ jsx19("span", { style: { fontSize: 11, color: "#94a3b8" }, children: isAll ? scOpen ? "\u25BE" : "\u25B8" : null })
|
|
7980
8140
|
]
|
|
7981
8141
|
}
|
|
7982
8142
|
),
|
|
7983
|
-
scOpen && SC_SUB_TABS.map((tab) => {
|
|
8143
|
+
(scOpen || !isAll) && SC_SUB_TABS.map((tab) => {
|
|
7984
8144
|
const isScActive = activeTab === tab.id;
|
|
7985
8145
|
return /* @__PURE__ */ jsxs19(
|
|
7986
8146
|
"button",
|
|
@@ -8058,6 +8218,7 @@ function UnifiedDashboard({
|
|
|
8058
8218
|
flex: 1,
|
|
8059
8219
|
minHeight: 0,
|
|
8060
8220
|
overflowY: "auto",
|
|
8221
|
+
overflowX: "hidden",
|
|
8061
8222
|
padding: "24px 28px"
|
|
8062
8223
|
},
|
|
8063
8224
|
children: [
|
|
@@ -8112,9 +8273,9 @@ function UnifiedDashboard({
|
|
|
8112
8273
|
activeTab === "geography" && gaData && /* @__PURE__ */ jsx19(GeographyTab, { data: gaData, loading: loadingGa }),
|
|
8113
8274
|
activeTab === "events" && gaData && /* @__PURE__ */ jsx19(EventsTab, { data: gaData, loading: loadingGa, apiUrl, dateRange }),
|
|
8114
8275
|
activeTab === "acquisition" && gaData && /* @__PURE__ */ jsx19(AcquisitionTab, { data: gaData, loading: loadingGa }),
|
|
8115
|
-
activeTab === "conditions-products" && /* @__PURE__ */ jsx19(ConditionsProductsTab, { apiUrl, dateRange }),
|
|
8116
|
-
activeTab === "funnel" && /* @__PURE__ */ jsx19(FunnelEventsTab, { apiUrl, dateRange }),
|
|
8117
|
-
activeTab === "pm-funnel" && /* @__PURE__ */ jsx19(PmFunnelTab, { apiUrl, dateRange }),
|
|
8276
|
+
activeTab === "conditions-products" && /* @__PURE__ */ jsx19(ConditionsProductsTab, { apiUrl: mixpanelApiUrl, dateRange }),
|
|
8277
|
+
activeTab === "funnel" && /* @__PURE__ */ jsx19(FunnelEventsTab, { apiUrl: mixpanelApiUrl, dateRange }),
|
|
8278
|
+
activeTab === "pm-funnel" && /* @__PURE__ */ jsx19(PmFunnelTab, { apiUrl: mixpanelApiUrl, dateRange }),
|
|
8118
8279
|
activeTab.startsWith("sc-") && /* @__PURE__ */ jsx19(
|
|
8119
8280
|
SearchConsoleDashboard,
|
|
8120
8281
|
{
|
|
@@ -8154,6 +8315,7 @@ function pharmacyAnalyticsPlugin(config = {}) {
|
|
|
8154
8315
|
{
|
|
8155
8316
|
apiUrl: config.apiUrl || "/api/analytics",
|
|
8156
8317
|
scApiUrl: config.scApiUrl,
|
|
8318
|
+
mixpanelApiUrl: config.mixpanelApiUrl,
|
|
8157
8319
|
brandName: toolTitle
|
|
8158
8320
|
}
|
|
8159
8321
|
);
|