@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.
@@ -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 useState8, useEffect as useEffect7, useCallback as useCallback5, useRef as useRef3 } from "react";
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); left:50%; transform:translateX(-50%);
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%; left:50%; transform:translateX(-50%);
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
- return /* @__PURE__ */ jsxs("span", { className: "ga-tooltip-wrap", children: [
346
- /* @__PURE__ */ jsx("button", { className: "ga-info-btn", tabIndex: -1, children: "i" }),
347
- /* @__PURE__ */ jsx("span", { className: "ga-tip", children: text })
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: "displayDate",
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] = useState(null);
1145
- const [paramRows, setParamRows] = useState([]);
1146
- const [paramsLoading, setParamsLoading] = useState(false);
1147
- const [paramsError, setParamsError] = useState(null);
1148
- const [conditionValues, setConditionValues] = useState([]);
1149
- const [selectedCondition, setSelectedCondition] = useState(null);
1150
- const [filteredEvents, setFilteredEvents] = useState(null);
1151
- const [filterLoading, setFilterLoading] = useState(false);
1152
- const [pharmacyNameMap, setPharmacyNameMap] = useState({});
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 useState2 } from "react";
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
- if (!res.ok) {
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
- if (!res.ok) {
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 [subTab, setSubTab] = useState2("conditions");
1703
- const [data, setData] = useState2(null);
1704
- const [loading, setLoading] = useState2(true);
1705
- const [error, setError] = useState2(null);
1706
- const [fetchTick] = useState2(0);
1707
- const [topConditionSearches, setTopConditionSearches] = useState2(null);
1708
- const [topConditionSearchesLoading, setTopConditionSearchesLoading] = useState2(true);
1709
- const [conditionSearchVisible, setConditionSearchVisible] = useState2(20);
1710
- const [topProductSearches, setTopProductSearches] = useState2(null);
1711
- const [topProductSearchesLoading, setTopProductSearchesLoading] = useState2(true);
1712
- const [productSearchVisible, setProductSearchVisible] = useState2(20);
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(MIXPANEL_API_URL, dateRange, "condition", controller.signal).then((res) => {
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(MIXPANEL_API_URL, dateRange, "product", controller.signal).then((res) => {
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(MIXPANEL_API_URL, dateRange, void 0, controller.signal).then((res) => {
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 useState4, useEffect as useEffect4, useMemo as useMemo7, useCallback as useCallback2 } from "react";
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 useState3, useEffect as useEffect3, useRef, useMemo as useMemo4, useCallback } from "react";
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] = useState3(false);
2231
- const [search, setSearch] = useState3("");
2232
- const ref = useRef(null);
2233
- const searchRef = useRef(null);
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 React5, { useMemo as useMemo5 } from "react";
2600
+ import React6, { useMemo as useMemo5 } from "react";
2477
2601
  import { jsx as jsx11, jsxs as jsxs11 } from "react/jsx-runtime";
2478
- var PatientModal = React5.memo(function PatientModal2({
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 [loading, setLoading] = useState4(false);
3606
- const [error, setError] = useState4(null);
3607
- const [refreshKey, setRefreshKey] = useState4(0);
3608
- const [clearing, setClearing] = useState4(false);
3609
- const [conditionOptions, setConditionOptions] = useState4([]);
3610
- const [selectedCondition, setSelectedCondition] = useState4("");
3611
- const [conditionsLoading, setConditionsLoading] = useState4(true);
3612
- const [rows, setRows] = useState4([]);
3613
- const [qfsFlows, setQfsFlows] = useState4([]);
3614
- const [paramBreakdowns, setParamBreakdowns] = useState4({});
3615
- const [qfsStepBreakdowns, setQfsStepBreakdowns] = useState4({});
3616
- const [conditionServiceMap, setConditionServiceMap] = useState4({});
3617
- const [patientModalOpen, setPatientModalOpen] = useState4(false);
3618
- const [patientRecords, setPatientRecords] = useState4([]);
3619
- const [patientsLoading, setPatientsLoading] = useState4(false);
3620
- const [patientsError, setPatientsError] = useState4(null);
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(MIXPANEL_API_URL, dateRange, selectedCondition).then((records) => {
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(MIXPANEL_API_URL, dateRange, void 0, true, controller.signal).then((values) => {
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(MIXPANEL_API_URL, dateRange, selectedCondition, void 0, controller.signal).then((data) => {
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(MIXPANEL_API_URL);
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 useState5, useEffect as useEffect5, useCallback as useCallback3 } from "react";
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 [flowSource, setFlowSource] = useState5("product_detail");
4554
- const [refreshKey, setRefreshKey] = useState5(0);
4555
- const [clearing, setClearing] = useState5(false);
4556
- const [pdFilterOptions, setPdFilterOptions] = useState5([]);
4557
- const [pdSelectedFilter, setPdSelectedFilter] = useState5("");
4558
- const [pdOptionsLoading, setPdOptionsLoading] = useState5(true);
4559
- const [pdEventsData, setPdEventsData] = useState5(null);
4560
- const [pdLoading, setPdLoading] = useState5(false);
4561
- const [pdError, setPdError] = useState5(null);
4562
- const [cdConditionOptions, setCdConditionOptions] = useState5([]);
4563
- const [cdSelectedConditionId, setCdSelectedConditionId] = useState5("");
4564
- const [cdOptionsLoading, setCdOptionsLoading] = useState5(true);
4565
- const [cdConditionData, setCdConditionData] = useState5(null);
4566
- const [cdConditionLoading, setCdConditionLoading] = useState5(false);
4567
- const [cdConditionError, setCdConditionError] = useState5(null);
4568
- const [cdSelectedProduct, setCdSelectedProduct] = useState5(null);
4569
- const [cdCartData, setCdCartData] = useState5(null);
4570
- const [cdCartLoading, setCdCartLoading] = useState5(false);
4571
- const [cartAuthModalOpen, setCartAuthModalOpen] = useState5(false);
4572
- const [cartAuthPatients, setCartAuthPatients] = useState5([]);
4573
- const [cartAuthPatientsLoading, setCartAuthPatientsLoading] = useState5(false);
4574
- const [cartAuthPatientsError, setCartAuthPatientsError] = useState5(null);
4575
- const [authModalOpen, setAuthModalOpen] = useState5(false);
4576
- const [authPatients, setAuthPatients] = useState5([]);
4577
- const [authPatientsLoading, setAuthPatientsLoading] = useState5(false);
4578
- const [authPatientsError, setAuthPatientsError] = useState5(null);
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(MIXPANEL_API_URL);
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(MIXPANEL_API_URL, dateRange, "product_detail", controller.signal).then((values) => {
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(MIXPANEL_API_URL, dateRange, pdSelectedFilter, "product_detail", controller.signal).then((data) => {
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(MIXPANEL_API_URL, dateRange, controller.signal),
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(MIXPANEL_API_URL, dateRange, Number(cdSelectedConditionId), controller.signal).then((data) => {
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(MIXPANEL_API_URL, dateRange, Number(cdSelectedConditionId), productName).then((data) => {
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(MIXPANEL_API_URL, dateRange, { flowSource: "product_detail", product: pdSelectedFilter }).then((records) => {
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(MIXPANEL_API_URL, dateRange, { flowSource: "condition_detail", product: cdSelectedProduct, conditionId: Number(cdSelectedConditionId) }).then((records) => {
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(MIXPANEL_API_URL, dateRange, { flowSource: "product_detail", product: pdSelectedFilter }).then((records) => {
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(MIXPANEL_API_URL, dateRange, { flowSource: "condition_detail", conditionId: Number(cdSelectedConditionId) }).then((records) => {
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 useState7 } from "react";
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 useRef2, useState as useState6 } from "react";
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 = useRef2(null);
5979
- const [tip, setTip] = useState6(null);
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: rect.left + rect.width / 2,
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" && createPortal(
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(XAxis3, { dataKey: "displayDate", ...chartAxis }),
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(Tooltip5, { contentStyle: chartTooltipStyle, cursor: { stroke: "#cbd5e1", strokeDasharray: "4 4" } }),
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] = useState7(null);
6471
- const [submitMsg, setSubmitMsg] = useState7(null);
6472
- const [newSitemapUrl, setNewSitemapUrl] = useState7("");
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] = useState7(null);
6608
- const [loading, setLoading] = useState7(true);
6609
- const [error, setError] = useState7(null);
6610
- const [dateRange, setDateRange] = useState7("28");
6611
- const [uncontrolledTab, setUncontrolledTab] = useState7(initialTab);
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: (v) => v.slice(5).replace("-", "/"),
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: (v) => v.length === 8 ? `${v.slice(4, 6)}/${v.slice(6, 8)}` : v,
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] = useState8("all");
7400
- const [activeTab, setActiveTab] = useState8("all-overview");
7401
- const [scActiveTab, setScActiveTab] = useState8("overview");
7402
- const [dateRange, setDateRange] = useState8("30");
7403
- const [gaOpen, setGaOpen] = useState8(true);
7404
- const [customOpen, setCustomOpen] = useState8(true);
7405
- const [scOpen, setScOpen] = useState8(true);
7406
- const [gaData, setGaData] = useState8(null);
7407
- const [scData, setScData] = useState8(null);
7408
- const [loadingGa, setLoadingGa] = useState8(true);
7409
- const [loadingSc, setLoadingSc] = useState8(true);
7410
- const [gaError, setGaError] = useState8(null);
7411
- const [scError, setScError] = useState8(null);
7412
- const gaAbortRef = useRef3(null);
7413
- const scAbortRef = useRef3(null);
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
  );