@aranova/tracking-react 0.11.0 → 0.12.1
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 +83 -61
- package/dist/index.d.mts +137 -880
- package/dist/index.d.ts +137 -880
- package/dist/index.js +420 -89
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +405 -90
- package/dist/index.mjs.map +1 -1
- package/dist/phone-utils-DXAki8pO.d.mts +344 -0
- package/dist/phone-utils-DXAki8pO.d.ts +344 -0
- package/dist/phone.d.mts +3 -0
- package/dist/phone.d.ts +3 -0
- package/dist/phone.js +416 -0
- package/dist/phone.js.map +1 -0
- package/dist/phone.mjs +384 -0
- package/dist/phone.mjs.map +1 -0
- package/dist/sales.d.mts +784 -0
- package/dist/sales.d.ts +784 -0
- package/dist/sales.js +400 -0
- package/dist/sales.js.map +1 -0
- package/dist/sales.mjs +359 -0
- package/dist/sales.mjs.map +1 -0
- package/package.json +33 -4
package/README.md
CHANGED
|
@@ -14,7 +14,7 @@ Create one shared tracking module and import the scoped `TrackingProvider` / `us
|
|
|
14
14
|
|
|
15
15
|
```ts
|
|
16
16
|
// src/lib/tracking.ts
|
|
17
|
-
import { createTracking } from
|
|
17
|
+
import { createTracking } from "@aranova/tracking-react";
|
|
18
18
|
|
|
19
19
|
export const { TrackingProvider, useTracking } = createTracking({
|
|
20
20
|
apiKey: import.meta.env.VITE_ARANOVA_TRACKING_API_KEY,
|
|
@@ -24,7 +24,7 @@ export const { TrackingProvider, useTracking } = createTracking({
|
|
|
24
24
|
page_view: {},
|
|
25
25
|
time_on_site: { thresholdSeconds: 60 },
|
|
26
26
|
specific_page_visit: {
|
|
27
|
-
pages: [{ name:
|
|
27
|
+
pages: [{ name: "contact_page", pathPattern: /^\/(contact|book|get-started)/ }],
|
|
28
28
|
},
|
|
29
29
|
},
|
|
30
30
|
manual: {
|
|
@@ -33,7 +33,7 @@ export const { TrackingProvider, useTracking } = createTracking({
|
|
|
33
33
|
cta_click: {},
|
|
34
34
|
},
|
|
35
35
|
},
|
|
36
|
-
debug: import.meta.env.MODE !==
|
|
36
|
+
debug: import.meta.env.MODE !== "production",
|
|
37
37
|
});
|
|
38
38
|
```
|
|
39
39
|
|
|
@@ -41,11 +41,11 @@ Mount the provider at the root of your React tree.
|
|
|
41
41
|
|
|
42
42
|
```tsx
|
|
43
43
|
// src/main.tsx
|
|
44
|
-
import { createRoot } from
|
|
45
|
-
import { App } from
|
|
46
|
-
import { TrackingProvider } from
|
|
44
|
+
import { createRoot } from "react-dom/client";
|
|
45
|
+
import { App } from "./App";
|
|
46
|
+
import { TrackingProvider } from "./lib/tracking";
|
|
47
47
|
|
|
48
|
-
createRoot(document.getElementById(
|
|
48
|
+
createRoot(document.getElementById("root")!).render(
|
|
49
49
|
<TrackingProvider gtagId={import.meta.env.VITE_GTAG_ID}>
|
|
50
50
|
<App />
|
|
51
51
|
</TrackingProvider>,
|
|
@@ -54,36 +54,22 @@ createRoot(document.getElementById('root')!).render(
|
|
|
54
54
|
|
|
55
55
|
### Multiple gtag IDs
|
|
56
56
|
|
|
57
|
-
|
|
57
|
+
Pass `gtagIds` (instead of `gtagId`) to install several Google Ads tags at once — e.g. a real
|
|
58
|
+
account plus a test MCC. Each entry fires `gtag('config', …)` on every page; the labels surface in
|
|
59
|
+
the dashboard's SDK table. Stamp events with `environment` to filter out test traffic.
|
|
58
60
|
|
|
59
61
|
```tsx
|
|
60
|
-
<TrackingProvider
|
|
61
|
-
gtagIds={{
|
|
62
|
-
production: 'AW-111111111', // real client account
|
|
63
|
-
test: 'AW-222222222', // test MCC for development
|
|
64
|
-
}}
|
|
65
|
-
>
|
|
62
|
+
<TrackingProvider gtagIds={{ production: "AW-111111111", test: "AW-222222222" }}>
|
|
66
63
|
<App />
|
|
67
64
|
</TrackingProvider>
|
|
68
65
|
```
|
|
69
66
|
|
|
70
|
-
The labels are arbitrary and surface in the Aranova dashboard's SDK versions table. You can also stamp events with a deployment environment so the dashboard can filter out test traffic:
|
|
71
|
-
|
|
72
|
-
```ts
|
|
73
|
-
createTracking({
|
|
74
|
-
apiKey: import.meta.env.VITE_ARANOVA_TRACKING_API_KEY,
|
|
75
|
-
endpoint: import.meta.env.VITE_ARANOVA_TRACKING_ENDPOINT,
|
|
76
|
-
environment: import.meta.env.MODE, // 'production' | 'development'
|
|
77
|
-
triggers: { /* ... */ },
|
|
78
|
-
});
|
|
79
|
-
```
|
|
80
|
-
|
|
81
67
|
## Manual Events
|
|
82
68
|
|
|
83
69
|
Manual events must be registered under `triggers.manual` before `trackEvent()` accepts them.
|
|
84
70
|
|
|
85
71
|
```tsx
|
|
86
|
-
import { useTracking } from
|
|
72
|
+
import { useTracking } from "./lib/tracking";
|
|
87
73
|
|
|
88
74
|
export function LeadForm() {
|
|
89
75
|
const tracking = useTracking();
|
|
@@ -92,22 +78,22 @@ export function LeadForm() {
|
|
|
92
78
|
const form = event.currentTarget;
|
|
93
79
|
const data = new FormData(form);
|
|
94
80
|
|
|
95
|
-
tracking.trackEvent(
|
|
81
|
+
tracking.trackEvent("form_submit", {
|
|
96
82
|
form: {
|
|
97
83
|
id: form.id,
|
|
98
|
-
action: form.getAttribute(
|
|
84
|
+
action: form.getAttribute("action"),
|
|
99
85
|
fields: [
|
|
100
86
|
{
|
|
101
|
-
name:
|
|
102
|
-
type:
|
|
103
|
-
label:
|
|
104
|
-
value: String(data.get(
|
|
87
|
+
name: "service_interest",
|
|
88
|
+
type: "select",
|
|
89
|
+
label: "Service interest",
|
|
90
|
+
value: String(data.get("service_interest") ?? ""),
|
|
105
91
|
},
|
|
106
92
|
{
|
|
107
|
-
name:
|
|
108
|
-
type:
|
|
109
|
-
label:
|
|
110
|
-
value: data.get(
|
|
93
|
+
name: "is_existing_patient",
|
|
94
|
+
type: "checkbox",
|
|
95
|
+
label: "Existing patient",
|
|
96
|
+
value: data.get("is_existing_patient") === "on",
|
|
111
97
|
},
|
|
112
98
|
],
|
|
113
99
|
},
|
|
@@ -115,20 +101,41 @@ export function LeadForm() {
|
|
|
115
101
|
});
|
|
116
102
|
}
|
|
117
103
|
|
|
118
|
-
return
|
|
104
|
+
return (
|
|
105
|
+
<form id="lead-form" action="/api/lead" onSubmit={handleSubmit}>
|
|
106
|
+
{/* fields */}
|
|
107
|
+
</form>
|
|
108
|
+
);
|
|
119
109
|
}
|
|
120
110
|
```
|
|
121
111
|
|
|
122
112
|
`fields[].value` can be any JSON value: string, number, boolean, null, array, or object. Values must be JSON-serializable because events are stored as JSONB. Only send reviewed, allowlisted, non-sensitive values; do not send names, emails, phone numbers entered by the visitor, addresses, payment data, medical details, passwords, file contents, or free-text messages.
|
|
123
113
|
|
|
124
114
|
```tsx
|
|
125
|
-
tracking.trackEvent(
|
|
126
|
-
phone_number:
|
|
115
|
+
tracking.trackEvent("phone_click", {
|
|
116
|
+
phone_number: "+14165550199",
|
|
127
117
|
page: { path: window.location.pathname },
|
|
128
|
-
section:
|
|
118
|
+
section: "header",
|
|
129
119
|
});
|
|
130
120
|
```
|
|
131
121
|
|
|
122
|
+
## Phone Fields
|
|
123
|
+
|
|
124
|
+
Bundled `libphonenumber-js`: parse/format utils + a React input. Display is configurable; the
|
|
125
|
+
value sent to the backend is **always E.164**. Configure once via `createTracking({ phone: { defaultCountry: 'CA', display: 'national' } })`.
|
|
126
|
+
|
|
127
|
+
```tsx
|
|
128
|
+
import { usePhoneField, PhoneField, toE164 } from "@aranova/tracking-react";
|
|
129
|
+
|
|
130
|
+
const phone = usePhoneField(); // phone.value (display), phone.e164 (wire), .isValid, .error
|
|
131
|
+
<input {...phone.inputProps} />; // or the batteries-included <PhoneField name="phone" />
|
|
132
|
+
|
|
133
|
+
toE164("416-555-0199"); // '+14165550199' (null if invalid)
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
Pure, isomorphic utils are also at `@aranova/tracking-react/phone` (no React). Full guide:
|
|
137
|
+
[phone.md](https://github.com/AranovaIO/aranova_internal/blob/master/docs/tracking-package/phone.md).
|
|
138
|
+
|
|
132
139
|
## Recording Sales
|
|
133
140
|
|
|
134
141
|
Record a sale / conversion (a first-class, mutable resource — not a fire-and-forget
|
|
@@ -139,19 +146,23 @@ key may do is enforced by the backend, not by hiding methods. Browser write with
|
|
|
139
146
|
your **public** key:
|
|
140
147
|
|
|
141
148
|
```tsx
|
|
142
|
-
import { createSalesClient, toMinor } from
|
|
149
|
+
import { createSalesClient, toMinor } from "@aranova/tracking-react";
|
|
143
150
|
|
|
144
|
-
const sales = createSalesClient({
|
|
145
|
-
|
|
151
|
+
const sales = createSalesClient({
|
|
152
|
+
apiKey: import.meta.env.VITE_ARANOVA_TRACKING_API_KEY,
|
|
153
|
+
endpoint,
|
|
154
|
+
});
|
|
155
|
+
await sales.record({ currency: "CAD", amount_total_cents: toMinor(250, "CAD"), service: "tires" });
|
|
146
156
|
```
|
|
147
157
|
|
|
148
158
|
Reads / full CRUD require a **secret** key and must run server-side — never ship a
|
|
149
159
|
secret key in the browser bundle. In a Vite + Vercel app, hold it in a serverless
|
|
150
|
-
function
|
|
160
|
+
function. Import from the **`/sales`** subpath — it's React-free, so the server
|
|
161
|
+
bundle never pulls in the provider/components:
|
|
151
162
|
|
|
152
163
|
```ts
|
|
153
164
|
// api/sales.ts (Vercel serverless function — runs on the server)
|
|
154
|
-
import { createSalesClient } from
|
|
165
|
+
import { createSalesClient } from "@aranova/tracking-react/sales";
|
|
155
166
|
|
|
156
167
|
const sales = createSalesClient({
|
|
157
168
|
apiKey: process.env.ARANOVA_TRACKING_SECRET_KEY!,
|
|
@@ -162,12 +173,23 @@ export default async function handler(_req, res) {
|
|
|
162
173
|
}
|
|
163
174
|
```
|
|
164
175
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
176
|
+
Secret-key reads power dashboards (all currency-grouped — never summed across currencies):
|
|
177
|
+
|
|
178
|
+
```ts
|
|
179
|
+
await sales.summary({ range: "mtd", timezone: "America/Toronto", compare_to: "previous_period" });
|
|
180
|
+
await sales.list({ sort: "amount_total_cents", want_total: true }); // → { items, total_count, … }
|
|
181
|
+
await sales.customers.list({ segment: "returning", sort: "total_spent" }); // phone-keyed roster
|
|
182
|
+
await sales.customers.summary({ range: "mtd" });
|
|
183
|
+
const cfg = await sales.business.config(); // tz / currencies / services (pk or sk)
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
`summary()` gains tz-aware calendar/custom ranges, `granularity`, and period-over-period
|
|
187
|
+
`compare_to`; legacy `24h/7d/30d` are unchanged. A **customer is their phone (E.164)** — `customers.*`
|
|
188
|
+
is a live roster, no separate table. A public-key client calling any read gets a `403`.
|
|
168
189
|
|
|
169
|
-
|
|
170
|
-
|
|
190
|
+
The root entry (`@aranova/tracking-react`) still re-exports `createSalesClient` and the
|
|
191
|
+
money/date helpers for back-compat, so existing imports keep working — but prefer `/sales`
|
|
192
|
+
in server code so the React surface never reaches your server bundle.
|
|
171
193
|
|
|
172
194
|
Generate the typed `AranovaService` union from your dashboard services with the CLI
|
|
173
195
|
(install it as a **devDependency**):
|
|
@@ -185,10 +207,10 @@ and the CLI reference: [cli.md](https://github.com/AranovaIO/aranova_internal/bl
|
|
|
185
207
|
The bundled `<ConsentBanner />` renders a non-blocking bottom-docked banner while consent is `pending`, persists the visitor's choice to `localStorage`, and propagates it to Google Consent Mode v2 when gtag is loaded. **Inline-styled** — no Tailwind or CSS imports required at the consumer.
|
|
186
208
|
|
|
187
209
|
```tsx
|
|
188
|
-
import { ConsentBanner } from
|
|
210
|
+
import { ConsentBanner } from "@aranova/tracking-react";
|
|
189
211
|
|
|
190
212
|
// Drop-in, defaults work everywhere
|
|
191
|
-
<ConsentBanner
|
|
213
|
+
<ConsentBanner />;
|
|
192
214
|
```
|
|
193
215
|
|
|
194
216
|
All props are optional:
|
|
@@ -200,13 +222,13 @@ All props are optional:
|
|
|
200
222
|
acceptLabel="Sure"
|
|
201
223
|
declineLabel="No thanks"
|
|
202
224
|
policyHref="/privacy"
|
|
203
|
-
policyLabel="Privacy policy"
|
|
204
|
-
onAccept={() => track(
|
|
205
|
-
onDecline={() => track(
|
|
206
|
-
position="bottom"
|
|
207
|
-
theme="light"
|
|
225
|
+
policyLabel="Privacy policy" // default: "Learn more"
|
|
226
|
+
onAccept={() => track("consent_accepted")}
|
|
227
|
+
onDecline={() => track("consent_declined")}
|
|
228
|
+
position="bottom" // or "top"
|
|
229
|
+
theme="light" // "light" | "dark" | "auto"
|
|
208
230
|
className="my-extra-classes"
|
|
209
|
-
style={{ background:
|
|
231
|
+
style={{ background: "#fafafa" }} // wins over the theme defaults
|
|
210
232
|
/>
|
|
211
233
|
```
|
|
212
234
|
|
|
@@ -215,7 +237,7 @@ All props are optional:
|
|
|
215
237
|
For a bespoke banner, skip the component and drive your own UI with the headless hook:
|
|
216
238
|
|
|
217
239
|
```tsx
|
|
218
|
-
import { useConsent } from
|
|
240
|
+
import { useConsent } from "@aranova/tracking-react";
|
|
219
241
|
|
|
220
242
|
function CookieBar() {
|
|
221
243
|
const { state, accept, decline, reset, isPending } = useConsent();
|
|
@@ -244,7 +266,7 @@ The hook handles localStorage persistence, gtag sync, and cross-tab propagation
|
|
|
244
266
|
- Consent hooks: `useConsent()` + `UseConsentResult` (headless); `useConsentState()` (read-only alias)
|
|
245
267
|
- Standalone consent helpers: `getConsentState()`, `setConsentState()`, `resetConsent()`
|
|
246
268
|
- Attribution hooks: `useTrackingParams()`, `useGclid()`
|
|
247
|
-
- `createSalesClient()` (isomorphic
|
|
269
|
+
- `createSalesClient()` (isomorphic — public key writes; secret key reads/CRUD, `summary`, `customers.*`, `business.config`) + money/date helpers (`toMinor`/`fromMinor`/`formatMoney`/`formatDateInTz`). Also available React-free at **`@aranova/tracking-react/sales`** (with all sale/customer/config types) — the recommended import for server/serverless code.
|
|
270
|
+
- Phone: `parsePhone`/`toE164`/`formatPhone`/`formatPhoneAsTyped`/`phoneField`, `usePhoneField`, `PhoneField` (utils also at `/phone`)
|
|
248
271
|
- Codegen: [`@aranova/tracking-cli`](https://www.npmjs.com/package/@aranova/tracking-cli) — `gen` typed service unions (devDependency)
|
|
249
272
|
- Event metadata/config types such as `FormSubmitMetadata`, `PhoneClickMetadata`, and `JsonValue`
|
|
250
|
-
|