hazo_umetrics 1.7.1 → 1.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGE_LOG.md CHANGED
@@ -1,5 +1,26 @@
1
1
  # hazo_umetrics — Change Log
2
2
 
3
+ ## 1.8.0 — Journey: anonymous-session bucket, session drilldown, rich user dropdown
4
+
5
+ **New features (minor — no breaking changes):**
6
+
7
+ ### `userJourney` — `anonymous` filter
8
+
9
+ - New `anonymous?: boolean` option on `userJourney(adapter, opts)` in `src/server/behavior.ts`.
10
+ - When true, keeps only sessions with **no** `user_id` (anonymous visitors); mutually exclusive with `user_id`. The aggregated graph then reflects anonymous traffic only.
11
+
12
+ ### `getJourney` API handler — `anonymous=1`
13
+
14
+ - `GET analytics/journey` now parses `anonymous=1` and passes it through to `userJourney`.
15
+ - Existing `user_id` / `session_id` params unchanged.
16
+
17
+ ### `JourneyPanel` — anonymous bucket, drilldown, avatar+email dropdown
18
+
19
+ - The user filter is now a custom dropdown (replaces the native `<select>`): renders **profile picture + email** per logged-in user with a session count, plus an **`Anonymous (N)`** bucket that filters to `anonymous=1`.
20
+ - Options and counts come from an identity summary computed on the all-users fetch, so they stay stable while a single identity is selected.
21
+ - New **session drilldown**: once a user or the anonymous bucket is selected, a second picker lists that group's individual sessions (short `session_id` + step count) and filters to one via the existing `session_id` param.
22
+ - No prop or public-type changes — `JourneyPanelProps` and `resolveUserProfiles` are unchanged, so consumers upgrade with no code changes.
23
+
3
24
  ## 1.7.0 — Search query analytics: `searchQueries`, `SearchQueriesPanel`, `getIdentity`
4
25
 
