update docs

This commit is contained in:
Carl-Gerhard Lindesvärd
2024-08-06 23:04:08 +02:00
committed by Carl-Gerhard Lindesvärd
parent 2226cb463d
commit 03cee826ff
23 changed files with 684 additions and 570 deletions

1
.prettierignore Normal file
View File

@@ -0,0 +1 @@
*.mdx

View File

@@ -64,7 +64,7 @@ const Instructions = ({ framework, client }: Props) => {
<div className="flex flex-col gap-4"> <div className="flex flex-col gap-4">
<p>Copy the code below and insert it to you website</p> <p>Copy the code below and insert it to you website</p>
<Syntax <Syntax
code={`<script src="https://openpanel.dev/op.js" defer async></script> code={`<script src="https://openpanel.dev/op1.js" defer async></script>
<script> <script>
window.op = window.op || function (...args) { (window.op.q = window.op.q || []).push(args); }; window.op = window.op || function (...args) { (window.op.q = window.op.q || []).push(args); };
window.op('ctor', { window.op('ctor', {
@@ -252,13 +252,13 @@ app.listen(3000, () => {
<strong>Create a instance</strong> <strong>Create a instance</strong>
<p> <p>
Create a new instance of OpenpanelSdk. You can use this SDK in any JS Create a new instance of OpenPanel. You can use this SDK in any JS
environment. You should omit clientSecret if you use this on web! environment. You should omit clientSecret if you use this on web!
</p> </p>
<Syntax <Syntax
code={`import { OpenpanelSdk } from '@openpanel/sdk'; code={`import { OpenPanel } from '@openpanel/sdk';
const op = new OpenpanelSdk({ const op = new OpenPanel({
clientId: '${clientId}', clientId: '${clientId}',
// mostly for backend and apps that can't rely on CORS // mostly for backend and apps that can't rely on CORS
clientSecret: '${clientSecret}', clientSecret: '${clientSecret}',

View File

@@ -43,4 +43,4 @@
] ]
}, },
"prettier": "@openpanel/prettier-config" "prettier": "@openpanel/prettier-config"
} }

View File

@@ -0,0 +1,8 @@
##### Common options
- `apiUrl` - The url of the openpanel API or your self-hosted instance
- `clientId` - The client id of your application
- `clientSecret` - The client secret of your application (**only required for server-side events**)
- `filter` - A function that will be called before sending an event. If it returns false, the event will not be sent
- `disabled` - If true, the library will not send any events
- `waitForProfile` - If true, the library will wait for the profile to be set before sending events

View File

@@ -1,5 +1,6 @@
- `url` - The url of the openpanel API or your self-hosted instance ##### Web options
- `clientId` - The client id of your application
- `trackScreenViews` - If true, the library will automatically track screen views - `trackScreenViews` - If true, the library will automatically track screen views
- `trackOutgoingLinks` - If true, the library will automatically track outgoing links - `trackOutgoingLinks` - If true, the library will automatically track outgoing links
- `trackAttributes` - If true, the library will automatically track attributes - `trackAttributes` - If true, the library will automatically track attributes

View File

@@ -12,7 +12,7 @@ export default function App({ Component, pageProps }: AppProps) {
</Head> </Head>
<Component {...pageProps} /> <Component {...pageProps} />
<Script <Script
src="https://openpanel.dev/op.js" src="https://openpanel.dev/op1.js"
async async
defer defer
strategy="afterInteractive" strategy="afterInteractive"

View File

@@ -4,21 +4,22 @@
"type": "separator", "type": "separator",
"title": "Frameworks" "title": "Frameworks"
}, },
"script": "Script", "script": "Web (Script Tag)",
"web": "Web (Module)",
"nextjs": "Next.js", "nextjs": "Next.js",
"react": "React", "react": "React",
"react-native": "React-Native", "react-native": "React-Native",
"remix": "Remix", "remix": "Remix",
"vue": "Vue", "vue": "Vue",
"astro": "Astro", "astro": "Astro",
"node": "Node (backend)", "node": "Node",
"express": "Express (backend)", "express": "Express (backend)",
"-- Others": { "-- Others": {
"type": "separator", "type": "separator",
"title": "Others" "title": "Others"
}, },
"javascript": "Javascript SDK", "javascript": "JavaScript",
"web": "Web SDK",
"api": "API", "api": "API",
"export": "Export" "export": "Export",
} "migration": "Migrations"
}

View File

@@ -1,32 +1,144 @@
import Link from 'next/link'; # OpenPanel REST API with cURL
import { Steps, Tabs } from 'nextra/components';
# API This guide demonstrates how to interact with the OpenPanel API using cURL. These examples provide a low-level understanding of the API endpoints and can be useful for testing or for integrations where a full SDK isn't available.
We'll add more SDKs in the future but you can always use our REST API to send events. ## Good to know
- If you want to track **geo location** you'll need to pass the `ip` property as a header `x-client-ip`
- If you want to track **device information** you'll need to pass the `user-agent` property as a header `user-agent`
## Authentication ## Authentication
To authenticate with the API, you need to use your `clientId` and `clientSecret` (you can find your clients under Settings -> Clients, create a new client if you don't have one with a `clientSecret`). All requests to the OpenPanel API require authentication. You'll need to include your `clientId` and `clientSecret` in the headers of each request.
We expect you to send `openpanel-client-id` and `openpanel-client-secret` headers with your requests.
### Example
This request will create an event with the name `my_event` and the property `foo` set to `bar` and the timestamp set to `2024-03-28T08:42:54.319Z`.
```bash ```bash
curl 'https://api.openpanel.dev/event' \ -H "openpanel-client-id: YOUR_CLIENT_ID" \
-H 'content-type: application/json' \ -H "openpanel-client-secret: YOUR_CLIENT_SECRET"
-H 'openpanel-client-id: CLIENT_ID' \
-H 'openpanel-client-secret: CLIENT_SECRET' \
--data-raw '{"name":"my_event","properties":{"foo":"bar"},"timestamp":"2024-03-28T08:42:54.319Z"}'
``` ```
### Payload ## Usage
The payload should be a JSON object with the following fields: ### Base URL
- `name` (string): The name of the event. All API requests should be made to:
- `properties` (object): The properties of the event.
- `timestamp` (string): The timestamp of the event in ISO 8601 format. ```
https://api.openpanel.dev
```
### Tracking Events
To track an event:
```bash
curl -X POST https://api.openpanel.dev/track \
-H "Content-Type: application/json" \
-H "openpanel-client-id: YOUR_CLIENT_ID" \
-H "openpanel-client-secret: YOUR_CLIENT_SECRET" \
-d '{
"type": "track",
"payload": {
"name": "my_event",
"properties": {
"foo": "bar"
}
}
}'
```
### Identifying Users
To identify a user:
```bash
curl -X POST https://api.openpanel.dev/track \
-H "Content-Type: application/json" \
-H "openpanel-client-id: YOUR_CLIENT_ID" \
-H "openpanel-client-secret: YOUR_CLIENT_SECRET" \
-d '{
"type": "identify",
"payload": {
"profileId": "123",
"firstName": "Joe",
"lastName": "Doe",
"email": "joe@doe.com",
"properties": {
"tier": "premium"
}
}
}'
```
### Creating Aliases
To create an alias for a user:
```bash
curl -X POST https://api.openpanel.dev/track \
-H "Content-Type: application/json" \
-H "openpanel-client-id: YOUR_CLIENT_ID" \
-H "openpanel-client-secret: YOUR_CLIENT_SECRET" \
-d '{
"type": "alias",
"payload": {
"profileId": "1",
"alias": "a1"
}
}'
```
### Incrementing Properties
To increment a numeric property:
```bash
curl -X POST https://api.openpanel.dev/track \
-H "Content-Type: application/json" \
-H "openpanel-client-id: YOUR_CLIENT_ID" \
-H "openpanel-client-secret: YOUR_CLIENT_SECRET" \
-d '{
"type": "increment",
"payload": {
"profileId": "1",
"property": "visits",
"value": 1
}
}'
```
### Decrementing Properties
To decrement a numeric property:
```bash
curl -X POST https://api.openpanel.dev/track \
-H "Content-Type: application/json" \
-H "openpanel-client-id: YOUR_CLIENT_ID" \
-H "openpanel-client-secret: YOUR_CLIENT_SECRET" \
-d '{
"type": "decrement",
"payload": {
"profileId": "1",
"property": "visits",
"value": 1
}
}'
```
### Error Handling
The API uses standard HTTP response codes to indicate the success or failure of requests. In case of an error, the response body will contain more information about the error.
Example error response:
```json
{
"error": "Invalid client credentials",
"status": 401
}
```
### Rate Limiting
The API implements rate limiting to prevent abuse. If you exceed the rate limit, you'll receive a 429 (Too Many Requests) response. The response will include headers indicating your rate limit status.
Best Practices
1. Always use HTTPS to ensure secure communication.
2. Store your clientId and clientSecret securely and never expose them in client-side code.
3. Implement proper error handling in your applications.
4. Respect rate limits and implement exponential backoff for retries.

View File

@@ -3,7 +3,7 @@ import { Callout, Steps, Tabs } from 'nextra/components';
import { DeviceIdWarning } from 'src/components/device-id-warning'; import { DeviceIdWarning } from 'src/components/device-id-warning';
import { PersonalDataWarning } from 'src/components/personal-data-warning'; import { PersonalDataWarning } from 'src/components/personal-data-warning';
import SdkConfig from 'src/components/sdk-config.mdx'; import CommonSdkConfig from 'src/components/common-sdk-config.mdx';
# Express # Express
@@ -43,7 +43,7 @@ app.use(
app.get('/sign-up', (req, res) => { app.get('/sign-up', (req, res) => {
// track sign up events // track sign up events
req.op.event('sign-up', { req.op.track('sign-up', {
email: req.body.email, email: req.body.email,
}); });
res.send('Hello World'); res.send('Hello World');
@@ -54,10 +54,12 @@ app.listen(3000, () => {
}); });
``` ```
### Config ### Options
<CommonSdkConfig />
#### Express options
- `clientId` - Your OpenPanel client ID.
- `clientSecret` - Your OpenPanel client secret.
- `trackRequest` - A function that returns `true` if the request should be tracked. - `trackRequest` - A function that returns `true` if the request should be tracked.
- `getProfileId` - A function that returns the profile ID of the user making the request. - `getProfileId` - A function that returns the profile ID of the user making the request.
@@ -66,12 +68,12 @@ app.listen(3000, () => {
If `req.op` is not typed you can extend the `Request` interface. If `req.op` is not typed you can extend the `Request` interface.
```ts ```ts
import { OpenpanelSdk } from '@openpanel/express'; import { OpenPanel } from '@openpanel/express';
declare global { declare global {
namespace Express { namespace Express {
export interface Request { export interface Request {
op: OpenpanelSdk; op: OpenPanel;
} }
} }
} }

View File

@@ -1,9 +1,47 @@
import { Card, Cards } from 'nextra/components'; import { Callout, Card, Cards } from 'nextra/components';
import { BrandLogo } from 'src/components/brand-logo'; import { BrandLogo } from 'src/components/brand-logo';
# Get started # Documentation
Create an account on [Openpanel](https://openpanel.dev) and setup your first project (takes 2 minutes). You will get a `clientId` that you will use to initialize Openpanel in your app. The OpenPanel SDKs provide a set of core methods that allow you to track events, identify users, and more. Here's an overview of the key methods available in the SDKs.
<Callout>
While all OpenPanel SDKs share a common set of core methods, some may have
syntax variations or additional methods specific to their environment. This
documentation provides an overview of the base methods and available SDKs.
</Callout>
## Core Methods
### setGlobalProperties
Sets global properties that will be included with every subsequent event.
### track
Tracks a custom event with the given name and optional properties.
### identify
Associates the current user with a unique identifier and optional traits.
### alias
Creates an alias for a user identifier.
### increment
Increments a numeric property for a user.
### decrement
Decrements a numeric property for a user.
### clear
Clears the current user identifier and ends the session.
## Official SDKs
<Cards> <Cards>
<Card <Card
@@ -79,3 +117,7 @@ Create an account on [Openpanel](https://openpanel.dev) and setup your first pro
{' '} {' '}
</Card> </Card>
</Cards> </Cards>
## Unofficial SDKs
While not officially supported, the following community-contributed SDKs are available.

View File

@@ -1,138 +1,141 @@
import Link from 'next/link'; import { Callout, Tabs, Steps } from 'nextra/components';
import { Callout, Steps, Tabs } from 'nextra/components';
import { DeviceIdWarning } from 'src/components/device-id-warning';
import { PersonalDataWarning } from 'src/components/personal-data-warning'; import { PersonalDataWarning } from 'src/components/personal-data-warning';
import CommonSdkConfig from 'src/components/common-sdk-config.mdx';
import SdkConfig from 'src/components/sdk-config.mdx'; import WebSdkConfig from 'src/components/web-sdk-config.mdx';
# Javascript SDK # Javascript SDK
This is the base SDK for Openpanel. All other SDKs/frameworks are built on top of this one. The OpenPanel Web SDK allows you to track user behavior on your website using a simple script tag. This guide provides instructions for installing and using the Web SDK in your project.
## Installation ## Installation
<Steps> <Steps>
### Install dependencies ### Step 1: Install
```bash ```bash
pnpm install @openpanel/sdk npm install @openpanel/sdk
``` ```
### Initialize ### Step 2: Initialize
```tsx ```js filename="op.ts"
import { OpenpanelSdk } from '@openpanel/sdk'; import { OpenPanel } from '@openpanel/sdk';
const op = new OpenpanelSdk({ const op = new OpenPanel({
clientId: '{YOUR_CLIENT_ID}', clientId: 'YOUR_CLIENT_ID',
// mostly for backend and apps that can't rely on CORS trackScreenViews: true,
clientSecret: '{YOUR_CLIENT_SECRET}', trackOutgoingLinks: true,
trackAttributes: true,
}); });
``` ```
#### Config #### Options
- `url` - The url of the openpanel API or your self-hosted instance <CommonSdkConfig />
- `clientId` - The client id of your application
- `clientSecret` - The client secret of your application (mostly for backend and apps that can't rely on CORS)
### Ready! ### Step 3: Usage
You're now ready to use the library. ```js filename="main.ts"
import { op } from './op.js';
```typescript op.track('my_event', { foo: 'bar' });
// Sends an event with payload foo: bar
op.event('my_event', { foo: 'bar' });
// Identify with profile id
op.setProfileId('123');
// or with additional data
op.setProfile({
profileId: '123',
firstName: 'John',
lastName: 'Doe',
email: 'john.doe@openpanel.dev',
});
// Increment a property
op.increment('app_opened'); // increment by 1
op.increment('app_opened', 5); // increment by 5
// Decrement a property
op.decrement('app_opened'); // decrement by 1
op.decrement('app_opened', 5); // decrement by 5
``` ```
</Steps> </Steps>
## Usage ## Usage
### Track event ### Tracking Events
```typescript You can track events with two different methods: by calling the `op.track( directly or by adding `data-track` attributes to your HTML elements.
op.event('my_event', { foo: 'bar' });
```ts filename="index.ts"
import { op } from './op.ts';
op.track('my_event', { foo: 'bar' });
``` ```
### Identify ### Identifying Users
#### Set Profile Id To identify a user, call the `op.identify( method with a unique identifier.
Keep track of your users by identifying them with a unique id. This is a good features if you have things behind a login and want to track user behavior. ```js filename="index.js"
import { op } from './op.ts';
<PersonalDataWarning /> op.identify({
profileId: '123', // Required
```typescript firstName: 'Joe',
const profileId = '123'; lastName: 'Doe',
op.setProfileId(profileId); email: 'joe@doe.com',
``` properties: {
tier: 'premium',
#### Additional data },
This method does the same as `setProfileId` but also allows you to update the profile with additional data.
<PersonalDataWarning />
```typescript
const profileId = '123';
op.setProfile({
profileId,
// firstName?: string;
// lastName?: string;
// email?: string;
// avatar?: string;
// properties?: Record<string, unknown>;
}); });
``` ```
#### Increment property ### Setting Global Properties
Increment a property on the profile. To set properties that will be sent with every event:
```typescript ```js filename="index.js"
// Increment by 1 import { op } from './op.ts'
op.increment('app_opened');
// Increment by 5 op.setGlobalProperties({
op.increment('app_opened', 5); app_version: '1.0.2',
environment: 'production',
});
``` ```
#### Decrement property ### Creating Aliases
Decrement a property on the profile. To create an alias for a user:
```typescript ```js filename="index.js"
// Increment by 1 import { op } from './op.ts'
op.decrement('app_opened');
// Increment by 5 op.alias({
op.decrement('app_opened', 5); alias: 'a1',
profileId: '1'
});
``` ```
#### Clear / Logout ### Incrementing Properties
Clear the profile id and all the data. To increment a numeric property on a user profile.
```typescript - `value` is the amount to increment the property by. If not provided, the property will be incremented by 1.
op.clear();
```js filename="index.js"
import { op } from './op.ts'
op.increment({
profileId: '1',
property: 'visits',
value: 1 // optional
});
``` ```
### Decrementing Properties
To decrement a numeric property on a user profile.
- `value` is the amount to decrement the property by. If not provided, the property will be decremented by 1.
```js filename="index.js"
import { op } from './op.ts'
op.decrement({
profileId: '1',
property: 'visits',
value: 1 // optional
});
```
### Clearing User Data
To clear the current user's data:
```js filename="index.js"
import { op } from './op.ts'
op.clear()
```

View File

@@ -0,0 +1,3 @@
{
"beta-v1": "Beta to v1"
}

View File

@@ -0,0 +1,38 @@
# Migrate from `beta` to `v1`
We are happy to announce the release of `v1` of the Openpanel SDK. This release includes a lot of improvements and changes to the SDK. This guide will help you migrate from the `beta` version to the `v1` version.
## General
The `Openpanel` class is now called `OpenPanel`!
## Options
- Renamed: `api` to `apiUrl`
- Added: `disabled`
- Added: `filter`
## Methods
- Renamed: `event` method is now called `track`
- Renamed: `setProfile` and `setProfileId` is now called `identify` (and combined)
- Changed: `increment('app_opened', 5)` is now `increment({ name: 'app_opened', value: 5, profileId: '123' })`. So profile ID is now required.
- Changed: `decrement('app_opened', 5)` is now `decrement({ name: 'app_opened', value: 5, profileId: '123' })`. So profile ID is now required.
- Improved: `screenView` method has 2 arguments now. This change is more aligned with `@openpanel/react-native`.
```ts
screenView(properties?: TrackProperties): void;
screenView(path: string, properties?: TrackProperties): void;
// Example
op.screenView('/home', { title: 'Home' }); // path will be "/home"
op.screenView({ title: 'Home' }); // path will be what ever window.location.pathname is
```
## Script tag
- New: `https://openpanel.dev/op1.js` should be used instead of `op.js` (note the filename)
## @openpanel/nextjs
- Renamed: `OpenpanelProvider` to `OpenPanelComponent`
- Removed: All exported methods (trackEvent etc). Use the `useOpenPanel` hook instead since these are client tracking only
- Moved: `createNextRouteHandler` is moved to `@openpanel/nextjs/server`

View File

@@ -3,7 +3,8 @@ import { Callout, Steps, Tabs } from 'nextra/components';
import { DeviceIdWarning } from 'src/components/device-id-warning'; import { DeviceIdWarning } from 'src/components/device-id-warning';
import { PersonalDataWarning } from 'src/components/personal-data-warning'; import { PersonalDataWarning } from 'src/components/personal-data-warning';
import SdkConfig from 'src/components/sdk-config.mdx'; import CommonSdkConfig from 'src/components/common-sdk-config.mdx';
import WebSdkConfig from 'src/components/web-sdk-config.mdx';
# Next.js # Next.js
@@ -22,16 +23,15 @@ pnpm install @openpanel/nextjs
### Initialize ### Initialize
Add `OpenpanelProvider` to your root layout component. Add `OpenpanelComponent` to your root layout component.
```tsx ```tsx
import { OpenpanelProvider } from '@openpanel/nextjs'; import { OpenpanelComponent } from '@openpanel/nextjs';
export default RootLayout({ children }) { export default RootLayout({ children }) {
return ( return (
<> <>
<OpenpanelProvider <OpenpanelComponent
url="https://api.openpanel.dev"
clientId="your-client-id" clientId="your-client-id"
trackScreenViews={true} trackScreenViews={true}
// trackAttributes={true} // trackAttributes={true}
@@ -45,176 +45,197 @@ export default RootLayout({ children }) {
} }
``` ```
#### Config #### Options
<SdkConfig /> <CommonSdkConfig />
<WebSdkConfig />
### Ready! ##### NextJS options
You're now ready to use the library. - `profileId` - If you have a user id, you can pass it here to identify the user
- `cdnUrl` - The url to the OpenPanel SDK (default: `https://openpanel.dev/op1.js`)
- `filter` - This is a function that will be called before tracking an event. If it returns false the event will not be tracked. [Read more](#filter)
```typescript ##### `filter`
import {
decrement,
increment,
setProfile,
setProfileId,
trackEvent,
} from '@openpanel/nextjs';
// Sends an event with payload foo: bar This options needs to be a stringified function and cannot access any variables outside of the function.
trackEvent('my_event', { foo: 'bar' });
// Identify with profile id ```tsx
setProfileId('123'); <OpenpanelComponent
clientId="your-client-id"
filter={`
function filter(event) {
return event.name !== 'my_event';
}
`}
/>
```
// or with additional data To take advantage of typescript you can do the following. _Note `toString`_
setProfile({ ```tsx /.toString();/
profileId: '123', import { type OpenPanelOptions } from '@openpanel/nextjs';
firstName: 'John',
lastName: 'Doe',
email: 'john.doe@openpanel.dev',
});
// Increment a property const opFilter = ((event: TrackHandlerPayload) => {
increment('app_opened'); // increment by 1 return event.type === 'track' && event.payload.name === 'my_event';
increment('app_opened', 5); // increment by 5 }).toString();
// Decrement a property <OpenpanelComponent
decrement('app_opened'); // decrement by 1 clientId="your-client-id"
decrement('app_opened', 5); // decrement by 5 filter={opFilter}
/>
``` ```
</Steps> </Steps>
## Usage ## Usage
### Track event ### Client components
<Tabs items={['JS', 'window', 'HTML']}> For client components you can just use the `useOpenPanel` hook.
<Tabs.Tab>
```typescript
import { trackEvent } from '@openpanel/nextjs';
trackEvent('my_event', { foo: 'bar' });
```
</Tabs.Tab>
<Tabs.Tab>
```javascript
window.op('event', 'my_event', { foo: 'bar' });
```
</Tabs.Tab>
<Tabs.Tab>
For this to work you need to enable `trackAttributes` in the config.
```html
<button data-event="my_event" data-foo="bar">Track event</button>
```
</Tabs.Tab>
</Tabs>
### Identify ```tsx
import { useOpenPanel } from '@openpanel/nextjs';
#### Set Profile Id function YourComponent() {
const op = useOpenPanel();
Keep track of your users by identifying them with a unique id. This is a good features if you have things behind a login and want to track user behavior. return <button onClick={() => op.track('my_event', { foo: 'bar' })}>Trigger event</button>
<PersonalDataWarning />
```typescript
import { setProfileId } from '@openpanel/nextjs';
const profileId = '123';
setProfileId(profileId);
```
Or if you want to identify the user with a server component.
```typescript
import { SetProfileId } from '@openpanel/nextjs';
export function Layout({ children }) {
return (
<>
<SetProfileId value={'123'} />
{children}
</>
)
} }
``` ```
#### Additional data ### Server components
This method does the same as `setProfileId` but also allows you to update the profile with additional data. Since you can't use hooks in server components, you need to create an instance of the SDK. This is exported from `@openpanel/nextjs`.
<PersonalDataWarning /> <Callout>Remember, your client secret is exposed here so do not use this on client side.</Callout>
```typescript ```tsx filename="utils/op.ts"
import { setProfile } from '@openpanel/nextjs'; import { Openpanel } from '@openpanel/nextjs';
const profileId = '123'; export const op = new Openpanel({
setProfile({ clientId: 'your-client-id',
profileId, clientSecret: 'your-client-secret',
// firstName?: string; });
// lastName?: string;
// email?: string; // Now you can use `op` to track events
// avatar?: string; op.track('my_event', { foo: 'bar' });
// properties?: Record<string, unknown>; ```
Refer to the [Javascript SDK](/docs/javascript#usage) for usage instructions.
### Tracking Events
You can track events with two different methods: by calling the `op.track( directly or by adding `data-track` attributes to your HTML elements.
```ts filename="index.ts"
useOpenPanel().track('my_event', { foo: 'bar' });
```
### Identifying Users
To identify a user, call the `op.identify( method with a unique identifier.
```js filename="index.js"
useOpenPanel().identify({
profileId: '123', // Required
firstName: 'Joe',
lastName: 'Doe',
email: 'joe@doe.com',
properties: {
tier: 'premium',
},
}); });
``` ```
Or if you want to identify the user with a server component. #### For server components
```typescript For server components you can use the `IdentifyComponent` component which is exported from `@openpanel/nextjs`.
import { SetProfile } from '@openpanel/nextjs';
> This component is great if you have the user data available on the server side.
```tsx filename="app/nested/layout.tsx"
import { IdentifyComponent } from '@openpanel/nextjs';
export default function Layout({ children }) {
const user = await getCurrentUser()
export function Layout({ children }) {
return ( return (
<> <>
<SetProfile profileId={'12'} firstName={'Joe'} lastName={'Doe'} email={'joe.doe@openpanel.dev'} avatar={'https://image.com'} properties={{...}} /> <IdentifyComponent
profileId={user.id}
firstName={user.firstName}
lastName={user.lastName}
email={user.email}
properties={{
tier: 'premium',
}}
/>
{children} {children}
</> </>
) )
} }
``` ```
#### Increment property
Increment a property on the profile. ### Setting Global Properties
```typescript To set properties that will be sent with every event:
import { increment } from '@openpanel/nextjs';
// Increment by 1 ```js filename="index.js"
increment('app_opened'); useOpenPanel().setGlobalProperties({
app_version: '1.0.2',
// Increment by 5 environment: 'production',
increment('app_opened', 5); });
``` ```
#### Decrement property ### Creating Aliases
Decrement a property on the profile. To create an alias for a user:
```typescript ```js filename="index.js"
import { decrement } from '@openpanel/nextjs'; useOpenPanel().alias({
alias: 'a1',
// Increment by 1 profileId: '1'
decrement('app_opened'); });
// Increment by 5
decrement('app_opened', 5);
``` ```
#### Clear / Logout ### Incrementing Properties
Clear the profile id and all the data. To increment a numeric property on a user profile.
```typescript - `value` is the amount to increment the property by. If not provided, the property will be incremented by 1.
import { clear } from '@openpanel/nextjs';
clear(); ```js filename="index.js"
useOpenPanel().increment({
profileId: '1',
property: 'visits',
value: 1 // optional
});
``` ```
## Track server events ### Decrementing Properties
To decrement a numeric property on a user profile.
- `value` is the amount to decrement the property by. If not provided, the property will be decremented by 1.
```js filename="index.js"
useOpenPanel().decrement({
profileId: '1',
property: 'visits',
value: 1 // optional
});
```
### Clearing User Data
To clear the current user's data:
```js filename="index.js"
useOpenPanel().clear()
```
## Server side
If you want to track server-side events, you should create an instance of our Javascript SDK. It's exported from `@openpanel/nextjs` If you want to track server-side events, you should create an instance of our Javascript SDK. It's exported from `@openpanel/nextjs`
@@ -256,25 +277,21 @@ export function GET() {
} }
``` ```
## Proxy events ### Proxy events
With `createNextRouteHandler` you can proxy your events through your server, this will ensure all events are tracked since there is a lot of adblockers that block requests to third party domains. With `createNextRouteHandler` you can proxy your events through your server, this will ensure all events are tracked since there is a lot of adblockers that block requests to third party domains.
```typescript ```typescript filename="/app/api/op/route.ts"
// file: /app/api/op/[...op]/route.ts import { createNextRouteHandler } from '@openpanel/nextjs/server';
import { createNextRouteHandler } from '@openpanel/nextjs';
export const { POST } = createNextRouteHandler({ export const POST = createNextRouteHandler();
clientId: '{YOUR_CLIENT_ID}',
clientSecret: '{YOUR_CLIENT_SECRET}',
});
``` ```
Remember to change the `url` in the `OpenpanelProvider` to your own server. Remember to change the `apiUrl` in the `OpenpanelComponent` to your own server.
```tsx ```tsx {2}
<OpenpanelProvider <OpenpanelComponent
url="/api/op" // <--- apiUrl="/api/op"
clientId="your-client-id" clientId="your-client-id"
trackScreenViews={true} trackScreenViews={true}
/> />

View File

@@ -3,7 +3,7 @@ import { Callout, Steps, Tabs } from 'nextra/components';
import { DeviceIdWarning } from 'src/components/device-id-warning'; import { DeviceIdWarning } from 'src/components/device-id-warning';
import { PersonalDataWarning } from 'src/components/personal-data-warning'; import { PersonalDataWarning } from 'src/components/personal-data-warning';
import SdkConfig from 'src/components/sdk-config.mdx'; import CommonSdkConfig from 'src/components/common-sdk-config.mdx';
# React-Native # React-Native
@@ -15,8 +15,8 @@ import SdkConfig from 'src/components/sdk-config.mdx';
We're dependent on `expo-application` for `buildNumber`, `versionNumber` (and `referrer` on android) and `expo-constants` to get the `user-agent`. We're dependent on `expo-application` for `buildNumber`, `versionNumber` (and `referrer` on android) and `expo-constants` to get the `user-agent`.
```bash ```bash
pnpm install @openpanel/react-native npm install @openpanel/react-native
npx expo install --pnpm expo-application expo-constants npx expo install expo-application expo-constants
``` ```
### Initialize ### Initialize
@@ -30,40 +30,9 @@ const op = new Openpanel({
}); });
``` ```
#### Config #### Options
- `url` - The url of the openpanel API or your self-hosted instance
- `clientId` - The client id of your application
- `clientSecret` - The client secret of your application
### Ready!
You're now ready to use the library.
```typescript
// Sends an event with payload foo: bar
op.event('my_event', { foo: 'bar' });
// Identify with profile id
op.setProfileId('123');
// or with additional data
op.setProfile({
profileId: '123',
firstName: 'John',
lastName: 'Doe',
email: 'john.doe@openpanel.dev',
});
// Increment a property
op.increment('app_opened'); // increment by 1
op.increment('app_opened', 5); // increment by 5
// Decrement a property
op.decrement('app_opened'); // decrement by 1
op.decrement('app_opened', 5); // decrement by 5
```
<CommonSdkConfig />
</Steps> </Steps>
## Usage ## Usage
@@ -71,7 +40,7 @@ op.decrement('app_opened', 5); // decrement by 5
### Track event ### Track event
```typescript ```typescript
op.event('my_event', { foo: 'bar' }); op.track('my_event', { foo: 'bar' });
``` ```
### Navigation / Screen views ### Navigation / Screen views
@@ -139,65 +108,4 @@ op.event('my_event', { foo: 'bar' });
</Tabs.Tab> </Tabs.Tab>
</Tabs> </Tabs>
### Identify For more information on how to use the SDK, check out the [Javascript SDK](/docs/javascript#usage).
#### Set Profile Id
Keep track of your users by identifying them with a unique id. This is a good features if you have things behind a login and want to track user behavior.
<PersonalDataWarning />
```typescript
const profileId = '123';
op.setProfileId(profileId);
```
#### Additional data
This method does the same as `setProfileId` but also allows you to update the profile with additional data.
<PersonalDataWarning />
```typescript
const profileId = '123';
op.setProfile({
profileId,
// firstName?: string;
// lastName?: string;
// email?: string;
// avatar?: string;
// properties?: Record<string, unknown>;
});
```
#### Increment property
Increment a property on the profile.
```typescript
// Increment by 1
op.increment('app_opened');
// Increment by 5
op.increment('app_opened', 5);
```
#### Decrement property
Decrement a property on the profile.
```typescript
// Increment by 1
op.decrement('app_opened');
// Increment by 5
op.decrement('app_opened', 5);
```
#### Clear / Logout
Clear the profile id and all the data.
```typescript
op.clear();
```

View File

@@ -1,129 +1,183 @@
import { Callout, Tabs } from 'nextra/components'; import { Callout, Tabs, Steps } from 'nextra/components';
import { PersonalDataWarning } from 'src/components/personal-data-warning'; import { PersonalDataWarning } from 'src/components/personal-data-warning';
import CommonSdkConfig from 'src/components/common-sdk-config.mdx';
import WebSdkConfig from 'src/components/web-sdk-config.mdx';
import SdkConfig from 'src/components/sdk-config.mdx'; # Web (Script Tag)
# Script tag The OpenPanel Web SDK allows you to track user behavior on your website using a simple script tag. This guide provides instructions for installing and using the Web SDK in your project.
## Installation
Just insert this snippet and replace `YOUR_CLIENT_ID` with your client id. Just insert this snippet and replace `YOUR_CLIENT_ID` with your client id.
```html ```html filename="index.html" /clientId: 'YOUR_CLIENT_ID'/
<script src="https://openpanel.dev/op.js" defer async></script>
<script> <script>
window.op = window.op = window.op||function(...args){(window.op.q=window.op.q||[]).push(args);};
window.op || window.op('init', {
function (...args) {
(window.op.q = window.op.q || []).push(args);
};
window.op('ctor', {
clientId: 'YOUR_CLIENT_ID', clientId: 'YOUR_CLIENT_ID',
trackScreenViews: true, trackScreenViews: true,
trackOutgoingLinks: true, trackOutgoingLinks: true,
trackAttributes: true, trackAttributes: true,
}); });
</script> </script>
<script src="https://openpanel.dev/op1.js" defer async></script>
``` ```
### Config #### Options
<SdkConfig /> <CommonSdkConfig />
<WebSdkConfig />
## Usage ## Usage
You can let the library track screen views, outgoing links and attributes tracking by setting the `trackScreenViews`, `trackOutgoingLinks` and `trackAttributes` options to `true`. ### Tracking Events
### Track event You can track events with two different methods: by calling the `window.op('track')` directly or by adding `data-track` attributes to your HTML elements.
<Tabs items={['JS', 'HTML']}> ```html filename="index.html"
<Tabs.Tab> <button onclick="window.op('track', 'my_event', { foo: 'bar' })">
```javascript Track event
window.op('event', 'my_event', { foo: 'bar' }); </button>
```
</Tabs.Tab>
<Tabs.Tab>
For this to work you need to enable `trackAttributes` in the config.
```html
<button data-event="my_event" data-foo="bar">Track event</button>
```
</Tabs.Tab>
</Tabs>
### Identify
#### Set Profile Id
Keep track of your users by identifying them with a unique id. This is a good features if you have things behind a login and want to track user behavior.
<PersonalDataWarning />
```javascript
const profileId = '123';
window.op('setProfileId', profileId);
``` ```
#### Additional data ```html filename="index.html"
<button data-track="my_event" data-foo="bar">Track event</button>
```
This method does the same as `setProfileId` but also allows you to update the profile with additional data. ### Identifying Users
<PersonalDataWarning /> To identify a user, call the `window.op('identify')` method with a unique identifier.
```javascript ```js filename="main.js"
const profileId = '123'; window.op('identify', {
window.op('setProfile', { profileId: '123', // Required
profileId, firstName: 'Joe',
// firstName?: string; lastName: 'Doe',
// lastName?: string; email: 'joe@doe.com',
// email?: string; properties: {
// avatar?: string; tier: 'premium',
// properties?: Record<string, unknown>; },
}); });
``` ```
#### Increment property ### Setting Global Properties
Increment a property on the profile. To set properties that will be sent with every event:
```javascript ```js filename="main.js"
// Increment by 1 window.op('setGlobalProperties', {
window.op('increment', 'app_opened'); app_version: '1.0.2',
environment: 'production',
// Increment by 5 });
window.op('increment', 'app_opened', 5);
``` ```
#### Decrement property ### Creating Aliases
Decrement a property on the profile. To create an alias for a user:
```javascript ```js filename="main.js"
// Increment by 1 window.op('alias', {
window.op('decrement', 'app_opened'); alias: 'a1',
profileId: '1'
// Increment by 5 });
window.op('decrement', 'app_opened', 5);
``` ```
#### Clear / Logout ### Incrementing Properties
Clear the profile id and all the data. To increment a numeric property on a user profile.
```typescript - `value` is the amount to increment the property by. If not provided, the property will be incremented by 1.
```js filename="main.js"
window.op('increment', {
profileId: '1',
property: 'visits',
value: 1 // optional
});
```
### Decrementing Properties
To decrement a numeric property on a user profile.
- `value` is the amount to decrement the property by. If not provided, the property will be decremented by 1.
```js filename="main.js"
window.op('decrement', {
profileId: '1',
property: 'visits',
value: 1 // optional
});
```
### Clearing User Data
To clear the current user's data:
```js filename="main.js"
window.op('clear'); window.op('clear');
``` ```
## Advanced Usage
### Using the Web SDK with NPM
<Steps>
#### Step 1: Install the SDK
```bash
npm install @openpanel/web
```
#### Step 2: Initialize the SDK
```js filename="op.js"
import { OpenPanel } from '@openpanel/web';
const op = new OpenPanel({
clientId: 'YOUR_CLIENT_ID',
trackScreenViews: true,
trackOutgoingLinks: true,
trackAttributes: true,
});
```
#### Step 3: Use the SDK
```js filename="main.js"
import { op } from './op.js';
op.track('my_event', { foo: 'bar' });
```
</Steps>
### Typescript ### Typescript
Is your IDE mad at you for not using typescript? We got you covered. Getting ts errors when using the SDK? You can add a custom type definition file to your project.
Add this and it will stop complain about `window.op` not being defined. #### Simple
```typescript Just paste this code in any of your `.d.ts` files.
```ts filename="op.d.ts"
declare global { declare global {
interface Window { interface Window {
op: { op: {
q?: [string, ...any[]]; q?: string[][];
(method: string, ...args: any[]): void; (...args: [
'init' | 'track' | 'identify' | 'setGlobalProperties' | 'alias' | 'increment' | 'decrement' | 'clear',
...any[]
]): void;
}; };
} }
} }
``` ```
#### Strict typing (from sdk)
Create a `op.d.ts`file and paste the following code:
```ts filename="op.d.ts"
/// <reference types="@openpanel/web" />
```

View File

@@ -1,137 +1,48 @@
import Link from 'next/link'; import { Callout, Tabs, Steps } from 'nextra/components';
import { Callout, Steps, Tabs } from 'nextra/components';
import { DeviceIdWarning } from 'src/components/device-id-warning';
import { PersonalDataWarning } from 'src/components/personal-data-warning'; import { PersonalDataWarning } from 'src/components/personal-data-warning';
import CommonSdkConfig from 'src/components/common-sdk-config.mdx';
import SdkConfig from 'src/components/sdk-config.mdx'; import WebSdkConfig from 'src/components/web-sdk-config.mdx';
# Web SDK # Web SDK
This is a wrapper of <Link href="/docs/javascript">Javascript SDK</Link>. It's a simple way to use the Openpanel SDK in your web application. The OpenPanel Web SDK allows you to track user behavior on your website using a simple script tag. This guide provides instructions for installing and using the Web SDK in your project.
## Installation ## Installation
<Steps> <Steps>
### Install dependencies ### Step 1: Install
```bash ```bash
pnpm install @openpanel/web npm install @openpanel/web
``` ```
### Initialize ### Step 2: Initialize
```tsx ```js filename="op.ts"
import { Openpanel } from '@openpanel/web'; import { OpenPanel } from '@openpanel/web';
const op = new Openpanel({ const op = new OpenPanel({
clientId: '{YOUR_CLIENT_ID}', clientId: 'YOUR_CLIENT_ID',
trackScreenViews: true, trackScreenViews: true,
// trackAttributes: true, trackOutgoingLinks: true,
// trackOutgoingLinks: true, trackAttributes: true,
}); });
``` ```
#### Config #### Options
<SdkConfig /> <CommonSdkConfig />
<WebSdkConfig />
### Ready! ### Step 3: Usage
You're now ready to use the library. ```js filename="main.ts"
import { op } from './op.js';
```typescript op.track('my_event', { foo: 'bar' });
// Sends an event with payload foo: bar
op.event('my_event', { foo: 'bar' });
// Identify with profile id
op.setProfileId('123');
// or with additional data
op.setProfile({
profileId: '123',
firstName: 'John',
lastName: 'Doe',
email: 'john.doe@openpanel.dev',
});
// Increment a property
op.increment('app_opened'); // increment by 1
op.increment('app_opened', 5); // increment by 5
// Decrement a property
op.decrement('app_opened'); // decrement by 1
op.decrement('app_opened', 5); // decrement by 5
``` ```
</Steps> </Steps>
## Usage ## Usage
### Track event Refer to the [Javascript SDK](/docs/javascript#usage) for usage instructions.
```typescript
op.event('my_event', { foo: 'bar' });
```
### Identify
#### Set Profile Id
Keep track of your users by identifying them with a unique id. This is a good features if you have things behind a login and want to track user behavior.
<PersonalDataWarning />
```typescript
const profileId = '123';
op.setProfileId(profileId);
```
#### Additional data
This method does the same as `setProfileId` but also allows you to update the profile with additional data.
<PersonalDataWarning />
```typescript
const profileId = '123';
op.setProfile({
profileId,
// firstName?: string;
// lastName?: string;
// email?: string;
// avatar?: string;
// properties?: Record<string, unknown>;
});
```
#### Increment property
Increment a property on the profile.
```typescript
// Increment by 1
op.increment('app_opened');
// Increment by 5
op.increment('app_opened', 5);
```
#### Decrement property
Decrement a property on the profile.
```typescript
// Increment by 1
op.decrement('app_opened');
// Increment by 5
op.decrement('app_opened', 5);
```
#### Clear / Logout
Clear the profile id and all the data.
```typescript
op.clear();
```

View File

@@ -3,6 +3,14 @@ import { useRouter } from 'next/router';
import { useConfig } from 'nextra-theme-docs'; import { useConfig } from 'nextra-theme-docs';
export default { export default {
banner: {
key: '1.0-release',
text: (
<a href="/docs/migration/beta-v1">
🎉 We have released v1. Read migration guide if needed!
</a>
),
},
logo: ( logo: (
<> <>
<Image <Image

View File

@@ -1,6 +1,4 @@
'use client'; import React from 'react';
import React, { useEffect } from 'react';
import Script from 'next/script'; import Script from 'next/script';
import type { import type {
@@ -14,7 +12,7 @@ import type {
export * from '@openpanel/web'; export * from '@openpanel/web';
const CDN_URL = 'https://openpanel.dev/op.js'; const CDN_URL = 'https://openpanel.dev/op1.js';
type OpenPanelComponentProps = Omit<OpenPanelOptions, 'filter'> & { type OpenPanelComponentProps = Omit<OpenPanelOptions, 'filter'> & {
profileId?: string; profileId?: string;

View File

@@ -67,12 +67,13 @@ export type OpenPanelOptions = {
sdkVersion?: string; sdkVersion?: string;
waitForProfile?: boolean; waitForProfile?: boolean;
filter?: (payload: TrackHandlerPayload) => boolean; filter?: (payload: TrackHandlerPayload) => boolean;
disable?: boolean;
}; };
export class OpenPanel { export class OpenPanel {
api: Api; api: Api;
profileId?: string; profileId?: string;
global?: Record<string, any>; global?: Record<string, unknown>;
queue: TrackHandlerPayload[] = []; queue: TrackHandlerPayload[] = [];
constructor(public options: OpenPanelOptions) { constructor(public options: OpenPanelOptions) {
@@ -94,6 +95,7 @@ export class OpenPanel {
}); });
} }
// placeholder for future use
init() { init() {
// empty // empty
} }
@@ -104,6 +106,10 @@ export class OpenPanel {
} }
async send(payload: TrackHandlerPayload) { async send(payload: TrackHandlerPayload) {
if (this.options.disable) {
return Promise.resolve();
}
if (this.options.filter && !this.options.filter(payload)) { if (this.options.filter && !this.options.filter(payload)) {
return Promise.resolve(); return Promise.resolve();
} }
@@ -115,7 +121,7 @@ export class OpenPanel {
return this.api.fetch('/track', payload); return this.api.fetch('/track', payload);
} }
setGlobalProperties(properties: Record<string, any>) { setGlobalProperties(properties: Record<string, unknown>) {
this.global = { this.global = {
...this.global, ...this.global,
...properties, ...properties,
@@ -179,18 +185,18 @@ export class OpenPanel {
clear() { clear() {
this.profileId = undefined; this.profileId = undefined;
// session end? // should we force a session end here?
} }
flush() { flush() {
this.queue.forEach((item) => { this.queue.forEach((item) => {
this.send({ this.send({
...item, ...item,
// Not user why ts-expect-error is needed here // Not sure why ts-expect-error is needed here
// @ts-expect-error // @ts-expect-error
payload: { payload: {
...item.payload, ...item.payload,
profileId: this.profileId, profileId: item.payload.profileId ?? this.profileId,
}, },
}); });
}); });

View File

@@ -6,7 +6,8 @@ import type {
} from '@openpanel/sdk'; } from '@openpanel/sdk';
import { OpenPanel as OpenPanelBase } from '@openpanel/sdk'; import { OpenPanel as OpenPanelBase } from '@openpanel/sdk';
export * from '@openpanel/sdk'; export type * from '@openpanel/sdk';
export { OpenPanel as OpenPanelBase } from '@openpanel/sdk';
export type OpenPanelOptions = OpenPanelBaseOptions & { export type OpenPanelOptions = OpenPanelBaseOptions & {
trackOutgoingLinks?: boolean; trackOutgoingLinks?: boolean;
@@ -129,20 +130,20 @@ export class OpenPanel extends OpenPanelBase {
const target = event.target as HTMLElement; const target = event.target as HTMLElement;
const btn = target.closest('button'); const btn = target.closest('button');
const anchor = target.closest('a'); const anchor = target.closest('a');
const element = btn?.getAttribute('data-event') const element = btn?.getAttribute('data-track')
? btn ? btn
: anchor?.getAttribute('data-event') : anchor?.getAttribute('data-track')
? anchor ? anchor
: null; : null;
if (element) { if (element) {
const properties: Record<string, unknown> = {}; const properties: Record<string, unknown> = {};
for (const attr of element.attributes) { for (const attr of element.attributes) {
if (attr.name.startsWith('data-') && attr.name !== 'data-event') { if (attr.name.startsWith('data-') && attr.name !== 'data-track') {
properties[toCamelCase(attr.name.replace(/^data-/, ''))] = properties[toCamelCase(attr.name.replace(/^data-/, ''))] =
attr.value; attr.value;
} }
} }
const name = element.getAttribute('data-event'); const name = element.getAttribute('data-track');
if (name) { if (name) {
super.track(name, properties); super.track(name, properties);
} }

View File

@@ -18,7 +18,7 @@ import { OpenPanel } from './index';
// @ts-expect-error // @ts-expect-error
fn(...args); fn(...args);
} else { } else {
console.warn(`op.js: ${t} is not a function`); console.warn(`OpenPanel: ${t} is not a function`);
} }
}; };

View File

@@ -228,7 +228,7 @@ function main() {
if (dependent === '@openpanel/web') { if (dependent === '@openpanel/web') {
execSync( execSync(
`cp ${workspacePath('packages/sdks/web/dist/src/tracker.global.js')} ${workspacePath('./apps/public/public/tracker.js')}` `cp ${workspacePath('packages/sdks/web/dist/src/tracker.global.js')} ${workspacePath('./apps/public/public/op1.js')}`
); );
} }
}); });