add documentation
This commit is contained in:
42
apps/docs/.gitignore
vendored
Normal file
42
apps/docs/.gitignore
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
/.pnp
|
||||
.pnp.js
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
|
||||
# database
|
||||
/prisma/db.sqlite
|
||||
/prisma/db.sqlite-journal
|
||||
|
||||
# next.js
|
||||
/.next/
|
||||
/out/
|
||||
next-env.d.ts
|
||||
|
||||
# production
|
||||
/build
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
*.pem
|
||||
|
||||
# debug
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
.pnpm-debug.log*
|
||||
|
||||
# local env files
|
||||
# do not commit any .env files to git, except for the .env.example file. https://create.t3.gg/en/usage/env-variables#using-environment-variables
|
||||
.env
|
||||
.env*.local
|
||||
|
||||
# vercel
|
||||
.vercel
|
||||
|
||||
# typescript
|
||||
*.tsbuildinfo
|
||||
1
apps/docs/.prettierignore
Normal file
1
apps/docs/.prettierignore
Normal file
@@ -0,0 +1 @@
|
||||
./src/pages/docs/*
|
||||
113
apps/docs/Dockerfile
Normal file
113
apps/docs/Dockerfile
Normal file
@@ -0,0 +1,113 @@
|
||||
FROM --platform=linux/amd64 node:20-slim AS base
|
||||
|
||||
ARG NEXT_PUBLIC_DASHBOARD_URL
|
||||
ENV NEXT_PUBLIC_DASHBOARD_URL=$NEXT_PUBLIC_DASHBOARD_URL
|
||||
|
||||
ARG NEXT_PUBLIC_API_URL
|
||||
ENV NEXT_PUBLIC_API_URL=$NEXT_PUBLIC_API_URL
|
||||
|
||||
ARG DATABASE_URL
|
||||
ENV DATABASE_URL=$DATABASE_URL
|
||||
|
||||
ARG CLICKHOUSE_DB
|
||||
ENV CLICKHOUSE_DB=$CLICKHOUSE_DB
|
||||
|
||||
ARG CLICKHOUSE_PASSWORD
|
||||
ENV CLICKHOUSE_PASSWORD=$CLICKHOUSE_PASSWORD
|
||||
|
||||
ARG CLICKHOUSE_URL
|
||||
ENV CLICKHOUSE_URL=$CLICKHOUSE_URL
|
||||
|
||||
ARG CLICKHOUSE_USER
|
||||
ENV CLICKHOUSE_USER=$CLICKHOUSE_USER
|
||||
|
||||
ARG REDIS_URL
|
||||
ENV REDIS_URL=$REDIS_URL
|
||||
|
||||
ARG NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY
|
||||
ENV NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=$NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY
|
||||
|
||||
ARG CLERK_SECRET_KEY
|
||||
ENV CLERK_SECRET_KEY=$CLERK_SECRET_KEY
|
||||
|
||||
ENV PNPM_HOME="/pnpm"
|
||||
|
||||
ENV PATH="$PNPM_HOME:$PATH"
|
||||
|
||||
RUN corepack enable
|
||||
|
||||
ARG NODE_VERSION=20
|
||||
|
||||
RUN apt update \
|
||||
&& apt install -y curl \
|
||||
&& curl -L https://raw.githubusercontent.com/tj/n/master/bin/n -o n \
|
||||
&& bash n $NODE_VERSION \
|
||||
&& rm n \
|
||||
&& npm install -g n
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY package.json package.json
|
||||
COPY pnpm-lock.yaml pnpm-lock.yaml
|
||||
COPY pnpm-workspace.yaml pnpm-workspace.yaml
|
||||
COPY apps/public/package.json apps/public/package.json
|
||||
COPY packages/db/package.json packages/db/package.json
|
||||
COPY packages/redis/package.json packages/redis/package.json
|
||||
COPY packages/queue/package.json packages/queue/package.json
|
||||
COPY packages/common/package.json packages/common/package.json
|
||||
COPY packages/constants/package.json packages/constants/package.json
|
||||
COPY packages/validation/package.json packages/validation/package.json
|
||||
COPY packages/sdk/package.json packages/sdk/package.json
|
||||
|
||||
# BUILD
|
||||
FROM base AS build
|
||||
|
||||
WORKDIR /app/apps/public
|
||||
RUN pnpm install --frozen-lockfile --ignore-scripts
|
||||
|
||||
WORKDIR /app
|
||||
COPY apps apps
|
||||
COPY packages packages
|
||||
COPY tooling tooling
|
||||
RUN pnpm db:codegen
|
||||
|
||||
WORKDIR /app/apps/public
|
||||
RUN pnpm run build
|
||||
|
||||
# PROD
|
||||
FROM base AS prod
|
||||
|
||||
WORKDIR /app/apps/public
|
||||
RUN pnpm install --frozen-lockfile --prod --ignore-scripts
|
||||
|
||||
# FINAL
|
||||
FROM base AS runner
|
||||
|
||||
COPY --from=build /app/package.json /app/package.json
|
||||
COPY --from=prod /app/node_modules /app/node_modules
|
||||
# Apps
|
||||
COPY --from=build /app/apps/public /app/apps/public
|
||||
# Apps node_modules
|
||||
COPY --from=prod /app/apps/public/node_modules /app/apps/public/node_modules
|
||||
# Packages
|
||||
COPY --from=build /app/packages/db /app/packages/db
|
||||
COPY --from=build /app/packages/redis /app/packages/redis
|
||||
COPY --from=build /app/packages/common /app/packages/common
|
||||
COPY --from=build /app/packages/queue /app/packages/queue
|
||||
COPY --from=build /app/packages/constants /app/packages/constants
|
||||
COPY --from=build /app/packages/validation /app/packages/validation
|
||||
COPY --from=build /app/packages/sdk /app/packages/sdk
|
||||
# Packages node_modules
|
||||
COPY --from=prod /app/packages/db/node_modules /app/packages/db/node_modules
|
||||
COPY --from=prod /app/packages/redis/node_modules /app/packages/redis/node_modules
|
||||
COPY --from=prod /app/packages/common/node_modules /app/packages/common/node_modules
|
||||
COPY --from=prod /app/packages/queue/node_modules /app/packages/queue/node_modules
|
||||
COPY --from=prod /app/packages/validation/node_modules /app/packages/validation/node_modules
|
||||
|
||||
RUN pnpm db:codegen
|
||||
|
||||
WORKDIR /app/apps/public
|
||||
|
||||
EXPOSE 3000
|
||||
|
||||
CMD ["pnpm", "start"]
|
||||
1
apps/docs/README.md
Normal file
1
apps/docs/README.md
Normal file
@@ -0,0 +1 @@
|
||||
# Docs
|
||||
33
apps/docs/next.config.mjs
Normal file
33
apps/docs/next.config.mjs
Normal file
@@ -0,0 +1,33 @@
|
||||
import nextra from 'nextra';
|
||||
|
||||
/** @type {import("next").NextConfig} */
|
||||
const config = {
|
||||
reactStrictMode: true,
|
||||
transpilePackages: ['@mixan/queue'],
|
||||
eslint: { ignoreDuringBuilds: true },
|
||||
typescript: { ignoreBuildErrors: true },
|
||||
experimental: {
|
||||
// Avoid "Critical dependency: the request of a dependency is an expression"
|
||||
serverComponentsExternalPackages: ['bullmq'],
|
||||
},
|
||||
/**
|
||||
* If you are using `appDir` then you must comment the below `i18n` config out.
|
||||
*
|
||||
* @see https://github.com/vercel/next.js/issues/41980
|
||||
*/
|
||||
i18n: {
|
||||
locales: ['en'],
|
||||
defaultLocale: 'en',
|
||||
},
|
||||
};
|
||||
|
||||
const withNextra = nextra({
|
||||
theme: 'nextra-theme-docs',
|
||||
themeConfig: './theme.config.jsx',
|
||||
flexsearch: {
|
||||
codeblocks: false,
|
||||
},
|
||||
defaultShowCopyCode: true,
|
||||
});
|
||||
|
||||
export default withNextra(config);
|
||||
46
apps/docs/package.json
Normal file
46
apps/docs/package.json
Normal file
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"name": "@mixan/docs",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "rm -rf .next && pnpm with-env next dev",
|
||||
"build": "next build",
|
||||
"start": "next start",
|
||||
"lint": "eslint .",
|
||||
"format": "prettier --write \"**/*.{tsx,mjs,ts,md,json}\"",
|
||||
"typecheck": "tsc --noEmit",
|
||||
"with-env": "dotenv -e ../../.env -c --"
|
||||
},
|
||||
"dependencies": {
|
||||
"next": "~14.0.4",
|
||||
"nextra": "^2.13.4",
|
||||
"nextra-theme-docs": "^2.13.4",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@mixan/eslint-config": "workspace:*",
|
||||
"@mixan/prettier-config": "workspace:*",
|
||||
"@mixan/tsconfig": "workspace:*",
|
||||
"@types/node": "^18.16.0",
|
||||
"@types/react": "^18.2.20",
|
||||
"@types/react-dom": "^18.2.7",
|
||||
"@typescript-eslint/eslint-plugin": "^6.6.0",
|
||||
"@typescript-eslint/parser": "^6.6.0",
|
||||
"autoprefixer": "^10.4.18",
|
||||
"eslint": "^8.48.0",
|
||||
"postcss": "^8.4.35",
|
||||
"prettier": "^3.0.3",
|
||||
"tailwindcss": "^3.4.1",
|
||||
"typescript": "^5.2.2"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"root": true,
|
||||
"extends": [
|
||||
"@mixan/eslint-config/base",
|
||||
"@mixan/eslint-config/react",
|
||||
"@mixan/eslint-config/nextjs"
|
||||
]
|
||||
},
|
||||
"prettier": "@mixan/prettier-config"
|
||||
}
|
||||
6
apps/docs/postcss.config.js
Normal file
6
apps/docs/postcss.config.js
Normal file
@@ -0,0 +1,6 @@
|
||||
module.exports = {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
}
|
||||
12
apps/docs/src/components/device-id-warning.tsx
Normal file
12
apps/docs/src/components/device-id-warning.tsx
Normal file
@@ -0,0 +1,12 @@
|
||||
import Link from 'next/link';
|
||||
import { Callout } from 'nextra/components';
|
||||
|
||||
export function DeviceIdWarning() {
|
||||
return (
|
||||
<Callout>
|
||||
Read more about{' '}
|
||||
<Link href="/docs/device-id">device id and why you might want it</Link>.
|
||||
**We recommend not to but it's up to you.**
|
||||
</Callout>
|
||||
);
|
||||
}
|
||||
10
apps/docs/src/components/personal-data-warning.tsx
Normal file
10
apps/docs/src/components/personal-data-warning.tsx
Normal file
@@ -0,0 +1,10 @@
|
||||
import { Callout } from 'nextra/components';
|
||||
|
||||
export function PersonalDataWarning() {
|
||||
return (
|
||||
<Callout emoji="⚠️">
|
||||
Keep in mind that this is considered personal data. Make sure you have the
|
||||
users consent before calling this!
|
||||
</Callout>
|
||||
);
|
||||
}
|
||||
5
apps/docs/src/components/sdk-config.mdx
Normal file
5
apps/docs/src/components/sdk-config.mdx
Normal file
@@ -0,0 +1,5 @@
|
||||
- `url` - The url of the openpanel API or your self-hosted instance
|
||||
- `clientId` - The client id of your application
|
||||
- `trackScreenViews` - If true, the library will automatically track screen views
|
||||
- `trackOutgoingLinks` - If true, the library will automatically track outgoing links
|
||||
- `trackAttributes` - If true, the library will automatically track attributes
|
||||
7
apps/docs/src/globals.css
Normal file
7
apps/docs/src/globals.css
Normal file
@@ -0,0 +1,7 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
article main a {
|
||||
@apply underline;
|
||||
}
|
||||
11
apps/docs/src/pages/_app.tsx
Normal file
11
apps/docs/src/pages/_app.tsx
Normal file
@@ -0,0 +1,11 @@
|
||||
import type { AppProps } from 'next/app';
|
||||
|
||||
import 'src/globals.css';
|
||||
|
||||
export default function App({ Component, pageProps }: AppProps) {
|
||||
return (
|
||||
<>
|
||||
<Component {...pageProps} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
10
apps/docs/src/pages/_meta.json
Normal file
10
apps/docs/src/pages/_meta.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"index": {
|
||||
"title": "Introduction",
|
||||
"type": "page"
|
||||
},
|
||||
"docs": {
|
||||
"title": "Documentation",
|
||||
"type": "page"
|
||||
}
|
||||
}
|
||||
14
apps/docs/src/pages/docs/_meta.json
Normal file
14
apps/docs/src/pages/docs/_meta.json
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"index": "Get Started",
|
||||
"-- Implementation": {
|
||||
"type": "separator",
|
||||
"title": "Implementation"
|
||||
},
|
||||
"script": "Script",
|
||||
"javascript": "Javascript SDK",
|
||||
"nextjs": "Next.js",
|
||||
"react": "React",
|
||||
"react-native": "React-Native",
|
||||
"remix": "Remix",
|
||||
"vue": "Vue"
|
||||
}
|
||||
80
apps/docs/src/pages/docs/index.mdx
Normal file
80
apps/docs/src/pages/docs/index.mdx
Normal file
@@ -0,0 +1,80 @@
|
||||
import { Card, Cards } from 'nextra/components';
|
||||
|
||||
# Get started
|
||||
|
||||
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.
|
||||
|
||||
<Cards>
|
||||
<Card
|
||||
icon={
|
||||
<img
|
||||
style={{ width: 32, height: 32, objectFit: 'contain' }}
|
||||
src="https://upload.wikimedia.org/wikipedia/commons/thumb/6/61/HTML5_logo_and_wordmark.svg/240px-HTML5_logo_and_wordmark.svg.png"
|
||||
/>
|
||||
}
|
||||
title="General / Script"
|
||||
href="/docs/general"
|
||||
>
|
||||
{' '}
|
||||
</Card>
|
||||
<Card
|
||||
icon={
|
||||
<img
|
||||
style={{ width: 32, height: 32, objectFit: 'contain' }}
|
||||
src="https://upload.wikimedia.org/wikipedia/commons/thumb/a/a7/React-icon.svg/2300px-React-icon.svg.png"
|
||||
/>
|
||||
}
|
||||
title="React"
|
||||
href="/docs/react"
|
||||
>
|
||||
{' '}
|
||||
</Card>
|
||||
<Card
|
||||
icon={
|
||||
<img
|
||||
style={{ width: 32, height: 32, objectFit: 'contain' }}
|
||||
src="https://upload.wikimedia.org/wikipedia/commons/thumb/a/a7/React-icon.svg/2300px-React-icon.svg.png"
|
||||
/>
|
||||
}
|
||||
title="React-Native"
|
||||
href="/docs/react-native"
|
||||
>
|
||||
{' '}
|
||||
</Card>
|
||||
<Card
|
||||
icon={
|
||||
<img
|
||||
style={{ width: 32, height: 32, objectFit: 'contain' }}
|
||||
src="https://static-00.iconduck.com/assets.00/nextjs-icon-512x512-y563b8iq.png"
|
||||
/>
|
||||
}
|
||||
title="Next.js"
|
||||
href="/docs/nextjs"
|
||||
>
|
||||
{' '}
|
||||
</Card>
|
||||
<Card
|
||||
icon={
|
||||
<img
|
||||
style={{ width: 32, height: 32, objectFit: 'contain' }}
|
||||
src="https://www.datocms-assets.com/205/1642515307-square-logo.svg"
|
||||
/>
|
||||
}
|
||||
title="Remix"
|
||||
href="/docs/remix"
|
||||
>
|
||||
{' '}
|
||||
</Card>
|
||||
<Card
|
||||
icon={
|
||||
<img
|
||||
style={{ width: 32, height: 32, objectFit: 'contain' }}
|
||||
src="https://upload.wikimedia.org/wikipedia/commons/thumb/9/95/Vue.js_Logo_2.svg/1024px-Vue.js_Logo_2.svg.png"
|
||||
/>
|
||||
}
|
||||
title="Vue"
|
||||
href="/docs/vue"
|
||||
>
|
||||
{' '}
|
||||
</Card>
|
||||
</Cards>
|
||||
139
apps/docs/src/pages/docs/javascript.mdx
Normal file
139
apps/docs/src/pages/docs/javascript.mdx
Normal file
@@ -0,0 +1,139 @@
|
||||
import Link from 'next/link';
|
||||
import { Callout, Steps, Tabs } from 'nextra/components';
|
||||
import { DeviceIdWarning } from 'src/components/device-id-warning';
|
||||
import { PersonalDataWarning } from 'src/components/personal-data-warning';
|
||||
|
||||
import SdkConfig from 'src/components/sdk-config.mdx';
|
||||
|
||||
# Javascript SDK
|
||||
|
||||
This is the base SDK for Openpanel. All other SDKs are built on top of this one.
|
||||
|
||||
## Installation
|
||||
|
||||
<Steps>
|
||||
### Install dependencies
|
||||
|
||||
```bash
|
||||
pnpm install @openpanel/sdk
|
||||
```
|
||||
|
||||
### Initialize
|
||||
|
||||
```tsx
|
||||
import { Openpanel } from '@openpanel/sdk';
|
||||
|
||||
const op = new Openpanel({
|
||||
url: 'https://api.openpanel.dev',
|
||||
clientId: '{YOUR_CLIENT_ID}',
|
||||
// mostly for backend and apps that can't rely on CORS
|
||||
clientSecret: '{YOUR_CLIENT_SECRET}',
|
||||
});
|
||||
```
|
||||
|
||||
#### Config
|
||||
|
||||
- `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 (mostly for backend and apps that can't rely on CORS)
|
||||
|
||||
### 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
|
||||
```
|
||||
|
||||
</Steps>
|
||||
|
||||
## Usage
|
||||
|
||||
### Track event
|
||||
|
||||
```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
|
||||
import { setProfileId } from '@openpanel/nextjs';
|
||||
|
||||
const profileId = '123';
|
||||
setProfileId(profileId);
|
||||
```
|
||||
|
||||
#### Additional data
|
||||
|
||||
This method does the same as `setProfileId` but also allows you to update the profile with additional data.
|
||||
|
||||
<PersonalDataWarning />
|
||||
|
||||
```typescript
|
||||
import { setProfile } from '@openpanel/nextjs';
|
||||
|
||||
const profileId = '123';
|
||||
setProfile({
|
||||
profileId,
|
||||
// firstName?: string;
|
||||
// lastName?: string;
|
||||
// email?: string;
|
||||
// avatar?: string;
|
||||
// properties?: Record<string, unknown>;
|
||||
});
|
||||
```
|
||||
|
||||
#### Increment property
|
||||
|
||||
Increment a property on the profile.
|
||||
|
||||
```typescript
|
||||
import { increment } from '@openpanel/nextjs';
|
||||
|
||||
// Increment by 1
|
||||
increment('app_opened');
|
||||
|
||||
// Increment by 5
|
||||
increment('app_opened', 5);
|
||||
```
|
||||
|
||||
#### Decrement property
|
||||
|
||||
Decrement a property on the profile.
|
||||
|
||||
```typescript
|
||||
import { decrement } from '@openpanel/nextjs';
|
||||
|
||||
// Increment by 1
|
||||
decrement('app_opened');
|
||||
|
||||
// Increment by 5
|
||||
decrement('app_opened', 5);
|
||||
```
|
||||
172
apps/docs/src/pages/docs/nextjs.mdx
Normal file
172
apps/docs/src/pages/docs/nextjs.mdx
Normal file
@@ -0,0 +1,172 @@
|
||||
import Link from 'next/link';
|
||||
import { Callout, Steps, Tabs } from 'nextra/components';
|
||||
import { DeviceIdWarning } from 'src/components/device-id-warning';
|
||||
import { PersonalDataWarning } from 'src/components/personal-data-warning';
|
||||
|
||||
import SdkConfig from 'src/components/sdk-config.mdx';
|
||||
|
||||
# Next.js
|
||||
|
||||
Keep in mind that all tracking here happens on the client! If you want to track server-side events, you should use the <Link href="/docs/node">node SDK</Link> 👀.
|
||||
|
||||
## Installation
|
||||
|
||||
<Steps>
|
||||
### Install dependencies
|
||||
|
||||
```bash
|
||||
pnpm install @openpanel/nextjs
|
||||
```
|
||||
|
||||
### Initialize
|
||||
|
||||
Add `OpenpanelProvider` to your root layout component.
|
||||
|
||||
```tsx
|
||||
import { OpenpanelProvider } from '@openpanel/nextjs';
|
||||
|
||||
export default RootLayout({ children }) {
|
||||
return (
|
||||
<>
|
||||
<OpenpanelProvider
|
||||
url="https://api.openpanel.dev"
|
||||
clientId="your-client-id"
|
||||
trackScreenViews={true}
|
||||
// trackAttributes={true}
|
||||
// trackOutgoingLinks={true}
|
||||
/>
|
||||
{children}
|
||||
</>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
#### Config
|
||||
|
||||
<SdkConfig />
|
||||
|
||||
### Ready!
|
||||
|
||||
You're now ready to use the library.
|
||||
|
||||
```typescript
|
||||
import {
|
||||
decrement,
|
||||
increment,
|
||||
setProfile,
|
||||
setProfileId,
|
||||
trackEvent,
|
||||
trackScreenView,
|
||||
} from '@openpanel/nextjs';
|
||||
|
||||
// Sends an event with payload foo: bar
|
||||
trackEvent('my_event', { foo: 'bar' });
|
||||
|
||||
// Identify with profile id
|
||||
setProfileId('123');
|
||||
|
||||
// or with additional data
|
||||
setProfile({
|
||||
profileId: '123',
|
||||
firstName: 'John',
|
||||
lastName: 'Doe',
|
||||
email: 'john.doe@openpanel.dev',
|
||||
});
|
||||
|
||||
// Increment a property
|
||||
increment('app_opened'); // increment by 1
|
||||
increment('app_opened', 5); // increment by 5
|
||||
|
||||
// Decrement a property
|
||||
decrement('app_opened'); // decrement by 1
|
||||
decrement('app_opened', 5); // decrement by 5
|
||||
```
|
||||
|
||||
</Steps>
|
||||
|
||||
## Usage
|
||||
|
||||
### Track event
|
||||
|
||||
<Tabs items={['JS', 'window', 'HTML']}>
|
||||
<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
|
||||
|
||||
#### 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
|
||||
import { setProfileId } from '@openpanel/nextjs';
|
||||
|
||||
const profileId = '123';
|
||||
setProfileId(profileId);
|
||||
```
|
||||
|
||||
#### Additional data
|
||||
|
||||
This method does the same as `setProfileId` but also allows you to update the profile with additional data.
|
||||
|
||||
<PersonalDataWarning />
|
||||
|
||||
```typescript
|
||||
import { setProfile } from '@openpanel/nextjs';
|
||||
|
||||
const profileId = '123';
|
||||
setProfile({
|
||||
profileId,
|
||||
// firstName?: string;
|
||||
// lastName?: string;
|
||||
// email?: string;
|
||||
// avatar?: string;
|
||||
// properties?: Record<string, unknown>;
|
||||
});
|
||||
```
|
||||
|
||||
#### Increment property
|
||||
|
||||
Increment a property on the profile.
|
||||
|
||||
```typescript
|
||||
import { increment } from '@openpanel/nextjs';
|
||||
|
||||
// Increment by 1
|
||||
increment('app_opened');
|
||||
|
||||
// Increment by 5
|
||||
increment('app_opened', 5);
|
||||
```
|
||||
|
||||
#### Decrement property
|
||||
|
||||
Decrement a property on the profile.
|
||||
|
||||
```typescript
|
||||
import { decrement } from '@openpanel/nextjs';
|
||||
|
||||
// Increment by 1
|
||||
decrement('app_opened');
|
||||
|
||||
// Increment by 5
|
||||
decrement('app_opened', 5);
|
||||
```
|
||||
196
apps/docs/src/pages/docs/react-native.mdx
Normal file
196
apps/docs/src/pages/docs/react-native.mdx
Normal file
@@ -0,0 +1,196 @@
|
||||
import Link from 'next/link';
|
||||
import { Callout, Steps, Tabs } from 'nextra/components';
|
||||
import { DeviceIdWarning } from 'src/components/device-id-warning';
|
||||
import { PersonalDataWarning } from 'src/components/personal-data-warning';
|
||||
|
||||
import SdkConfig from 'src/components/sdk-config.mdx';
|
||||
|
||||
# React-Native
|
||||
|
||||
## Installation
|
||||
|
||||
<Steps>
|
||||
### Install dependencies
|
||||
|
||||
We're dependent on `expo-application` for `buildNumber`, `versionNumber` (and `referrer` on android) and `expo-constants` to get the `user-agent`.
|
||||
|
||||
```bash
|
||||
pnpm install @openpanel/react-native
|
||||
expo install --pnpm expo-application expo-constants
|
||||
```
|
||||
|
||||
### Initialize
|
||||
|
||||
On native we use a clientSecret to authenticate the app.
|
||||
|
||||
```typescript
|
||||
const op = new Openpanel({
|
||||
url: 'https://api.openpanel.dev',
|
||||
clientId: '{YOUR_CLIENT_ID}',
|
||||
clientSecret: '{YOUR_CLIENT_SECRET}',
|
||||
});
|
||||
```
|
||||
|
||||
#### Config
|
||||
|
||||
- `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
|
||||
```
|
||||
|
||||
</Steps>
|
||||
|
||||
## Usage
|
||||
|
||||
### Track event
|
||||
|
||||
```typescript
|
||||
op.event('my_event', { foo: 'bar' });
|
||||
```
|
||||
|
||||
### Navigation / Screen views
|
||||
|
||||
<Tabs items={['expo-router', 'react-navigation (simple)']}>
|
||||
<Tabs.Tab>
|
||||
```typescript
|
||||
import { usePathname, useSegments } from 'expo-router';
|
||||
|
||||
function RootLayout() {
|
||||
// ...
|
||||
const pathname = usePathname()
|
||||
// Segments is optional but can be nice to have if you
|
||||
// want to group routes together
|
||||
// pathname = /posts/123
|
||||
// segements = ['posts', '[id]']
|
||||
const segments = useSegments()
|
||||
|
||||
useEffect(() => {
|
||||
// Simple
|
||||
op.screenView(pathname)
|
||||
|
||||
// With extra data
|
||||
op.screenView(pathname, {
|
||||
// segments is optional but nice to have
|
||||
segments: segments.join('/'),
|
||||
// other optional data you want to send with the screen view
|
||||
})
|
||||
}, [pathname,segments])
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
</Tabs.Tab>
|
||||
<Tabs.Tab>
|
||||
```tsx
|
||||
import { createNavigationContainerRef } from '@react-navigation/native'
|
||||
import { Openpanel } from '@openpanel/react-native'
|
||||
|
||||
const op = new Openpanel({ /* ... */ })
|
||||
const navigationRef = createNavigationContainerRef()
|
||||
|
||||
export function NavigationRoot() {
|
||||
const handleNavigationStateChange = () => {
|
||||
const current = navigationRef.getCurrentRoute()
|
||||
if (current) {
|
||||
op.screenView(current.name, {
|
||||
params: current.params,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<NavigationContainer
|
||||
ref={navigationRef}
|
||||
onReady={handleNavigationStateChange}
|
||||
onStateChange={handleNavigationStateChange}
|
||||
>
|
||||
<Stack.Navigator />
|
||||
</NavigationContainer>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
</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 />
|
||||
|
||||
```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);
|
||||
```
|
||||
5
apps/docs/src/pages/docs/react.mdx
Normal file
5
apps/docs/src/pages/docs/react.mdx
Normal file
@@ -0,0 +1,5 @@
|
||||
import Link from 'next/link';
|
||||
|
||||
# React
|
||||
|
||||
Use <Link href="/docs/general">script tag</Link> or <Link href="/docs/general">Javascript SDK</Link> for now. We'll add a dedicated react sdk soon.
|
||||
5
apps/docs/src/pages/docs/remix.mdx
Normal file
5
apps/docs/src/pages/docs/remix.mdx
Normal file
@@ -0,0 +1,5 @@
|
||||
import Link from 'next/link';
|
||||
|
||||
# Remix
|
||||
|
||||
Use <Link href="/docs/general">script tag</Link> or <Link href="/docs/general">Javascript SDK</Link> for now. We'll add a dedicated react sdk soon.
|
||||
122
apps/docs/src/pages/docs/script.mdx
Normal file
122
apps/docs/src/pages/docs/script.mdx
Normal file
@@ -0,0 +1,122 @@
|
||||
import { Callout, Tabs } from 'nextra/components';
|
||||
import { PersonalDataWarning } from 'src/components/personal-data-warning';
|
||||
|
||||
import SdkConfig from 'src/components/sdk-config.mdx';
|
||||
|
||||
# Script tag
|
||||
|
||||
Just insert this snippet and replace `{YOUR_CLIENT_ID}` with your client id.
|
||||
|
||||
```html
|
||||
<script src="https://openpanel.dev/op.js"></script>
|
||||
<script>
|
||||
window.op =
|
||||
window.op ||
|
||||
function (...args) {
|
||||
(window.op.q = window.op.q || []).push(args);
|
||||
};
|
||||
window.op('ctor', {
|
||||
url: 'https://api.openpanel.dev',
|
||||
clientId: '{YOUR_CLIENT_ID}',
|
||||
trackScreenViews: true,
|
||||
// trackOutgoingLinks: true,
|
||||
// trackAttributes: true,
|
||||
});
|
||||
</script>
|
||||
```
|
||||
|
||||
### Config
|
||||
|
||||
<SdkConfig />
|
||||
|
||||
## Usage
|
||||
|
||||
You can let the library track screen views, outgoing links and attributes tracking by setting the `trackScreenViews`, `trackOutgoingLinks` and `trackAttributes` options to `true`.
|
||||
|
||||
### Track event
|
||||
|
||||
<Tabs items={['JS', 'HTML']}>
|
||||
<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
|
||||
|
||||
#### 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
|
||||
|
||||
This method does the same as `setProfileId` but also allows you to update the profile with additional data.
|
||||
|
||||
<PersonalDataWarning />
|
||||
|
||||
```javascript
|
||||
const profileId = '123';
|
||||
window.op('setProfile', {
|
||||
profileId,
|
||||
// firstName?: string;
|
||||
// lastName?: string;
|
||||
// email?: string;
|
||||
// avatar?: string;
|
||||
// properties?: Record<string, unknown>;
|
||||
});
|
||||
```
|
||||
|
||||
#### Increment property
|
||||
|
||||
Increment a property on the profile.
|
||||
|
||||
```javascript
|
||||
// Increment by 1
|
||||
window.op('increment', 'app_opened');
|
||||
|
||||
// Increment by 5
|
||||
window.op('increment', 'app_opened', 5);
|
||||
```
|
||||
|
||||
#### Decrement property
|
||||
|
||||
Decrement a property on the profile.
|
||||
|
||||
```javascript
|
||||
// Increment by 1
|
||||
window.op('decrement', 'app_opened');
|
||||
|
||||
// Increment by 5
|
||||
window.op('decrement', 'app_opened', 5);
|
||||
```
|
||||
|
||||
### Typescript
|
||||
|
||||
Is your IDE mad at you for not using typescript? We got you covered.
|
||||
|
||||
Add this and it will stop complain about `window.op` not being defined.
|
||||
|
||||
```typescript
|
||||
declare global {
|
||||
interface Window {
|
||||
op: {
|
||||
q?: [string, ...any[]];
|
||||
(method: string, ...args: any[]): void;
|
||||
};
|
||||
}
|
||||
}
|
||||
```
|
||||
5
apps/docs/src/pages/docs/vue.mdx
Normal file
5
apps/docs/src/pages/docs/vue.mdx
Normal file
@@ -0,0 +1,5 @@
|
||||
import Link from 'next/link';
|
||||
|
||||
# Vue
|
||||
|
||||
Use <Link href="/docs/general">script tag</Link> or <Link href="/docs/general">Javascript SDK</Link> for now. We'll add a dedicated react sdk soon.
|
||||
14
apps/docs/src/pages/index.mdx
Normal file
14
apps/docs/src/pages/index.mdx
Normal file
@@ -0,0 +1,14 @@
|
||||
# Introduction
|
||||
|
||||
Openpanel is an open-source alternative to Mixpanel. Combining the power of Mixpanel with the ease of Plausible, Openpanel is a privacy-focused analytics tool that gives you the insights you need to make data-driven decisions.
|
||||
|
||||
## Features
|
||||
|
||||
- ✅ **Privacy-focused**: Openpanel is built with privacy in mind. We don't track any personal data and we don't use cookies.
|
||||
- ✅ **Open-source**: Openpanel is open-source and you can host it yourself.
|
||||
- ✅ **Cloud-hosted**: You can choose our cloud hosting if you don't want to host it yourself. We take care of the infrastructure and you can focus on your business.
|
||||
- ✅ **Real-time analytics**: Everything is updated in real-time, no delays to see your insights.
|
||||
- ✅ **Event-based tracking**: You can track any event you want, and you can track as many events as you want.
|
||||
- ✅ **Custom properties**: You can add custom properties to your events to track more data.
|
||||
- ✅ **Funnel analysis**: You can create funnels to see how your users are interacting with your product.
|
||||
- ✅ **Reports**: Create as many reports as you want to visualize the data you need.
|
||||
8
apps/docs/tailwind.config.js
Normal file
8
apps/docs/tailwind.config.js
Normal file
@@ -0,0 +1,8 @@
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
module.exports = {
|
||||
content: ['./src/**/*.{js,ts,jsx,tsx,mdx}'],
|
||||
theme: {
|
||||
extend: {},
|
||||
},
|
||||
plugins: [],
|
||||
};
|
||||
75
apps/docs/theme.config.jsx
Normal file
75
apps/docs/theme.config.jsx
Normal file
@@ -0,0 +1,75 @@
|
||||
import Image from 'next/image';
|
||||
import { useRouter } from 'next/router';
|
||||
import { useConfig } from 'nextra-theme-docs';
|
||||
|
||||
export default {
|
||||
logo: (
|
||||
<>
|
||||
<Image
|
||||
src="https://dashboard.openpanel.dev/logo.svg"
|
||||
alt="next-international logo"
|
||||
height="32"
|
||||
width="32"
|
||||
/>
|
||||
<strong style={{ marginLeft: '8px' }}>openpanel</strong>
|
||||
</>
|
||||
),
|
||||
head: (props) => {
|
||||
const router = useRouter();
|
||||
const config = useConfig();
|
||||
const title = config.title;
|
||||
const description =
|
||||
config.description || 'An open-source alternative to Mixpanel';
|
||||
const domain = 'https://docs.openpanel.dev';
|
||||
const canonicalUrl =
|
||||
`${domain}${router.asPath === '/' ? '' : router.asPath}`.split('?')[0];
|
||||
|
||||
return (
|
||||
<>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<meta property="description" content={description} />
|
||||
<link rel="canonical" href={canonicalUrl} />
|
||||
<meta name="twitter:card" content="summary_large_image" />
|
||||
<meta
|
||||
name="twitter:site:domain"
|
||||
content={domain.replace('https://', '')}
|
||||
/>
|
||||
<meta name="twitter:url" content={domain} />
|
||||
<meta name="og:type" content={`site`} />
|
||||
<meta name="og:url" content={canonicalUrl} />
|
||||
<meta name="og:title" content={`${title} - Openpanel Docs`} />
|
||||
<meta property="og:description" content={description} />
|
||||
<meta name="og:image" content={'https://openpanel.dev/ogimage.png'} />
|
||||
<meta name="title" content={title} />
|
||||
<meta name="description" content={description} />
|
||||
</>
|
||||
);
|
||||
},
|
||||
search: {
|
||||
placeholder: 'Search',
|
||||
},
|
||||
project: {
|
||||
link: 'https://github.com/openpanel-dev/openpanel',
|
||||
},
|
||||
docsRepositoryBase:
|
||||
'https://github.com/openpanel-dev/openpanel/blob/main/apps/docs',
|
||||
useNextSeoProps() {
|
||||
return {
|
||||
titleTemplate: '%s - Openpanel',
|
||||
};
|
||||
},
|
||||
footer: {
|
||||
text: (
|
||||
<span>
|
||||
Made with ❤️ by{' '}
|
||||
<a
|
||||
href="https://twitter.com/CarlLindesvard"
|
||||
target="_blank"
|
||||
rel="nofollow"
|
||||
>
|
||||
Carl
|
||||
</a>
|
||||
</span>
|
||||
),
|
||||
},
|
||||
};
|
||||
25
apps/docs/tsconfig.json
Normal file
25
apps/docs/tsconfig.json
Normal file
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"extends": "@mixan/tsconfig/base.json",
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@/*": [
|
||||
"./src/*"
|
||||
]
|
||||
},
|
||||
"plugins": [
|
||||
{
|
||||
"name": "next"
|
||||
}
|
||||
],
|
||||
"tsBuildInfoFile": "node_modules/.cache/tsbuildinfo.json",
|
||||
"strictNullChecks": true
|
||||
},
|
||||
"include": [
|
||||
".",
|
||||
".next/types/**/*.ts"
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules"
|
||||
]
|
||||
}
|
||||
2080
pnpm-lock.yaml
generated
2080
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user