5
26
  **New features (minor — no breaking changes):**
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/api/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/api/index.ts"],"names":[],"mappings":"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"}
package/dist/api/index.js CHANGED
@@ -858,6 +858,7 @@ export function createBehaviorHandlers(options) {
858
858
  const app_id = url.searchParams.get('app_id') ?? '';
859
859
  const user_id = url.searchParams.get('user_id') ?? undefined;
860
860
  const session_id = url.searchParams.get('session_id') ?? undefined;
861
+ const anonymous = url.searchParams.get('anonymous') === '1';
861
862
  const since = url.searchParams.get('since') ?? undefined;
862
863
  const splitByPage = url.searchParams.get('split_by_page') === '1';
863
864
  const pageFlow = url.searchParams.get('page_flow') === '1';
@@ -873,7 +874,7 @@ export function createBehaviorHandlers(options) {
873
874
  const adapter = await options.getAdapter();
874
875
  const catalog = await _listActions(adapter, { app_id });
875
876
  const catalogMap = new Map(catalog.map((a) => [a.action_key, { label: a.label, datapoint_schema: a.datapoint_schema }]));
876
- const result = await _userJourney(adapter, { app_id, user_id, session_id, since, catalogMap, splitByPage, pageFlow });
877
+ const result = await _userJourney(adapter, { app_id, user_id, session_id, anonymous, since, catalogMap, splitByPage, pageFlow });
877
878
  return jsonOk(result);
878
879
  },
879
880
  /**
@@ -108,6 +108,9 @@ export declare function userJourney(adapter: HazoConnectAdapter, opts: {
108
108
  app_id: string;
109
109
  user_id?: string;
110
110
  session_id?: string;
111
+ /** When true, keep only sessions with NO user_id (anonymous visitors).
112
+ * Mutually exclusive with user_id — the graph aggregates anon traffic only. */
113
+ anonymous?: boolean;
111
114
  since?: string;
112
115
  until?: string;
113
116
  catalogMap?: Map<string, {
@@ -1 +1 @@
1
- {"version":3,"file":"behavior.d.ts","sourceRoot":"","sources":["../../src/server/behavior.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"behavior.d.ts","sourceRoot":"","sources":["../../src/server/behavior.ts"],"names":[],"mappings":"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"}
@@ -374,6 +374,9 @@ export async function userJourney(adapter, opts) {
374
374
  // Drop rows with neither user_id nor session_id
375
375
  if (!uid && !sid)
376
376
  continue;
377
+ // Apply anonymous filter: keep only rows with no user_id
378
+ if (opts.anonymous && uid != null)
379
+ continue;
377
380
  // Apply user_id filter
378
381
  if (opts.user_id != null && uid !== opts.user_id)
379
382
  continue;
@@ -1 +1 @@
1
- {"version":3,"file":"journey_panel.d.ts","sourceRoot":"","sources":["../../src/ui/journey_panel.tsx"],"names":[],"mappings":"AAiBA,MAAM,WAAW,WAAW;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,QAAQ,GAAG,UAAU,GAAG,OAAO,GAAG,OAAO,GAAG,OAAO,CAAC;IAC1D,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,CAAC;IACtC,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,CAAC;IAC5C,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,cAAc;IAC7B,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,WAAW,EAAE,CAAC;CACtB;AAED,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,QAAQ,GAAG,UAAU,GAAG,OAAO,GAAG,OAAO,GAAG,OAAO,CAAC;IAC1D,KAAK,EAAE,MAAM,CAAC;IACd,sDAAsD;IACtD,KAAK,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAChD;AAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,gBAAgB,EAAE,CAAC;IAC1B,KAAK,EAAE,gBAAgB,EAAE,CAAC;CAC3B;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,cAAc,EAAE,CAAC;IAC3B,KAAK,EAAE,YAAY,CAAC;CACrB;AAMD,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,mBAAmB,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,OAAO,CAAC,KAAK,CAAC;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,mBAAmB,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC,CAAC;IAC1I,iGAAiG;IACjG,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,oFAAoF;IACpF,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;AA4kBD,wBAAgB,YAAY,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,mBAAmB,EAAE,cAAqB,EAAE,YAAmB,EAAE,EAAE,iBAAiB,+BAiLrJ"}
1
+ {"version":3,"file":"journey_panel.d.ts","sourceRoot":"","sources":["../../src/ui/journey_panel.tsx"],"names":[],"mappings":"AAiBA,MAAM,WAAW,WAAW;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,QAAQ,GAAG,UAAU,GAAG,OAAO,GAAG,OAAO,GAAG,OAAO,CAAC;IAC1D,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,CAAC;IACtC,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,CAAC;IAC5C,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,cAAc;IAC7B,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,WAAW,EAAE,CAAC;CACtB;AAED,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,QAAQ,GAAG,UAAU,GAAG,OAAO,GAAG,OAAO,GAAG,OAAO,CAAC;IAC1D,KAAK,EAAE,MAAM,CAAC;IACd,sDAAsD;IACtD,KAAK,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAChD;AAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,gBAAgB,EAAE,CAAC;IAC1B,KAAK,EAAE,gBAAgB,EAAE,CAAC;CAC3B;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,cAAc,EAAE,CAAC;IAC3B,KAAK,EAAE,YAAY,CAAC;CACrB;AAMD,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,mBAAmB,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,OAAO,CAAC,KAAK,CAAC;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,mBAAmB,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC,CAAC;IAC1I,iGAAiG;IACjG,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,oFAAoF;IACpF,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;AAsrBD,wBAAgB,YAAY,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,mBAAmB,EAAE,cAAqB,EAAE,YAAmB,EAAE,EAAE,iBAAiB,+BAiNrJ"}
@@ -332,6 +332,52 @@ function SessionDetail({ session, index, profile }) {
332
332
  return (_jsxs("div", { className: "rounded-lg border bg-white", children: [_jsxs("button", { type: "button", onClick: () => setOpen((v) => !v), className: "w-full flex items-center gap-2 px-4 py-3 text-left hover:bg-gray-50 transition-colors", children: [profile?.profile_picture_url && (_jsx("img", { src: profile.profile_picture_url, alt: "", width: 20, height: 20, className: "rounded-full object-cover flex-shrink-0", style: { width: 20, height: 20 } })), _jsx("span", { className: "text-sm font-mono text-gray-700 flex-1 truncate", children: headingText }), _jsxs("span", { className: "text-xs text-gray-400", children: [session.steps.length, " steps"] }), _jsx("span", { className: "text-gray-400 text-xs", children: open ? '▴' : '▾' })] }), open && (_jsx("div", { className: "border-t divide-y", children: session.steps.map((step, i) => (_jsxs("div", { className: "flex items-start gap-3 px-4 py-2", children: [_jsx("span", { className: "mt-0.5 w-2 h-2 rounded-full flex-shrink-0", style: { backgroundColor: kindColor(step.kind) } }), _jsxs("div", { className: "flex-1 min-w-0", children: [_jsx("p", { className: "text-xs font-medium text-gray-800 truncate", children: step.label }), step.path && (_jsx("p", { className: "text-[10px] text-gray-400 font-mono truncate", children: step.path }))] }), _jsx("span", { className: "text-[10px] text-gray-400 tabular-nums flex-shrink-0 mt-0.5", children: fmtMs(step.durationMsToNext) })] }, i))) }))] }));
333
333
  }
334
334
  // ---------------------------------------------------------------------------
335
+ // IdentityDropdown — custom user filter (avatars + email, anonymous bucket)
336
+ // ---------------------------------------------------------------------------
337
+ /** Sentinel identity value meaning "anonymous sessions only" (no user_id). */
338
+ const ANON_IDENTITY = '__anon__';
339
+ function IdentityDropdown({ value, onChange, userIds, userSessions, anonCount, profiles }) {
340
+ const [open, setOpen] = useState(false);
341
+ const rootRef = useRef(null);
342
+ // Close on outside click / Escape.
343
+ useEffect(() => {
344
+ if (!open)
345
+ return;
346
+ const onDown = (e) => {
347
+ if (rootRef.current && !rootRef.current.contains(e.target))
348
+ setOpen(false);
349
+ };
350
+ const onKey = (e) => { if (e.key === 'Escape')
351
+ setOpen(false); };
352
+ document.addEventListener('mousedown', onDown);
353
+ document.addEventListener('keydown', onKey);
354
+ return () => { document.removeEventListener('mousedown', onDown); document.removeEventListener('keydown', onKey); };
355
+ }, [open]);
356
+ const options = [
357
+ { value: '', sessionCount: 0 },
358
+ ...(anonCount > 0 ? [{ value: ANON_IDENTITY, sessionCount: anonCount }] : []),
359
+ ...userIds.map((id) => ({ value: id, sessionCount: userSessions[id] ?? 0 })),
360
+ ];
361
+ function labelFor(id) {
362
+ if (id === '')
363
+ return 'All users';
364
+ if (id === ANON_IDENTITY)
365
+ return 'Anonymous';
366
+ const p = profiles.get(id);
367
+ return p?.email ?? p?.name ?? `${id.slice(0, 12)}…`;
368
+ }
369
+ function avatarFor(id) {
370
+ if (id === '' || id === ANON_IDENTITY)
371
+ return undefined;
372
+ return profiles.get(id)?.profile_picture_url;
373
+ }
374
+ const renderRow = (id, count) => {
375
+ const pic = avatarFor(id);
376
+ return (_jsxs("span", { className: "flex items-center gap-2 min-w-0", children: [id !== '' && id !== ANON_IDENTITY && (pic ? (_jsx("img", { src: pic, alt: "", width: 18, height: 18, className: "rounded-full object-cover flex-shrink-0", style: { width: 18, height: 18 } })) : (_jsx("span", { className: "inline-flex items-center justify-center w-[18px] h-[18px] rounded-full bg-gray-200 text-[9px] text-gray-500 flex-shrink-0", children: labelFor(id).charAt(0).toUpperCase() }))), id === ANON_IDENTITY && (_jsx("span", { className: "inline-flex items-center justify-center w-[18px] h-[18px] rounded-full bg-gray-100 text-gray-400 flex-shrink-0", title: "Anonymous visitors", children: "?" })), _jsx("span", { className: "truncate", children: labelFor(id) }), count > 0 && _jsxs("span", { className: "text-gray-400 tabular-nums flex-shrink-0", children: ["(", count, ")"] })] }));
377
+ };
378
+ return (_jsxs("div", { ref: rootRef, className: "relative inline-block", children: [_jsxs("button", { type: "button", onClick: () => setOpen((v) => !v), className: "flex items-center gap-1.5 rounded border px-2 py-1 text-xs bg-white hover:bg-gray-50 focus:outline-none focus:ring-1 focus:ring-indigo-400 max-w-[220px]", title: "Filter by user", children: [renderRow(value, value === '' ? 0 : (value === ANON_IDENTITY ? anonCount : (userSessions[value] ?? 0))), _jsx("span", { className: "text-gray-400 flex-shrink-0", children: "\u25BE" })] }), open && (_jsx("div", { className: "absolute right-0 z-20 mt-1 max-h-72 w-64 overflow-auto rounded-lg border bg-white py-1 shadow-lg", children: options.map((opt) => (_jsx("button", { type: "button", onClick: () => { onChange(opt.value); setOpen(false); }, className: `flex w-full items-center px-3 py-1.5 text-left text-xs hover:bg-gray-50 ${opt.value === value ? 'bg-indigo-50 font-medium' : ''}`, children: renderRow(opt.value, opt.sessionCount) }, opt.value || '__all__'))) }))] }));
379
+ }
380
+ // ---------------------------------------------------------------------------
335
381
  // JourneyPanel — main export
336
382
  // ---------------------------------------------------------------------------
337
383
  export function JourneyPanel({ appId, apiBase, userId, sessionId, resolveUserProfiles, multiSidePorts = true, backEdgeLoop = true }) {
@@ -339,21 +385,25 @@ export function JourneyPanel({ appId, apiBase, userId, sessionId, resolveUserPro
339
385
  const [loading, setLoading] = useState(false);
340
386
  const [error, setError] = useState(null);
341
387
  const [userProfiles, setUserProfiles] = useState(new Map());
342
- // Persistent set of all user IDs ever seen (so the filter dropdown keeps its
343
- // full option list even while a single-user filter is active).
344
- const [knownUserIds, setKnownUserIds] = useState([]);
345
- // Internal filter (falls back to the userId prop when unset, e.g. from the
346
- // Behavior tab's single-user selection).
347
- const [selectedUserId, setSelectedUserId] = useState('');
348
- const effectiveUserId = selectedUserId || userId;
388
+ // Identity summary computed from the all-users fetch: distinct user_ids, their
389
+ // session counts, and the anonymous session count. Persisted so the dropdown
390
+ // keeps stable options/counts even while a single-identity filter is active.
391
+ const [identity, setIdentity] = useState({ userIds: [], userSessions: {}, anonCount: 0 });
392
+ // Selected identity: '' = all users, ANON_IDENTITY = anonymous, else a user_id.
393
+ // Falls back to the userId prop (e.g. the Behavior tab's single-user selection).
394
+ const [selectedIdentity, setSelectedIdentity] = useState('');
395
+ const effectiveIdentity = selectedIdentity || userId || '';
396
+ const isAnon = effectiveIdentity === ANON_IDENTITY;
397
+ const effectiveUserId = isAnon ? undefined : (effectiveIdentity || undefined);
398
+ // Drilldown: a specific session within the selected identity ('' = whole group).
399
+ const [selectedSessionId, setSelectedSessionId] = useState('');
400
+ // Sessions of the currently selected group — captured when a group is active
401
+ // and no single session is drilled, so the drilldown list stays populated.
402
+ const [groupSessions, setGroupSessions] = useState([]);
349
403
  // Split-by-page: ask the server to key nodes by event+page (refetch on change).
350
404
  const [splitByPage, setSplitByPage] = useState(false);
351
405
  // Page-flow: server returns only pageviews, linked page→page (skips clicks).
352
406
  const [pageFlow, setPageFlow] = useState(false);
353
- function userLabel(id) {
354
- const p = userProfiles.get(id);
355
- return p?.name ?? p?.email ?? `${id.slice(0, 12)}…`;
356
- }
357
407
  useEffect(() => {
358
408
  if (!appId)
359
409
  return;
@@ -361,10 +411,15 @@ export function JourneyPanel({ appId, apiBase, userId, sessionId, resolveUserPro
361
411
  setLoading(true);
362
412
  setError(null);
363
413
  const params = new URLSearchParams({ app_id: appId });
364
- if (effectiveUserId)
414
+ if (isAnon)
415
+ params.set('anonymous', '1');
416
+ else if (effectiveUserId)
365
417
  params.set('user_id', effectiveUserId);
366
- if (sessionId)
367
- params.set('session_id', sessionId);
418
+ // Drilldown session takes precedence; the sessionId prop (from the Behavior
419
+ // tab) still applies when no drilldown is active.
420
+ const effSession = selectedSessionId || sessionId;
421
+ if (effSession)
422
+ params.set('session_id', effSession);
368
423
  if (splitByPage)
369
424
  params.set('split_by_page', '1');
370
425
  if (pageFlow)
@@ -380,8 +435,24 @@ export function JourneyPanel({ appId, apiBase, userId, sessionId, resolveUserPro
380
435
  // Resolve user profiles for all sessions that have a user_id
381
436
  if (data?.sessions) {
382
437
  const ids = [...new Set(data.sessions.map((s) => s.user_id).filter((id) => Boolean(id)))];
383
- // Grow the known-users set (never shrink keeps the filter options stable)
384
- setKnownUserIds((prev) => [...new Set([...prev, ...ids])]);
438
+ // All-users view (no identity/session filter) the result holds every
439
+ // session, so recompute the identity summary that drives the dropdown.
440
+ if (!effectiveUserId && !isAnon && !effSession) {
441
+ const userSessions = {};
442
+ let anonCount = 0;
443
+ for (const s of data.sessions) {
444
+ if (s.user_id)
445
+ userSessions[s.user_id] = (userSessions[s.user_id] ?? 0) + 1;
446
+ else
447
+ anonCount += 1;
448
+ }
449
+ setIdentity({ userIds: Object.keys(userSessions), userSessions, anonCount });
450
+ }
451
+ // A single group is selected but not yet drilled → capture its sessions
452
+ // so the drilldown picker keeps its full list.
453
+ if ((effectiveUserId || isAnon) && !selectedSessionId) {
454
+ setGroupSessions(data.sessions);
455
+ }
385
456
  if (resolveUserProfiles && ids.length > 0) {
386
457
  resolveUserProfiles(ids)
387
458
  .then((profiles) => {
@@ -405,11 +476,11 @@ export function JourneyPanel({ appId, apiBase, userId, sessionId, resolveUserPro
405
476
  setLoading(false);
406
477
  });
407
478
  return () => { cancelled = true; };
408
- }, [appId, apiBase, effectiveUserId, sessionId, splitByPage, pageFlow]);
479
+ }, [appId, apiBase, effectiveUserId, isAnon, selectedSessionId, sessionId, splitByPage, pageFlow]);
409
480
  const sessionCount = result?.sessions.length ?? 0;
410
481
  const totalSteps = result?.sessions.reduce((s, sess) => s + sess.steps.length, 0) ?? 0;
411
482
  // Single source of truth for the data-fetch controls — rendered in the toolbar,
412
483
  // and passed into the flow graph so they stay reachable in graph fullscreen.
413
- const toolbarControls = (_jsxs(_Fragment, { children: [_jsxs("label", { className: "flex items-center gap-1 cursor-pointer select-none", title: "Show only page-to-page navigation (each page links to the next page visited)", children: [_jsx("input", { type: "checkbox", checked: pageFlow, onChange: (e) => setPageFlow(e.target.checked), className: "w-3 h-3 cursor-pointer", style: { accentColor: '#10b981' } }), "Pages only"] }), _jsxs("label", { className: "flex items-center gap-1 cursor-pointer select-none", title: "Split each event into one box per page", children: [_jsx("input", { type: "checkbox", checked: splitByPage, onChange: (e) => setSplitByPage(e.target.checked), className: "w-3 h-3 cursor-pointer" }), "Group by page"] }), knownUserIds.length > 0 && (_jsxs(_Fragment, { children: [_jsx("label", { className: "text-gray-500 ml-1", children: "User" }), _jsxs("select", { value: selectedUserId, onChange: (e) => setSelectedUserId(e.target.value), className: "rounded border px-2 py-1 text-xs focus:outline-none focus:ring-1 focus:ring-indigo-400", children: [_jsx("option", { value: "", children: "All users" }), knownUserIds.map((id) => (_jsx("option", { value: id, children: userLabel(id) }, id)))] })] }))] }));
484
+ const toolbarControls = (_jsxs(_Fragment, { children: [_jsxs("label", { className: "flex items-center gap-1 cursor-pointer select-none", title: "Show only page-to-page navigation (each page links to the next page visited)", children: [_jsx("input", { type: "checkbox", checked: pageFlow, onChange: (e) => setPageFlow(e.target.checked), className: "w-3 h-3 cursor-pointer", style: { accentColor: '#10b981' } }), "Pages only"] }), _jsxs("label", { className: "flex items-center gap-1 cursor-pointer select-none", title: "Split each event into one box per page", children: [_jsx("input", { type: "checkbox", checked: splitByPage, onChange: (e) => setSplitByPage(e.target.checked), className: "w-3 h-3 cursor-pointer" }), "Group by page"] }), _jsx("label", { className: "text-gray-500 ml-1", children: "User" }), _jsx(IdentityDropdown, { value: effectiveIdentity, onChange: (v) => { setSelectedIdentity(v); setSelectedSessionId(''); }, userIds: identity.userIds, userSessions: identity.userSessions, anonCount: identity.anonCount, profiles: userProfiles }), (effectiveUserId || isAnon) && groupSessions.length > 1 && (_jsxs("select", { value: selectedSessionId, onChange: (e) => setSelectedSessionId(e.target.value), className: "rounded border px-2 py-1 text-xs focus:outline-none focus:ring-1 focus:ring-indigo-400", title: "Drill into one session", children: [_jsxs("option", { value: "", children: ["All sessions (", groupSessions.length, ")"] }), groupSessions.map((s) => (_jsxs("option", { value: s.session_id, children: [s.session_id.slice(0, 8), "\u2026 \u00B7 ", s.steps.length, " steps"] }, s.session_id)))] }))] }));
414
485
  return (_jsxs("div", { className: "flex flex-col gap-4 p-4", children: [_jsxs("div", { className: "flex flex-wrap items-center gap-4 text-xs text-gray-500", children: [_jsxs("span", { children: [_jsx("span", { className: "font-semibold text-gray-700", children: sessionCount }), " session", sessionCount !== 1 ? 's' : ''] }), _jsxs("span", { children: [_jsx("span", { className: "font-semibold text-gray-700", children: totalSteps }), " total steps"] }), _jsx("div", { className: "ml-auto flex items-center gap-3", children: toolbarControls })] }), error && (_jsx("div", { className: "rounded-lg border border-red-200 bg-red-50 p-3 text-xs text-red-700", children: error })), loading && !result && (_jsx("div", { className: "p-4 text-xs text-gray-400", children: "Loading journey data\u2026" })), result && (_jsxs(_Fragment, { children: [result.graph.nodes.length > 0 && (_jsx(JourneyFlowGraph, { graph: result.graph, userFilter: toolbarControls, loading: loading, multiSidePorts: multiSidePorts, backEdgeLoop: backEdgeLoop })), result.sessions.length === 0 ? (_jsx("div", { className: "rounded-lg border p-6 text-center text-sm text-gray-400", children: "No journey sessions found." })) : (_jsxs("div", { className: "flex flex-col gap-3", children: [_jsx("h3", { className: "text-xs font-semibold text-gray-500 uppercase tracking-wide", children: "Sessions" }), result.sessions.map((session, i) => (_jsx(SessionDetail, { session: session, index: i, profile: session.user_id ? userProfiles.get(session.user_id) : undefined }, session.session_id)))] }))] })), !loading && !result && !error && (_jsx("div", { className: "rounded-lg border p-6 text-center text-sm text-gray-400", children: "No data yet." }))] }));
415
486
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hazo_umetrics",
3
- "version": "1.7.1",
3
+ "version": "1.8.0",
4
4
  "description": "Product analytics for hazo apps — GA4 hybrid + first-party stat store + feature flags",
5
5
  "type": "module",
6
6
  "module": "./dist/index.js",
@@ -42,16 +42,16 @@
42
42
  "build:test-app:pg": "HAZO_CONNECT_TYPE=postgrest npm run build:test-app"
43
43
  },
44
44
  "peerDependencies": {
45
- "hazo_core": "^1.2.0",
46
- "hazo_dataviz": "^0.3.0",
47
- "hazo_logs": "^2.0.3",
48
- "hazo_config": "^2.2.0",
49
- "hazo_connect": "^3.7.0",
50
- "hazo_api": "^2.3.1",
51
- "hazo_auth": "^10.2.0",
45
+ "hazo_core": "^1.2.1",
46
+ "hazo_dataviz": "^0.4.2",
47
+ "hazo_logs": "^2.1.1",
48
+ "hazo_config": "^2.4.1",
49
+ "hazo_connect": "^3.9.2",
50
+ "hazo_api": "^2.5.1",
51
+ "hazo_auth": "^10.8.0",
52
52
  "hazo_secure": "^1.3.0",
53
- "hazo_audit": "^2.1.1",
54
- "hazo_ui": "^4.4.0",
53
+ "hazo_audit": "^2.1.2",
54
+ "hazo_ui": "^4.10.0",
55
55
  "next": "^14.0.0 || ^16.0.0",
56
56
  "react": "^18.0.0 || ^19.0.0",
57
57
  "react-dom": "^18.0.0 || ^19.0.0"