@liiift-studio/sanity-visitor-insights 0.11.0 → 0.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/README.md +12 -0
- package/dist/index.d.mts +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +12 -6
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +12 -6
- package/dist/index.mjs.map +1 -1
- package/dist/server.d.mts +2 -0
- package/dist/server.d.ts +2 -0
- package/dist/server.js +15 -1
- package/dist/server.js.map +1 -1
- package/dist/server.mjs +15 -1
- package/dist/server.mjs.map +1 -1
- package/package.json +1 -1
- package/src/core/core.test.ts +26 -0
- package/src/server/createHandler.ts +34 -0
- package/src/server/reports/reports.test.ts +55 -0
- package/src/studio/VisitorInsightsTool.tsx +14 -6
- package/src/studio/useReport.ts +11 -6
package/README.md
CHANGED
|
@@ -184,6 +184,7 @@ empty charts — pasting a measurement id here is caught by name.
|
|
|
184
184
|
|
|
185
185
|
| Variable | Where | What |
|
|
186
186
|
|---|---|---|
|
|
187
|
+
| `VISITOR_INSIGHTS_ENABLED` | Site (server) | **Required.** Any truthy value switches the route on |
|
|
187
188
|
| `VISITOR_INSIGHTS_GA4_SERVICE_ACCOUNT` | Site (server) | Service-account JSON, raw or base64 |
|
|
188
189
|
| `VISITOR_INSIGHTS_VERCEL_TOKEN` | Site (server) | Vercel API token with project read access |
|
|
189
190
|
| `SANITY_STUDIO_PROJECT_ID` | Site (server) | Already set; used to verify Studio tokens |
|
|
@@ -191,6 +192,17 @@ empty charts — pasting a measurement id here is caught by name.
|
|
|
191
192
|
The service account needs **Viewer** on each GA4 property. Nothing here is `NEXT_PUBLIC_`; none of
|
|
192
193
|
it reaches the browser.
|
|
193
194
|
|
|
195
|
+
#### The master switch
|
|
196
|
+
|
|
197
|
+
`VISITOR_INSIGHTS_ENABLED` is an explicit opt-in. Unset, the route answers `503` with
|
|
198
|
+
`disabled: true` and does no Sanity or GA4 work, and the Studio tool shows a neutral "Switched off"
|
|
199
|
+
card rather than an error. Set it to anything that is not `false`, `0`, `off`, `no`, `disabled` or
|
|
200
|
+
empty — a word, a code, `true`, whatever the site's operator prefers.
|
|
201
|
+
|
|
202
|
+
This is deliberately the opposite default to `SALES_PORTAL_ENABLED`, which is on unless the value
|
|
203
|
+
is literally `"false"`. Mounting this route wires up analytics credentials, so it should not begin
|
|
204
|
+
answering on a site where nobody chose to switch it on.
|
|
205
|
+
|
|
194
206
|
---
|
|
195
207
|
|
|
196
208
|
## Before it will show anything useful
|
package/dist/index.d.mts
CHANGED
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -103,12 +103,14 @@ function useReport({ apiBaseUrl, report, range, custom }) {
|
|
|
103
103
|
if (requestIdRef.current !== requestId) return;
|
|
104
104
|
if (!response.ok) {
|
|
105
105
|
let detail = response.status === 401 ? "Not authorised \u2014 sign in to the Studio again." : `Request failed (${response.status})`;
|
|
106
|
-
|
|
106
|
+
let disabled = false;
|
|
107
|
+
if (response.status === 400 || response.status === 503) {
|
|
107
108
|
const body = await response.json().catch(() => null);
|
|
108
109
|
if (body?.error) detail = body.error;
|
|
110
|
+
disabled = body?.disabled === true;
|
|
109
111
|
}
|
|
110
112
|
if (requestIdRef.current !== requestId) return;
|
|
111
|
-
setState({ status: "error", message: detail });
|
|
113
|
+
setState({ status: "error", message: detail, disabled });
|
|
112
114
|
return;
|
|
113
115
|
}
|
|
114
116
|
const envelope = await response.json();
|
|
@@ -1126,16 +1128,20 @@ function ReportPanel({
|
|
|
1126
1128
|
custom
|
|
1127
1129
|
}) {
|
|
1128
1130
|
const { state, reload } = useReport({ apiBaseUrl, report, range, custom });
|
|
1129
|
-
const liveMessage = state.status === "loading" ? "Loading report" : state.status === "ready" ? `${report} updated for ${range === "custom" ? `${custom.start} to ${custom.end}` : `the selected ${range}`}` : state.status === "error" ? `Report failed: ${state.message}` : "";
|
|
1131
|
+
const liveMessage = state.status === "loading" ? "Loading report" : state.status === "ready" ? `${report} updated for ${range === "custom" ? `${custom.start} to ${custom.end}` : `the selected ${range}`}` : state.status === "error" ? state.disabled ? "Visitor insights is switched off for this site" : `Report failed: ${state.message}` : "";
|
|
1130
1132
|
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_sanity_ui_compat3.Stack, { space: 4, children: [
|
|
1131
1133
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_sanity_ui_compat3.Box, { "aria-live": "polite", style: { position: "absolute", width: 1, height: 1, overflow: "hidden", clip: "rect(0 0 0 0)" }, children: liveMessage }),
|
|
1132
1134
|
state.status === "loading" && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_sanity_ui_compat3.Flex, { align: "center", gap: 3, padding: 4, children: [
|
|
1133
1135
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_sanity_ui_compat3.Spinner, { muted: true }),
|
|
1134
1136
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_sanity_ui_compat3.Text, { size: 1, muted: true, children: "Loading\u2026" })
|
|
1135
1137
|
] }),
|
|
1136
|
-
state.status === "error" &&
|
|
1137
|
-
|
|
1138
|
-
|
|
1138
|
+
state.status === "error" && // A switched-off site is a configuration state, not a fault: it gets a neutral card
|
|
1139
|
+
// and no retry, because retrying cannot change the answer. Everything else keeps
|
|
1140
|
+
// the critical tone and the retry.
|
|
1141
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_sanity_ui_compat3.Card, { padding: 4, radius: 2, tone: state.disabled ? "transparent" : "critical", border: true, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_sanity_ui_compat3.Stack, { space: 3, children: [
|
|
1142
|
+
state.disabled && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_sanity_ui_compat3.Text, { size: 1, weight: "medium", children: "Switched off" }),
|
|
1143
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_sanity_ui_compat3.Text, { size: 1, muted: state.disabled, children: state.message }),
|
|
1144
|
+
!state.disabled && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_sanity_ui_compat3.Box, { children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_sanity_ui_compat3.Button, { text: "Try again", mode: "ghost", fontSize: 1, onClick: reload }) })
|
|
1139
1145
|
] }) }),
|
|
1140
1146
|
state.status === "ready" && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_sanity_ui_compat3.Stack, { space: 4, children: [
|
|
1141
1147
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(SourceStatusRow, { sources: state.envelope.sources }),
|