@polishd/next 0.2.3-dev.2 → 0.2.3-dev.4
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/CHANGELOG.md +60 -0
- package/dist/ai/actions.d.ts +7 -1
- package/dist/ai/actions.d.ts.map +1 -1
- package/dist/ai/actions.js +10 -0
- package/dist/ai/actions.js.map +1 -1
- package/dist/ai/fix.d.ts +14 -0
- package/dist/ai/fix.d.ts.map +1 -0
- package/dist/ai/fix.js +254 -0
- package/dist/ai/fix.js.map +1 -0
- package/dist/ai/issues.d.ts +12 -0
- package/dist/ai/issues.d.ts.map +1 -1
- package/dist/ai/issues.js +16 -2
- package/dist/ai/issues.js.map +1 -1
- package/dist/ai/types.d.ts +49 -0
- package/dist/ai/types.d.ts.map +1 -1
- package/dist/dashboard/chrome.d.ts +15 -0
- package/dist/dashboard/chrome.d.ts.map +1 -1
- package/dist/dashboard/chrome.js +5 -1
- package/dist/dashboard/chrome.js.map +1 -1
- package/dist/dashboard/data.d.ts +18 -0
- package/dist/dashboard/data.d.ts.map +1 -0
- package/dist/dashboard/data.js +70 -0
- package/dist/dashboard/data.js.map +1 -0
- package/dist/dashboard/index.d.ts +1 -1
- package/dist/dashboard/index.d.ts.map +1 -1
- package/dist/dashboard/index.js +211 -85
- package/dist/dashboard/index.js.map +1 -1
- package/dist/dashboard/issue-list.d.ts +11 -0
- package/dist/dashboard/issue-list.d.ts.map +1 -0
- package/dist/dashboard/issue-list.js +159 -0
- package/dist/dashboard/issue-list.js.map +1 -0
- package/dist/dashboard/issues.d.ts +31 -2
- package/dist/dashboard/issues.d.ts.map +1 -1
- package/dist/dashboard/issues.js +4 -71
- package/dist/dashboard/issues.js.map +1 -1
- package/dist/dashboard/markdown-parse.d.ts +87 -0
- package/dist/dashboard/markdown-parse.d.ts.map +1 -0
- package/dist/dashboard/markdown-parse.js +244 -0
- package/dist/dashboard/markdown-parse.js.map +1 -0
- package/dist/dashboard/markdown.d.ts +5 -0
- package/dist/dashboard/markdown.d.ts.map +1 -0
- package/dist/dashboard/markdown.js +65 -0
- package/dist/dashboard/markdown.js.map +1 -0
- package/dist/dashboard.css +74 -18
- package/package.json +1 -1
|
@@ -23,4 +23,19 @@ export default function DashboardChrome({ active, showInstalls, showIssues, chil
|
|
|
23
23
|
showIssues?: boolean;
|
|
24
24
|
children: ReactNode;
|
|
25
25
|
}): import("react").JSX.Element;
|
|
26
|
+
/**
|
|
27
|
+
* Stand-in for the tab being fetched.
|
|
28
|
+
*
|
|
29
|
+
* Deliberately the *shape* every tab shares — a title block, a wide card, a
|
|
30
|
+
* grid, a list — rather than a per-tab replica. A skeleton that mirrors the
|
|
31
|
+
* real layout too closely reads as a broken render when the real thing lands
|
|
32
|
+
* slightly different; this one reads as "loading" and gets out of the way.
|
|
33
|
+
*
|
|
34
|
+
* Exported so the server page can reuse it as the Suspense fallback while a
|
|
35
|
+
* tab's data streams in — the same skeleton the client shows on click, so the
|
|
36
|
+
* handoff from "navigating" to "loading data" is invisible.
|
|
37
|
+
*/
|
|
38
|
+
export declare function TabSkeleton({ name }: {
|
|
39
|
+
name: string;
|
|
40
|
+
}): import("react").JSX.Element;
|
|
26
41
|
//# sourceMappingURL=chrome.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chrome.d.ts","sourceRoot":"","sources":["../../src/dashboard/chrome.tsx"],"names":[],"mappings":"AA4BA,OAAO,EAQL,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AAaf,MAAM,MAAM,mBAAmB,GAC3B,WAAW,GACX,QAAQ,GACR,QAAQ,GACR,UAAU,GACV,UAAU,CAAC;AAsBf;;;GAGG;AACH,wBAAgB,OAAO,CAAC,EACtB,GAAG,EACH,SAAS,EACT,KAAK,EACL,OAAe,EACf,IAAY,EACZ,YAAY,EAAE,SAAS,EACvB,QAAQ,GACT,EAAE;IACD,GAAG,EAAE,mBAAmB,CAAC;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,wDAAwD;IACxD,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,wDAAwD;IACxD,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,SAAS,CAAC;CACrB,+BAoBA;AAkBD,MAAM,CAAC,OAAO,UAAU,eAAe,CAAC,EACtC,MAAM,EACN,YAAoB,EACpB,UAAkB,EAClB,QAAQ,GACT,EAAE;IACD,MAAM,EAAE,mBAAmB,CAAC;IAC5B,wEAAwE;IACxE,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,6EAA6E;IAC7E,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,QAAQ,EAAE,SAAS,CAAC;CACrB,+BA8EA"}
|
|
1
|
+
{"version":3,"file":"chrome.d.ts","sourceRoot":"","sources":["../../src/dashboard/chrome.tsx"],"names":[],"mappings":"AA4BA,OAAO,EAQL,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AAaf,MAAM,MAAM,mBAAmB,GAC3B,WAAW,GACX,QAAQ,GACR,QAAQ,GACR,UAAU,GACV,UAAU,CAAC;AAsBf;;;GAGG;AACH,wBAAgB,OAAO,CAAC,EACtB,GAAG,EACH,SAAS,EACT,KAAK,EACL,OAAe,EACf,IAAY,EACZ,YAAY,EAAE,SAAS,EACvB,QAAQ,GACT,EAAE;IACD,GAAG,EAAE,mBAAmB,CAAC;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,wDAAwD;IACxD,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,wDAAwD;IACxD,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,SAAS,CAAC;CACrB,+BAoBA;AAkBD,MAAM,CAAC,OAAO,UAAU,eAAe,CAAC,EACtC,MAAM,EACN,YAAoB,EACpB,UAAkB,EAClB,QAAQ,GACT,EAAE;IACD,MAAM,EAAE,mBAAmB,CAAC;IAC5B,wEAAwE;IACxE,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,6EAA6E;IAC7E,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,QAAQ,EAAE,SAAS,CAAC;CACrB,+BA8EA;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,WAAW,CAAC,EAAE,IAAI,EAAE,EAAE;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,+BAiDrD"}
|
package/dist/dashboard/chrome.js
CHANGED
|
@@ -116,8 +116,12 @@ export default function DashboardChrome({ active, showInstalls = false, showIssu
|
|
|
116
116
|
* grid, a list — rather than a per-tab replica. A skeleton that mirrors the
|
|
117
117
|
* real layout too closely reads as a broken render when the real thing lands
|
|
118
118
|
* slightly different; this one reads as "loading" and gets out of the way.
|
|
119
|
+
*
|
|
120
|
+
* Exported so the server page can reuse it as the Suspense fallback while a
|
|
121
|
+
* tab's data streams in — the same skeleton the client shows on click, so the
|
|
122
|
+
* handoff from "navigating" to "loading data" is invisible.
|
|
119
123
|
*/
|
|
120
|
-
function TabSkeleton({ name }) {
|
|
124
|
+
export function TabSkeleton({ name }) {
|
|
121
125
|
return (_jsxs("div", { className: "animate-pulse text-white", children: [_jsxs("span", { className: "sr-only", role: "status", children: ["Loading ", name] }), _jsxs("div", { className: "mb-8 flex items-start justify-between gap-3 border-b border-[#2e2e2e] pb-6", children: [_jsxs("div", { children: [_jsx("div", { className: "h-[22px] w-40 rounded bg-[#1a1a1a]" }), _jsx("div", { className: "mt-2.5 h-3 w-64 max-w-full rounded bg-[#141414]" })] }), _jsx("div", { className: "h-6 w-28 shrink-0 rounded-full bg-[#141414]" })] }), _jsxs("div", { className: `mb-8 ${card} px-4 py-5 sm:px-5`, children: [_jsx("div", { className: "h-3 w-32 rounded bg-[#1a1a1a]" }), _jsxs("div", { className: "mt-4 space-y-2", children: [_jsx("div", { className: "h-3 w-full rounded bg-[#141414]" }), _jsx("div", { className: "h-3 w-[92%] rounded bg-[#141414]" }), _jsx("div", { className: "h-3 w-[68%] rounded bg-[#141414]" })] })] }), _jsx("div", { className: "mb-8 grid grid-cols-2 gap-3 sm:grid-cols-3 lg:grid-cols-6", children: Array.from({ length: 6 }, (_, i) => (_jsxs("div", { className: `${card} px-4 py-4`, children: [_jsx("div", { className: "h-6 w-14 rounded bg-[#1a1a1a]" }), _jsx("div", { className: "mt-3 h-2.5 w-16 rounded bg-[#141414]" })] }, i))) }), _jsx("div", { className: `${card} overflow-hidden`, children: Array.from({ length: 4 }, (_, i) => (_jsxs("div", { className: "flex items-center justify-between gap-4 border-t border-[#2e2e2e] px-5 py-3.5 first:border-t-0", children: [_jsx("div", { className: "h-3 flex-1 rounded bg-[#141414]", style: { maxWidth: `${70 - i * 12}%` } }), _jsx("div", { className: "h-3 w-12 shrink-0 rounded bg-[#141414]" })] }, i))) })] }));
|
|
122
126
|
}
|
|
123
127
|
//# sourceMappingURL=chrome.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chrome.js","sourceRoot":"","sources":["../../src/dashboard/chrome.tsx"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"chrome.js","sourceRoot":"","sources":["../../src/dashboard/chrome.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { type PolishdDashboardData } from "../server/queries";
|
|
2
|
+
export declare const loadOverview: () => Promise<import("../server").OverviewStats>;
|
|
3
|
+
export declare const loadHealth: () => Promise<import("../server").CaptureHealth>;
|
|
4
|
+
export declare const loadPages: () => Promise<import("../server").TopPage[]>;
|
|
5
|
+
export declare const loadTrends: () => Promise<import("../server").MetricTrends>;
|
|
6
|
+
export declare const loadElements: () => Promise<import("../server").ElementStat[]>;
|
|
7
|
+
export declare const loadDevices: () => Promise<import("../server").DeviceBucket[]>;
|
|
8
|
+
export declare const loadTopUsed: () => Promise<import("../server").TopInteraction[]>;
|
|
9
|
+
export declare const loadJourneys: () => Promise<import("../server").SessionJourney[]>;
|
|
10
|
+
export declare const loadErrors: () => Promise<import("../server").RecentError[]>;
|
|
11
|
+
export declare const loadMonitored: () => Promise<import("../server").MonitoredComponent[]>;
|
|
12
|
+
/**
|
|
13
|
+
* The full analytics payload, assembled from the same cached parts the
|
|
14
|
+
* sections use — so the summary card asking for everything costs nothing
|
|
15
|
+
* beyond what the sections are already fetching for themselves.
|
|
16
|
+
*/
|
|
17
|
+
export declare const loadDashboardData: () => Promise<PolishdDashboardData>;
|
|
18
|
+
//# sourceMappingURL=data.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"data.d.ts","sourceRoot":"","sources":["../../src/dashboard/data.ts"],"names":[],"mappings":"AAgBA,OAAO,EAWL,KAAK,oBAAoB,EAC1B,MAAM,mBAAmB,CAAC;AAe3B,eAAO,MAAM,YAAY,kDAAqB,CAAC;AAC/C,eAAO,MAAM,UAAU,kDAA0B,CAAC;AAClD,eAAO,MAAM,SAAS,8CAA8B,CAAC;AACrD,eAAO,MAAM,UAAU,iDAAyB,CAAC;AACjD,eAAO,MAAM,YAAY,kDAAmC,CAAC;AAC7D,eAAO,MAAM,WAAW,mDAA4B,CAAC;AACrD,eAAO,MAAM,WAAW,qDAAsC,CAAC;AAC/D,eAAO,MAAM,YAAY,qDAAqC,CAAC;AAC/D,eAAO,MAAM,UAAU,kDAAkC,CAAC;AAC1D,eAAO,MAAM,aAAa,yDAAgC,CAAC;AAE3D;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,QAAmB,OAAO,CAAC,oBAAoB,CAoC3E,CAAC"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-request cached loaders for the dashboard's streamed sections.
|
|
3
|
+
*
|
|
4
|
+
* Each section of the analytics tab is an async server component that awaits
|
|
5
|
+
* only the queries it actually renders, so a slow aggregate (journeys, metric
|
|
6
|
+
* trends) can't hold back a fast one (devices). React's `cache()` scopes the
|
|
7
|
+
* memoization to the request, so sections that share an input — the summary
|
|
8
|
+
* card wants everything, the capture banner wants overview + health — still
|
|
9
|
+
* run each underlying query exactly once per render.
|
|
10
|
+
*
|
|
11
|
+
* Lives here rather than in server/queries.ts because that module is shared
|
|
12
|
+
* with the AI summary layer, which deliberately doesn't import React.
|
|
13
|
+
*/
|
|
14
|
+
import { cache } from "react";
|
|
15
|
+
import { ensureReclassifiedHistory } from "../server/reclassify";
|
|
16
|
+
import { getCaptureHealth, getDeviceBreakdown, getElementStats, getMetricTrends, getMonitoredComponents, getOverview, getRecentErrors, getSessionJourneys, getTopInteractions, getTopPages, } from "../server/queries";
|
|
17
|
+
/**
|
|
18
|
+
* Wrap a query so it (a) waits for the one-time history reclassification that
|
|
19
|
+
* the batch loader used to run up front — the sweep is module-memoized, so
|
|
20
|
+
* concurrent sections join a single pass — and (b) runs once per request no
|
|
21
|
+
* matter how many sections await it.
|
|
22
|
+
*/
|
|
23
|
+
function swept(fn) {
|
|
24
|
+
return cache(async () => {
|
|
25
|
+
await ensureReclassifiedHistory();
|
|
26
|
+
return fn();
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
export const loadOverview = swept(getOverview);
|
|
30
|
+
export const loadHealth = swept(getCaptureHealth);
|
|
31
|
+
export const loadPages = swept(() => getTopPages(8));
|
|
32
|
+
export const loadTrends = swept(getMetricTrends);
|
|
33
|
+
export const loadElements = swept(() => getElementStats(12));
|
|
34
|
+
export const loadDevices = swept(getDeviceBreakdown);
|
|
35
|
+
export const loadTopUsed = swept(() => getTopInteractions(12));
|
|
36
|
+
export const loadJourneys = swept(() => getSessionJourneys(6));
|
|
37
|
+
export const loadErrors = swept(() => getRecentErrors(8));
|
|
38
|
+
export const loadMonitored = swept(getMonitoredComponents);
|
|
39
|
+
/**
|
|
40
|
+
* The full analytics payload, assembled from the same cached parts the
|
|
41
|
+
* sections use — so the summary card asking for everything costs nothing
|
|
42
|
+
* beyond what the sections are already fetching for themselves.
|
|
43
|
+
*/
|
|
44
|
+
export const loadDashboardData = cache(async () => {
|
|
45
|
+
const [overview, health, pages, trends, elements, devices, topUsed, journeys, errors, monitored,] = await Promise.all([
|
|
46
|
+
loadOverview(),
|
|
47
|
+
loadHealth(),
|
|
48
|
+
loadPages(),
|
|
49
|
+
loadTrends(),
|
|
50
|
+
loadElements(),
|
|
51
|
+
loadDevices(),
|
|
52
|
+
loadTopUsed(),
|
|
53
|
+
loadJourneys(),
|
|
54
|
+
loadErrors(),
|
|
55
|
+
loadMonitored(),
|
|
56
|
+
]);
|
|
57
|
+
return {
|
|
58
|
+
overview,
|
|
59
|
+
health,
|
|
60
|
+
pages,
|
|
61
|
+
trends,
|
|
62
|
+
elements,
|
|
63
|
+
devices,
|
|
64
|
+
topUsed,
|
|
65
|
+
journeys,
|
|
66
|
+
errors,
|
|
67
|
+
monitored,
|
|
68
|
+
};
|
|
69
|
+
});
|
|
70
|
+
//# sourceMappingURL=data.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"data.js","sourceRoot":"","sources":["../../src/dashboard/data.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,EAAE,KAAK,EAAE,MAAM,OAAO,CAAC;AAE9B,OAAO,EAAE,yBAAyB,EAAE,MAAM,sBAAsB,CAAC;AACjE,OAAO,EACL,gBAAgB,EAChB,kBAAkB,EAClB,eAAe,EACf,eAAe,EACf,sBAAsB,EACtB,WAAW,EACX,eAAe,EACf,kBAAkB,EAClB,kBAAkB,EAClB,WAAW,GAEZ,MAAM,mBAAmB,CAAC;AAE3B;;;;;GAKG;AACH,SAAS,KAAK,CAAI,EAAoB;IACpC,OAAO,KAAK,CAAC,KAAK,IAAI,EAAE;QACtB,MAAM,yBAAyB,EAAE,CAAC;QAClC,OAAO,EAAE,EAAE,CAAC;IACd,CAAC,CAAC,CAAC;AACL,CAAC;AAED,MAAM,CAAC,MAAM,YAAY,GAAG,KAAK,CAAC,WAAW,CAAC,CAAC;AAC/C,MAAM,CAAC,MAAM,UAAU,GAAG,KAAK,CAAC,gBAAgB,CAAC,CAAC;AAClD,MAAM,CAAC,MAAM,SAAS,GAAG,KAAK,CAAC,GAAG,EAAE,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC;AACrD,MAAM,CAAC,MAAM,UAAU,GAAG,KAAK,CAAC,eAAe,CAAC,CAAC;AACjD,MAAM,CAAC,MAAM,YAAY,GAAG,KAAK,CAAC,GAAG,EAAE,CAAC,eAAe,CAAC,EAAE,CAAC,CAAC,CAAC;AAC7D,MAAM,CAAC,MAAM,WAAW,GAAG,KAAK,CAAC,kBAAkB,CAAC,CAAC;AACrD,MAAM,CAAC,MAAM,WAAW,GAAG,KAAK,CAAC,GAAG,EAAE,CAAC,kBAAkB,CAAC,EAAE,CAAC,CAAC,CAAC;AAC/D,MAAM,CAAC,MAAM,YAAY,GAAG,KAAK,CAAC,GAAG,EAAE,CAAC,kBAAkB,CAAC,CAAC,CAAC,CAAC,CAAC;AAC/D,MAAM,CAAC,MAAM,UAAU,GAAG,KAAK,CAAC,GAAG,EAAE,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC,CAAC;AAC1D,MAAM,CAAC,MAAM,aAAa,GAAG,KAAK,CAAC,sBAAsB,CAAC,CAAC;AAE3D;;;;GAIG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,KAAK,CAAC,KAAK,IAAmC,EAAE;IAC/E,MAAM,CACJ,QAAQ,EACR,MAAM,EACN,KAAK,EACL,MAAM,EACN,QAAQ,EACR,OAAO,EACP,OAAO,EACP,QAAQ,EACR,MAAM,EACN,SAAS,EACV,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;QACpB,YAAY,EAAE;QACd,UAAU,EAAE;QACZ,SAAS,EAAE;QACX,UAAU,EAAE;QACZ,YAAY,EAAE;QACd,WAAW,EAAE;QACb,WAAW,EAAE;QACb,YAAY,EAAE;QACd,UAAU,EAAE;QACZ,aAAa,EAAE;KAChB,CAAC,CAAC;IACH,OAAO;QACL,QAAQ;QACR,MAAM;QACN,KAAK;QACL,MAAM;QACN,QAAQ;QACR,OAAO;QACP,OAAO;QACP,QAAQ;QACR,MAAM;QACN,SAAS;KACV,CAAC;AACJ,CAAC,CAAC,CAAC"}
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
* });
|
|
16
16
|
*/
|
|
17
17
|
import type { Metadata } from "next";
|
|
18
|
-
import type
|
|
18
|
+
import { type ReactNode } from "react";
|
|
19
19
|
import { type PolishdConfig } from "../config";
|
|
20
20
|
import { type PolishdDashboardData } from "../server/queries";
|
|
21
21
|
import { type PolishdAuthenticate } from "../ai/guard";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/dashboard/index.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,MAAM,CAAC;AAErC,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/dashboard/index.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,MAAM,CAAC;AAErC,OAAO,EAAY,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAEjD,OAAO,EAAiC,KAAK,aAAa,EAAE,MAAM,WAAW,CAAC;AAG9E,OAAO,EAIL,KAAK,oBAAoB,EAO1B,MAAM,mBAAmB,CAAC;AAc3B,OAAO,EAGL,KAAK,mBAAmB,EAEzB,MAAM,aAAa,CAAC;AACrB,OAAO,EAGL,KAAK,uBAAuB,EAC7B,MAAM,cAAc,CAAC;AAYtB,OAAO,KAAK,EACV,uBAAuB,EACvB,qBAAqB,EACrB,cAAc,EACf,MAAM,aAAa,CAAC;AAcrB,eAAO,MAAM,OAAO,WAAW,CAAC;AAChC,eAAO,MAAM,OAAO,kBAAkB,CAAC;AAEvC,eAAO,MAAM,QAAQ,EAAE,QAGtB,CAAC;AA0HF,OAAO,EAAE,wBAAwB,EAAE,KAAK,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AAIxF,OAAO,EACL,gBAAgB,EAChB,wBAAwB,EACxB,KAAK,uBAAuB,GAC7B,MAAM,cAAc,CAAC;AAEtB,kDAAkD;AAClD,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,cAAc,GAAG,IAAI,CAAC;IAC/B,QAAQ,EAAE,uBAAuB,CAAC;IAClC,KAAK,EAAE,OAAO,CAAC;IACf,OAAO,EAAE,qBAAqB,GAAG,IAAI,CAAC;IACtC,iEAAiE;IACjE,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,wEAAwE;IACxE,eAAe,EAAE,OAAO,CAAC;CAC1B;AAID,YAAY,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAC;AAgXpD,wBAAgB,gBAAgB,CAAC,EAC/B,IAAI,EACJ,EAAE,EACF,YAAoB,EACpB,UAAkB,GACnB,EAAE;IACD,IAAI,EAAE,oBAAoB,CAAC;IAC3B,EAAE,EAAE,eAAe,CAAC;IACpB,wEAAwE;IACxE,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,mEAAmE;IACnE,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB,+BA0CA;AAsWD;;;;GAIG;AACH,MAAM,WAAW,gBAAgB;IAC/B,YAAY,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAAC,CAAC,CAAC;CACvE;AAED,MAAM,WAAW,wBAAwB;IACvC;;;;;;;;;OASG;IACH,YAAY,CAAC,EAAE,mBAAmB,CAAC;IACnC;;;OAGG;IACH,YAAY,CAAC,EAAE,SAAS,CAAC;IACzB,iEAAiE;IACjE,SAAS,CAAC,EAAE,uBAAuB,CAAC;IACpC;;;;;;;OAOG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC,aAAa,CAAC,CAAC;IAChC;;;;;OAKG;IACH,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AA0ID;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,GAAE,wBAA6B,IA4BjC,QAAO,gBAAqB,0CAqF/D"}
|
package/dist/dashboard/index.js
CHANGED
|
@@ -1,21 +1,24 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { after } from "next/server";
|
|
3
|
+
import { Suspense } from "react";
|
|
3
4
|
import { registerPolishdDashboardRoute } from "../config";
|
|
4
5
|
import { captureStatus } from "../shared/capture-status";
|
|
5
|
-
import {
|
|
6
|
+
import { getTelemetryInstalls, getTelemetryTopPaths, hasTelemetryInstalls, } from "../server/queries";
|
|
7
|
+
import { loadDashboardData, loadDevices, loadElements, loadErrors, loadHealth, loadJourneys, loadMonitored, loadOverview, loadPages, loadTopUsed, loadTrends, } from "./data";
|
|
6
8
|
import { polishdAuthContext, registerPolishdAuth, } from "../ai/guard";
|
|
7
9
|
import { polishdDashboardToken, polishdTokenAuth, } from "./token-auth";
|
|
8
10
|
import { unlockPolishdDashboard } from "./unlock";
|
|
9
11
|
import { loadPolishdTelemetryState } from "../server/telemetry";
|
|
10
12
|
import { PolishdTelemetryConsent, PolishdTelemetryEmitter } from "./telemetry";
|
|
11
13
|
import { loadDesignReviewState } from "../ai/design";
|
|
14
|
+
import { listFixSuggestions } from "../ai/fix";
|
|
12
15
|
import { isGithubConnected } from "../ai/github";
|
|
13
16
|
import { listFiledIssues } from "../ai/issues";
|
|
14
17
|
import { loadProfileState } from "../ai/profile";
|
|
15
18
|
import { generateSummary, getAISettingsPublic, loadSummaryState } from "../ai/summary";
|
|
16
19
|
import { getDesignData } from "../server/design";
|
|
17
20
|
import { listSnapshots, snapshotCaptureIssue } from "../server/snapshots";
|
|
18
|
-
import DashboardChrome from "./chrome";
|
|
21
|
+
import DashboardChrome, { TabSkeleton } from "./chrome";
|
|
19
22
|
import { DesignPanel } from "./design";
|
|
20
23
|
import InstallsView from "./installs";
|
|
21
24
|
import IssuesView from "./issues";
|
|
@@ -76,18 +79,205 @@ export { loadPolishdDashboardData } from "../server/queries";
|
|
|
76
79
|
// Named explicitly by hosts that want token auth alongside their own options;
|
|
77
80
|
// `createPolishdPage()` already applies it when POLISHD_DASHBOARD_TOKEN is set.
|
|
78
81
|
export { polishdTokenAuth, POLISHD_DASHBOARD_COOKIE, } from "./token-auth";
|
|
79
|
-
// ──
|
|
80
|
-
|
|
81
|
-
|
|
82
|
+
// ── Analytics: header + status (shared by batch and streamed renders) ────────
|
|
83
|
+
function AnalyticsHeader({ pill }) {
|
|
84
|
+
return (_jsxs("div", { className: `mb-8 flex items-start justify-between gap-3 border-b ${border} pb-6`, children: [_jsxs("div", { children: [_jsx("h1", { className: "text-[22px] font-semibold tracking-tight text-white", children: "Analytics" }), _jsxs("p", { className: "mt-1 text-[13px] text-[#666]", children: ["Stage 1 \u2014 Capture. Hover", " ", _jsx("span", { className: "inline-flex h-3 w-3 items-center justify-center rounded-full border border-[#444] text-[8px] font-bold text-[#666]", children: "i" }), " ", "for calculation details."] })] }), pill] }));
|
|
85
|
+
}
|
|
86
|
+
function StatusPill({ ready }) {
|
|
87
|
+
return (_jsx("div", { className: `shrink-0 whitespace-nowrap rounded-full px-3 py-1 text-[11px] font-medium ${ready
|
|
88
|
+
? "bg-emerald-950 text-emerald-400"
|
|
89
|
+
: "bg-[#1a1a1a] text-[#666]"}`, children: ready ? "● collecting" : "○ no store" }));
|
|
90
|
+
}
|
|
91
|
+
function StatusBanners({ overview, health, }) {
|
|
82
92
|
// Events arriving without a session cookie mean the proxy didn't run on
|
|
83
93
|
// those requests — or that something posted straight at the public endpoint.
|
|
84
94
|
// Telling those apart is the whole job of `captureStatus`, which lives in
|
|
85
95
|
// shared/capture-status.ts with the record ingest writes, and is tested.
|
|
86
96
|
const capture = captureStatus(health, overview.totalEvents);
|
|
87
97
|
const windowLabel = `${Math.round(health.windowMs / 3_600_000)}h`;
|
|
88
|
-
return (
|
|
89
|
-
|
|
90
|
-
|
|
98
|
+
return (_jsxs(_Fragment, { children: [!overview.ready && (_jsxs("div", { className: `mb-8 rounded-lg border border-amber-900/50 bg-amber-950/30 px-4 py-3 text-[13px] text-amber-400`, children: ["The analytics store isn't writable in this environment. Run locally or configure a database \u2014 see the ", _jsx("span", { className: "font-mono text-amber-300", children: "@polishd/next" }), " DATABASE.md guide."] })), capture.level === "broken" && (_jsxs("div", { className: "mb-8 rounded-lg border border-red-900/50 bg-red-950/30 px-4 py-3 text-[13px] text-red-400", children: [_jsx("p", { className: "font-medium text-red-300", children: "Receiving events, but no session cookie \u2014 your proxy isn't running." }), _jsxs("p", { className: "mt-1.5", children: [capture.droppedBatches.toLocaleString(), " ", capture.droppedBatches === 1 ? "batch has" : "batches have", " been dropped", capture.nothingStored ? "" : ` and nothing has been stored in the last ${windowLabel}`, ". Polishd attributes every event to the anonymous cookie the proxy mints", capture.nothingStored ? ", so nothing is being stored" : "", ". Check that you have a", " ", _jsx("span", { className: "font-mono text-red-300", children: "proxy.ts" }), " (Next 16+) or", " ", _jsx("span", { className: "font-mono text-red-300", children: "middleware.ts" }), " (Next 15) at your project root \u2014 matching your installed Next major \u2014 exporting a function of the same name, with", " ", _jsx("span", { className: "font-mono text-red-300", children: "config.matcher" }), " written as an inline literal that covers the pages you're capturing."] }), _jsxs("p", { className: "mt-1.5 text-red-500/80", children: ["Run", " ", _jsx("span", { className: "font-mono text-red-300", children: "npx @polishd/next doctor --url <your deployed origin>" }), " ", "to check this against the running site. Plain", " ", _jsx("span", { className: "font-mono text-red-300", children: "doctor" }), " only reads your source tree, so it can pass every check while the deployed build still doesn't mint a cookie \u2014", " ", _jsx("span", { className: "font-mono text-red-300", children: "--url" }), " is the part that proves it."] })] })), capture.level === "notice" && (_jsx("div", { className: `mb-8 rounded-lg border ${border} bg-[#0f0f0f] px-4 py-3 text-[13px] text-[#999]`, children: _jsxs("p", { children: [capture.droppedBatches.toLocaleString(), " ", capture.droppedBatches === 1 ? "batch" : "batches", " arrived without a session cookie in the last ", windowLabel, capture.sharePct !== null && ` — ${formatShare(capture.sharePct)} of traffic`, " (bots, or visitors with cookies blocked). Those events aren't stored; capture itself is working", capture.storedSinceDrop ? " — a batch has been stored since the most recent drop" : "", "."] }) }))] }));
|
|
99
|
+
}
|
|
100
|
+
// ── Analytics: section shells ────────────────────────────────────────────────
|
|
101
|
+
// The titles render before their data arrives, so each shell takes the card
|
|
102
|
+
// as children — the batch render passes it resolved, the streamed render
|
|
103
|
+
// passes a Suspense boundary.
|
|
104
|
+
function OverviewSection({ children }) {
|
|
105
|
+
return (_jsx(Section, { title: _jsxs(_Fragment, { children: ["Overview", _jsx(InfoTip, { anchor: "left", text: "All-time totals for everything captured. Click any tile to open that metric as a chart over time, bucketed by hour or by day." })] }), children: children }));
|
|
106
|
+
}
|
|
107
|
+
function DevicesSection({ children }) {
|
|
108
|
+
return (_jsx(Section, { title: _jsxs(_Fragment, { children: ["Device sizes", _jsx(InfoTip, { anchor: "left", text: "One viewport sample per session, taken at session start, bucketed by CSS width: mobile (<640px), tablet (640\u20131023px), desktop (\u22651024px). Shows what screen size visitors actually use." })] }), children: children }));
|
|
109
|
+
}
|
|
110
|
+
function PagesSection({ children }) {
|
|
111
|
+
return (_jsx(Section, { title: _jsxs(_Fragment, { children: ["Top pages", _jsx(InfoTip, { anchor: "left", text: "The most-visited routes, ranked by distinct sessions. Click a page to open a chart of its sessions over time." })] }), children: children }));
|
|
112
|
+
}
|
|
113
|
+
function DevicesCard({ devices }) {
|
|
114
|
+
return (_jsx("div", { className: card, children: devices.length === 0 ? (_jsx("p", { className: "px-5 py-8 text-center text-[13px] text-[#555]", children: "No viewport data yet \u2014 browse the site then refresh." })) : (devices.map((d) => _jsx(DeviceRow, { bucket: d }, d.category))) }));
|
|
115
|
+
}
|
|
116
|
+
function MonitoredSection({ children }) {
|
|
117
|
+
return (_jsx(Section, { title: _jsxs(_Fragment, { children: ["Monitored components", _jsx(InfoTip, { anchor: "left", text: "Components explicitly wrapped in <PolishdMonitor>. Shows viewport engagement (views, avg time visible, scroll depth, dimensions) alongside click activity. A high view count with low clicks often signals interest without commitment." })] }), children: children }));
|
|
118
|
+
}
|
|
119
|
+
function MonitoredCard({ monitored }) {
|
|
120
|
+
return (_jsx("div", { className: card, children: monitored.length === 0 ? (_jsxs("p", { className: "px-5 py-8 text-center text-[13px] text-[#555]", children: ["No monitored components yet \u2014 wrap elements with", " ", _jsx("code", { className: "font-mono text-[#777]", children: "<PolishdMonitor name=\"…\">" }), " to track them here."] })) : (_jsx("div", { className: "overflow-x-auto", children: _jsxs("table", { className: "w-full min-w-[820px]", children: [_jsx("thead", { children: _jsxs("tr", { children: [_jsx(Th, { align: "left", tip: "The name prop passed to <PolishdMonitor>. Sessions and pages shown beneath.", children: "Component" }), _jsx(Th, { tip: "Times this region rendered (mount events). Only content-tracked monitors emit these.", children: "Mounts" }), _jsx(Th, { tip: "Times this component entered the viewport for \u2265500ms.", children: "Views" }), _jsx(Th, { tip: "Average time visible per viewport visit \u2014 a proxy for reading/engagement time.", children: "Avg time" }), _jsx(Th, { tip: "Average % of the component's height scrolled through per visit. 100% = user reached the bottom. Only measured for components taller than one screen \u2014 shorter ones show no value.", children: "Scroll depth" }), _jsx(Th, { tip: "Largest rendered height in px seen for this component \u2014 for an article, its full content height.", children: "Height" }), _jsx(Th, { tip: "Normal (non-rage, non-dead) clicks.", children: "Clicks" }), _jsx(Th, { tip: "Deliberate pointer hovers (\u2265200ms dwell).", children: "Hovers" }), _jsx(Th, { tip: "Rage clicks (3+ rapid clicks) \u2014 frustration signal.", children: "Rage" })] }) }), _jsx("tbody", { children: monitored.map((m) => (_jsx(MonitoredRow, { m: m }, m.name))) })] }) })) }));
|
|
121
|
+
}
|
|
122
|
+
function JourneysSection({ children }) {
|
|
123
|
+
return (_jsx(Section, { title: _jsxs(_Fragment, { children: ["Sampled user journeys", _jsx(InfoTip, { anchor: "left", text: "Full sessions sampled and ranked by a composite score (rage\u00D73 + dead\u00D72 + errors\u00D72.5), preferring complete recordings and recent ones. Click a session to open its start-to-finish flow chart." })] }), children: children }));
|
|
124
|
+
}
|
|
125
|
+
function FeaturesSection({ children }) {
|
|
126
|
+
return (_jsx(Section, { title: _jsxs(_Fragment, { children: ["Most-used features", _jsx(InfoTip, { anchor: "left", text: "Interactive elements ranked by raw click volume (successful clicks only \u2014 rage and dead clicks excluded). The buttons and features visitors actually use most." })] }), children: children }));
|
|
127
|
+
}
|
|
128
|
+
function FeaturesCard({ topUsed }) {
|
|
129
|
+
return (_jsx("div", { className: card, children: topUsed.length === 0 ? (_jsx("p", { className: "px-5 py-8 text-center text-[13px] text-[#555]", children: "No clicks captured yet \u2014 browse the site then refresh." })) : (_jsx(TopFeaturesTable, { features: topUsed, header: _jsxs("tr", { children: [_jsx(Th, { align: "left", tip: "Component name (from data-component) or DOM selector. Sample text and selector shown beneath.", children: "Element" }), _jsx(Th, { tip: "Total successful (interactive) clicks across all pages.", children: "Clicks" }), _jsx(Th, { tip: "Distinct sessions that clicked this element.", children: "Sessions" }), _jsx(Th, { tip: "How many distinct pages this element was clicked on.", children: "Pages" })] }) })) }));
|
|
130
|
+
}
|
|
131
|
+
function ElementsSection({ children }) {
|
|
132
|
+
return (_jsx(Section, { title: _jsxs(_Fragment, { children: ["Interactions by element", _jsx(InfoTip, { anchor: "left", text: "Click-type events grouped by DOM selector. This is which UI element the issues are on \u2014 the primary input to Stage 2 synthesis. Components wrapped in <PolishdMonitor> are listed separately under Monitored components." })] }), children: children }));
|
|
133
|
+
}
|
|
134
|
+
function ElementsCard({ elements }) {
|
|
135
|
+
return (_jsx("div", { className: card, children: elements.length === 0 ? (_jsx("p", { className: "px-5 py-8 text-center text-[13px] text-[#555]", children: "No interactions captured yet \u2014 browse the site then refresh." })) : (_jsx(ElementsTable, { elements: elements, header: _jsxs("tr", { children: [_jsx(Th, { align: "left", tip: "DOM selector path for the interacted element. Sample text shown beneath.", children: "Element" }), _jsx(Th, { tip: "rage\u00D73 + dead\u00D72 for this element across all pages.", children: "Score" }), _jsx(Th, { tip: "Normal (non-rage, non-dead) clicks.", children: "Clicks" }), _jsx(Th, { tip: "Rage clicks on this element.", children: "Rage" }), _jsx(Th, { tip: "Dead clicks on this element.", children: "Dead" }), _jsx(Th, { tip: "How many distinct pages this element appeared on.", children: "Pages" })] }) })) }));
|
|
136
|
+
}
|
|
137
|
+
function RecentErrors({ errors }) {
|
|
138
|
+
if (errors.length === 0)
|
|
139
|
+
return null;
|
|
140
|
+
return (_jsx(Section, { title: "Recent errors", children: _jsx("div", { className: card, children: errors.map((e, i) => (_jsx("div", { className: i > 0 ? divider : "", children: _jsxs("div", { className: "px-5 py-3", children: [_jsx("div", { className: `font-mono text-[13px] ${e.origin ? "text-[#8a6a6a]" : "text-red-400"}`, children: e.message }), _jsxs("div", { className: "mt-0.5 text-[11px] text-[#555]", children: [e.path, e.component ? ` · ${e.component}` : "", e.origin === "foreign" ? " · not your code" : "", e.origin === "unknown" ? " · unattributable" : ""] })] }) }, i))) }) }));
|
|
141
|
+
}
|
|
142
|
+
// ── Dashboard (presentational) ───────────────────────────────────────────────
|
|
143
|
+
// Renders from a fully resolved dataset in one pass. `createPolishdPage()` no
|
|
144
|
+
// longer uses this — it streams each section independently — but the component
|
|
145
|
+
// remains for hosts that already load the data themselves.
|
|
146
|
+
export function PolishdDashboard({ data, ai, showInstalls = false, showIssues = false, }) {
|
|
147
|
+
return (_jsx(DashboardChrome, { active: "analytics", showInstalls: showInstalls, showIssues: showIssues, children: _jsxs("main", { className: "text-white", children: [_jsx(AnalyticsHeader, { pill: _jsx(StatusPill, { ready: data.overview.ready }) }), _jsx(StatusBanners, { overview: data.overview, health: data.health }), _jsx(SummaryCard, { initialSummary: ai.summary, initialSettings: ai.settings, initialStale: ai.stale, initialProfile: ai.profile, initialGaps: ai.gaps, sourceAvailable: ai.sourceAvailable }), _jsx(OverviewSection, { children: _jsx(OverviewStatTiles, { overview: data.overview, trends: data.trends }) }), _jsx(DevicesSection, { children: _jsx(DevicesCard, { devices: data.devices }) }), _jsx(PagesSection, { children: _jsx(TopPagesTable, { pages: data.pages }) }), _jsx(MonitoredSection, { children: _jsx(MonitoredCard, { monitored: data.monitored }) }), _jsx(JourneysSection, { children: _jsx(JourneyList, { journeys: data.journeys }) }), _jsx(FeaturesSection, { children: _jsx(FeaturesCard, { topUsed: data.topUsed }) }), _jsx(ElementsSection, { children: _jsx(ElementsCard, { elements: data.elements }) }), _jsx(RecentErrors, { errors: data.errors })] }) }));
|
|
148
|
+
}
|
|
149
|
+
// ── Streaming render ─────────────────────────────────────────────────────────
|
|
150
|
+
// The page used to await every query before sending a byte, so the whole tab
|
|
151
|
+
// arrived at the speed of its slowest aggregate. Instead, each section below
|
|
152
|
+
// is its own async server component behind its own Suspense boundary: the
|
|
153
|
+
// chrome, header, and section titles stream out immediately, and each card
|
|
154
|
+
// replaces its skeleton the moment its own data resolves. The per-request
|
|
155
|
+
// cache in ./data keeps sections that share inputs from re-running queries.
|
|
156
|
+
function CardSkeleton({ rows = 4 }) {
|
|
157
|
+
return (_jsx("div", { className: `${card} animate-pulse overflow-hidden`, "aria-hidden": "true", children: Array.from({ length: rows }, (_, i) => (_jsxs("div", { className: "flex items-center justify-between gap-4 border-t border-[#2e2e2e] px-5 py-3.5 first:border-t-0", children: [_jsx("div", { className: "h-3 flex-1 rounded bg-[#141414]", style: { maxWidth: `${70 - i * 12}%` } }), _jsx("div", { className: "h-3 w-12 shrink-0 rounded bg-[#141414]" })] }, i))) }));
|
|
158
|
+
}
|
|
159
|
+
function TileGridSkeleton() {
|
|
160
|
+
return (_jsx("div", { className: "grid animate-pulse grid-cols-2 gap-3 sm:grid-cols-3 lg:grid-cols-6", "aria-hidden": "true", children: Array.from({ length: 6 }, (_, i) => (_jsxs("div", { className: `${card} px-4 py-4`, children: [_jsx("div", { className: "h-6 w-14 rounded bg-[#1a1a1a]" }), _jsx("div", { className: "mt-3 h-2.5 w-16 rounded bg-[#141414]" })] }, i))) }));
|
|
161
|
+
}
|
|
162
|
+
function SummarySkeleton() {
|
|
163
|
+
return (_jsxs("section", { className: `mb-8 ${card} animate-pulse overflow-hidden px-4 py-5 sm:px-5`, "aria-hidden": "true", children: [_jsx("div", { className: "h-3 w-32 rounded bg-[#1a1a1a]" }), _jsxs("div", { className: "mt-4 space-y-2", children: [_jsx("div", { className: "h-3 w-full rounded bg-[#141414]" }), _jsx("div", { className: "h-3 w-[92%] rounded bg-[#141414]" }), _jsx("div", { className: "h-3 w-[68%] rounded bg-[#141414]" })] })] }));
|
|
164
|
+
}
|
|
165
|
+
function PillSkeleton() {
|
|
166
|
+
return (_jsx("div", { className: "h-6 w-24 shrink-0 animate-pulse rounded-full bg-[#141414]", "aria-hidden": "true" }));
|
|
167
|
+
}
|
|
168
|
+
async function StatusPillData() {
|
|
169
|
+
const overview = await loadOverview();
|
|
170
|
+
return _jsx(StatusPill, { ready: overview.ready });
|
|
171
|
+
}
|
|
172
|
+
async function StatusBannersData() {
|
|
173
|
+
const [overview, health] = await Promise.all([loadOverview(), loadHealth()]);
|
|
174
|
+
return _jsx(StatusBanners, { overview: overview, health: health });
|
|
175
|
+
}
|
|
176
|
+
async function SummaryData() {
|
|
177
|
+
// The summary genuinely needs the whole dataset (its staleness fingerprint
|
|
178
|
+
// hashes it), so this is the one section that waits for everything — behind
|
|
179
|
+
// its own boundary, where it can't hold up the sections around it.
|
|
180
|
+
const data = await loadDashboardData();
|
|
181
|
+
const [summaryState, settings, profileState] = await Promise.all([
|
|
182
|
+
loadSummaryState(data),
|
|
183
|
+
getAISettingsPublic(),
|
|
184
|
+
loadProfileState(data),
|
|
185
|
+
]);
|
|
186
|
+
// Cadence-based auto-refresh: when the owner opted into daily/weekly and
|
|
187
|
+
// the summary is both past its cadence AND built from different data,
|
|
188
|
+
// regenerate in the background after this response is sent. Unchanged data
|
|
189
|
+
// never triggers a model call, so the cadence only spends tokens when
|
|
190
|
+
// there's genuinely new behavior to narrate.
|
|
191
|
+
const cadence = settings.refreshCadence ?? "manual";
|
|
192
|
+
if (cadence !== "manual" &&
|
|
193
|
+
settings.hasApiKey &&
|
|
194
|
+
data.overview.ready &&
|
|
195
|
+
data.overview.totalEvents > 0) {
|
|
196
|
+
const cadenceMs = cadence === "daily" ? 86_400_000 : 604_800_000;
|
|
197
|
+
const due = summaryState.summary === null ||
|
|
198
|
+
(summaryState.stale &&
|
|
199
|
+
Date.now() - summaryState.summary.generatedAt > cadenceMs);
|
|
200
|
+
if (due) {
|
|
201
|
+
after(async () => {
|
|
202
|
+
try {
|
|
203
|
+
await generateSummary();
|
|
204
|
+
}
|
|
205
|
+
catch (err) {
|
|
206
|
+
console.warn("[polishd] scheduled summary refresh failed:", err);
|
|
207
|
+
}
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
return (_jsx(SummaryCard, { initialSummary: summaryState.summary, initialSettings: settings, initialStale: summaryState.stale, initialProfile: profileState.profile, initialGaps: profileState.gaps, sourceAvailable: profileState.sourceAvailable }));
|
|
212
|
+
}
|
|
213
|
+
async function OverviewData() {
|
|
214
|
+
const [overview, trends] = await Promise.all([loadOverview(), loadTrends()]);
|
|
215
|
+
return _jsx(OverviewStatTiles, { overview: overview, trends: trends });
|
|
216
|
+
}
|
|
217
|
+
async function DevicesData() {
|
|
218
|
+
return _jsx(DevicesCard, { devices: await loadDevices() });
|
|
219
|
+
}
|
|
220
|
+
async function PagesData() {
|
|
221
|
+
return _jsx(TopPagesTable, { pages: await loadPages() });
|
|
222
|
+
}
|
|
223
|
+
async function MonitoredData() {
|
|
224
|
+
return _jsx(MonitoredCard, { monitored: await loadMonitored() });
|
|
225
|
+
}
|
|
226
|
+
async function JourneysData() {
|
|
227
|
+
return _jsx(JourneyList, { journeys: await loadJourneys() });
|
|
228
|
+
}
|
|
229
|
+
async function FeaturesData() {
|
|
230
|
+
return _jsx(FeaturesCard, { topUsed: await loadTopUsed() });
|
|
231
|
+
}
|
|
232
|
+
async function ElementsData() {
|
|
233
|
+
return _jsx(ElementsCard, { elements: await loadElements() });
|
|
234
|
+
}
|
|
235
|
+
async function ErrorsData() {
|
|
236
|
+
return _jsx(RecentErrors, { errors: await loadErrors() });
|
|
237
|
+
}
|
|
238
|
+
/** The analytics tab, streamed section by section. */
|
|
239
|
+
function AnalyticsStream() {
|
|
240
|
+
return (_jsxs("main", { className: "text-white", children: [_jsx(AnalyticsHeader, { pill: _jsx(Suspense, { fallback: _jsx(PillSkeleton, {}), children: _jsx(StatusPillData, {}) }) }), _jsx(Suspense, { fallback: null, children: _jsx(StatusBannersData, {}) }), _jsx(Suspense, { fallback: _jsx(SummarySkeleton, {}), children: _jsx(SummaryData, {}) }), _jsx(OverviewSection, { children: _jsx(Suspense, { fallback: _jsx(TileGridSkeleton, {}), children: _jsx(OverviewData, {}) }) }), _jsx(DevicesSection, { children: _jsx(Suspense, { fallback: _jsx(CardSkeleton, { rows: 3 }), children: _jsx(DevicesData, {}) }) }), _jsx(PagesSection, { children: _jsx(Suspense, { fallback: _jsx(CardSkeleton, {}), children: _jsx(PagesData, {}) }) }), _jsx(MonitoredSection, { children: _jsx(Suspense, { fallback: _jsx(CardSkeleton, { rows: 3 }), children: _jsx(MonitoredData, {}) }) }), _jsx(JourneysSection, { children: _jsx(Suspense, { fallback: _jsx(CardSkeleton, {}), children: _jsx(JourneysData, {}) }) }), _jsx(FeaturesSection, { children: _jsx(Suspense, { fallback: _jsx(CardSkeleton, {}), children: _jsx(FeaturesData, {}) }) }), _jsx(ElementsSection, { children: _jsx(Suspense, { fallback: _jsx(CardSkeleton, {}), children: _jsx(ElementsData, {}) }) }), _jsx(Suspense, { fallback: null, children: _jsx(ErrorsData, {}) })] }));
|
|
241
|
+
}
|
|
242
|
+
// ── The other tabs, each loading behind the chrome ───────────────────────────
|
|
243
|
+
async function DesignTabData() {
|
|
244
|
+
const [design, settings, snapshots, captureIssue] = await Promise.all([
|
|
245
|
+
getDesignData(),
|
|
246
|
+
getAISettingsPublic(),
|
|
247
|
+
listSnapshots(),
|
|
248
|
+
snapshotCaptureIssue(),
|
|
249
|
+
]);
|
|
250
|
+
const reviewState = await loadDesignReviewState(design);
|
|
251
|
+
return (_jsx(DesignPanel, { data: design, review: reviewState.review, reviewStale: reviewState.stale, settings: settings, snapshots: snapshots, captureIssue: captureIssue }));
|
|
252
|
+
}
|
|
253
|
+
async function IssuesTabData({ connected }) {
|
|
254
|
+
// Reachable by URL with nothing connected, in which case the listing is
|
|
255
|
+
// empty and the view says how to fix that.
|
|
256
|
+
const [issues, settings, suggestions] = await Promise.all([
|
|
257
|
+
listFiledIssues(),
|
|
258
|
+
getAISettingsPublic(),
|
|
259
|
+
listFixSuggestions(),
|
|
260
|
+
]);
|
|
261
|
+
return (_jsx(IssuesView, { issues: issues, repo: settings.githubRepo, connected: connected, suggestions: suggestions, canSuggest: settings.hasApiKey }));
|
|
262
|
+
}
|
|
263
|
+
async function InstallsTabData() {
|
|
264
|
+
const [installs, topPaths] = await Promise.all([
|
|
265
|
+
getTelemetryInstalls(),
|
|
266
|
+
getTelemetryTopPaths(),
|
|
267
|
+
]);
|
|
268
|
+
return _jsx(InstallsView, { installs: installs, topPaths: topPaths });
|
|
269
|
+
}
|
|
270
|
+
async function SettingsTabData() {
|
|
271
|
+
// `loadProfileState` only reads component identifiers out of the dashboard
|
|
272
|
+
// data, so this tab loads those two queries rather than the whole analytics
|
|
273
|
+
// set it would otherwise never look at.
|
|
274
|
+
const [settings, monitored, topUsed] = await Promise.all([
|
|
275
|
+
getAISettingsPublic(),
|
|
276
|
+
loadMonitored(),
|
|
277
|
+
loadTopUsed(),
|
|
278
|
+
]);
|
|
279
|
+
const profileState = await loadProfileState({ monitored, topUsed });
|
|
280
|
+
return (_jsx(SettingsView, { initialSettings: settings, initialProfile: profileState.profile, initialGaps: profileState.gaps, sourceAvailable: profileState.sourceAvailable }));
|
|
91
281
|
}
|
|
92
282
|
// ── Built-in fallback for a failed auth check ────────────────────────────────
|
|
93
283
|
function Unauthorized() {
|
|
@@ -252,83 +442,19 @@ export function createPolishdPage(opts = {}) {
|
|
|
252
442
|
const telemetryUi = (_jsxs(_Fragment, { children: [telemetry.status === "granted" && (_jsx(PolishdTelemetryEmitter, { endpoint: telemetry.endpoint, installState: telemetry.installState })), telemetry.status === "unset" && _jsx(PolishdTelemetryConsent, {})] }));
|
|
253
443
|
// Which tab to render, from the `?tab=` query the sidebar links carry.
|
|
254
444
|
const rawTab = Array.isArray(sp.tab) ? sp.tab[0] : sp.tab;
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
const [issues, settings] = await Promise.all([
|
|
269
|
-
listFiledIssues(),
|
|
270
|
-
getAISettingsPublic(),
|
|
271
|
-
]);
|
|
272
|
-
return wrap(_jsxs(_Fragment, { children: [_jsx(DashboardChrome, { active: "issues", showInstalls: showInstalls, showIssues: showIssues, children: _jsx(IssuesView, { issues: issues, repo: settings.githubRepo, connected: showIssues }) }), telemetryUi] }));
|
|
273
|
-
}
|
|
274
|
-
if (rawTab === "installs") {
|
|
275
|
-
const [installs, topPaths] = await Promise.all([
|
|
276
|
-
getTelemetryInstalls(),
|
|
277
|
-
getTelemetryTopPaths(),
|
|
278
|
-
]);
|
|
279
|
-
return wrap(_jsxs(_Fragment, { children: [_jsx(DashboardChrome, { active: "installs", showInstalls: showInstalls, showIssues: showIssues, children: _jsx(InstallsView, { installs: installs, topPaths: topPaths }) }), telemetryUi] }));
|
|
280
|
-
}
|
|
281
|
-
if (rawTab === "settings") {
|
|
282
|
-
// `loadProfileState` only reads component identifiers out of the
|
|
283
|
-
// dashboard data, so this tab loads those two queries rather than the
|
|
284
|
-
// whole analytics set it would otherwise never look at.
|
|
285
|
-
const [settings, monitored, topUsed] = await Promise.all([
|
|
286
|
-
getAISettingsPublic(),
|
|
287
|
-
getMonitoredComponents(),
|
|
288
|
-
getTopInteractions(),
|
|
289
|
-
]);
|
|
290
|
-
const profileState = await loadProfileState({ monitored, topUsed });
|
|
291
|
-
return wrap(_jsxs(_Fragment, { children: [_jsx(DashboardChrome, { active: "settings", showInstalls: showInstalls, showIssues: showIssues, children: _jsx(SettingsView, { initialSettings: settings, initialProfile: profileState.profile, initialGaps: profileState.gaps, sourceAvailable: profileState.sourceAvailable }) }), telemetryUi] }));
|
|
292
|
-
}
|
|
293
|
-
const data = await loadPolishdDashboardData();
|
|
294
|
-
const [summaryState, settings, profileState] = await Promise.all([
|
|
295
|
-
loadSummaryState(data),
|
|
296
|
-
getAISettingsPublic(),
|
|
297
|
-
loadProfileState(data),
|
|
298
|
-
]);
|
|
299
|
-
// Cadence-based auto-refresh: when the owner opted into daily/weekly and
|
|
300
|
-
// the summary is both past its cadence AND built from different data,
|
|
301
|
-
// regenerate in the background after this response is sent. Unchanged data
|
|
302
|
-
// never triggers a model call, so the cadence only spends tokens when
|
|
303
|
-
// there's genuinely new behavior to narrate.
|
|
304
|
-
const cadence = settings.refreshCadence ?? "manual";
|
|
305
|
-
if (cadence !== "manual" &&
|
|
306
|
-
settings.hasApiKey &&
|
|
307
|
-
data.overview.ready &&
|
|
308
|
-
data.overview.totalEvents > 0) {
|
|
309
|
-
const cadenceMs = cadence === "daily" ? 86_400_000 : 604_800_000;
|
|
310
|
-
const due = summaryState.summary === null ||
|
|
311
|
-
(summaryState.stale &&
|
|
312
|
-
Date.now() - summaryState.summary.generatedAt > cadenceMs);
|
|
313
|
-
if (due) {
|
|
314
|
-
after(async () => {
|
|
315
|
-
try {
|
|
316
|
-
await generateSummary();
|
|
317
|
-
}
|
|
318
|
-
catch (err) {
|
|
319
|
-
console.warn("[polishd] scheduled summary refresh failed:", err);
|
|
320
|
-
}
|
|
321
|
-
});
|
|
322
|
-
}
|
|
323
|
-
}
|
|
324
|
-
return wrap(_jsxs(_Fragment, { children: [_jsx(PolishdDashboard, { data: data, showInstalls: showInstalls, showIssues: showIssues, ai: {
|
|
325
|
-
summary: summaryState.summary,
|
|
326
|
-
settings,
|
|
327
|
-
stale: summaryState.stale,
|
|
328
|
-
profile: profileState.profile,
|
|
329
|
-
gaps: profileState.gaps,
|
|
330
|
-
sourceAvailable: profileState.sourceAvailable,
|
|
331
|
-
} }), telemetryUi] }));
|
|
445
|
+
const tab = rawTab === "design" || rawTab === "issues" || rawTab === "installs" || rawTab === "settings"
|
|
446
|
+
? rawTab
|
|
447
|
+
: "analytics";
|
|
448
|
+
// Every tab streams: the chrome and rail go out immediately, and the
|
|
449
|
+
// tab's data loads behind a Suspense boundary showing the same skeleton
|
|
450
|
+
// the client paints while navigating — so a slow query never blanks the
|
|
451
|
+
// frame, it just holds the skeleton a beat longer.
|
|
452
|
+
const body = tab === "design" ? (_jsx(Suspense, { fallback: _jsx(TabSkeleton, { name: "Design" }), children: _jsx(DesignTabData, {}) })) : tab === "issues" ? (_jsx(Suspense, { fallback: _jsx(TabSkeleton, { name: "Issues" }), children: _jsx(IssuesTabData, { connected: showIssues }) })) : tab === "installs" ? (_jsx(Suspense, { fallback: _jsx(TabSkeleton, { name: "Installs" }), children: _jsx(InstallsTabData, {}) })) : tab === "settings" ? (_jsx(Suspense, { fallback: _jsx(TabSkeleton, { name: "Settings" }), children: _jsx(SettingsTabData, {}) })) : (
|
|
453
|
+
// Analytics carries per-section boundaries instead of one big one, so
|
|
454
|
+
// its header and section titles paint before any query has finished
|
|
455
|
+
// and each card fills in on its own schedule.
|
|
456
|
+
_jsx(AnalyticsStream, {}));
|
|
457
|
+
return wrap(_jsxs(_Fragment, { children: [_jsx(DashboardChrome, { active: tab, showInstalls: showInstalls, showIssues: showIssues, children: body }), telemetryUi] }));
|
|
332
458
|
};
|
|
333
459
|
}
|
|
334
460
|
//# sourceMappingURL=index.js.map
|