hazo_umetrics 1.11.0 → 1.12.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 +17 -0
- package/dist/api/index.d.ts +5 -0
- package/dist/api/index.d.ts.map +1 -1
- package/dist/api/index.js +24 -1
- package/dist/server/behavior.d.ts +24 -0
- package/dist/server/behavior.d.ts.map +1 -1
- package/dist/server/behavior.js +63 -0
- package/dist/ui/index.d.ts +1 -0
- package/dist/ui/index.d.ts.map +1 -1
- package/dist/ui/index.js +1 -0
- package/dist/ui/not_found_panel.d.ts +6 -0
- package/dist/ui/not_found_panel.d.ts.map +1 -0
- package/dist/ui/not_found_panel.js +79 -0
- package/package.json +4 -4
package/CHANGE_LOG.md
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
# hazo_umetrics — Change Log
|
|
2
2
|
|
|
3
|
+
## 1.12.0 — 404 / broken-link analytics
|
|
4
|
+
|
|
5
|
+
**New behavioral cut for `action.404` events (minor — purely additive; no existing signatures changed):**
|
|
6
|
+
|
|
7
|
+
### Server (`src/server/behavior.ts`)
|
|
8
|
+
- `notFoundQueries(adapter, opts)` — app-side aggregation over `action.404` events (mirrors `searchQueries`). Reuses the existing `fetchWindow`/`parseDims`/`tsBucket`/`defaultSince` helpers. Filters `metric_key === 'action.404'`, skips rows with an empty/missing `requested_path`, defaults `referrer_kind` to `direct` and folds unrecognized kinds into `external`.
|
|
9
|
+
- `NotFoundQueriesResult`: `total_404s`, `unique_paths`, `internal_referrer_count`, `top_paths` (`{ path, count, lastAt, internalCount, sampleReferrer }`, desc by count), `by_source` (`{ internal, search, external, direct }`), `over_time` (`EventDayBucket[]`, second-precision, most-recent 100 chronological). New exported types `NotFoundPathEntry`, `NotFoundSourceBreakdown`.
|
|
10
|
+
|
|
11
|
+
### API (`src/api/index.ts`)
|
|
12
|
+
- `createBehaviorHandlers` gains `getNotFound(req)` — `GET /...?app_id=<id>[&since=<iso>][&top_n=<n>]`, requires `metrics.view`, returns `jsonOk(result)`. No per-user cut (404s have no `user_ids` param).
|
|
13
|
+
|
|
14
|
+
### UI (`src/ui/not_found_panel.tsx`, new; exported from `src/ui/index.ts`)
|
|
15
|
+
- `NotFoundPanel({ appId, apiBase })` — self-contained window tabs (Total / 24h / 7d / 28d / 90d), summary cards (Total 404s / Unique Paths / Internal Broken Links / Direct-Typed), Top Missing Paths (amber "broken internal link" badge when `internalCount > 0`), Traffic Source breakdown, and 404s Over Time. Consumes `GET {apiBase}/analytics/not-found`.
|
|
16
|
+
|
|
17
|
+
### Tests
|
|
18
|
+
- `__tests__/not_found.test.ts` — mirrors `search_queries.test.ts` (counts, `internal_referrer_count`, `by_source`, `top_paths` ordering, empty-path skip).
|
|
19
|
+
|
|
3
20
|
## 1.11.0 — M1 GA4 live (OAuth connect + Admin provisioner + Data API funnels + windowed ga4 metrics)
|
|
4
21
|
|
|
5
22
|
**M1 GA4 half shipped (minor — stubs replaced with real implementations; signatures widened, not broken):**
|
package/dist/api/index.d.ts
CHANGED
|
@@ -208,5 +208,10 @@ export declare function createBehaviorHandlers(options: ApiFactoryOptions): {
|
|
|
208
208
|
* Returns search query analytics. Requires metrics.view.
|
|
209
209
|
*/
|
|
210
210
|
getSearchQueries(req: Request): Promise<Response>;
|
|
211
|
+
/**
|
|
212
|
+
* GET /...?app_id=<id>[&since=<iso>][&top_n=<n>]
|
|
213
|
+
* Returns 404 / broken-link analytics. Requires metrics.view.
|
|
214
|
+
*/
|
|
215
|
+
getNotFound(req: Request): Promise<Response>;
|
|
211
216
|
};
|
|
212
217
|
//# sourceMappingURL=index.d.ts.map
|
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":"AAQA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;
|
|
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
|
@@ -11,7 +11,7 @@ import { createGa4MetricWindowReader } from '../server/ga4_metrics.js';
|
|
|
11
11
|
import { resolveVariant } from '../server/flags.js';
|
|
12
12
|
import { getActivitySummary as _getActivitySummary, getActivityTrend as _getActivityTrend, } from '../server/activity.js';
|
|
13
13
|
import { ensureActionDef as _ensureActionDef, listActions as _listActions, getActionTree as _getActionTree, updateActionDef as _updateActionDef, updateActionDefBulk as _updateActionDefBulk, applyLinksBulk as _applyLinksBulk, getLinks as _getLinks, addLink as _addLink, removeLink as _removeLink, } from '../server/actions.js';
|
|
14
|
-
import { topActions as _topActions, firstSessionActions as _firstSessionActions, postReloginActions as _postReloginActions, actionsBeforeGoal as _actionsBeforeGoal, topErrors as _topErrors, tabDwell as _tabDwell, frequentActionsReturning as _frequentActionsReturning, eventsOverTime as _eventsOverTime, datapointStats as _datapointStats, userJourney as _userJourney, searchQueries as _searchQueries, } from '../server/behavior.js';
|
|
14
|
+
import { topActions as _topActions, firstSessionActions as _firstSessionActions, postReloginActions as _postReloginActions, actionsBeforeGoal as _actionsBeforeGoal, topErrors as _topErrors, tabDwell as _tabDwell, frequentActionsReturning as _frequentActionsReturning, eventsOverTime as _eventsOverTime, datapointStats as _datapointStats, userJourney as _userJourney, searchQueries as _searchQueries, notFoundQueries as _notFoundQueries, } from '../server/behavior.js';
|
|
15
15
|
import { createGa4Client } from '../server/ga4.js';
|
|
16
16
|
import { createGa4Provisioner } from '../server/ga4_provision.js';
|
|
17
17
|
import { getFunnel } from '../server/funnel.js';
|
|
@@ -1144,5 +1144,28 @@ export function createBehaviorHandlers(options) {
|
|
|
1144
1144
|
const result = await _searchQueries(adapter, { app_id, since, topN, userIds });
|
|
1145
1145
|
return jsonOk(result);
|
|
1146
1146
|
},
|
|
1147
|
+
/**
|
|
1148
|
+
* GET /...?app_id=<id>[&since=<iso>][&top_n=<n>]
|
|
1149
|
+
* Returns 404 / broken-link analytics. Requires metrics.view.
|
|
1150
|
+
*/
|
|
1151
|
+
async getNotFound(req) {
|
|
1152
|
+
const url = new URL(req.url);
|
|
1153
|
+
const app_id = url.searchParams.get('app_id') ?? '';
|
|
1154
|
+
const since = url.searchParams.get('since') ?? undefined;
|
|
1155
|
+
const topNParam = url.searchParams.get('top_n');
|
|
1156
|
+
const topN = topNParam != null ? parseInt(topNParam, 10) : undefined;
|
|
1157
|
+
const gate = await gateAuth(req, {
|
|
1158
|
+
required_permissions: ['metrics.view'],
|
|
1159
|
+
app_id: app_id || undefined,
|
|
1160
|
+
getAuth,
|
|
1161
|
+
});
|
|
1162
|
+
if (!gate.ok)
|
|
1163
|
+
return gate.response;
|
|
1164
|
+
if (!app_id)
|
|
1165
|
+
return jsonError('BAD_REQUEST', 'app_id is required', 400);
|
|
1166
|
+
const adapter = await options.getAdapter();
|
|
1167
|
+
const result = await _notFoundQueries(adapter, { app_id, since, topN });
|
|
1168
|
+
return jsonOk(result);
|
|
1169
|
+
},
|
|
1147
1170
|
};
|
|
1148
1171
|
}
|
|
@@ -184,4 +184,28 @@ export interface SearchQueriesResult {
|
|
|
184
184
|
export declare function searchQueries(adapter: HazoConnectAdapter, opts: BehaviorOptions & {
|
|
185
185
|
topN?: number;
|
|
186
186
|
}): Promise<SearchQueriesResult>;
|
|
187
|
+
export interface NotFoundPathEntry {
|
|
188
|
+
path: string;
|
|
189
|
+
count: number;
|
|
190
|
+
lastAt: string;
|
|
191
|
+
internalCount: number;
|
|
192
|
+
sampleReferrer: string;
|
|
193
|
+
}
|
|
194
|
+
export interface NotFoundSourceBreakdown {
|
|
195
|
+
internal: number;
|
|
196
|
+
search: number;
|
|
197
|
+
external: number;
|
|
198
|
+
direct: number;
|
|
199
|
+
}
|
|
200
|
+
export interface NotFoundQueriesResult {
|
|
201
|
+
total_404s: number;
|
|
202
|
+
unique_paths: number;
|
|
203
|
+
internal_referrer_count: number;
|
|
204
|
+
top_paths: NotFoundPathEntry[];
|
|
205
|
+
by_source: NotFoundSourceBreakdown;
|
|
206
|
+
over_time: EventDayBucket[];
|
|
207
|
+
}
|
|
208
|
+
export declare function notFoundQueries(adapter: HazoConnectAdapter, opts: BehaviorOptions & {
|
|
209
|
+
topN?: number;
|
|
210
|
+
}): Promise<NotFoundQueriesResult>;
|
|
187
211
|
//# sourceMappingURL=behavior.d.ts.map
|
|
@@ -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
|
@@ -677,3 +677,66 @@ export async function searchQueries(adapter, opts) {
|
|
|
677
677
|
per_user,
|
|
678
678
|
};
|
|
679
679
|
}
|
|
680
|
+
export async function notFoundQueries(adapter, opts) {
|
|
681
|
+
const { app_id, scope_id, since, limit = 10000 } = opts;
|
|
682
|
+
const topN_ = opts.topN ?? 20;
|
|
683
|
+
const rows = await fetchWindow(adapter, { app_id, scope_id, since: since ?? defaultSince(), cap: limit });
|
|
684
|
+
const notFoundRows = rows.filter((r) => r.metric_key === 'action.404');
|
|
685
|
+
const pathMap = new Map();
|
|
686
|
+
const bySource = { internal: 0, search: 0, external: 0, direct: 0 };
|
|
687
|
+
const dayMap = new Map();
|
|
688
|
+
let total404s = 0;
|
|
689
|
+
let internalReferrerCount = 0;
|
|
690
|
+
for (const row of notFoundRows) {
|
|
691
|
+
const dims = parseDims(row.dimensions);
|
|
692
|
+
const requestedPath = typeof dims.requested_path === 'string' ? dims.requested_path : '';
|
|
693
|
+
if (!requestedPath)
|
|
694
|
+
continue;
|
|
695
|
+
total404s++;
|
|
696
|
+
const referrerKind = typeof dims.referrer_kind === 'string' && dims.referrer_kind ? dims.referrer_kind : 'direct';
|
|
697
|
+
const referrer = typeof dims.referrer === 'string' ? dims.referrer : '';
|
|
698
|
+
if (!pathMap.has(requestedPath)) {
|
|
699
|
+
pathMap.set(requestedPath, { count: 0, lastAt: row.captured_at, internalCount: 0, sampleReferrer: '' });
|
|
700
|
+
}
|
|
701
|
+
const acc = pathMap.get(requestedPath);
|
|
702
|
+
acc.count++;
|
|
703
|
+
if (row.captured_at > acc.lastAt)
|
|
704
|
+
acc.lastAt = row.captured_at;
|
|
705
|
+
if (referrerKind === 'internal')
|
|
706
|
+
acc.internalCount++;
|
|
707
|
+
if (!acc.sampleReferrer && referrer)
|
|
708
|
+
acc.sampleReferrer = referrer;
|
|
709
|
+
if (referrerKind === 'internal')
|
|
710
|
+
internalReferrerCount++;
|
|
711
|
+
if (referrerKind === 'internal' || referrerKind === 'search' || referrerKind === 'external' || referrerKind === 'direct') {
|
|
712
|
+
bySource[referrerKind]++;
|
|
713
|
+
}
|
|
714
|
+
else {
|
|
715
|
+
bySource.external++;
|
|
716
|
+
}
|
|
717
|
+
const ts = tsBucket(row.captured_at);
|
|
718
|
+
dayMap.set(ts, (dayMap.get(ts) ?? 0) + 1);
|
|
719
|
+
}
|
|
720
|
+
const top_paths = [...pathMap.entries()]
|
|
721
|
+
.map(([path, acc]) => ({
|
|
722
|
+
path,
|
|
723
|
+
count: acc.count,
|
|
724
|
+
lastAt: acc.lastAt,
|
|
725
|
+
internalCount: acc.internalCount,
|
|
726
|
+
sampleReferrer: acc.sampleReferrer,
|
|
727
|
+
}))
|
|
728
|
+
.sort((a, b) => b.count - a.count)
|
|
729
|
+
.slice(0, topN_);
|
|
730
|
+
const over_time = [...dayMap.entries()]
|
|
731
|
+
.sort((a, b) => a[0].localeCompare(b[0]))
|
|
732
|
+
.slice(-100)
|
|
733
|
+
.map(([day, count]) => ({ day, count }));
|
|
734
|
+
return {
|
|
735
|
+
total_404s: total404s,
|
|
736
|
+
unique_paths: pathMap.size,
|
|
737
|
+
internal_referrer_count: internalReferrerCount,
|
|
738
|
+
top_paths,
|
|
739
|
+
by_source: bySource,
|
|
740
|
+
over_time,
|
|
741
|
+
};
|
|
742
|
+
}
|
package/dist/ui/index.d.ts
CHANGED
|
@@ -5,5 +5,6 @@ export * from './analytics_panel.js';
|
|
|
5
5
|
export * from './datapoint_panel.js';
|
|
6
6
|
export * from './journey_panel.js';
|
|
7
7
|
export * from './search_queries_panel.js';
|
|
8
|
+
export * from './not_found_panel.js';
|
|
8
9
|
export * from './ga4_connection_card.js';
|
|
9
10
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/ui/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/ui/index.ts"],"names":[],"mappings":"AAGA,cAAc,oBAAoB,CAAC;AACnC,cAAc,kBAAkB,CAAC;AACjC,cAAc,oBAAoB,CAAC;AACnC,cAAc,sBAAsB,CAAC;AACrC,cAAc,sBAAsB,CAAC;AACrC,cAAc,oBAAoB,CAAC;AACnC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,0BAA0B,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/ui/index.ts"],"names":[],"mappings":"AAGA,cAAc,oBAAoB,CAAC;AACnC,cAAc,kBAAkB,CAAC;AACjC,cAAc,oBAAoB,CAAC;AACnC,cAAc,sBAAsB,CAAC;AACrC,cAAc,sBAAsB,CAAC;AACrC,cAAc,oBAAoB,CAAC;AACnC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,sBAAsB,CAAC;AACrC,cAAc,0BAA0B,CAAC"}
|
package/dist/ui/index.js
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"not_found_panel.d.ts","sourceRoot":"","sources":["../../src/ui/not_found_panel.tsx"],"names":[],"mappings":"AA4FA,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,wBAAgB,aAAa,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,kBAAkB,+BAiKnE"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useState, useEffect } from 'react';
|
|
4
|
+
const WINDOW_KEYS = ['Total', '24h', '7d', '28d', '90d'];
|
|
5
|
+
function windowKeyToSince(key) {
|
|
6
|
+
if (key === 'Total')
|
|
7
|
+
return undefined;
|
|
8
|
+
const days = key === '24h' ? 1 : key === '7d' ? 7 : key === '28d' ? 28 : 90;
|
|
9
|
+
const d = new Date();
|
|
10
|
+
d.setDate(d.getDate() - days);
|
|
11
|
+
return d.toISOString();
|
|
12
|
+
}
|
|
13
|
+
// ---------------------------------------------------------------------------
|
|
14
|
+
// MiniBar
|
|
15
|
+
// ---------------------------------------------------------------------------
|
|
16
|
+
// Format a stored timestamp ("…T…Z" or "YYYY-MM-DD HH:MM:SS") to second precision.
|
|
17
|
+
function fmtTs(iso) {
|
|
18
|
+
if (!iso)
|
|
19
|
+
return '';
|
|
20
|
+
return iso.slice(0, 19).replace('T', ' ');
|
|
21
|
+
}
|
|
22
|
+
function MiniBar({ value, max, color = '#6366f1' }) {
|
|
23
|
+
const pct = max > 0 ? (value / max) * 100 : 0;
|
|
24
|
+
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 })] }));
|
|
25
|
+
}
|
|
26
|
+
// ---------------------------------------------------------------------------
|
|
27
|
+
// SummaryCard
|
|
28
|
+
// ---------------------------------------------------------------------------
|
|
29
|
+
function SummaryCard({ label, value }) {
|
|
30
|
+
return (_jsxs("div", { className: "rounded-lg border bg-white p-4 flex flex-col gap-1", children: [_jsx("p", { className: "text-[10px] text-gray-400 uppercase tracking-wide", children: label }), _jsx("p", { className: "text-2xl font-semibold text-gray-800 tabular-nums", children: value })] }));
|
|
31
|
+
}
|
|
32
|
+
export function NotFoundPanel({ appId, apiBase }) {
|
|
33
|
+
const [windowKey, setWindowKey] = useState('Total');
|
|
34
|
+
const [data, setData] = useState(null);
|
|
35
|
+
const [loading, setLoading] = useState(false);
|
|
36
|
+
const [error, setError] = useState(null);
|
|
37
|
+
const since = windowKeyToSince(windowKey);
|
|
38
|
+
useEffect(() => {
|
|
39
|
+
if (!appId)
|
|
40
|
+
return;
|
|
41
|
+
let cancelled = false;
|
|
42
|
+
setLoading(true);
|
|
43
|
+
setError(null);
|
|
44
|
+
const params = new URLSearchParams({ app_id: appId });
|
|
45
|
+
if (since)
|
|
46
|
+
params.set('since', since);
|
|
47
|
+
fetch(`${apiBase}/analytics/not-found?${params}`, { credentials: 'include' })
|
|
48
|
+
.then((r) => r.json().catch(() => null))
|
|
49
|
+
.then((body) => {
|
|
50
|
+
if (cancelled)
|
|
51
|
+
return;
|
|
52
|
+
setData(body?.data ?? null);
|
|
53
|
+
setLoading(false);
|
|
54
|
+
})
|
|
55
|
+
.catch((err) => {
|
|
56
|
+
if (cancelled)
|
|
57
|
+
return;
|
|
58
|
+
setError(err instanceof Error ? err.message : 'Load error');
|
|
59
|
+
setLoading(false);
|
|
60
|
+
});
|
|
61
|
+
return () => { cancelled = true; };
|
|
62
|
+
}, [appId, apiBase, since]);
|
|
63
|
+
const windowTabs = (_jsx("div", { className: "flex flex-wrap gap-2", children: WINDOW_KEYS.map((key) => (_jsx("button", { type: "button", onClick: () => setWindowKey(key), className: key === windowKey
|
|
64
|
+
? 'px-3 py-1 rounded text-xs font-medium bg-gray-800 text-white'
|
|
65
|
+
: 'px-3 py-1 rounded text-xs font-medium border border-gray-200 text-gray-500 hover:bg-gray-50', children: key }, key))) }));
|
|
66
|
+
if (loading) {
|
|
67
|
+
return (_jsxs("div", { className: "flex flex-col gap-6 p-4", children: [windowTabs, _jsx("div", { className: "p-4 text-xs text-gray-400", children: "Loading\u2026" })] }));
|
|
68
|
+
}
|
|
69
|
+
if (error) {
|
|
70
|
+
return (_jsxs("div", { className: "flex flex-col gap-6 p-4", children: [windowTabs, _jsx("div", { className: "rounded-lg border border-red-200 bg-red-50 p-3 text-xs text-red-700", children: error })] }));
|
|
71
|
+
}
|
|
72
|
+
if (!data) {
|
|
73
|
+
return (_jsxs("div", { className: "flex flex-col gap-6 p-4", children: [windowTabs, _jsx("div", { className: "rounded-lg border p-6 text-center text-sm text-gray-400", children: "No data yet." })] }));
|
|
74
|
+
}
|
|
75
|
+
const maxPathCount = data.top_paths.length > 0 ? Math.max(...data.top_paths.map((p) => p.count), 1) : 1;
|
|
76
|
+
const maxDayCount = data.over_time.length > 0 ? Math.max(...data.over_time.map((d) => d.count), 1) : 1;
|
|
77
|
+
const maxSourceCount = Math.max(data.by_source.internal, data.by_source.search, data.by_source.external, data.by_source.direct, 1);
|
|
78
|
+
return (_jsxs("div", { className: "flex flex-col gap-6 p-4", children: [windowTabs, _jsxs("div", { className: "grid grid-cols-2 md:grid-cols-4 gap-4", children: [_jsx(SummaryCard, { label: "Total 404s", value: data.total_404s }), _jsx(SummaryCard, { label: "Unique Paths", value: data.unique_paths }), _jsx(SummaryCard, { label: "Internal Broken Links", value: data.internal_referrer_count }), _jsx(SummaryCard, { label: "Direct / Typed", value: data.by_source.direct })] }), _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 Missing Paths" }), data.top_paths.length === 0 ? (_jsx("p", { className: "text-xs text-gray-400", children: "No 404s recorded yet." })) : (_jsx("div", { className: "flex flex-col gap-1", children: data.top_paths.map((p) => (_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("span", { className: "text-xs text-gray-600 w-40 truncate", title: p.path, children: p.path }), p.internalCount > 0 && (_jsx("span", { className: "text-[10px] font-medium px-1.5 py-0.5 rounded-full border border-amber-200 bg-amber-50 text-amber-800 whitespace-nowrap", children: "broken internal link" })), _jsx(MiniBar, { value: p.count, max: maxPathCount }), _jsx("span", { className: "text-[10px] text-gray-400 tabular-nums whitespace-nowrap", title: `Last seen ${p.lastAt}`, children: fmtTs(p.lastAt) })] }, p.path))) }))] }), _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: "Traffic Source" }), _jsxs("div", { className: "flex flex-col gap-1", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("span", { className: "text-xs text-gray-600 w-40", children: "Internal (broken links)" }), _jsx(MiniBar, { value: data.by_source.internal, max: maxSourceCount, color: "#f59e0b" })] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx("span", { className: "text-xs text-gray-600 w-40", children: "Search" }), _jsx(MiniBar, { value: data.by_source.search, max: maxSourceCount })] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx("span", { className: "text-xs text-gray-600 w-40", children: "External" }), _jsx(MiniBar, { value: data.by_source.external, max: maxSourceCount })] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx("span", { className: "text-xs text-gray-600 w-40", children: "Direct" }), _jsx(MiniBar, { value: data.by_source.direct, max: maxSourceCount })] })] })] }), 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: "404s 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))) })] }))] }));
|
|
79
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hazo_umetrics",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.12.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",
|
|
@@ -51,11 +51,11 @@
|
|
|
51
51
|
"hazo_logs": "^2.1.1",
|
|
52
52
|
"hazo_config": "^2.4.1",
|
|
53
53
|
"hazo_connect": "^3.9.2",
|
|
54
|
-
"hazo_api": "^2.
|
|
55
|
-
"hazo_auth": "^10.
|
|
54
|
+
"hazo_api": "^2.6.0",
|
|
55
|
+
"hazo_auth": "^10.9.0",
|
|
56
56
|
"hazo_secure": "^1.4.0",
|
|
57
57
|
"hazo_audit": "^2.1.2",
|
|
58
|
-
"hazo_ui": "^6.
|
|
58
|
+
"hazo_ui": "^6.1.0",
|
|
59
59
|
"next": "^14.0.0 || ^16.0.0",
|
|
60
60
|
"react": "^18.0.0 || ^19.0.0",
|
|
61
61
|
"react-dom": "^18.0.0 || ^19.0.0"
|