@aranova/tracking-next 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 +79 -71
- package/dist/index.d.mts +137 -722
- package/dist/index.d.ts +137 -722
- package/dist/index.js +408 -66
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +385 -60
- package/dist/index.mjs.map +1 -1
- package/dist/middleware.js +101 -0
- package/dist/middleware.js.map +1 -1
- package/dist/middleware.mjs +101 -0
- package/dist/middleware.mjs.map +1 -1
- package/dist/phone-utils-dd1tW7sb.d.mts +344 -0
- package/dist/phone-utils-dd1tW7sb.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/dist/server.d.mts +3 -1
- package/dist/server.d.ts +3 -1
- package/dist/server.js +101 -0
- package/dist/server.js.map +1 -1
- package/dist/server.mjs +101 -0
- package/dist/server.mjs.map +1 -1
- package/package.json +51 -10
- package/dist/types-BPJLgMsG.d.mts +0 -161
- package/dist/types-BPJLgMsG.d.ts +0 -161
package/README.md
CHANGED
|
@@ -14,12 +14,12 @@ Install the tracking middleware so `gclid`, `fbclid`, and UTM params are written
|
|
|
14
14
|
|
|
15
15
|
```ts
|
|
16
16
|
// middleware.ts
|
|
17
|
-
import { createTrackingMiddleware } from
|
|
17
|
+
import { createTrackingMiddleware } from "@aranova/tracking-next/middleware";
|
|
18
18
|
|
|
19
19
|
export default createTrackingMiddleware();
|
|
20
20
|
|
|
21
21
|
export const config = {
|
|
22
|
-
matcher: [
|
|
22
|
+
matcher: ["/((?!api|_next/static|_next/image|favicon.ico).*)"],
|
|
23
23
|
};
|
|
24
24
|
```
|
|
25
25
|
|
|
@@ -29,7 +29,7 @@ Create one shared tracking module and use its scoped exports throughout your app
|
|
|
29
29
|
|
|
30
30
|
```ts
|
|
31
31
|
// lib/tracking.ts
|
|
32
|
-
import { createTracking } from
|
|
32
|
+
import { createTracking } from "@aranova/tracking-next";
|
|
33
33
|
|
|
34
34
|
export const { TrackingProvider, useTracking } = createTracking({
|
|
35
35
|
apiKey: process.env.NEXT_PUBLIC_ARANOVA_TRACKING_API_KEY!,
|
|
@@ -39,7 +39,7 @@ export const { TrackingProvider, useTracking } = createTracking({
|
|
|
39
39
|
page_view: {},
|
|
40
40
|
time_on_site: { thresholdSeconds: 60 },
|
|
41
41
|
specific_page_visit: {
|
|
42
|
-
pages: [{ name:
|
|
42
|
+
pages: [{ name: "contact_page", pathPattern: /^\/(contact|book|get-started)/ }],
|
|
43
43
|
},
|
|
44
44
|
},
|
|
45
45
|
manual: {
|
|
@@ -48,7 +48,7 @@ export const { TrackingProvider, useTracking } = createTracking({
|
|
|
48
48
|
cta_click: {},
|
|
49
49
|
},
|
|
50
50
|
},
|
|
51
|
-
debug: process.env.NODE_ENV !==
|
|
51
|
+
debug: process.env.NODE_ENV !== "production",
|
|
52
52
|
});
|
|
53
53
|
```
|
|
54
54
|
|
|
@@ -56,8 +56,8 @@ Wrap the root layout.
|
|
|
56
56
|
|
|
57
57
|
```tsx
|
|
58
58
|
// app/layout.tsx
|
|
59
|
-
import { ConsentBanner, GoogleAdsTracking } from
|
|
60
|
-
import { TrackingProvider } from
|
|
59
|
+
import { ConsentBanner, GoogleAdsTracking } from "@aranova/tracking-next";
|
|
60
|
+
import { TrackingProvider } from "@/lib/tracking";
|
|
61
61
|
|
|
62
62
|
export default function RootLayout({ children }: { children: React.ReactNode }) {
|
|
63
63
|
return (
|
|
@@ -80,31 +80,12 @@ export default function RootLayout({ children }: { children: React.ReactNode })
|
|
|
80
80
|
|
|
81
81
|
### Multiple gtag IDs
|
|
82
82
|
|
|
83
|
-
|
|
83
|
+
Pass `gtagIds` (instead of `gtagId`) to install several Google Ads tags at once — e.g. a real
|
|
84
|
+
account plus a test MCC. Each entry fires `gtag('config', …)` on every page; the labels surface in
|
|
85
|
+
the dashboard's SDK table. Pass `environment` to the factory to tag events for dashboard filtering.
|
|
84
86
|
|
|
85
87
|
```tsx
|
|
86
|
-
<GoogleAdsTracking
|
|
87
|
-
gtagIds={{
|
|
88
|
-
production: 'AW-111111111', // real client account
|
|
89
|
-
test: 'AW-222222222', // test MCC for development
|
|
90
|
-
}}
|
|
91
|
-
/>
|
|
92
|
-
```
|
|
93
|
-
|
|
94
|
-
The labels are arbitrary and surface in the Aranova dashboard's SDK versions table. Also pass `environment` to the factory so events are tagged with the deployment context for dashboard filtering:
|
|
95
|
-
|
|
96
|
-
```ts
|
|
97
|
-
// lib/tracking.ts
|
|
98
|
-
createTracking({
|
|
99
|
-
apiKey: process.env.NEXT_PUBLIC_ARANOVA_TRACKING_API_KEY!,
|
|
100
|
-
endpoint: process.env.NEXT_PUBLIC_ARANOVA_TRACKING_ENDPOINT!,
|
|
101
|
-
environment: process.env.NEXT_PUBLIC_TRACKING_ENVIRONMENT, // 'production' | 'development'
|
|
102
|
-
gtagIds: { // optional — reports the configured tags in heartbeat metadata
|
|
103
|
-
production: process.env.NEXT_PUBLIC_GTAG_PROD!,
|
|
104
|
-
test: process.env.NEXT_PUBLIC_GTAG_TEST!,
|
|
105
|
-
},
|
|
106
|
-
triggers: { /* ... */ },
|
|
107
|
-
});
|
|
88
|
+
<GoogleAdsTracking gtagIds={{ production: "AW-111111111", test: "AW-222222222" }} />
|
|
108
89
|
```
|
|
109
90
|
|
|
110
91
|
## Manual Events
|
|
@@ -112,9 +93,9 @@ createTracking({
|
|
|
112
93
|
Import `useTracking` from your local `lib/tracking` module, not directly from the package.
|
|
113
94
|
|
|
114
95
|
```tsx
|
|
115
|
-
|
|
96
|
+
"use client";
|
|
116
97
|
|
|
117
|
-
import { useTracking } from
|
|
98
|
+
import { useTracking } from "@/lib/tracking";
|
|
118
99
|
|
|
119
100
|
export function LeadForm() {
|
|
120
101
|
const tracking = useTracking();
|
|
@@ -127,22 +108,22 @@ export function LeadForm() {
|
|
|
127
108
|
const form = event.currentTarget;
|
|
128
109
|
const data = new FormData(form);
|
|
129
110
|
|
|
130
|
-
tracking.trackEvent(
|
|
111
|
+
tracking.trackEvent("form_submit", {
|
|
131
112
|
form: {
|
|
132
113
|
id: form.id,
|
|
133
|
-
action: form.getAttribute(
|
|
114
|
+
action: form.getAttribute("action"),
|
|
134
115
|
fields: [
|
|
135
116
|
{
|
|
136
|
-
name:
|
|
137
|
-
type:
|
|
138
|
-
label:
|
|
139
|
-
value: String(data.get(
|
|
117
|
+
name: "service_interest",
|
|
118
|
+
type: "select",
|
|
119
|
+
label: "Service interest",
|
|
120
|
+
value: String(data.get("service_interest") ?? ""),
|
|
140
121
|
},
|
|
141
122
|
{
|
|
142
|
-
name:
|
|
143
|
-
type:
|
|
144
|
-
label:
|
|
145
|
-
value: data.get(
|
|
123
|
+
name: "is_existing_patient",
|
|
124
|
+
type: "checkbox",
|
|
125
|
+
label: "Existing patient",
|
|
126
|
+
value: data.get("is_existing_patient") === "on",
|
|
146
127
|
},
|
|
147
128
|
],
|
|
148
129
|
},
|
|
@@ -158,16 +139,33 @@ export function LeadForm() {
|
|
|
158
139
|
|
|
159
140
|
`fields[].value` can be any JSON value: string, number, boolean, null, array, or object. 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.
|
|
160
141
|
|
|
142
|
+
## Phone Fields
|
|
143
|
+
|
|
144
|
+
Bundled `libphonenumber-js`: parse/format utils + a React input. Display is configurable; the
|
|
145
|
+
value sent to the backend is **always E.164**. Configure once via `createTracking({ phone: { defaultCountry: 'CA', display: 'national' } })`.
|
|
146
|
+
|
|
147
|
+
```tsx
|
|
148
|
+
"use client";
|
|
149
|
+
import { usePhoneField, PhoneField, toE164 } from "@aranova/tracking-next";
|
|
150
|
+
|
|
151
|
+
const phone = usePhoneField(); // phone.value (display), phone.e164 (wire), .isValid, .error
|
|
152
|
+
<input {...phone.inputProps} />; // or the batteries-included <PhoneField name="phone" />
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
Pure, isomorphic utils (usable in Server Components / route handlers) are at
|
|
156
|
+
`@aranova/tracking-next/phone`. Full guide:
|
|
157
|
+
[phone.md](https://github.com/AranovaIO/aranova_internal/blob/master/docs/tracking-package/phone.md).
|
|
158
|
+
|
|
161
159
|
## Server Reads
|
|
162
160
|
|
|
163
161
|
Read attribution cookies from Server Components or server actions.
|
|
164
162
|
|
|
165
163
|
```tsx
|
|
166
|
-
import { getTrackingParamsServer } from
|
|
164
|
+
import { getTrackingParamsServer } from "@aranova/tracking-next/server";
|
|
167
165
|
|
|
168
166
|
export default function Page() {
|
|
169
167
|
const { gclid, utm_source } = getTrackingParamsServer();
|
|
170
|
-
return gclid || utm_source ===
|
|
168
|
+
return gclid || utm_source === "google" ? <GoogleLanding /> : <OrganicLanding />;
|
|
171
169
|
}
|
|
172
170
|
```
|
|
173
171
|
|
|
@@ -181,34 +179,43 @@ key may do is enforced by the backend, not by hiding methods. Browser write with
|
|
|
181
179
|
your **public** key, from a client component:
|
|
182
180
|
|
|
183
181
|
```tsx
|
|
184
|
-
|
|
185
|
-
import { createSalesClient, toMinor } from
|
|
182
|
+
"use client";
|
|
183
|
+
import { createSalesClient, toMinor } from "@aranova/tracking-next";
|
|
186
184
|
|
|
187
|
-
const sales = createSalesClient({
|
|
188
|
-
|
|
185
|
+
const sales = createSalesClient({
|
|
186
|
+
apiKey: process.env.NEXT_PUBLIC_ARANOVA_TRACKING_API_KEY!,
|
|
187
|
+
endpoint,
|
|
188
|
+
});
|
|
189
|
+
await sales.record({ currency: "CAD", amount_total_cents: toMinor(250, "CAD"), service: "tires" });
|
|
189
190
|
```
|
|
190
191
|
|
|
191
|
-
Full CRUD from a route handler / server action
|
|
192
|
-
|
|
192
|
+
Full CRUD from a route handler / server action / **Server Component** with your
|
|
193
|
+
**secret** key. Import from the **`/sales`** subpath — it's React-free, so it loads
|
|
194
|
+
in an RSC or any server module without dragging the provider/components into the
|
|
195
|
+
server graph (keep the secret key in server env, never in client code):
|
|
193
196
|
|
|
194
197
|
```ts
|
|
195
|
-
import { createSalesClient } from
|
|
196
|
-
import type { AranovaService } from
|
|
198
|
+
import { createSalesClient } from "@aranova/tracking-next/sales";
|
|
199
|
+
import type { AranovaService } from "./aranova-services"; // generated, see below
|
|
197
200
|
|
|
198
201
|
const sales = createSalesClient<AranovaService>({
|
|
199
202
|
apiKey: process.env.ARANOVA_TRACKING_SECRET_KEY!,
|
|
200
203
|
endpoint: process.env.ARANOVA_TRACKING_ENDPOINT!,
|
|
201
204
|
});
|
|
202
|
-
|
|
203
|
-
|
|
205
|
+
await sales.list({ sort: "amount_total_cents", want_total: true }); // → { items, total_count, … }
|
|
206
|
+
await sales.summary({ range: "mtd", timezone: "America/Toronto", compare_to: "previous_period" });
|
|
207
|
+
await sales.customers.list({ segment: "returning", sort: "total_spent" }); // phone-keyed roster
|
|
208
|
+
const cfg = await sales.business.config(); // tz / currencies / services
|
|
204
209
|
```
|
|
205
210
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
211
|
+
The root entry (`@aranova/tracking-next`) still re-exports `createSalesClient` and the
|
|
212
|
+
money/date helpers for back-compat, so existing imports keep working — but prefer
|
|
213
|
+
`/sales` in server code so the React surface never reaches your server bundle.
|
|
209
214
|
|
|
210
|
-
|
|
211
|
-
|
|
215
|
+
Dashboard reads are all currency-grouped (never summed). `summary()` gains tz-aware calendar/custom
|
|
216
|
+
ranges, `granularity`, and period-over-period `compare_to` (legacy `24h/7d/30d` unchanged); a
|
|
217
|
+
**customer is their phone (E.164)**, so `customers.*` is a live roster with no separate table.
|
|
218
|
+
A public-key client calling any read gets a `403`.
|
|
212
219
|
|
|
213
220
|
Generate the typed `AranovaService` union from your dashboard services with the CLI
|
|
214
221
|
(install it as a **devDependency**):
|
|
@@ -226,10 +233,10 @@ and the CLI reference: [cli.md](https://github.com/AranovaIO/aranova_internal/bl
|
|
|
226
233
|
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.
|
|
227
234
|
|
|
228
235
|
```tsx
|
|
229
|
-
import { ConsentBanner } from
|
|
236
|
+
import { ConsentBanner } from "@aranova/tracking-next";
|
|
230
237
|
|
|
231
238
|
// Drop-in (already wired in the root layout example above)
|
|
232
|
-
<ConsentBanner
|
|
239
|
+
<ConsentBanner />;
|
|
233
240
|
```
|
|
234
241
|
|
|
235
242
|
All props are optional:
|
|
@@ -241,13 +248,13 @@ All props are optional:
|
|
|
241
248
|
acceptLabel="Sure"
|
|
242
249
|
declineLabel="No thanks"
|
|
243
250
|
policyHref="/privacy"
|
|
244
|
-
policyLabel="Privacy policy"
|
|
245
|
-
onAccept={() => track(
|
|
246
|
-
onDecline={() => track(
|
|
247
|
-
position="bottom"
|
|
248
|
-
theme="light"
|
|
251
|
+
policyLabel="Privacy policy" // default: "Learn more"
|
|
252
|
+
onAccept={() => track("consent_accepted")}
|
|
253
|
+
onDecline={() => track("consent_declined")}
|
|
254
|
+
position="bottom" // or "top"
|
|
255
|
+
theme="light" // "light" | "dark" | "auto"
|
|
249
256
|
className="my-extra-classes"
|
|
250
|
-
style={{ background:
|
|
257
|
+
style={{ background: "#fafafa" }} // wins over the theme defaults
|
|
251
258
|
/>
|
|
252
259
|
```
|
|
253
260
|
|
|
@@ -256,8 +263,8 @@ All props are optional:
|
|
|
256
263
|
For a bespoke banner, skip the component and drive your own UI with the headless hook:
|
|
257
264
|
|
|
258
265
|
```tsx
|
|
259
|
-
|
|
260
|
-
import { useConsent } from
|
|
266
|
+
"use client";
|
|
267
|
+
import { useConsent } from "@aranova/tracking-next";
|
|
261
268
|
|
|
262
269
|
function CookieBar() {
|
|
263
270
|
const { state, accept, decline, reset, isPending } = useConsent();
|
|
@@ -279,8 +286,9 @@ The hook handles localStorage persistence, gtag sync, and cross-tab propagation
|
|
|
279
286
|
|
|
280
287
|
## Exports
|
|
281
288
|
|
|
282
|
-
- Root package: `createTracking`, `TrackingProvider`, `useTracking`, `GoogleAdsTracking`, `ConsentBanner` (+ `ConsentBannerProps`),
|
|
289
|
+
- Root package: `createTracking`, `TrackingProvider`, `useTracking`, `GoogleAdsTracking`, `ConsentBanner` (+ `ConsentBannerProps`), consent hooks/helpers, attribution hooks, event types; `createSalesClient()` (isomorphic — public key writes; secret key reads/CRUD, `summary`, `customers.*`, `business.config`) + `toMinor`/`fromMinor`/`formatMoney`/`formatDateInTz`; phone (`parsePhone`/`toE164`/`formatPhone`/`phoneField`, `usePhoneField`, `PhoneField`)
|
|
290
|
+
- `@aranova/tracking-next/sales`: **React-free** server-safe SDK — `createSalesClient`, money/date helpers, and all sale/customer/config types. Use this in Server Components, route handlers, and Node servers; the root entry re-exports the same symbols for back-compat.
|
|
283
291
|
- `@aranova/tracking-next/middleware`: `createTrackingMiddleware()`
|
|
284
292
|
- `@aranova/tracking-next/server`: `getTrackingParamsServer()`
|
|
293
|
+
- `@aranova/tracking-next/phone`: isomorphic phone utils (no React)
|
|
285
294
|
- Codegen: [`@aranova/tracking-cli`](https://www.npmjs.com/package/@aranova/tracking-cli) — `gen` typed service unions (devDependency)
|
|
286
|
-
|