@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.
@@ -70,7 +70,7 @@ module.exports = __toCommonJS(studio_exports);
70
70
  var import_lucide_react5 = require("lucide-react");
71
71
 
72
72
  // src/studio/components/UnifiedDashboard.tsx
73
- var import_react11 = require("react");
73
+ var import_react12 = require("react");
74
74
  var import_lucide_react4 = require("lucide-react");
75
75
 
76
76
  // src/studio/utils-ga-api.ts
@@ -364,6 +364,43 @@ var fDur = (s) => {
364
364
  };
365
365
  var fPct = (v) => `${(v * 100).toFixed(1)}%`;
366
366
  var fEngPct = (v) => `${(v * 100).toFixed(0)}%`;
367
+ var MONTH_NAMES = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
368
+ var evenTickInterval = (count, maxTicks = 8) => Math.max(0, Math.ceil(count / maxTicks) - 1);
369
+ var formatDateTick = (v) => {
370
+ if (!v || typeof v !== "string") return "";
371
+ if (v.length === 10 && v[4] === "-" && v[7] === "-") {
372
+ const m = parseInt(v.slice(5, 7), 10) - 1;
373
+ const d = parseInt(v.slice(8, 10), 10);
374
+ return `${d} ${MONTH_NAMES[m] || v.slice(5, 7)}`;
375
+ }
376
+ if (v.length === 8 && !v.includes("-") && !v.includes("/")) {
377
+ const m = parseInt(v.slice(4, 6), 10) - 1;
378
+ const d = parseInt(v.slice(6, 8), 10);
379
+ return `${d} ${MONTH_NAMES[m] || v.slice(4, 6)}`;
380
+ }
381
+ if (v.length === 5 && v[2] === "/") {
382
+ const m = parseInt(v.slice(0, 2), 10) - 1;
383
+ const d = parseInt(v.slice(3, 5), 10);
384
+ return `${d} ${MONTH_NAMES[m] || v.slice(0, 2)}`;
385
+ }
386
+ return v;
387
+ };
388
+ var formatTooltipDate = (v) => {
389
+ if (typeof v !== "string") return typeof v === "number" ? String(v) : "";
390
+ if (v.length === 10 && v[4] === "-" && v[7] === "-") {
391
+ const y = v.slice(0, 4);
392
+ const m = parseInt(v.slice(5, 7), 10) - 1;
393
+ const d = parseInt(v.slice(8, 10), 10);
394
+ return `${d} ${MONTH_NAMES[m] || v.slice(5, 7)} ${y}`;
395
+ }
396
+ if (v.length === 8 && !v.includes("-") && !v.includes("/")) {
397
+ const y = v.slice(0, 4);
398
+ const m = parseInt(v.slice(4, 6), 10) - 1;
399
+ const d = parseInt(v.slice(6, 8), 10);
400
+ return `${d} ${MONTH_NAMES[m] || v.slice(4, 6)} ${y}`;
401
+ }
402
+ return v;
403
+ };
367
404
  var GLOBAL_CSS = `
368
405
  @keyframes ga-spin { to { transform: rotate(360deg); } }
369
406
  @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)} }
@@ -376,13 +413,14 @@ var GLOBAL_CSS = `
376
413
  .ga-tooltip-wrap { position:relative; display:inline-flex; align-items:center; }
377
414
  .ga-tooltip-wrap .ga-tip {
378
415
  visibility:hidden; opacity:0; pointer-events:none;
379
- position:absolute; bottom:calc(100% + 6px); left:50%; transform:translateX(-50%);
416
+ position:absolute; bottom:calc(100% + 6px); right:-6px;
380
417
  background:#1e293b; color:#f1f5f9; font-size:12px; line-height:1.5;
381
418
  padding:8px 12px; border-radius:7px; white-space:normal; width:220px;
419
+ max-width:calc(100vw - 32px);
382
420
  box-shadow:0 4px 16px rgba(0,0,0,.18); z-index:9999; transition:opacity .15s;
383
421
  }
384
422
  .ga-tooltip-wrap .ga-tip::after {
385
- content:''; position:absolute; top:100%; left:50%; transform:translateX(-50%);
423
+ content:''; position:absolute; top:100%; right:10px;
386
424
  border:5px solid transparent; border-top-color:#1e293b;
387
425
  }
388
426
  .ga-tooltip-wrap:hover .ga-tip { visibility:visible; opacity:1; }
@@ -400,13 +438,84 @@ var GLOBAL_CSS = `
400
438
  `;
401
439
 
402
440
  // src/studio/components/shared-ui.tsx
441
+ var import_react = require("react");
442
+ var import_react_dom = require("react-dom");
403
443
  var import_recharts = require("recharts");
404
444
  var import_jsx_runtime = require("react/jsx-runtime");
405
445
  function InfoIcon({ text }) {
406
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "ga-tooltip-wrap", children: [
407
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { className: "ga-info-btn", tabIndex: -1, children: "i" }),
408
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "ga-tip", children: text })
409
- ] });
446
+ const triggerRef = (0, import_react.useRef)(null);
447
+ const [tip, setTip] = (0, import_react.useState)(null);
448
+ const show = () => {
449
+ const el = triggerRef.current;
450
+ if (!el || typeof window === "undefined") return;
451
+ const rect = el.getBoundingClientRect();
452
+ const placeBelow = rect.top < 100;
453
+ const tipWidth = 230;
454
+ const pad = 12;
455
+ const centerX = rect.left + rect.width / 2;
456
+ const clampedLeft = Math.max(
457
+ tipWidth / 2 + pad,
458
+ Math.min(centerX, window.innerWidth - tipWidth / 2 - pad)
459
+ );
460
+ setTip({
461
+ top: placeBelow ? rect.bottom + 8 : rect.top - 8,
462
+ left: clampedLeft,
463
+ placement: placeBelow ? "bottom" : "top"
464
+ });
465
+ };
466
+ const hide = () => setTip(null);
467
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
468
+ "span",
469
+ {
470
+ className: "ga-tooltip-wrap",
471
+ onMouseEnter: show,
472
+ onMouseLeave: hide,
473
+ style: { position: "relative", display: "inline-flex", alignItems: "center" },
474
+ children: [
475
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
476
+ "button",
477
+ {
478
+ ref: triggerRef,
479
+ className: "ga-info-btn",
480
+ tabIndex: -1,
481
+ type: "button",
482
+ onFocus: show,
483
+ onBlur: hide,
484
+ children: "i"
485
+ }
486
+ ),
487
+ tip && typeof document !== "undefined" && (0, import_react_dom.createPortal)(
488
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
489
+ "div",
490
+ {
491
+ style: {
492
+ position: "fixed",
493
+ top: tip.top,
494
+ left: tip.left,
495
+ transform: tip.placement === "top" ? "translate(-50%, -100%)" : "translate(-50%, 0)",
496
+ width: 230,
497
+ background: "#0f172a",
498
+ color: "#f8fafc",
499
+ fontSize: 12,
500
+ lineHeight: 1.45,
501
+ padding: "8px 12px",
502
+ borderRadius: 8,
503
+ boxShadow: "0 10px 25px -5px rgba(0,0,0,0.3), 0 8px 10px -6px rgba(0,0,0,0.2)",
504
+ border: "1px solid #334155",
505
+ pointerEvents: "none",
506
+ zIndex: 99999,
507
+ wordWrap: "break-word",
508
+ whiteSpace: "normal",
509
+ fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif'
510
+ },
511
+ children: text
512
+ }
513
+ ),
514
+ document.body
515
+ )
516
+ ]
517
+ }
518
+ );
410
519
  }
