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 +21 -0
- package/dist/api/index.d.ts.map +1 -1
- package/dist/api/index.js +2 -1
- package/dist/server/behavior.d.ts +3 -0
- package/dist/server/behavior.d.ts.map +1 -1
- package/dist/server/behavior.js +3 -0
- package/dist/ui/journey_panel.d.ts.map +1 -1
- package/dist/ui/journey_panel.js +89 -18
- package/package.json +10 -10
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):**
|
package/dist/api/index.d.ts.map
CHANGED
|
@@ -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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"}
|
package/dist/server/behavior.js
CHANGED
|
@@ -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;
|
|
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"}
|
package/dist/ui/journey_panel.js
CHANGED
|
@@ -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
|
-
//
|
|
343
|
-
//
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
//
|
|
347
|
-
|
|
348
|
-
const
|
|
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 (
|
|
414
|
+
if (isAnon)
|
|
415
|
+
params.set('anonymous', '1');
|
|
416
|
+
else if (effectiveUserId)
|
|
365
417
|
params.set('user_id', effectiveUserId);
|
|
366
|
-
|
|
367
|
-
|
|
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
|
-
//
|
|
384
|
-
|
|
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"] }),
|
|
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.
|
|
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.
|
|
46
|
-
"hazo_dataviz": "^0.
|
|
47
|
-
"hazo_logs": "^2.
|
|
48
|
-
"hazo_config": "^2.
|
|
49
|
-
"hazo_connect": "^3.
|
|
50
|
-
"hazo_api": "^2.
|
|
51
|
-
"hazo_auth": "^10.
|
|
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.
|
|
54
|
-
"hazo_ui": "^4.
|
|
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"
|