hazo_umetrics 1.8.0 → 1.9.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 +23 -0
- package/dist/api/index.d.ts.map +1 -1
- package/dist/api/index.js +2 -1
- package/dist/server/behavior.d.ts.map +1 -1
- package/dist/server/behavior.js +10 -3
- package/dist/ui/journey_panel.d.ts.map +1 -1
- package/dist/ui/journey_panel.js +54 -3
- package/dist/ui/search_queries_panel.d.ts.map +1 -1
- package/dist/ui/search_queries_panel.js +7 -1
- package/package.json +1 -1
package/CHANGE_LOG.md
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
# hazo_umetrics — Change Log
|
|
2
2
|
|
|
3
|
+
## 1.9.0 — Search queries: per-term last-searched timestamps, second-precision "over time"
|
|
4
|
+
|
|
5
|
+
**New features (minor — no code-level breaking changes):**
|
|
6
|
+
|
|
7
|
+
### `searchQueries()` — finer-grained `over_time`
|
|
8
|
+
|
|
9
|
+
- `over_time` buckets are now **second-precision timestamps** (`YYYY-MM-DD HH:MM:SS`) instead of day-only (`YYYY-MM-DD`), capped to the **most recent 100** buckets in chronological order.
|
|
10
|
+
- Added a `tsBucket()` helper in `src/server/behavior.ts`; the existing `dayBucket()` (used by other aggregations) is unchanged.
|
|
11
|
+
- Per-term `lastSearchedAt` was already computed and returned — no change there.
|
|
12
|
+
- **Behavior note for consumers:** the `EventDayBucket.day` field returned by `searchQueries` now carries a full timestamp string rather than a date. Shape is unchanged, so consumers upgrade without code changes, but any UI that rendered `day` as date-only will now display the full timestamp.
|
|
13
|
+
|
|
14
|
+
### `SearchQueriesPanel` — visible timestamps
|
|
15
|
+
|
|
16
|
+
- **Top Search Terms** and **Zero-Result Terms** rows now show a right-aligned **last-searched** timestamp (from `lastSearchedAt`).
|
|
17
|
+
- **Searches Over Time** now renders the full second-precision timestamp per bucket (was date-only).
|
|
18
|
+
- Added an `fmtTs()` formatter; no prop or public-type changes, so consumers upgrade with no code changes.
|
|
19
|
+
|
|
3
20
|
## 1.8.0 — Journey: anonymous-session bucket, session drilldown, rich user dropdown
|
|
4
21
|
|
|
5
22
|
**New features (minor — no breaking changes):**
|
|
@@ -21,6 +38,12 @@
|
|
|
21
38
|
- 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
39
|
- No prop or public-type changes — `JourneyPanelProps` and `resolveUserProfiles` are unchanged, so consumers upgrade with no code changes.
|
|
23
40
|
|
|
41
|
+
### `JourneyPanel` — date-range filter + session timestamps
|
|
42
|
+
|
|
43
|
+
- New **date filter** in the toolbar: a preset dropdown (`24 hours` · `7 days` · `30 days` · `90 days` · `All time`) plus optional **from/to date pickers** that override the preset. Maps to the `since` / `until` query params.
|
|
44
|
+
- `getJourney` now also parses `until=<iso>` (previously only `since`) and passes it through to `userJourney`.
|
|
45
|
+
- Session step lists now show an **absolute timestamp** per step (local `HH:MM:SS`, above the existing time-to-next duration), and each session header shows its **start time** (`Mon D, HH:MM`).
|
|
46
|
+
|
|
24
47
|
## 1.7.0 — Search query analytics: `searchQueries`, `SearchQueriesPanel`, `getIdentity`
|
|
25
48
|
|
|
26
49
|
**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
|
@@ -860,6 +860,7 @@ export function createBehaviorHandlers(options) {
|
|
|
860
860
|
const session_id = url.searchParams.get('session_id') ?? undefined;
|
|
861
861
|
const anonymous = url.searchParams.get('anonymous') === '1';
|
|
862
862
|
const since = url.searchParams.get('since') ?? undefined;
|
|
863
|
+
const until = url.searchParams.get('until') ?? undefined;
|
|
863
864
|
const splitByPage = url.searchParams.get('split_by_page') === '1';
|
|
864
865
|
const pageFlow = url.searchParams.get('page_flow') === '1';
|
|
865
866
|
const gate = await gateAuth(req, {
|
|
@@ -874,7 +875,7 @@ export function createBehaviorHandlers(options) {
|
|
|
874
875
|
const adapter = await options.getAdapter();
|
|
875
876
|
const catalog = await _listActions(adapter, { app_id });
|
|
876
877
|
const catalogMap = new Map(catalog.map((a) => [a.action_key, { label: a.label, datapoint_schema: a.datapoint_schema }]));
|
|
877
|
-
const result = await _userJourney(adapter, { app_id, user_id, session_id, anonymous, since, catalogMap, splitByPage, pageFlow });
|
|
878
|
+
const result = await _userJourney(adapter, { app_id, user_id, session_id, anonymous, since, until, catalogMap, splitByPage, pageFlow });
|
|
878
879
|
return jsonOk(result);
|
|
879
880
|
},
|
|
880
881
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"behavior.d.ts","sourceRoot":"","sources":["../../src/server/behavior.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAC9D,OAAO,KAAK,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAsB7D,MAAM,MAAM,WAAW,GAAG;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AACzE,MAAM,MAAM,UAAU,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAC3D,MAAM,MAAM,cAAc,GAAG;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE,CAAC;AAM7F,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;IACpC,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;
|
|
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
|
@@ -41,6 +41,11 @@ function topN(tally, n, catalogMap) {
|
|
|
41
41
|
function dayBucket(iso) {
|
|
42
42
|
return iso.slice(0, 10);
|
|
43
43
|
}
|
|
44
|
+
// Second-precision bucket: "YYYY-MM-DD HH:MM:SS". Handles both ISO ("…T…Z")
|
|
45
|
+
// and SQLite space-separated timestamps.
|
|
46
|
+
function tsBucket(iso) {
|
|
47
|
+
return iso.slice(0, 19).replace('T', ' ');
|
|
48
|
+
}
|
|
44
49
|
async function fetchWindow(adapter, params) {
|
|
45
50
|
const crud = getCrud(adapter);
|
|
46
51
|
return crud.list((qb) => {
|
|
@@ -624,9 +629,9 @@ export async function searchQueries(adapter, opts) {
|
|
|
624
629
|
}
|
|
625
630
|
if (row.captured_at > acc.lastAt)
|
|
626
631
|
acc.lastAt = row.captured_at;
|
|
627
|
-
//
|
|
628
|
-
const
|
|
629
|
-
dayMap.set(
|
|
632
|
+
// Timestamp bucket (second precision) for fine-grained "over time"
|
|
633
|
+
const ts = tsBucket(row.captured_at);
|
|
634
|
+
dayMap.set(ts, (dayMap.get(ts) ?? 0) + 1);
|
|
630
635
|
// Per-user
|
|
631
636
|
if (uid) {
|
|
632
637
|
if (!userMap.has(uid))
|
|
@@ -648,8 +653,10 @@ export async function searchQueries(adapter, opts) {
|
|
|
648
653
|
.filter((e) => e.zeroResults > 0)
|
|
649
654
|
.sort((a, b) => b.zeroResults - a.zeroResults)
|
|
650
655
|
.slice(0, topN_);
|
|
656
|
+
// Keep the most recent 100 timestamp buckets, chronological order.
|
|
651
657
|
const over_time = [...dayMap.entries()]
|
|
652
658
|
.sort((a, b) => a[0].localeCompare(b[0]))
|
|
659
|
+
.slice(-100)
|
|
653
660
|
.map(([day, count]) => ({ day, count }));
|
|
654
661
|
const per_user = [...userMap.entries()]
|
|
655
662
|
.map(([user_id, { count, terms }]) => ({
|
|
@@ -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;AAotBD,wBAAgB,YAAY,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,mBAAmB,EAAE,cAAqB,EAAE,YAAmB,EAAE,EAAE,iBAAiB,+BA+QrJ"}
|
package/dist/ui/journey_panel.js
CHANGED
|
@@ -14,6 +14,29 @@ function fmtMs(ms) {
|
|
|
14
14
|
return `${(ms / 1000).toFixed(1)}s`;
|
|
15
15
|
return `${(ms / 60000).toFixed(1)}m`;
|
|
16
16
|
}
|
|
17
|
+
/** Absolute clock time for a step (local), e.g. "22:29:07". */
|
|
18
|
+
function fmtClock(iso) {
|
|
19
|
+
const d = new Date(iso);
|
|
20
|
+
if (isNaN(d.getTime()))
|
|
21
|
+
return '—';
|
|
22
|
+
return d.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit', second: '2-digit', hour12: false });
|
|
23
|
+
}
|
|
24
|
+
/** Session-start stamp for a header, e.g. "Jul 9, 22:29". */
|
|
25
|
+
function fmtDateTime(iso) {
|
|
26
|
+
const d = new Date(iso);
|
|
27
|
+
if (isNaN(d.getTime()))
|
|
28
|
+
return '—';
|
|
29
|
+
return d.toLocaleString([], { month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit', hour12: false });
|
|
30
|
+
}
|
|
31
|
+
/** Date-range presets for the journey filter. `days: null` = all time. */
|
|
32
|
+
const RANGE_PRESETS = [
|
|
33
|
+
{ key: '24h', label: '24 hours', days: 1 },
|
|
34
|
+
{ key: '7d', label: '7 days', days: 7 },
|
|
35
|
+
{ key: '30d', label: '30 days', days: 30 },
|
|
36
|
+
{ key: '90d', label: '90 days', days: 90 },
|
|
37
|
+
{ key: 'all', label: 'All time', days: null },
|
|
38
|
+
];
|
|
39
|
+
const ALL_TIME_SINCE = '2000-01-01T00:00:00.000Z';
|
|
17
40
|
function kindColor(kind) {
|
|
18
41
|
switch (kind) {
|
|
19
42
|
case 'action': return '#6366f1';
|
|
@@ -329,7 +352,7 @@ function SessionDetail({ session, index, profile }) {
|
|
|
329
352
|
const headingText = session.user_id
|
|
330
353
|
? (profile?.name ?? profile?.email ?? session.user_id.slice(0, 16))
|
|
331
354
|
: `Anonymous (sid ${session.session_id.slice(0, 8)}…)`;
|
|
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-
|
|
355
|
+
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 }), session.steps[0] && (_jsx("span", { className: "text-[10px] text-gray-400 tabular-nums flex-shrink-0", title: "Session start", children: fmtDateTime(session.steps[0].captured_at) })), _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 }))] }), _jsxs("span", { className: "flex flex-col items-end flex-shrink-0 mt-0.5 leading-tight", children: [_jsx("span", { className: "text-[10px] text-gray-500 tabular-nums", title: "Timestamp", children: fmtClock(step.captured_at) }), _jsx("span", { className: "text-[10px] text-gray-400 tabular-nums", title: "Time to next step", children: fmtMs(step.durationMsToNext) })] })] }, i))) }))] }));
|
|
333
356
|
}
|
|
334
357
|
// ---------------------------------------------------------------------------
|
|
335
358
|
// IdentityDropdown — custom user filter (avatars + email, anonymous bucket)
|
|
@@ -404,6 +427,30 @@ export function JourneyPanel({ appId, apiBase, userId, sessionId, resolveUserPro
|
|
|
404
427
|
const [splitByPage, setSplitByPage] = useState(false);
|
|
405
428
|
// Page-flow: server returns only pageviews, linked page→page (skips clicks).
|
|
406
429
|
const [pageFlow, setPageFlow] = useState(false);
|
|
430
|
+
// Date range: a preset window, overridden by explicit from/to dates when set.
|
|
431
|
+
const [rangePreset, setRangePreset] = useState('30d');
|
|
432
|
+
const [fromDate, setFromDate] = useState(''); // yyyy-mm-dd
|
|
433
|
+
const [toDate, setToDate] = useState(''); // yyyy-mm-dd
|
|
434
|
+
const customRange = Boolean(fromDate || toDate);
|
|
435
|
+
// Resolve the active window to since/until ISO params.
|
|
436
|
+
let sinceParam;
|
|
437
|
+
let untilParam;
|
|
438
|
+
if (customRange) {
|
|
439
|
+
sinceParam = fromDate ? new Date(`${fromDate}T00:00:00`).toISOString() : ALL_TIME_SINCE;
|
|
440
|
+
if (toDate)
|
|
441
|
+
untilParam = new Date(`${toDate}T23:59:59.999`).toISOString();
|
|
442
|
+
}
|
|
443
|
+
else {
|
|
444
|
+
const preset = RANGE_PRESETS.find((p) => p.key === rangePreset);
|
|
445
|
+
if (preset?.days != null) {
|
|
446
|
+
const d = new Date();
|
|
447
|
+
d.setDate(d.getDate() - preset.days);
|
|
448
|
+
sinceParam = d.toISOString();
|
|
449
|
+
}
|
|
450
|
+
else {
|
|
451
|
+
sinceParam = ALL_TIME_SINCE; // all time
|
|
452
|
+
}
|
|
453
|
+
}
|
|
407
454
|
useEffect(() => {
|
|
408
455
|
if (!appId)
|
|
409
456
|
return;
|
|
@@ -420,6 +467,10 @@ export function JourneyPanel({ appId, apiBase, userId, sessionId, resolveUserPro
|
|
|
420
467
|
const effSession = selectedSessionId || sessionId;
|
|
421
468
|
if (effSession)
|
|
422
469
|
params.set('session_id', effSession);
|
|
470
|
+
if (sinceParam)
|
|
471
|
+
params.set('since', sinceParam);
|
|
472
|
+
if (untilParam)
|
|
473
|
+
params.set('until', untilParam);
|
|
423
474
|
if (splitByPage)
|
|
424
475
|
params.set('split_by_page', '1');
|
|
425
476
|
if (pageFlow)
|
|
@@ -476,11 +527,11 @@ export function JourneyPanel({ appId, apiBase, userId, sessionId, resolveUserPro
|
|
|
476
527
|
setLoading(false);
|
|
477
528
|
});
|
|
478
529
|
return () => { cancelled = true; };
|
|
479
|
-
}, [appId, apiBase, effectiveUserId, isAnon, selectedSessionId, sessionId, splitByPage, pageFlow]);
|
|
530
|
+
}, [appId, apiBase, effectiveUserId, isAnon, selectedSessionId, sessionId, sinceParam, untilParam, splitByPage, pageFlow]);
|
|
480
531
|
const sessionCount = result?.sessions.length ?? 0;
|
|
481
532
|
const totalSteps = result?.sessions.reduce((s, sess) => s + sess.steps.length, 0) ?? 0;
|
|
482
533
|
// Single source of truth for the data-fetch controls — rendered in the toolbar,
|
|
483
534
|
// and passed into the flow graph so they stay reachable in graph fullscreen.
|
|
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)))] }))] }));
|
|
535
|
+
const toolbarControls = (_jsxs(_Fragment, { children: [_jsx("label", { className: "text-gray-500", children: "Date" }), _jsx("select", { value: rangePreset, onChange: (e) => { setRangePreset(e.target.value); setFromDate(''); setToDate(''); }, disabled: customRange, className: "rounded border px-2 py-1 text-xs focus:outline-none focus:ring-1 focus:ring-indigo-400 disabled:opacity-40", title: customRange ? 'Clear the from/to dates to use a preset' : 'Preset date range', children: RANGE_PRESETS.map((p) => (_jsx("option", { value: p.key, children: p.label }, p.key))) }), _jsx("input", { type: "date", value: fromDate, max: toDate || undefined, onChange: (e) => setFromDate(e.target.value), className: "rounded border px-2 py-1 text-xs focus:outline-none focus:ring-1 focus:ring-indigo-400", title: "From date (overrides preset)" }), _jsx("span", { className: "text-gray-400", children: "\u2013" }), _jsx("input", { type: "date", value: toDate, min: fromDate || undefined, onChange: (e) => setToDate(e.target.value), className: "rounded border px-2 py-1 text-xs focus:outline-none focus:ring-1 focus:ring-indigo-400", title: "To date (overrides preset)" }), customRange && (_jsx("button", { type: "button", onClick: () => { setFromDate(''); setToDate(''); }, className: "text-xs text-gray-400 hover:text-gray-600", title: "Clear date range", children: "\u2715" })), _jsxs("label", { className: "flex items-center gap-1 cursor-pointer select-none ml-1", 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)))] }))] }));
|
|
485
536
|
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." }))] }));
|
|
486
537
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"search_queries_panel.d.ts","sourceRoot":"","sources":["../../src/ui/search_queries_panel.tsx"],"names":[],"mappings":"AA4BA,UAAU,WAAW;IACnB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;
|
|
1
|
+
{"version":3,"file":"search_queries_panel.d.ts","sourceRoot":"","sources":["../../src/ui/search_queries_panel.tsx"],"names":[],"mappings":"AA4BA,UAAU,WAAW;IACnB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAoDD,MAAM,WAAW,uBAAuB;IACtC,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,mBAAmB,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC;CACjE;AAED,wBAAgB,kBAAkB,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,mBAAmB,EAAE,EAAE,uBAAuB,+BAmJzG"}
|
|
@@ -4,6 +4,12 @@ import { useState, useEffect } from 'react';
|
|
|
4
4
|
// ---------------------------------------------------------------------------
|
|
5
5
|
// MiniBar
|
|
6
6
|
// ---------------------------------------------------------------------------
|
|
7
|
+
// Format a stored timestamp ("…T…Z" or "YYYY-MM-DD HH:MM:SS") to second precision.
|
|
8
|
+
function fmtTs(iso) {
|
|
9
|
+
if (!iso)
|
|
10
|
+
return '';
|
|
11
|
+
return iso.slice(0, 19).replace('T', ' ');
|
|
12
|
+
}
|
|
7
13
|
function MiniBar({ value, max, color = '#6366f1' }) {
|
|
8
14
|
const pct = max > 0 ? (value / max) * 100 : 0;
|
|
9
15
|
return (_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("div", { className: "flex-1 h-2 rounded bg-gray-100 overflow-hidden", children: _jsx("div", { className: "h-full rounded", style: { width: `${pct}%`, backgroundColor: color } }) }), _jsx("span", { className: "text-xs tabular-nums text-gray-600 w-8 text-right", children: value })] }));
|
|
@@ -63,7 +69,7 @@ export function SearchQueriesPanel({ appId, apiBase, since, resolveUserProfiles
|
|
|
63
69
|
const maxTermCount = data.top_terms.length > 0 ? Math.max(...data.top_terms.map((t) => t.count), 1) : 1;
|
|
64
70
|
const maxZeroCount = data.zero_result_terms.length > 0 ? Math.max(...data.zero_result_terms.map((t) => t.zeroResults), 1) : 1;
|
|
65
71
|
const maxDayCount = data.over_time.length > 0 ? Math.max(...data.over_time.map((d) => d.count), 1) : 1;
|
|
66
|
-
return (_jsxs("div", { className: "flex flex-col gap-6 p-4", children: [_jsxs("div", { className: "grid grid-cols-2 md:grid-cols-4 gap-4", children: [_jsx(SummaryCard, { label: "Total Searches", value: data.total_searches }), _jsx(SummaryCard, { label: "Unique Terms", value: data.unique_terms }), _jsx(SummaryCard, { label: "Unique Searchers", value: data.unique_searchers }), _jsx(SummaryCard, { label: "Zero-Result Searches", value: data.zero_result_searches })] }), _jsxs("div", { className: "rounded-lg border bg-white p-4 flex flex-col gap-3", children: [_jsx("h3", { className: "text-sm font-semibold text-gray-700", children: "Top Search Terms" }), data.top_terms.length === 0 ? (_jsx("p", { className: "text-xs text-gray-400", children: "No searches recorded yet." })) : (_jsx("div", { className: "flex flex-col gap-1", children: data.top_terms.map((t) => (_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("span", { className: "text-xs text-gray-600 w-32 truncate", title: t.term, children: t.term }), _jsx(MiniBar, { value: t.count, max: maxTermCount })] }, t.term))) }))] }), data.zero_result_terms.length > 0 && (_jsxs("div", { className: "rounded-lg border border-amber-200 bg-amber-50 p-4 flex flex-col gap-3", children: [_jsxs("h3", { className: "text-sm font-semibold text-amber-800", children: ["Zero-Result Terms ", _jsx("span", { className: "text-xs font-normal text-amber-600", children: "(content gaps)" })] }), _jsx("div", { className: "flex flex-col gap-1", children: data.zero_result_terms.map((t) => (_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("span", { className: "text-xs text-amber-700 w-32 truncate", title: t.term, children: t.term }), _jsx(MiniBar, { value: t.zeroResults, max: maxZeroCount, color: "#f59e0b" })] }, t.term))) })] })), data.over_time.length > 0 && (_jsxs("div", { className: "rounded-lg border bg-white p-4 flex flex-col gap-3", children: [_jsx("h3", { className: "text-sm font-semibold text-gray-700", children: "Searches Over Time" }), _jsx("div", { className: "flex flex-col gap-1", children: data.over_time.map((d) => (_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("span", { className: "text-[10px] text-gray-400 w-
|
|
72
|
+
return (_jsxs("div", { className: "flex flex-col gap-6 p-4", children: [_jsxs("div", { className: "grid grid-cols-2 md:grid-cols-4 gap-4", children: [_jsx(SummaryCard, { label: "Total Searches", value: data.total_searches }), _jsx(SummaryCard, { label: "Unique Terms", value: data.unique_terms }), _jsx(SummaryCard, { label: "Unique Searchers", value: data.unique_searchers }), _jsx(SummaryCard, { label: "Zero-Result Searches", value: data.zero_result_searches })] }), _jsxs("div", { className: "rounded-lg border bg-white p-4 flex flex-col gap-3", children: [_jsx("h3", { className: "text-sm font-semibold text-gray-700", children: "Top Search Terms" }), data.top_terms.length === 0 ? (_jsx("p", { className: "text-xs text-gray-400", children: "No searches recorded yet." })) : (_jsx("div", { className: "flex flex-col gap-1", children: data.top_terms.map((t) => (_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("span", { className: "text-xs text-gray-600 w-32 truncate", title: t.term, children: t.term }), _jsx(MiniBar, { value: t.count, max: maxTermCount }), _jsx("span", { className: "text-[10px] text-gray-400 tabular-nums whitespace-nowrap w-32 text-right", title: `Last searched ${t.lastSearchedAt}`, children: fmtTs(t.lastSearchedAt) })] }, t.term))) }))] }), data.zero_result_terms.length > 0 && (_jsxs("div", { className: "rounded-lg border border-amber-200 bg-amber-50 p-4 flex flex-col gap-3", children: [_jsxs("h3", { className: "text-sm font-semibold text-amber-800", children: ["Zero-Result Terms ", _jsx("span", { className: "text-xs font-normal text-amber-600", children: "(content gaps)" })] }), _jsx("div", { className: "flex flex-col gap-1", children: data.zero_result_terms.map((t) => (_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("span", { className: "text-xs text-amber-700 w-32 truncate", title: t.term, children: t.term }), _jsx(MiniBar, { value: t.zeroResults, max: maxZeroCount, color: "#f59e0b" }), _jsx("span", { className: "text-[10px] text-amber-600 tabular-nums whitespace-nowrap w-32 text-right", title: `Last searched ${t.lastSearchedAt}`, children: fmtTs(t.lastSearchedAt) })] }, t.term))) })] })), data.over_time.length > 0 && (_jsxs("div", { className: "rounded-lg border bg-white p-4 flex flex-col gap-3", children: [_jsx("h3", { className: "text-sm font-semibold text-gray-700", children: "Searches Over Time" }), _jsx("div", { className: "flex flex-col gap-1", children: data.over_time.map((d) => (_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("span", { className: "text-[10px] text-gray-400 tabular-nums w-36 whitespace-nowrap", children: fmtTs(d.day) }), _jsx(MiniBar, { value: d.count, max: maxDayCount, color: "#10b981" })] }, d.day))) })] })), data.per_user.length > 0 && (_jsxs("div", { className: "rounded-lg border bg-white p-4 flex flex-col gap-3", children: [_jsx("h3", { className: "text-sm font-semibold text-gray-700", children: "Per-User Searches" }), _jsx("div", { className: "overflow-x-auto", children: _jsxs("table", { className: "w-full text-xs border-collapse", children: [_jsx("thead", { children: _jsxs("tr", { className: "text-left text-gray-400 border-b", children: [_jsx("th", { className: "py-1 pr-4 font-medium", children: "User" }), _jsx("th", { className: "py-1 pr-4 font-medium tabular-nums", children: "Searches" }), _jsx("th", { className: "py-1 pr-4 font-medium tabular-nums", children: "Distinct Terms" }), _jsx("th", { className: "py-1 font-medium", children: "Recent Terms" })] }) }), _jsx("tbody", { children: data.per_user.map((u) => {
|
|
67
73
|
const profile = profiles.get(u.user_id);
|
|
68
74
|
const displayName = profile?.name ?? profile?.email ?? u.user_id.slice(0, 8) + '…';
|
|
69
75
|
return (_jsxs("tr", { className: "border-b border-gray-50", children: [_jsxs("td", { className: "py-1 pr-4 text-gray-700", children: [profile?.avatar_url && (_jsx("img", { src: profile.avatar_url, alt: "", className: "inline-block w-4 h-4 rounded-full mr-1 align-middle" })), displayName] }), _jsx("td", { className: "py-1 pr-4 tabular-nums text-gray-600", children: u.count }), _jsx("td", { className: "py-1 pr-4 tabular-nums text-gray-600", children: u.distinct_terms }), _jsxs("td", { className: "py-1 text-gray-500", children: [u.terms.slice(0, 5).join(', '), u.terms.length > 5 ? '…' : ''] })] }, u.user_id));
|