411
520
  function Skel({
412
521
  w,
@@ -811,11 +920,11 @@ function OverviewTab({ data, loading }) {
811
920
  }
812
921
 
813
922
  // src/studio/tabs/traffic-tab.tsx
814
- var import_react = require("react");
923
+ var import_react2 = require("react");
815
924
  var import_recharts2 = require("recharts");
816
925
  var import_jsx_runtime3 = require("react/jsx-runtime");
817
926
  function TrafficTab({ data, loading }) {
818
- const channelBar = (0, import_react.useMemo)(
927
+ const channelBar = (0, import_react2.useMemo)(
819
928
  () => data.channels.map((c) => ({
820
929
  name: c.channel,
821
930
  Sessions: c.sessions,
@@ -841,7 +950,11 @@ function TrafficTab({ data, loading }) {
841
950
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
842
951
  import_recharts2.XAxis,
843
952
  {
844
- dataKey: "displayDate",
953
+ dataKey: "date",
954
+ tickFormatter: formatDateTick,
955
+ minTickGap: 28,
956
+ interval: evenTickInterval(data.timeSeries?.length ?? 0),
957
+ padding: { left: 2, right: 15 },
845
958
  fontSize: 11,
846
959
  tick: { fill: "#94a3b8" },
847
960
  axisLine: false,
@@ -861,6 +974,7 @@ function TrafficTab({ data, loading }) {
861
974
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
862
975
  import_recharts2.Tooltip,
863
976
  {
977
+ labelFormatter: formatTooltipDate,
864
978
  contentStyle: {
865
979
  background: "#fff",
866
980
  border: "1px solid #e2e8f0",
@@ -1157,7 +1271,7 @@ function GeographyTab({ data, loading }) {
1157
1271
  }
1158
1272
 
1159
1273
  // src/studio/tabs/events-tab.tsx
1160
- var import_react2 = require("react");
1274
+ var import_react3 = require("react");
1161
1275
 
1162
1276
  // src/studio/sanity-client-shim.ts
1163
1277
  var projectId = typeof process !== "undefined" ? process.env.SANITY_STUDIO_PROJECT_ID || process.env.NEXT_PUBLIC_SANITY_PROJECT_ID || "" : "";
@@ -1190,16 +1304,16 @@ function EventsTab({
1190
1304
  apiUrl,
1191
1305
  dateRange
1192
1306
  }) {
1193
- const [selectedEvent, setSelectedEvent] = (0, import_react2.useState)(null);
1194
- const [paramRows, setParamRows] = (0, import_react2.useState)([]);
1195
- const [paramsLoading, setParamsLoading] = (0, import_react2.useState)(false);
1196
- const [paramsError, setParamsError] = (0, import_react2.useState)(null);
1197
- const [conditionValues, setConditionValues] = (0, import_react2.useState)([]);
1198
- const [selectedCondition, setSelectedCondition] = (0, import_react2.useState)(null);
1199
- const [filteredEvents, setFilteredEvents] = (0, import_react2.useState)(null);
1200
- const [filterLoading, setFilterLoading] = (0, import_react2.useState)(false);
1201
- const [pharmacyNameMap, setPharmacyNameMap] = (0, import_react2.useState)({});
1202
- (0, import_react2.useEffect)(() => {
1307
+ const [selectedEvent, setSelectedEvent] = (0, import_react3.useState)(null);
1308
+ const [paramRows, setParamRows] = (0, import_react3.useState)([]);
1309
+ const [paramsLoading, setParamsLoading] = (0, import_react3.useState)(false);
1310
+ const [paramsError, setParamsError] = (0, import_react3.useState)(null);
1311
+ const [conditionValues, setConditionValues] = (0, import_react3.useState)([]);
1312
+ const [selectedCondition, setSelectedCondition] = (0, import_react3.useState)(null);
1313
+ const [filteredEvents, setFilteredEvents] = (0, import_react3.useState)(null);
1314
+ const [filterLoading, setFilterLoading] = (0, import_react3.useState)(false);
1315
+ const [pharmacyNameMap, setPharmacyNameMap] = (0, import_react3.useState)({});
1316
+ (0, import_react3.useEffect)(() => {
1203
1317
  sanityClient.fetch(`*[_type == "pharmacies"]{corporateId, name}`).then((pharmacies) => {
1204
1318
  const map = {};
1205
1319
  for (const p of pharmacies) {
@@ -1208,7 +1322,7 @@ function EventsTab({
1208
1322
  setPharmacyNameMap(map);
1209
1323
  }).catch((err) => console.error("[EventsTab] fetchPharmacies error:", err));
1210
1324
  }, []);
1211
- (0, import_react2.useEffect)(() => {
1325
+ (0, import_react3.useEffect)(() => {
1212
1326
  let cancelled = false;
1213
1327
  fetchConditionValues(apiUrl, dateRange).then((values) => {
1214
1328
  if (!cancelled) setConditionValues(values);
@@ -1217,7 +1331,7 @@ function EventsTab({
1217
1331
  cancelled = true;
1218
1332
  };
1219
1333
  }, [apiUrl, dateRange]);
1220
- (0, import_react2.useEffect)(() => {
1334
+ (0, import_react3.useEffect)(() => {
1221
1335
  if (!selectedCondition) {
1222
1336
  setFilteredEvents(null);
1223
1337
  return;
@@ -1236,7 +1350,7 @@ function EventsTab({
1236
1350
  cancelled = true;
1237
1351
  };
1238
1352
  }, [selectedCondition, apiUrl, dateRange]);
1239
- (0, import_react2.useEffect)(() => {
1353
+ (0, import_react3.useEffect)(() => {
1240
1354
  if (!selectedEvent) {
1241
1355
  setParamRows([]);
1242
1356
  return;
@@ -1256,7 +1370,7 @@ function EventsTab({
1256
1370
  cancelled = true;
1257
1371
  };
1258
1372
  }, [selectedEvent, apiUrl, dateRange]);
1259
- const displayEvents = (0, import_react2.useMemo)(
1373
+ const displayEvents = (0, import_react3.useMemo)(
1260
1374
  () => filteredEvents ?? data.topEvents,
1261
1375
  [filteredEvents, data.topEvents]
1262
1376
  );
@@ -1442,7 +1556,7 @@ function AcquisitionTab({ data, loading }) {
1442
1556
  }
1443
1557
 
1444
1558
  // src/studio/tabs/conditions-products-tab.tsx
1445
- var import_react3 = require("react");
1559
+ var import_react4 = require("react");
1446
1560
  var import_recharts3 = require("recharts");
1447
1561
 
1448
1562
  // src/studio/utils-mixpanel-api.ts
@@ -1551,18 +1665,30 @@ async function fetchPmProductCart(apiUrl, dateRange, conditionId, product, signa
1551
1665
  }
1552
1666
  return res.json();
1553
1667
  }
1668
+ async function parseJsonResponse(res, label, url) {
1669
+ const text = await res.text();
1670
+ if (!res.ok) {
1671
+ let msg = `${label} API error: ${res.status} ${res.statusText}`;
1672
+ try {
1673
+ const err = JSON.parse(text);
1674
+ if (err?.error) msg = err.error;
1675
+ } catch {
1676
+ }
1677
+ throw new Error(msg);
1678
+ }
1679
+ if (/^\s*<(!doctype|html)/i.test(text)) {
1680
+ throw new Error(
1681
+ `${label} API (${url}) returned HTML instead of JSON. Check the Mixpanel API URL (mixpanelApiUrl) points at the /api/mixpanel route.`
1682
+ );
1683
+ }
1684
+ return JSON.parse(text);
1685
+ }
1554
1686
  async function fetchTopEntities(apiUrl, dateRange, opts, signal) {
1555
1687
  let url = `${apiUrl}?mode=top-entities&dateRange=${dateRange}`;
1556
1688
  if (opts?.condition) url += `&condition=${encodeURIComponent(opts.condition)}`;
1557
1689
  if (opts?.product) url += `&product=${encodeURIComponent(opts.product)}`;
1558
1690
  const res = await fetch(url, { cache: "no-store", signal });
1559
- if (!res.ok) {
1560
- const err = await res.json().catch(() => null);
1561
- throw new Error(
1562
- err?.error || `Top entities API error: ${res.status} ${res.statusText}`
1563
- );
1564
- }
1565
- const json = await res.json();
1691
+ const json = await parseJsonResponse(res, "Top entities", url);
1566
1692
  return {
1567
1693
  conditions: json.conditions ?? [],
1568
1694
  products: json.products ?? [],
@@ -1573,13 +1699,7 @@ async function fetchTopEntities(apiUrl, dateRange, opts, signal) {
1573
1699
  async function fetchTopSearches(apiUrl, dateRange, type, signal) {
1574
1700
  const url = `${apiUrl}?mode=top-searches&dateRange=${dateRange}&type=${type}`;
1575
1701
  const res = await fetch(url, { cache: "no-store", signal });
1576
- if (!res.ok) {
1577
- const err = await res.json().catch(() => null);
1578
- throw new Error(
1579
- err?.error || `Top searches API error: ${res.status} ${res.statusText}`
1580
- );
1581
- }
1582
- const json = await res.json();
1702
+ const json = await parseJsonResponse(res, "Top searches", url);
1583
1703
  return { topSearches: json.topSearches ?? [] };
1584
1704
  }
1585
1705
  async function clearMixpanelCache(apiUrl) {
@@ -1735,22 +1855,23 @@ function EmptyState({ label }) {
1735
1855
  }
1736
1856
  );
1737
1857
  }
1738
- function ConditionsProductsTab({ dateRange }) {
1739
- const [subTab, setSubTab] = (0, import_react3.useState)("conditions");
1740
- const [data, setData] = (0, import_react3.useState)(null);
1741
- const [loading, setLoading] = (0, import_react3.useState)(true);
1742
- const [error, setError] = (0, import_react3.useState)(null);
1743
- const [fetchTick] = (0, import_react3.useState)(0);
1744
- const [topConditionSearches, setTopConditionSearches] = (0, import_react3.useState)(null);
1745
- const [topConditionSearchesLoading, setTopConditionSearchesLoading] = (0, import_react3.useState)(true);
1746
- const [conditionSearchVisible, setConditionSearchVisible] = (0, import_react3.useState)(20);
1747
- const [topProductSearches, setTopProductSearches] = (0, import_react3.useState)(null);
1748
- const [topProductSearchesLoading, setTopProductSearchesLoading] = (0, import_react3.useState)(true);
1749
- const [productSearchVisible, setProductSearchVisible] = (0, import_react3.useState)(20);
1750
- (0, import_react3.useEffect)(() => {
1858
+ function ConditionsProductsTab({ apiUrl, dateRange }) {
1859
+ const targetUrl = apiUrl || MIXPANEL_API_URL;
1860
+ const [subTab, setSubTab] = (0, import_react4.useState)("conditions");
1861
+ const [data, setData] = (0, import_react4.useState)(null);
1862
+ const [loading, setLoading] = (0, import_react4.useState)(true);
1863
+ const [error, setError] = (0, import_react4.useState)(null);
1864
+ const [fetchTick] = (0, import_react4.useState)(0);
1865
+ const [topConditionSearches, setTopConditionSearches] = (0, import_react4.useState)(null);
1866
+ const [topConditionSearchesLoading, setTopConditionSearchesLoading] = (0, import_react4.useState)(true);
1867
+ const [conditionSearchVisible, setConditionSearchVisible] = (0, import_react4.useState)(20);
1868
+ const [topProductSearches, setTopProductSearches] = (0, import_react4.useState)(null);
1869
+ const [topProductSearchesLoading, setTopProductSearchesLoading] = (0, import_react4.useState)(true);
1870
+ const [productSearchVisible, setProductSearchVisible] = (0, import_react4.useState)(20);
1871
+ (0, import_react4.useEffect)(() => {
1751
1872
  const controller = new AbortController();
1752
1873
  setTopConditionSearchesLoading(true);
1753
- fetchTopSearches(MIXPANEL_API_URL, dateRange, "condition", controller.signal).then((res) => {
1874
+ fetchTopSearches(targetUrl, dateRange, "condition", controller.signal).then((res) => {
1754
1875
  if (!controller.signal.aborted) setTopConditionSearches(res);
1755
1876
  }).catch((err) => {
1756
1877
  if (err instanceof Error && err.name === "AbortError") return;
@@ -1758,11 +1879,11 @@ function ConditionsProductsTab({ dateRange }) {
1758
1879
  if (!controller.signal.aborted) setTopConditionSearchesLoading(false);
1759
1880
  });
1760
1881
  return () => controller.abort();
1761
- }, [dateRange, fetchTick]);
1762
- (0, import_react3.useEffect)(() => {
1882
+ }, [targetUrl, dateRange, fetchTick]);
1883
+ (0, import_react4.useEffect)(() => {
1763
1884
  const controller = new AbortController();
1764
1885
  setTopProductSearchesLoading(true);
1765
- fetchTopSearches(MIXPANEL_API_URL, dateRange, "product", controller.signal).then((res) => {
1886
+ fetchTopSearches(targetUrl, dateRange, "product", controller.signal).then((res) => {
1766
1887
  if (!controller.signal.aborted) setTopProductSearches(res);
1767
1888
  }).catch((err) => {
1768
1889
  if (err instanceof Error && err.name === "AbortError") return;
@@ -1770,12 +1891,12 @@ function ConditionsProductsTab({ dateRange }) {
1770
1891
  if (!controller.signal.aborted) setTopProductSearchesLoading(false);
1771
1892
  });
1772
1893
  return () => controller.abort();
1773
- }, [dateRange, fetchTick]);
1774
- (0, import_react3.useEffect)(() => {
1894
+ }, [targetUrl, dateRange, fetchTick]);
1895
+ (0, import_react4.useEffect)(() => {
1775
1896
  const controller = new AbortController();
1776
1897
  setLoading(true);
1777
1898
  setError(null);
1778
- fetchTopEntities(MIXPANEL_API_URL, dateRange, void 0, controller.signal).then((res) => {
1899
+ fetchTopEntities(targetUrl, dateRange, void 0, controller.signal).then((res) => {
1779
1900
  if (!controller.signal.aborted) setData(res);
1780
1901
  }).catch((err) => {
1781
1902
  if (err instanceof Error && err.name === "AbortError") return;
@@ -1784,12 +1905,12 @@ function ConditionsProductsTab({ dateRange }) {
1784
1905
  if (!controller.signal.aborted) setLoading(false);
1785
1906
  });
1786
1907
  return () => controller.abort();
1787
- }, [dateRange, fetchTick]);
1788
- const conditionBarData = (0, import_react3.useMemo)(
1908
+ }, [targetUrl, dateRange, fetchTick]);
1909
+ const conditionBarData = (0, import_react4.useMemo)(
1789
1910
  () => [...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 })),
1790
1911
  [data]
1791
1912
  );
1792
- const pieData = (0, import_react3.useMemo)(() => {
1913
+ const pieData = (0, import_react4.useMemo)(() => {
1793
1914
  const sb = data?.serviceBreakdown ?? { nhs: 0, private_service: 0, private_medication: 0 };
1794
1915
  return [
1795
1916
  { name: "NHS", value: sb.nhs, color: SERVICE_COLORS["NHS"] },
@@ -1797,7 +1918,7 @@ function ConditionsProductsTab({ dateRange }) {
1797
1918
  { name: "Private Medication", value: sb.private_medication, color: SERVICE_COLORS["Private Medication"] }
1798
1919
  ].filter((e) => e.value > 0);
1799
1920
  }, [data]);
1800
- const productBarData = (0, import_react3.useMemo)(
1921
+ const productBarData = (0, import_react4.useMemo)(
1801
1922
  () => [...data?.products ?? []].sort((a, b) => b.product_viewed - a.product_viewed).filter((p) => p.product_viewed > 0).slice(0, 12).map((p) => ({ fullName: p.name, name: shortLabel(p.name), Views: p.product_viewed })),
1802
1923
  [data]
1803
1924
  );
@@ -1830,6 +1951,7 @@ function ConditionsProductsTab({ dateRange }) {
1830
1951
  border: "1px solid #e2e8f0",
1831
1952
  boxShadow: "0 4px 16px rgba(0,0,0,.08)"
1832
1953
  };
1954
+ const barTooltipLabelStyle = { color: "#0f172a", fontWeight: 600 };
1833
1955
  return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { style: { animation: "ga-fadein .25s ease", display: "flex", flexDirection: "column", gap: 20 }, children: [
1834
1956
  /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1835
1957
  "div",
@@ -1941,6 +2063,7 @@ function ConditionsProductsTab({ dateRange }) {
1941
2063
  formatter: (v) => [fNum(Number(v ?? 0)), "Views"],
1942
2064
  labelFormatter: (label, payload) => payload?.[0]?.payload?.fullName ?? label,
1943
2065
  contentStyle: barTooltipStyle,
2066
+ labelStyle: barTooltipLabelStyle,
1944
2067
  cursor: { fill: "rgba(99,102,241,0.06)" }
1945
2068
  }
1946
2069
  ),
@@ -2197,6 +2320,7 @@ function ConditionsProductsTab({ dateRange }) {
2197
2320
  formatter: (v) => [fNum(Number(v ?? 0)), "Views"],
2198
2321
  labelFormatter: (label, payload) => payload?.[0]?.payload?.fullName ?? label,
2199
2322
  contentStyle: barTooltipStyle,
2323
+ labelStyle: barTooltipLabelStyle,
2200
2324
  cursor: { fill: "rgba(99,102,241,0.06)" }
2201
2325
  }
2202
2326
  ),
@@ -2250,10 +2374,10 @@ function ConditionsProductsTab({ dateRange }) {
2250
2374
  }
2251
2375
 
2252
2376
  // src/studio/funnel/funnel-events-tab.tsx
2253
- var import_react7 = require("react");
2377
+ var import_react8 = require("react");
2254
2378
 
2255
2379
  // src/studio/funnel/custom-select.tsx
2256
- var import_react4 = require("react");
2380
+ var import_react5 = require("react");
2257
2381
  var import_jsx_runtime10 = require("react/jsx-runtime");
2258
2382
  function CustomSelect({
2259
2383
  label,
@@ -2264,16 +2388,16 @@ function CustomSelect({
2264
2388
  loading,
2265
2389
  onChange
2266
2390
  }) {
2267
- const [open, setOpen] = (0, import_react4.useState)(false);
2268
- const [search, setSearch] = (0, import_react4.useState)("");
2269
- const ref = (0, import_react4.useRef)(null);
2270
- const searchRef = (0, import_react4.useRef)(null);
2271
- const selected = (0, import_react4.useMemo)(() => options.find((o) => o.value === value), [options, value]);
2272
- const filtered = (0, import_react4.useMemo)(
2391
+ const [open, setOpen] = (0, import_react5.useState)(false);
2392
+ const [search, setSearch] = (0, import_react5.useState)("");
2393
+ const ref = (0, import_react5.useRef)(null);
2394
+ const searchRef = (0, import_react5.useRef)(null);
2395
+ const selected = (0, import_react5.useMemo)(() => options.find((o) => o.value === value), [options, value]);
2396
+ const filtered = (0, import_react5.useMemo)(
2273
2397
  () => search.trim() ? options.filter((o) => o.label.toLowerCase().includes(search.toLowerCase())) : options,
2274
2398
  [options, search]
2275
2399
  );
2276
- (0, import_react4.useEffect)(() => {
2400
+ (0, import_react5.useEffect)(() => {
2277
2401
  if (!open) return;
2278
2402
  function handler(e) {
2279
2403
  if (ref.current && !ref.current.contains(e.target)) setOpen(false);
@@ -2281,11 +2405,11 @@ function CustomSelect({
2281
2405
  document.addEventListener("mousedown", handler);
2282
2406
  return () => document.removeEventListener("mousedown", handler);
2283
2407
  }, [open]);
2284
- (0, import_react4.useEffect)(() => {
2408
+ (0, import_react5.useEffect)(() => {
2285
2409
  if (open) setTimeout(() => searchRef.current?.focus(), 40);
2286
2410
  else setSearch("");
2287
2411
  }, [open]);
2288
- const handleSelect = (0, import_react4.useCallback)(
2412
+ const handleSelect = (0, import_react5.useCallback)(
2289
2413
  (val) => {
2290
2414
  onChange(val);
2291
2415
  setOpen(false);
@@ -2510,9 +2634,9 @@ function CustomSelect({
2510
2634
  }
2511
2635
 
2512
2636
  // src/studio/funnel/patient-modal.tsx
2513
- var import_react5 = __toESM(require("react"));
2637
+ var import_react6 = __toESM(require("react"));
2514
2638
  var import_jsx_runtime11 = require("react/jsx-runtime");
2515
- var PatientModal = import_react5.default.memo(function PatientModal2({
2639
+ var PatientModal = import_react6.default.memo(function PatientModal2({
2516
2640
  open,
2517
2641
  onClose,
2518
2642
  loading,
@@ -2523,7 +2647,7 @@ var PatientModal = import_react5.default.memo(function PatientModal2({
2523
2647
  pharmacyNameMap,
2524
2648
  onExport
2525
2649
  }) {
2526
- const formattedRecords = (0, import_react5.useMemo)(
2650
+ const formattedRecords = (0, import_react6.useMemo)(
2527
2651
  () => records.map((p) => ({
2528
2652
  ...p,
2529
2653
  _date: p.timestamp ? new Date(p.timestamp * 1e3).toLocaleDateString("en-GB", {
@@ -2839,7 +2963,7 @@ var PatientModal = import_react5.default.memo(function PatientModal2({
2839
2963
  });
2840
2964
 
2841
2965
  // src/studio/funnel/funnel-card.tsx
2842
- var import_react6 = require("react");
2966
+ var import_react7 = require("react");
2843
2967
 
2844
2968
  // src/studio/funnel/card-sections.tsx
2845
2969
  var import_jsx_runtime12 = require("react/jsx-runtime");
@@ -3413,18 +3537,18 @@ function FunnelCard({
3413
3537
  }) {
3414
3538
  const color = FUNNEL_EVENT_COLOR[r.event] ?? "#888";
3415
3539
  const isQfs = r.event === "questionnaire_flow_step";
3416
- const breakdown = (0, import_react6.useMemo)(
3540
+ const breakdown = (0, import_react7.useMemo)(
3417
3541
  () => !isQfs && r.event !== "condition_viewed" ? paramBreakdowns[r.event] : void 0,
3418
3542
  [isQfs, paramBreakdowns, r.event]
3419
3543
  );
3420
- const filteredBreakdownEntries = (0, import_react6.useMemo)(
3544
+ const filteredBreakdownEntries = (0, import_react7.useMemo)(
3421
3545
  () => breakdown ? Object.entries(breakdown).filter(
3422
3546
  ([pk]) => pk !== "condition_slug" && pk !== "date_of_birth" && pk !== "is_pre_consult" && !(r.event === "start_assessment_clicked" && pk === "is_nhs")
3423
3547
  ) : [],
3424
3548
  [breakdown, r.event]
3425
3549
  );
3426
3550
  const hasBreakdown = filteredBreakdownEntries.length > 0;
3427
- const visibleFlows = (0, import_react6.useMemo)(
3551
+ const visibleFlows = (0, import_react7.useMemo)(
3428
3552
  () => isQfs ? qfsFlows.filter((f) => f.steps.some((s) => s.count > 0)) : [],
3429
3553
  [isQfs, qfsFlows]
3430
3554
  );
@@ -3638,28 +3762,29 @@ function FunnelCard({
3638
3762
 
3639
3763
  // src/studio/funnel/funnel-events-tab.tsx
3640
3764
  var import_jsx_runtime14 = require("react/jsx-runtime");
3641
- function FunnelEventsTab({ dateRange }) {
3642
- const [loading, setLoading] = (0, import_react7.useState)(false);
3643
- const [error, setError] = (0, import_react7.useState)(null);
3644
- const [refreshKey, setRefreshKey] = (0, import_react7.useState)(0);
3645
- const [clearing, setClearing] = (0, import_react7.useState)(false);
3646
- const [conditionOptions, setConditionOptions] = (0, import_react7.useState)([]);
3647
- const [selectedCondition, setSelectedCondition] = (0, import_react7.useState)("");
3648
- const [conditionsLoading, setConditionsLoading] = (0, import_react7.useState)(true);
3649
- const [rows, setRows] = (0, import_react7.useState)([]);
3650
- const [qfsFlows, setQfsFlows] = (0, import_react7.useState)([]);
3651
- const [paramBreakdowns, setParamBreakdowns] = (0, import_react7.useState)({});
3652
- const [qfsStepBreakdowns, setQfsStepBreakdowns] = (0, import_react7.useState)({});
3653
- const [conditionServiceMap, setConditionServiceMap] = (0, import_react7.useState)({});
3654
- const [patientModalOpen, setPatientModalOpen] = (0, import_react7.useState)(false);
3655
- const [patientRecords, setPatientRecords] = (0, import_react7.useState)([]);
3656
- const [patientsLoading, setPatientsLoading] = (0, import_react7.useState)(false);
3657
- const [patientsError, setPatientsError] = (0, import_react7.useState)(null);
3658
- const doFetchPatients = (0, import_react7.useCallback)(() => {
3765
+ function FunnelEventsTab({ apiUrl, dateRange }) {
3766
+ const targetUrl = apiUrl || MIXPANEL_API_URL;
3767
+ const [loading, setLoading] = (0, import_react8.useState)(false);
3768
+ const [error, setError] = (0, import_react8.useState)(null);
3769
+ const [refreshKey, setRefreshKey] = (0, import_react8.useState)(0);
3770
+ const [clearing, setClearing] = (0, import_react8.useState)(false);
3771
+ const [conditionOptions, setConditionOptions] = (0, import_react8.useState)([]);
3772
+ const [selectedCondition, setSelectedCondition] = (0, import_react8.useState)("");
3773
+ const [conditionsLoading, setConditionsLoading] = (0, import_react8.useState)(true);
3774
+ const [rows, setRows] = (0, import_react8.useState)([]);
3775
+ const [qfsFlows, setQfsFlows] = (0, import_react8.useState)([]);
3776
+ const [paramBreakdowns, setParamBreakdowns] = (0, import_react8.useState)({});
3777
+ const [qfsStepBreakdowns, setQfsStepBreakdowns] = (0, import_react8.useState)({});
3778
+ const [conditionServiceMap, setConditionServiceMap] = (0, import_react8.useState)({});
3779
+ const [patientModalOpen, setPatientModalOpen] = (0, import_react8.useState)(false);
3780
+ const [patientRecords, setPatientRecords] = (0, import_react8.useState)([]);
3781
+ const [patientsLoading, setPatientsLoading] = (0, import_react8.useState)(false);
3782
+ const [patientsError, setPatientsError] = (0, import_react8.useState)(null);
3783
+ const doFetchPatients = (0, import_react8.useCallback)(() => {
3659
3784
  if (!selectedCondition) return;
3660
3785
  setPatientsLoading(true);
3661
3786
  setPatientsError(null);
3662
- fetchSignupPatients(MIXPANEL_API_URL, dateRange, selectedCondition).then((records) => {
3787
+ fetchSignupPatients(targetUrl, dateRange, selectedCondition).then((records) => {
3663
3788
  setPatientRecords(records);
3664
3789
  setPatientsLoading(false);
3665
3790
  }).catch((err) => {
@@ -3667,18 +3792,18 @@ function FunnelEventsTab({ dateRange }) {
3667
3792
  setPatientsError(err instanceof Error ? err.message : String(err));
3668
3793
  setPatientsLoading(false);
3669
3794
  });
3670
- }, [dateRange, selectedCondition]);
3671
- const handleSignupCountClick = (0, import_react7.useCallback)(() => {
3795
+ }, [targetUrl, dateRange, selectedCondition]);
3796
+ const handleSignupCountClick = (0, import_react8.useCallback)(() => {
3672
3797
  if (!selectedCondition) return;
3673
3798
  setPatientModalOpen(true);
3674
3799
  doFetchPatients();
3675
3800
  }, [selectedCondition, doFetchPatients]);
3676
- (0, import_react7.useEffect)(() => {
3801
+ (0, import_react8.useEffect)(() => {
3677
3802
  if (patientModalOpen && refreshKey > 0) {
3678
3803
  doFetchPatients();
3679
3804
  }
3680
3805
  }, [refreshKey]);
3681
- const exportPatientsCSV = (0, import_react7.useCallback)(() => {
3806
+ const exportPatientsCSV = (0, import_react8.useCallback)(() => {
3682
3807
  if (!patientRecords.length) return;
3683
3808
  const headers = [
3684
3809
  "Date",
@@ -3721,11 +3846,11 @@ function FunnelEventsTab({ dateRange }) {
3721
3846
  a.click();
3722
3847
  URL.revokeObjectURL(url);
3723
3848
  }, [patientRecords, selectedCondition]);
3724
- (0, import_react7.useEffect)(() => {
3849
+ (0, import_react8.useEffect)(() => {
3725
3850
  const controller = new AbortController();
3726
3851
  setConditionsLoading(true);
3727
3852
  setSelectedCondition("");
3728
- fetchFunnelConditions(MIXPANEL_API_URL, dateRange, void 0, true, controller.signal).then((values) => {
3853
+ fetchFunnelConditions(targetUrl, dateRange, void 0, true, controller.signal).then((values) => {
3729
3854
  setConditionOptions(values);
3730
3855
  setConditionsLoading(false);
3731
3856
  }).catch((err) => {
@@ -3744,8 +3869,8 @@ function FunnelEventsTab({ dateRange }) {
3744
3869
  setConditionServiceMap(map);
3745
3870
  }).catch((err) => console.error("[FunnelEventsTab] fetchConditionServices error:", err));
3746
3871
  return () => controller.abort();
3747
- }, [dateRange, refreshKey]);
3748
- (0, import_react7.useEffect)(() => {
3872
+ }, [targetUrl, dateRange, refreshKey]);
3873
+ (0, import_react8.useEffect)(() => {
3749
3874
  if (!selectedCondition) {
3750
3875
  setRows([]);
3751
3876
  setQfsFlows([]);
@@ -3757,7 +3882,7 @@ function FunnelEventsTab({ dateRange }) {
3757
3882
  const controller = new AbortController();
3758
3883
  setLoading(true);
3759
3884
  setError(null);
3760
- fetchFunnelEvents(MIXPANEL_API_URL, dateRange, selectedCondition, void 0, controller.signal).then((data) => {
3885
+ fetchFunnelEvents(targetUrl, dateRange, selectedCondition, void 0, controller.signal).then((data) => {
3761
3886
  const { eventCounts, qfsStepsByFlow, paramBreakdowns: bd, qfsStepBreakdowns: qfsStepBd } = data;
3762
3887
  setParamBreakdowns(bd);
3763
3888
  setQfsStepBreakdowns(qfsStepBd ?? {});
@@ -3780,19 +3905,19 @@ function FunnelEventsTab({ dateRange }) {
3780
3905
  setLoading(false);
3781
3906
  });
3782
3907
  return () => controller.abort();
3783
- }, [dateRange, selectedCondition, refreshKey]);
3784
- const qfsTotal = (0, import_react7.useMemo)(
3908
+ }, [targetUrl, dateRange, selectedCondition, refreshKey]);
3909
+ const qfsTotal = (0, import_react8.useMemo)(
3785
3910
  () => qfsFlows.reduce((sum, f) => sum + (f.steps[0]?.count ?? 0), 0),
3786
3911
  [qfsFlows]
3787
3912
  );
3788
- const allRows = (0, import_react7.useMemo)(
3913
+ const allRows = (0, import_react8.useMemo)(
3789
3914
  () => [
3790
3915
  ...rows.filter((r) => !r.isStep),
3791
3916
  { event: "questionnaire_flow_step", label: "Pre Consult Journey", count: qfsTotal, isStep: false }
3792
3917
  ],
3793
3918
  [rows, qfsTotal]
3794
3919
  );
3795
- const conditionSelectOptions = (0, import_react7.useMemo)(
3920
+ const conditionSelectOptions = (0, import_react8.useMemo)(
3796
3921
  () => conditionOptions.map((c) => {
3797
3922
  const svc = conditionServiceMap[String(c.id)];
3798
3923
  return {
@@ -3804,21 +3929,21 @@ function FunnelEventsTab({ dateRange }) {
3804
3929
  }),
3805
3930
  [conditionOptions, conditionServiceMap]
3806
3931
  );
3807
- const handleClearFilters = (0, import_react7.useCallback)(() => {
3932
+ const handleClearFilters = (0, import_react8.useCallback)(() => {
3808
3933
  setSelectedCondition("");
3809
3934
  }, []);
3810
- const handleFetchLatest = (0, import_react7.useCallback)(async () => {
3935
+ const handleFetchLatest = (0, import_react8.useCallback)(async () => {
3811
3936
  setClearing(true);
3812
3937
  try {
3813
- await clearMixpanelCache(MIXPANEL_API_URL);
3938
+ await clearMixpanelCache(targetUrl);
3814
3939
  } catch (err) {
3815
3940
  console.error("[FunnelEventsTab] clear-cache error:", err);
3816
3941
  } finally {
3817
3942
  setClearing(false);
3818
3943
  setRefreshKey((k) => k + 1);
3819
3944
  }
3820
- }, []);
3821
- const handleClosePatientModal = (0, import_react7.useCallback)(() => setPatientModalOpen(false), []);
3945
+ }, [targetUrl]);
3946
+ const handleClosePatientModal = (0, import_react8.useCallback)(() => setPatientModalOpen(false), []);
3822
3947
  return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { style: { padding: "28px 0 48px", background: "#f8f9fb", minHeight: 500 }, children: [
3823
3948
  /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
3824
3949
  "div",
@@ -3982,7 +4107,7 @@ function FunnelEventsTab({ dateRange }) {
3982
4107
  }
3983
4108
 
3984
4109
  // src/studio/funnel/pm-funnel-tab.tsx
3985
- var import_react8 = require("react");
4110
+ var import_react9 = require("react");
3986
4111
  var import_jsx_runtime15 = require("react/jsx-runtime");
3987
4112
  var HIDDEN_PARAMS = /* @__PURE__ */ new Set([
3988
4113
  "product_name",
@@ -4586,51 +4711,52 @@ function FlowTab({ label, icon, desc, active, onClick }) {
4586
4711
  }
4587
4712
  );
4588
4713
  }
4589
- function PmFunnelTab({ dateRange }) {
4590
- const [flowSource, setFlowSource] = (0, import_react8.useState)("product_detail");
4591
- const [refreshKey, setRefreshKey] = (0, import_react8.useState)(0);
4592
- const [clearing, setClearing] = (0, import_react8.useState)(false);
4593
- const [pdFilterOptions, setPdFilterOptions] = (0, import_react8.useState)([]);
4594
- const [pdSelectedFilter, setPdSelectedFilter] = (0, import_react8.useState)("");
4595
- const [pdOptionsLoading, setPdOptionsLoading] = (0, import_react8.useState)(true);
4596
- const [pdEventsData, setPdEventsData] = (0, import_react8.useState)(null);
4597
- const [pdLoading, setPdLoading] = (0, import_react8.useState)(false);
4598
- const [pdError, setPdError] = (0, import_react8.useState)(null);
4599
- const [cdConditionOptions, setCdConditionOptions] = (0, import_react8.useState)([]);
4600
- const [cdSelectedConditionId, setCdSelectedConditionId] = (0, import_react8.useState)("");
4601
- const [cdOptionsLoading, setCdOptionsLoading] = (0, import_react8.useState)(true);
4602
- const [cdConditionData, setCdConditionData] = (0, import_react8.useState)(null);
4603
- const [cdConditionLoading, setCdConditionLoading] = (0, import_react8.useState)(false);
4604
- const [cdConditionError, setCdConditionError] = (0, import_react8.useState)(null);
4605
- const [cdSelectedProduct, setCdSelectedProduct] = (0, import_react8.useState)(null);
4606
- const [cdCartData, setCdCartData] = (0, import_react8.useState)(null);
4607
- const [cdCartLoading, setCdCartLoading] = (0, import_react8.useState)(false);
4608
- const [cartAuthModalOpen, setCartAuthModalOpen] = (0, import_react8.useState)(false);
4609
- const [cartAuthPatients, setCartAuthPatients] = (0, import_react8.useState)([]);
4610
- const [cartAuthPatientsLoading, setCartAuthPatientsLoading] = (0, import_react8.useState)(false);
4611
- const [cartAuthPatientsError, setCartAuthPatientsError] = (0, import_react8.useState)(null);
4612
- const [authModalOpen, setAuthModalOpen] = (0, import_react8.useState)(false);
4613
- const [authPatients, setAuthPatients] = (0, import_react8.useState)([]);
4614
- const [authPatientsLoading, setAuthPatientsLoading] = (0, import_react8.useState)(false);
4615
- const [authPatientsError, setAuthPatientsError] = (0, import_react8.useState)(null);
4616
- const handleFetchLatest = (0, import_react8.useCallback)(async () => {
4714
+ function PmFunnelTab({ apiUrl, dateRange }) {
4715
+ const targetUrl = apiUrl || MIXPANEL_API_URL;
4716
+ const [flowSource, setFlowSource] = (0, import_react9.useState)("product_detail");
4717
+ const [refreshKey, setRefreshKey] = (0, import_react9.useState)(0);
4718
+ const [clearing, setClearing] = (0, import_react9.useState)(false);
4719
+ const [pdFilterOptions, setPdFilterOptions] = (0, import_react9.useState)([]);
4720
+ const [pdSelectedFilter, setPdSelectedFilter] = (0, import_react9.useState)("");
4721
+ const [pdOptionsLoading, setPdOptionsLoading] = (0, import_react9.useState)(true);
4722
+ const [pdEventsData, setPdEventsData] = (0, import_react9.useState)(null);
4723
+ const [pdLoading, setPdLoading] = (0, import_react9.useState)(false);
4724
+ const [pdError, setPdError] = (0, import_react9.useState)(null);
4725
+ const [cdConditionOptions, setCdConditionOptions] = (0, import_react9.useState)([]);
4726
+ const [cdSelectedConditionId, setCdSelectedConditionId] = (0, import_react9.useState)("");
4727
+ const [cdOptionsLoading, setCdOptionsLoading] = (0, import_react9.useState)(true);
4728
+ const [cdConditionData, setCdConditionData] = (0, import_react9.useState)(null);
4729
+ const [cdConditionLoading, setCdConditionLoading] = (0, import_react9.useState)(false);
4730
+ const [cdConditionError, setCdConditionError] = (0, import_react9.useState)(null);
4731
+ const [cdSelectedProduct, setCdSelectedProduct] = (0, import_react9.useState)(null);
4732
+ const [cdCartData, setCdCartData] = (0, import_react9.useState)(null);
4733
+ const [cdCartLoading, setCdCartLoading] = (0, import_react9.useState)(false);
4734
+ const [cartAuthModalOpen, setCartAuthModalOpen] = (0, import_react9.useState)(false);
4735
+ const [cartAuthPatients, setCartAuthPatients] = (0, import_react9.useState)([]);
4736
+ const [cartAuthPatientsLoading, setCartAuthPatientsLoading] = (0, import_react9.useState)(false);
4737
+ const [cartAuthPatientsError, setCartAuthPatientsError] = (0, import_react9.useState)(null);
4738
+ const [authModalOpen, setAuthModalOpen] = (0, import_react9.useState)(false);
4739
+ const [authPatients, setAuthPatients] = (0, import_react9.useState)([]);
4740
+ const [authPatientsLoading, setAuthPatientsLoading] = (0, import_react9.useState)(false);
4741
+ const [authPatientsError, setAuthPatientsError] = (0, import_react9.useState)(null);
4742
+ const handleFetchLatest = (0, import_react9.useCallback)(async () => {
4617
4743
  setClearing(true);
4618
4744
  try {
4619
- await clearMixpanelCache(MIXPANEL_API_URL);
4745
+ await clearMixpanelCache(targetUrl);
4620
4746
  } catch (err) {
4621
4747
  console.error("PM funnel clear-cache error:", err);
4622
4748
  } finally {
4623
4749
  setClearing(false);
4624
4750
  setRefreshKey((k) => k + 1);
4625
4751
  }
4626
- }, []);
4627
- (0, import_react8.useEffect)(() => {
4752
+ }, [targetUrl]);
4753
+ (0, import_react9.useEffect)(() => {
4628
4754
  if (flowSource !== "product_detail") return;
4629
4755
  const controller = new AbortController();
4630
4756
  setPdOptionsLoading(true);
4631
4757
  setPdSelectedFilter("");
4632
4758
  setPdEventsData(null);
4633
- fetchPmFunnelProducts(MIXPANEL_API_URL, dateRange, "product_detail", controller.signal).then((values) => {
4759
+ fetchPmFunnelProducts(targetUrl, dateRange, "product_detail", controller.signal).then((values) => {
4634
4760
  if (!controller.signal.aborted) {
4635
4761
  setPdFilterOptions(values);
4636
4762
  setPdOptionsLoading(false);
@@ -4641,8 +4767,8 @@ function PmFunnelTab({ dateRange }) {
4641
4767
  if (!controller.signal.aborted) setPdOptionsLoading(false);
4642
4768
  });
4643
4769
  return () => controller.abort();
4644
- }, [dateRange, flowSource, refreshKey]);
4645
- (0, import_react8.useEffect)(() => {
4770
+ }, [targetUrl, dateRange, flowSource, refreshKey]);
4771
+ (0, import_react9.useEffect)(() => {
4646
4772
  if (flowSource !== "product_detail" || !pdSelectedFilter) {
4647
4773
  setPdEventsData(null);
4648
4774
  setPdLoading(false);
@@ -4651,7 +4777,7 @@ function PmFunnelTab({ dateRange }) {
4651
4777
  const controller = new AbortController();
4652
4778
  setPdLoading(true);
4653
4779
  setPdError(null);
4654
- fetchPmFunnelEvents(MIXPANEL_API_URL, dateRange, pdSelectedFilter, "product_detail", controller.signal).then((data) => {
4780
+ fetchPmFunnelEvents(targetUrl, dateRange, pdSelectedFilter, "product_detail", controller.signal).then((data) => {
4655
4781
  if (!controller.signal.aborted) {
4656
4782
  setPdEventsData(data);
4657
4783
  setPdLoading(false);
@@ -4664,8 +4790,8 @@ function PmFunnelTab({ dateRange }) {
4664
4790
  }
4665
4791
  });
4666
4792
  return () => controller.abort();
4667
- }, [pdSelectedFilter, dateRange, flowSource, refreshKey]);
4668
- (0, import_react8.useEffect)(() => {
4793
+ }, [targetUrl, pdSelectedFilter, dateRange, flowSource, refreshKey]);
4794
+ (0, import_react9.useEffect)(() => {
4669
4795
  if (flowSource !== "condition_detail") return;
4670
4796
  const controller = new AbortController();
4671
4797
  setCdOptionsLoading(true);
@@ -4674,7 +4800,7 @@ function PmFunnelTab({ dateRange }) {
4674
4800
  setCdSelectedProduct(null);
4675
4801
  setCdCartData(null);
4676
4802
  Promise.all([
4677
- fetchPmConditionOptions(MIXPANEL_API_URL, dateRange, controller.signal),
4803
+ fetchPmConditionOptions(targetUrl, dateRange, controller.signal),
4678
4804
  sanityClient.fetch(
4679
4805
  `*[_type == "singleCondition" && defined(conditionId)]{conditionId, title}`
4680
4806
  )
@@ -4696,8 +4822,8 @@ function PmFunnelTab({ dateRange }) {
4696
4822
  if (!controller.signal.aborted) setCdOptionsLoading(false);
4697
4823
  });
4698
4824
  return () => controller.abort();
4699
- }, [dateRange, flowSource, refreshKey]);
4700
- (0, import_react8.useEffect)(() => {
4825
+ }, [targetUrl, dateRange, flowSource, refreshKey]);
4826
+ (0, import_react9.useEffect)(() => {
4701
4827
  if (flowSource !== "condition_detail" || !cdSelectedConditionId) {
4702
4828
  setCdConditionData(null);
4703
4829
  setCdSelectedProduct(null);
@@ -4710,7 +4836,7 @@ function PmFunnelTab({ dateRange }) {
4710
4836
  setCdConditionError(null);
4711
4837
  setCdSelectedProduct(null);
4712
4838
  setCdCartData(null);
4713
- fetchPmConditionEvents(MIXPANEL_API_URL, dateRange, Number(cdSelectedConditionId), controller.signal).then((data) => {
4839
+ fetchPmConditionEvents(targetUrl, dateRange, Number(cdSelectedConditionId), controller.signal).then((data) => {
4714
4840
  if (!controller.signal.aborted) {
4715
4841
  setCdConditionData(data);
4716
4842
  setCdConditionLoading(false);
@@ -4723,76 +4849,76 @@ function PmFunnelTab({ dateRange }) {
4723
4849
  }
4724
4850
  });
4725
4851
  return () => controller.abort();
4726
- }, [cdSelectedConditionId, dateRange, flowSource, refreshKey]);
4727
- const handleProductSelect = (0, import_react8.useCallback)((productName) => {
4852
+ }, [targetUrl, cdSelectedConditionId, dateRange, flowSource, refreshKey]);
4853
+ const handleProductSelect = (0, import_react9.useCallback)((productName) => {
4728
4854
  if (productName === cdSelectedProduct) return;
4729
4855
  setCdSelectedProduct(productName);
4730
4856
  setCdCartData(null);
4731
4857
  setCdCartLoading(true);
4732
- fetchPmProductCart(MIXPANEL_API_URL, dateRange, Number(cdSelectedConditionId), productName).then((data) => {
4858
+ fetchPmProductCart(targetUrl, dateRange, Number(cdSelectedConditionId), productName).then((data) => {
4733
4859
  setCdCartData(data);
4734
4860
  setCdCartLoading(false);
4735
4861
  }).catch((err) => {
4736
4862
  console.error("PM funnel cart error:", err);
4737
4863
  setCdCartLoading(false);
4738
4864
  });
4739
- }, [cdSelectedConditionId, cdSelectedProduct, dateRange]);
4740
- const handlePdCartAuthClick = (0, import_react8.useCallback)(() => {
4865
+ }, [targetUrl, cdSelectedConditionId, cdSelectedProduct, dateRange]);
4866
+ const handlePdCartAuthClick = (0, import_react9.useCallback)(() => {
4741
4867
  if (!pdSelectedFilter) return;
4742
4868
  setCartAuthModalOpen(true);
4743
4869
  setCartAuthPatientsLoading(true);
4744
4870
  setCartAuthPatientsError(null);
4745
4871
  setCartAuthPatients([]);
4746
- fetchCartAuthPatients(MIXPANEL_API_URL, dateRange, { flowSource: "product_detail", product: pdSelectedFilter }).then((records) => {
4872
+ fetchCartAuthPatients(targetUrl, dateRange, { flowSource: "product_detail", product: pdSelectedFilter }).then((records) => {
4747
4873
  setCartAuthPatients(records);
4748
4874
  setCartAuthPatientsLoading(false);
4749
4875
  }).catch((err) => {
4750
4876
  setCartAuthPatientsError(err instanceof Error ? err.message : String(err));
4751
4877
  setCartAuthPatientsLoading(false);
4752
4878
  });
4753
- }, [pdSelectedFilter, dateRange]);
4754
- const handleCdCartAuthClick = (0, import_react8.useCallback)(() => {
4879
+ }, [targetUrl, pdSelectedFilter, dateRange]);
4880
+ const handleCdCartAuthClick = (0, import_react9.useCallback)(() => {
4755
4881
  if (!cdSelectedProduct || !cdSelectedConditionId) return;
4756
4882
  setCartAuthModalOpen(true);
4757
4883
  setCartAuthPatientsLoading(true);
4758
4884
  setCartAuthPatientsError(null);
4759
4885
  setCartAuthPatients([]);
4760
- fetchCartAuthPatients(MIXPANEL_API_URL, dateRange, { flowSource: "condition_detail", product: cdSelectedProduct, conditionId: Number(cdSelectedConditionId) }).then((records) => {
4886
+ fetchCartAuthPatients(targetUrl, dateRange, { flowSource: "condition_detail", product: cdSelectedProduct, conditionId: Number(cdSelectedConditionId) }).then((records) => {
4761
4887
  setCartAuthPatients(records);
4762
4888
  setCartAuthPatientsLoading(false);
4763
4889
  }).catch((err) => {
4764
4890
  setCartAuthPatientsError(err instanceof Error ? err.message : String(err));
4765
4891
  setCartAuthPatientsLoading(false);
4766
4892
  });
4767
- }, [cdSelectedConditionId, cdSelectedProduct, dateRange]);
4768
- const handlePdAuthClick = (0, import_react8.useCallback)(() => {
4893
+ }, [targetUrl, cdSelectedConditionId, cdSelectedProduct, dateRange]);
4894
+ const handlePdAuthClick = (0, import_react9.useCallback)(() => {
4769
4895
  if (!pdSelectedFilter) return;
4770
4896
  setAuthModalOpen(true);
4771
4897
  setAuthPatientsLoading(true);
4772
4898
  setAuthPatientsError(null);
4773
4899
  setAuthPatients([]);
4774
- fetchAuthPatients(MIXPANEL_API_URL, dateRange, { flowSource: "product_detail", product: pdSelectedFilter }).then((records) => {
4900
+ fetchAuthPatients(targetUrl, dateRange, { flowSource: "product_detail", product: pdSelectedFilter }).then((records) => {
4775
4901
  setAuthPatients(records);
4776
4902
  setAuthPatientsLoading(false);
4777
4903
  }).catch((err) => {
4778
4904
  setAuthPatientsError(err instanceof Error ? err.message : String(err));
4779
4905
  setAuthPatientsLoading(false);
4780
4906
  });
4781
- }, [pdSelectedFilter, dateRange]);
4782
- const handleCdAuthClick = (0, import_react8.useCallback)(() => {
4907
+ }, [targetUrl, pdSelectedFilter, dateRange]);
4908
+ const handleCdAuthClick = (0, import_react9.useCallback)(() => {
4783
4909
  if (!cdSelectedConditionId) return;
4784
4910
  setAuthModalOpen(true);
4785
4911
  setAuthPatientsLoading(true);
4786
4912
  setAuthPatientsError(null);
4787
4913
  setAuthPatients([]);
4788
- fetchAuthPatients(MIXPANEL_API_URL, dateRange, { flowSource: "condition_detail", conditionId: Number(cdSelectedConditionId) }).then((records) => {
4914
+ fetchAuthPatients(targetUrl, dateRange, { flowSource: "condition_detail", conditionId: Number(cdSelectedConditionId) }).then((records) => {
4789
4915
  setAuthPatients(records);
4790
4916
  setAuthPatientsLoading(false);
4791
4917
  }).catch((err) => {
4792
4918
  setAuthPatientsError(err instanceof Error ? err.message : String(err));
4793
4919
  setAuthPatientsLoading(false);
4794
4920
  });
4795
- }, [cdSelectedConditionId, dateRange]);
4921
+ }, [targetUrl, cdSelectedConditionId, dateRange]);
4796
4922
  const pdFunnelEvents = [...PM_PRODUCT_DETAIL_EVENTS];
4797
4923
  const pdAuthMethods = pdEventsData?.paramBreakdowns?.auth_completed?.method ?? {};
4798
4924
  const pdGuestCount = (pdAuthMethods["guest"] ?? 0) + (pdAuthMethods["Guest"] ?? 0);
@@ -5102,13 +5228,13 @@ function PmFunnelTab({ dateRange }) {
5102
5228
  }
5103
5229
 
5104
5230
  // src/studio/search-console/search-console-dashboard.tsx
5105
- var import_react10 = require("react");
5231
+ var import_react11 = require("react");
5106
5232
  var import_recharts5 = require("recharts");
5107
5233
  var import_lucide_react2 = require("lucide-react");
5108
5234
 
5109
5235
  // src/studio/search-console/dashboard-ui.tsx
5110
- var import_react9 = require("react");
5111
- var import_react_dom = require("react-dom");
5236
+ var import_react10 = require("react");
5237
+ var import_react_dom2 = require("react-dom");
5112
5238
  var import_lucide_react = require("lucide-react");
5113
5239
  var import_recharts4 = require("recharts");
5114
5240
  var import_jsx_runtime16 = require("react/jsx-runtime");
@@ -5994,16 +6120,20 @@ function ErrorState({
5994
6120
  ] });
5995
6121
  }
5996
6122
  function InfoIcon2({ text }) {
5997
- const triggerRef = (0, import_react9.useRef)(null);
5998
- const [tip, setTip] = (0, import_react9.useState)(null);
6123
+ const triggerRef = (0, import_react10.useRef)(null);
6124
+ const [tip, setTip] = (0, import_react10.useState)(null);
5999
6125
  const show = () => {
6000
6126
  const el = triggerRef.current;
6001
6127
  if (!el) return;
6002
6128
  const rect = el.getBoundingClientRect();
6003
6129
  const placeBelow = rect.top < 120;
6130
+ const tipWidth = 230;
6131
+ const pad = 12;
6132
+ const centerX = rect.left + rect.width / 2;
6133
+ const clampedLeft = typeof window !== "undefined" ? Math.max(tipWidth / 2 + pad, Math.min(centerX, window.innerWidth - tipWidth / 2 - pad)) : centerX;
6004
6134
  setTip({
6005
6135
  top: placeBelow ? rect.bottom + 8 : rect.top - 8,
6006
- left: rect.left + rect.width / 2,
6136
+ left: clampedLeft,
6007
6137
  placement: placeBelow ? "bottom" : "top"
6008
6138
  });
6009
6139
  };
@@ -6021,7 +6151,7 @@ function InfoIcon2({ text }) {
6021
6151
  children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_lucide_react.Info, { size: 12 })
6022
6152
  }
6023
6153
  ),
6024
- tip && typeof document !== "undefined" && (0, import_react_dom.createPortal)(
6154
+ tip && typeof document !== "undefined" && (0, import_react_dom2.createPortal)(
6025
6155
  /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
6026
6156
  "span",
6027
6157
  {
@@ -6293,9 +6423,26 @@ function ScOverviewTab({ data, loading }) {
6293
6423
  /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("stop", { offset: "95%", stopColor: color, stopOpacity: 0 })
6294
6424
  ] }, id)) }),
6295
6425
  /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_recharts5.CartesianGrid, { strokeDasharray: "4 6", stroke: CHART_GRID }),
6296
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_recharts5.XAxis, { dataKey: "displayDate", ...chartAxis }),
6426
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
6427
+ import_recharts5.XAxis,
6428
+ {
6429
+ dataKey: "date",
6430
+ tickFormatter: formatDateTick,
6431
+ minTickGap: 28,
6432
+ interval: evenTickInterval(clicksSeries?.length ?? 0),
6433
+ padding: { left: 2, right: 15 },
6434
+ ...chartAxis
6435
+ }
6436
+ ),
6297
6437
  /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_recharts5.YAxis, { ...chartAxis, width: 38 }),
6298
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_recharts5.Tooltip, { contentStyle: chartTooltipStyle, cursor: { stroke: "#cbd5e1", strokeDasharray: "4 4" } }),
6438
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
6439
+ import_recharts5.Tooltip,
6440
+ {
6441
+ labelFormatter: formatTooltipDate,
6442
+ contentStyle: chartTooltipStyle,
6443
+ cursor: { stroke: "#cbd5e1", strokeDasharray: "4 4" }
6444
+ }
6445
+ ),
6299
6446
  /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_recharts5.Legend, { wrapperStyle: { fontSize: 12, fontWeight: 700, paddingTop: 10 } }),
6300
6447
  /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_recharts5.Area, { type: "monotone", dataKey: "clicks", name: "Clicks", stroke: "#0f9f6e", fill: "url(#scClicks)", strokeWidth: 2.4, dot: false }),
6301
6448
  /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_recharts5.Area, { type: "monotone", dataKey: "impressions", name: "Impressions", stroke: "#6366f1", fill: "url(#scImpressions)", strokeWidth: 2.4, dot: false })
@@ -6486,9 +6633,9 @@ function ScSitemapsTab({
6486
6633
  onRefreshData,
6487
6634
  loading
6488
6635
  }) {
6489
- const [submitting, setSubmitting] = (0, import_react10.useState)(null);
6490
- const [submitMsg, setSubmitMsg] = (0, import_react10.useState)(null);
6491
- const [newSitemapUrl, setNewSitemapUrl] = (0, import_react10.useState)("");
6636
+ const [submitting, setSubmitting] = (0, import_react11.useState)(null);
6637
+ const [submitMsg, setSubmitMsg] = (0, import_react11.useState)(null);
6638
+ const [newSitemapUrl, setNewSitemapUrl] = (0, import_react11.useState)("");
6492
6639
  const totalSubmitted = data.sitemaps.reduce((sum, sitemap) => sum + sitemap.contents.reduce((contentSum, content) => contentSum + content.submitted, 0), 0);
6493
6640
  const totalIndexed = data.sitemaps.reduce((sum, sitemap) => sum + sitemap.contents.reduce((contentSum, content) => contentSum + content.indexed, 0), 0);
6494
6641
  const indexRate = totalSubmitted > 0 ? totalIndexed / totalSubmitted : 0;
@@ -6623,25 +6770,25 @@ function SearchConsoleDashboard({
6623
6770
  activeTab: controlledTab,
6624
6771
  onTabChange
6625
6772
  }) {
6626
- const [data, setData] = (0, import_react10.useState)(null);
6627
- const [loading, setLoading] = (0, import_react10.useState)(true);
6628
- const [error, setError] = (0, import_react10.useState)(null);
6629
- const [dateRange, setDateRange] = (0, import_react10.useState)("28");
6630
- const [uncontrolledTab, setUncontrolledTab] = (0, import_react10.useState)(initialTab);
6773
+ const [data, setData] = (0, import_react11.useState)(null);
6774
+ const [loading, setLoading] = (0, import_react11.useState)(true);
6775
+ const [error, setError] = (0, import_react11.useState)(null);
6776
+ const [dateRange, setDateRange] = (0, import_react11.useState)("28");
6777
+ const [uncontrolledTab, setUncontrolledTab] = (0, import_react11.useState)(initialTab);
6631
6778
  const activeTab = controlledTab !== void 0 ? controlledTab : uncontrolledTab;
6632
- const handleTabChange = (0, import_react10.useCallback)(
6779
+ const handleTabChange = (0, import_react11.useCallback)(
6633
6780
  (nextTab) => {
6634
6781
  setUncontrolledTab(nextTab);
6635
6782
  onTabChange?.(nextTab);
6636
6783
  },
6637
6784
  [onTabChange]
6638
6785
  );
6639
- (0, import_react10.useEffect)(() => {
6786
+ (0, import_react11.useEffect)(() => {
6640
6787
  if (initialTab) {
6641
6788
  setUncontrolledTab(initialTab);
6642
6789
  }
6643
6790
  }, [initialTab]);
6644
- const loadData = (0, import_react10.useCallback)(async () => {
6791
+ const loadData = (0, import_react11.useCallback)(async () => {
6645
6792
  try {
6646
6793
  setLoading(true);
6647
6794
  setError(null);
@@ -6653,7 +6800,7 @@ function SearchConsoleDashboard({
6653
6800
  setLoading(false);
6654
6801
  }
6655
6802
  }, [apiUrl, dateRange]);
6656
- (0, import_react10.useEffect)(() => {
6803
+ (0, import_react11.useEffect)(() => {
6657
6804
  loadData();
6658
6805
  }, [loadData]);
6659
6806
  if (loading && !data) {
@@ -7199,7 +7346,10 @@ function CombinedOverviewTab({
7199
7346
  import_recharts6.XAxis,
7200
7347
  {
7201
7348
  dataKey: "date",
7202
- tickFormatter: (v) => v.slice(5).replace("-", "/"),
7349
+ tickFormatter: formatDateTick,
7350
+ minTickGap: 28,
7351
+ interval: evenTickInterval(scTimeSeries?.length ?? 0),
7352
+ padding: { left: 2, right: 15 },
7203
7353
  tick: { fontSize: 10, fill: "#94a3b8" },
7204
7354
  axisLine: false,
7205
7355
  tickLine: false
@@ -7209,6 +7359,7 @@ function CombinedOverviewTab({
7209
7359
  /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
7210
7360
  import_recharts6.Tooltip,
7211
7361
  {
7362
+ labelFormatter: formatTooltipDate,
7212
7363
  contentStyle: {
7213
7364
  background: "#1e293b",
7214
7365
  border: "none",
@@ -7248,7 +7399,10 @@ function CombinedOverviewTab({
7248
7399
  import_recharts6.XAxis,
7249
7400
  {
7250
7401
  dataKey: "date",
7251
- tickFormatter: (v) => v.length === 8 ? `${v.slice(4, 6)}/${v.slice(6, 8)}` : v,
7402
+ tickFormatter: formatDateTick,
7403
+ minTickGap: 28,
7404
+ interval: evenTickInterval(gaTimeSeries?.length ?? 0),
7405
+ padding: { left: 2, right: 15 },
7252
7406
  tick: { fontSize: 10, fill: "#94a3b8" },
7253
7407
  axisLine: false,
7254
7408
  tickLine: false
@@ -7258,6 +7412,7 @@ function CombinedOverviewTab({
7258
7412
  /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
7259
7413
  import_recharts6.Tooltip,
7260
7414
  {
7415
+ labelFormatter: formatTooltipDate,
7261
7416
  contentStyle: {
7262
7417
  background: "#1e293b",
7263
7418
  border: "none",
@@ -7399,25 +7554,27 @@ function mapToScRange(range) {
7399
7554
  function UnifiedDashboard({
7400
7555
  apiUrl = "/api/analytics",
7401
7556
  scApiUrl,
7557
+ mixpanelApiUrl,
7402
7558
  brandName = "Pharmacy Analytics"
7403
7559
  }) {
7404
- const [activeMainSection, setActiveMainSection] = (0, import_react11.useState)("all");
7405
- const [activeTab, setActiveTab] = (0, import_react11.useState)("all-overview");
7406
- const [scActiveTab, setScActiveTab] = (0, import_react11.useState)("overview");
7407
- const [dateRange, setDateRange] = (0, import_react11.useState)("30");
7408
- const [gaOpen, setGaOpen] = (0, import_react11.useState)(true);
7409
- const [customOpen, setCustomOpen] = (0, import_react11.useState)(true);
7410
- const [scOpen, setScOpen] = (0, import_react11.useState)(true);
7411
- const [gaData, setGaData] = (0, import_react11.useState)(null);
7412
- const [scData, setScData] = (0, import_react11.useState)(null);
7413
- const [loadingGa, setLoadingGa] = (0, import_react11.useState)(true);
7414
- const [loadingSc, setLoadingSc] = (0, import_react11.useState)(true);
7415
- const [gaError, setGaError] = (0, import_react11.useState)(null);
7416
- const [scError, setScError] = (0, import_react11.useState)(null);
7417
- const gaAbortRef = (0, import_react11.useRef)(null);
7418
- const scAbortRef = (0, import_react11.useRef)(null);
7560
+ const [activeMainSection, setActiveMainSection] = (0, import_react12.useState)("all");
7561
+ const [activeTab, setActiveTab] = (0, import_react12.useState)("all-overview");
7562
+ const [scActiveTab, setScActiveTab] = (0, import_react12.useState)("overview");
7563
+ const [dateRange, setDateRange] = (0, import_react12.useState)("30");
7564
+ const [gaOpen, setGaOpen] = (0, import_react12.useState)(true);
7565
+ const [customOpen, setCustomOpen] = (0, import_react12.useState)(true);
7566
+ const [scOpen, setScOpen] = (0, import_react12.useState)(true);
7567
+ const isAll = activeMainSection === "all";
7568
+ const [gaData, setGaData] = (0, import_react12.useState)(null);
7569
+ const [scData, setScData] = (0, import_react12.useState)(null);
7570
+ const [loadingGa, setLoadingGa] = (0, import_react12.useState)(true);
7571
+ const [loadingSc, setLoadingSc] = (0, import_react12.useState)(true);
7572
+ const [gaError, setGaError] = (0, import_react12.useState)(null);
7573
+ const [scError, setScError] = (0, import_react12.useState)(null);
7574
+ const gaAbortRef = (0, import_react12.useRef)(null);
7575
+ const scAbortRef = (0, import_react12.useRef)(null);
7419
7576
  const effectiveScUrl = scApiUrl || apiUrl;
7420
- const loadGaData = (0, import_react11.useCallback)(async () => {
7577
+ const loadGaData = (0, import_react12.useCallback)(async () => {
7421
7578
  gaAbortRef.current?.abort();
7422
7579
  const controller = new AbortController();
7423
7580
  gaAbortRef.current = controller;
@@ -7434,7 +7591,7 @@ function UnifiedDashboard({
7434
7591
  if (!controller.signal.aborted) setLoadingGa(false);
7435
7592
  }
7436
7593
  }, [apiUrl, dateRange]);
7437
- const loadScData = (0, import_react11.useCallback)(async () => {
7594
+ const loadScData = (0, import_react12.useCallback)(async () => {
7438
7595
  scAbortRef.current?.abort();
7439
7596
  const controller = new AbortController();
7440
7597
  scAbortRef.current = controller;
@@ -7452,11 +7609,11 @@ function UnifiedDashboard({
7452
7609
  if (!controller.signal.aborted) setLoadingSc(false);
7453
7610
  }
7454
7611
  }, [effectiveScUrl, dateRange]);
7455
- const loadAllData = (0, import_react11.useCallback)(() => {
7612
+ const loadAllData = (0, import_react12.useCallback)(() => {
7456
7613
  loadGaData();
7457
7614
  loadScData();
7458
7615
  }, [loadGaData, loadScData]);
7459
- (0, import_react11.useEffect)(() => {
7616
+ (0, import_react12.useEffect)(() => {
7460
7617
  loadAllData();
7461
7618
  return () => {
7462
7619
  gaAbortRef.current?.abort();
@@ -7813,6 +7970,7 @@ function UnifiedDashboard({
7813
7970
  /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
7814
7971
  "button",
7815
7972
  {
7973
+ disabled: !isAll,
7816
7974
  onClick: () => setGaOpen((o) => !o),
7817
7975
  style: {
7818
7976
  display: "flex",
@@ -7823,7 +7981,7 @@ function UnifiedDashboard({
7823
7981
  borderRadius: 6,
7824
7982
  background: "transparent",
7825
7983
  border: "none",
7826
- cursor: "pointer",
7984
+ cursor: isAll ? "pointer" : "default",
7827
7985
  marginBottom: 2
7828
7986
  },
7829
7987
  children: [
@@ -7843,11 +8001,11 @@ function UnifiedDashboard({
7843
8001
  children: "Google Analytics"
7844
8002
  }
7845
8003
  ),
7846
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { style: { fontSize: 11, color: "#94a3b8" }, children: gaOpen ? "\u25BE" : "\u25B8" })
8004
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { style: { fontSize: 11, color: "#94a3b8" }, children: isAll ? gaOpen ? "\u25BE" : "\u25B8" : null })
7847
8005
  ]
7848
8006
  }
7849
8007
  ),
7850
- gaOpen && GA_SUB_TABS.map((tab) => {
8008
+ (gaOpen || !isAll) && GA_SUB_TABS.map((tab) => {
7851
8009
  const active = activeTab === tab.id;
7852
8010
  return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
7853
8011
  "button",
@@ -7882,6 +8040,7 @@ function UnifiedDashboard({
7882
8040
  /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
7883
8041
  "button",
7884
8042
  {
8043
+ disabled: !isAll,
7885
8044
  onClick: () => setCustomOpen((o) => !o),
7886
8045
  style: {
7887
8046
  display: "flex",
@@ -7892,7 +8051,7 @@ function UnifiedDashboard({
7892
8051
  borderRadius: 6,
7893
8052
  background: "transparent",
7894
8053
  border: "none",
7895
- cursor: "pointer",
8054
+ cursor: isAll ? "pointer" : "default",
7896
8055
  marginBottom: 2
7897
8056
  },
7898
8057
  children: [
@@ -7912,11 +8071,11 @@ function UnifiedDashboard({
7912
8071
  children: "Custom Analytics"
7913
8072
  }
7914
8073
  ),
7915
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { style: { fontSize: 11, color: "#94a3b8" }, children: customOpen ? "\u25BE" : "\u25B8" })
8074
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { style: { fontSize: 11, color: "#94a3b8" }, children: isAll ? customOpen ? "\u25BE" : "\u25B8" : null })
7916
8075
  ]
7917
8076
  }
7918
8077
  ),
7919
- customOpen && CUSTOM_SUB_TABS.map((tab) => {
8078
+ (customOpen || !isAll) && CUSTOM_SUB_TABS.map((tab) => {
7920
8079
  const active = activeTab === tab.id;
7921
8080
  return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
7922
8081
  "button",
@@ -7951,6 +8110,7 @@ function UnifiedDashboard({
7951
8110
  /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
7952
8111
  "button",
7953
8112
  {
8113
+ disabled: !isAll,
7954
8114
  onClick: () => setScOpen((o) => !o),
7955
8115
  style: {
7956
8116
  display: "flex",
@@ -7961,7 +8121,7 @@ function UnifiedDashboard({
7961
8121
  borderRadius: 6,
7962
8122
  background: "transparent",
7963
8123
  border: "none",
7964
- cursor: "pointer",
8124
+ cursor: isAll ? "pointer" : "default",
7965
8125
  marginBottom: 2
7966
8126
  },
7967
8127
  children: [
@@ -7981,11 +8141,11 @@ function UnifiedDashboard({
7981
8141
  children: "Search Console"
7982
8142
  }
7983
8143
  ),
7984
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { style: { fontSize: 11, color: "#94a3b8" }, children: scOpen ? "\u25BE" : "\u25B8" })
8144
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { style: { fontSize: 11, color: "#94a3b8" }, children: isAll ? scOpen ? "\u25BE" : "\u25B8" : null })
7985
8145
  ]
7986
8146
  }
7987
8147
  ),
7988
- scOpen && SC_SUB_TABS.map((tab) => {
8148
+ (scOpen || !isAll) && SC_SUB_TABS.map((tab) => {
7989
8149
  const isScActive = activeTab === tab.id;
7990
8150
  return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
7991
8151
  "button",
@@ -8063,6 +8223,7 @@ function UnifiedDashboard({
8063
8223
  flex: 1,
8064
8224
  minHeight: 0,
8065
8225
  overflowY: "auto",
8226
+ overflowX: "hidden",
8066
8227
  padding: "24px 28px"
8067
8228
  },
8068
8229
  children: [
@@ -8117,9 +8278,9 @@ function UnifiedDashboard({
8117
8278
  activeTab === "geography" && gaData && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(GeographyTab, { data: gaData, loading: loadingGa }),
8118
8279
  activeTab === "events" && gaData && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(EventsTab, { data: gaData, loading: loadingGa, apiUrl, dateRange }),
8119
8280
  activeTab === "acquisition" && gaData && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(AcquisitionTab, { data: gaData, loading: loadingGa }),
8120
- activeTab === "conditions-products" && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(ConditionsProductsTab, { apiUrl, dateRange }),
8121
- activeTab === "funnel" && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(FunnelEventsTab, { apiUrl, dateRange }),
8122
- activeTab === "pm-funnel" && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(PmFunnelTab, { apiUrl, dateRange }),
8281
+ activeTab === "conditions-products" && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(ConditionsProductsTab, { apiUrl: mixpanelApiUrl, dateRange }),
8282
+ activeTab === "funnel" && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(FunnelEventsTab, { apiUrl: mixpanelApiUrl, dateRange }),
8283
+ activeTab === "pm-funnel" && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(PmFunnelTab, { apiUrl: mixpanelApiUrl, dateRange }),
8123
8284
  activeTab.startsWith("sc-") && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
8124
8285
  SearchConsoleDashboard,
8125
8286
  {
@@ -8159,6 +8320,7 @@ function pharmacyAnalyticsPlugin(config = {}) {
8159
8320
  {
8160
8321
  apiUrl: config.apiUrl || "/api/analytics",
8161
8322
  scApiUrl: config.scApiUrl,
8323
+ mixpanelApiUrl: config.mixpanelApiUrl,
8162
8324
  brandName: toolTitle
8163
8325
  }
8164
8326
  );